Last active
July 11, 2020 13:11
-
-
Save Nordwald/9d0cd13df6ac039d21df4fbdea36d28f to your computer and use it in GitHub Desktop.
Code Injection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Gets Thread token for current thread. | |
* Returns NULL on failure. | |
*/ | |
HANDLE GetCurrentThreadToken() | |
{ | |
HANDLE hToken; | |
if (!OpenThreadToken( | |
GetCurrentThread(), | |
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
FALSE, | |
&hToken)) | |
{ | |
if (GetLastError() != ERROR_NO_TOKEN) { | |
printf("First OpenThreadToken() failed: %d\n", (int)GetLastError()); | |
return NULL; | |
} else { | |
if (!ImpersonateSelf(SecurityImpersonation)) { | |
printf("ImpersonateSelf() failed: %d\n", (int)GetLastError()); | |
return NULL; | |
} | |
if (!OpenThreadToken( | |
GetCurrentThread(), | |
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
FALSE, | |
&hToken)) | |
{ | |
printf("Second OpenThreadToken() failed: %d\n", (int)GetLastError()); | |
return NULL; | |
} | |
} | |
} | |
return hToken; | |
} | |
/* | |
* Sets a privilege for a given token tp TRUE or FALSE. | |
* Returns error status. | |
* Adapted from http://support.microsoft.com/kb/131065/en-us | |
*/ | |
BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { | |
TOKEN_PRIVILEGES tp; | |
LUID luid; | |
TOKEN_PRIVILEGES tpPrevious; | |
DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); | |
if(!LookupPrivilegeValue(NULL, Privilege, &luid)) { | |
printf("LookupPrivilegeValue() failed: %d\n", (int)GetLastError()); | |
return FALSE; | |
} | |
// Get current privilege setting | |
tp.PrivilegeCount = 1; | |
tp.Privileges[0].Luid = luid; | |
tp.Privileges[0].Attributes = 0; | |
if (!AdjustTokenPrivileges( | |
hToken, | |
FALSE, | |
&tp, | |
sizeof(TOKEN_PRIVILEGES), | |
&tpPrevious, | |
&cbPrevious)) | |
{ | |
printf("First AdjustTokenPriviliges() failed: %d\n", (int)GetLastError()); | |
return FALSE; | |
} | |
// Set privilege based on previous setting | |
tpPrevious.PrivilegeCount = 1; | |
tpPrevious.Privileges[0].Luid = luid; | |
if(bEnablePrivilege) { | |
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); | |
} else { | |
tpPrevious.Privileges[0].Attributes ^= | |
(SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes); | |
} | |
if (!AdjustTokenPrivileges( | |
hToken, | |
FALSE, | |
&tpPrevious, | |
cbPrevious, | |
NULL, | |
NULL)) | |
{ | |
printf("Second AdjustTokenPrivileges() failed: %d\n", (int)GetLastError()); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/* | |
* Temporarily gives this process debug privileges and openes | |
* target process. | |
* Returns handle to process, or NULL on failure. | |
*/ | |
HANDLE OpenTargetProcess(DWORD remoteProcessID) { | |
HANDLE hToken; | |
HANDLE hRemoteProcess; | |
// Get Debug Privilege | |
hToken = GetCurrentThreadToken(); | |
if (hToken == NULL) { | |
printf("GetCurrentThreadToken() failed!\n"); | |
return NULL; | |
} | |
if(!SetPrivilege(hToken, SE_DEBUG_NAME, TRUE)) { | |
printf("SetPrivilege() failed\n"); | |
CloseHandle(hToken); | |
return NULL; | |
} | |
// Open remote process | |
hRemoteProcess = OpenProcess( | |
PROCESS_ALL_ACCESS, | |
FALSE, | |
remoteProcessID); | |
if (hRemoteProcess == NULL) { | |
printf("OpenProcess() failed: %d\n", (int)GetLastError()); | |
CloseHandle(hToken); | |
return NULL; | |
} | |
// disable SeDebugPrivilege | |
SetPrivilege(hToken, SE_DEBUG_NAME, FALSE); | |
CloseHandle(hToken); | |
return hRemoteProcess; | |
} | |
/* | |
* Determine the ID of a process by name with the help | |
* of CreateToolhelp32Snapshot. | |
* Returns 0 if process ís not found. | |
*/ | |
DWORD GetProcessIDByName(char* name) { | |
PROCESSENTRY32 pProcessEntry = {0}; | |
HANDLE hSnapshot; | |
DWORD ID; | |
// Create process snapshot | |
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
if (hSnapshot == INVALID_HANDLE_VALUE) { | |
printf("CreateToolhelp32Snapshot() failed: %d\n", (int)GetLastError()); | |
return 0; | |
} | |
// Loop over all entries | |
pProcessEntry.dwSize = sizeof(PROCESSENTRY32); | |
if (!Process32First(hSnapshot, &pProcessEntry)) { | |
printf("Process32First() failed: %d\n", (int)GetLastError()); | |
CloseHandle(hSnapshot); | |
return 0; | |
} | |
do { | |
if (strcmp((char*)pProcessEntry.szExeFile, name) == 0) { | |
ID = pProcessEntry.th32ProcessID; | |
CloseHandle(hSnapshot); | |
return ID; | |
} | |
} while (Process32Next(hSnapshot, &pProcessEntry)); | |
// Target process couldn't be found | |
CloseHandle(hSnapshot); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <TlHelp32.h> // Process by Name | |
#include <stdio.h> // printing to stdout, bufferfiles | |
#include <string.h> // for strlen, strcat | |
#include <stdarg.h> // for PrintError | |
#include "debug.c" | |
#define JUMPSIZE 5 // Size of JMP-Instruction | |
#define FILENAME "dead" // Name for bufferfile (Injection) | |
// Define NtMapViewOfSection | |
typedef ULONG (__stdcall * func_NtMapViewOfSection) | |
(HANDLE, HANDLE, LPVOID, ULONG, SIZE_T,LARGE_INTEGER*, SIZE_T *,int,ULONG,ULONG); | |
// Define NtReadVirtualMemory als replacement for ReadProcessMemory | |
typedef ULONG (__stdcall * func_NtReadVirtualMemory) | |
(HANDLE, HANDLE, PVOID, ULONG, PVOID); | |
typedef ULONG (__stdcall * func_NtWriteVirtualMemory) | |
(HANDLE, HANDLE, PVOID, ULONG, PVOID); | |
typedef ULONG (__stdcall * func_NtProtectVirtualMemory) | |
(HANDLE, PVOID*, ULONG*, ULONG, ULONG*); | |
// BackupFunction | |
// Struct to save the first 6 bytes of the | |
// hooked function | |
struct BackupFunction | |
{ | |
HANDLE proc; // Process-Handle | |
HANDLE addr; // Function-Address | |
byte* data; | |
}; | |
// PrintError | |
// (obvious) | |
// | |
void PrintError(char* error, ...) | |
{ | |
va_list buf; | |
va_start(buf,error); | |
vprintf(strcat("Error: ", error), buf); | |
exit(-1); | |
} | |
// DumpFunction | |
// Trys to dump a function to struct BackupFunction. | |
// End of Function is guessed by NOPs | |
struct BackupFunction DumpFunction(HANDLE process, HANDLE function) | |
{ | |
byte* buf; | |
SIZE_T t1; | |
struct BackupFunction bf; | |
func_NtReadVirtualMemory NtReadVirtualMemory; | |
NtReadVirtualMemory = (func_NtReadVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtReadVirtualMemory"); | |
// Reads function to buffer | |
t1 = sizeof(byte)*JUMPSIZE; | |
buf = malloc(t1); | |
if (buf == NULL) | |
PrintError("Could not allocate %d bytes", JUMPSIZE); | |
NtReadVirtualMemory(process, function, buf, t1, NULL); | |
// PrintError("Could not read hook data (Error %d)", GetLastError()); | |
// Set up the struct | |
bf.data = malloc(JUMPSIZE); | |
bf.addr = function; | |
bf.proc = process; | |
memcpy(bf.data, buf, JUMPSIZE); | |
free(buf); | |
return bf; | |
} | |
// WriteJmp | |
// Generates the machine code for a jump and | |
// writes it to memory | |
void WriteJmp(HANDLE proc, HANDLE to, HANDLE from, BOOL syslevel) | |
{ | |
byte* buf; | |
HANDLE jmp; | |
// Get relative jmp address | |
jmp = (HANDLE)((int)to - (int)from - JUMPSIZE); | |
buf = malloc(sizeof(byte)*JUMPSIZE); | |
// 0xE9 is the opcode for jmp | |
buf[0] = (byte)0xE9; | |
memcpy(&buf[1], &jmp, 4); | |
if(syslevel) | |
{ | |
func_NtWriteVirtualMemory NtWriteVirtualMemory; | |
func_NtProtectVirtualMemory NtProtectVirtualMemory; | |
ULONG status, size = JUMPSIZE; | |
printf("syslevel\n"); | |
NtWriteVirtualMemory = (func_NtWriteVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtWriteVirtualMemory"); | |
NtProtectVirtualMemory = (func_NtProtectVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtProtectVirtualMemory"); | |
//if(!NtProtectVirtualMemory(proc, &proc, &size, PAGE_EXECUTE_READWRITE, NULL)) | |
// PrintError("Could not unprotect memory (system) (Error %d)", GetLastError()); | |
if(!NtWriteVirtualMemory(proc, from, buf, JUMPSIZE, NULL)) | |
PrintError("Could not write hook (system) (Error %d)", GetLastError()); | |
} | |
else | |
{ | |
if(!WriteProcessMemory(proc, from, buf, JUMPSIZE, NULL)) | |
printf("Could not write hook (Error %d)", GetLastError()); | |
} | |
free(buf); | |
} | |
// CreateInjection | |
// Creates a file with machine code to remove the hook and | |
// load a libary (string) while returning the size s | |
FILE* CreateInjection(struct BackupFunction bf, char* string, int* s) | |
{ | |
int size; | |
byte* buf; | |
HANDLE buf2; | |
FILE* f; | |
byte hexData[75] = { | |
0x6A, 0x00, // PUSH 0 | |
0x6A, 0x05, // PUSH 5 | |
0xEB, 0x36, // JMP (short) | |
0xEB, 0x2B, // JMP (short) | |
0x5A, // POP EDX | |
0xFF, 0x32, // PUSH [EDX] | |
0xBA, 0x90, 0x90, 0x90, 0x90, // MOV EDX, kernel32.GetCurrentProcess | |
0xFF, 0xD2, // CALL EDX | |
0x50, // PUSH EAX | |
0xBA, 0x90, 0x90, 0x90, 0x90, // MOV EDX, kernel32.WriteProcessMemory | |
0xFF, 0xD2, // CALL EDX | |
0xEB, 0x2A, // JMP (short) | |
0xBA, 0x90, 0x90, 0x90, 0x90, // MOV EDX, kernel32.LoadLibaryA | |
0xFF, 0xD2, // CALL EDX | |
0xEB, 0x05, // JMP (short) | |
0x59, // POP ECX | |
0x8B, 0x11, // MOV EDX [ECX] | |
0xFF, 0xE2, // JMP EDX | |
0xE8, 0xF6, 0xFF, 0xFF, 0xFF, // CALL | |
0x90, 0x90, 0x90, 0x90, // return address | |
0xE8, 0xD0, 0xFF, 0xFF, 0xFF, // CALL | |
0x61, 0x64, 0x64, 0x72, // hooked function address | |
0xE8, 0xC5, 0xFF, 0xFF, 0xFF, // CALL | |
0x90, 0x90, 0x90, 0x90, 0x90, // first 5 bytes of function | |
0xE8, 0xD1, 0xFF, 0xFF, 0xFF }; // CALL | |
size = sizeof(hexData)+strlen(string)+1; | |
buf = malloc(size); | |
if(buf == NULL) | |
PrintError("Could not allocate %d bytes to create injection", size); | |
// Dump machine code and bibary-path together | |
memcpy(&buf[0], hexData, sizeof(hexData)); | |
memcpy(&buf[sizeof(hexData)], string, strlen(string)+1); | |
// Fill in bytes to remove the hook | |
memcpy(&buf[47], &bf.addr, 4); | |
memcpy(&buf[56], &bf.addr, 4); | |
memcpy(&buf[65], bf.data, JUMPSIZE); | |
// Fill in addresses for libary c | |
buf2 = GetProcAddress(GetModuleHandle("kernel32"), "GetCurrentProcess"); | |
memcpy(&buf[12], &buf2, 4); | |
buf2 = GetProcAddress(GetModuleHandle("kernel32"), "WriteProcessMemory"); | |
memcpy(&buf[20], &buf2, 4); | |
buf2 = GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"); | |
memcpy(&buf[29], &buf2, 4); | |
*s = size; | |
// Dump to file | |
f = fopen(FILENAME, "w"); | |
if(f == NULL) | |
PrintError("Could not create file to inject (%s)", FILENAME); | |
fwrite(buf, 1, size, f); | |
fclose(f); | |
free(buf); | |
return f; | |
} | |
// MapFile | |
// Maps a file to memory as executeable | |
// with NtMapViewOfSection | |
HANDLE MapFile(HANDLE process, int size) | |
{ | |
char path[MAX_PATH]; | |
HANDLE injection, f, base = NULL; | |
ULONG status; | |
LARGE_INTEGER SectionOffset; | |
SIZE_T ViewSize = 0; | |
func_NtMapViewOfSection NtMapViewOfSection; | |
// Get full path to injection | |
GetCurrentDirectory(MAX_PATH,path); | |
sprintf(path, "%s\\%s", path, FILENAME); | |
// Initializing | |
SectionOffset.LowPart = 0; | |
SectionOffset.HighPart = 0; | |
// Get file handle | |
f = CreateFile (path, GENERIC_READ | GENERIC_EXECUTE, | |
0,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); | |
if(f == INVALID_HANDLE_VALUE) | |
PrintError("Could not open file to inject (%s (Error %d)", path, | |
GetLastError()); | |
// Map the file | |
injection = CreateFileMapping (f, NULL, PAGE_EXECUTE_READ, 0, 0, NULL); | |
if(injection == NULL) | |
PrintError("Could not open file to inject (%s (Error %d))", path, | |
GetLastError()); | |
// "Share" the file | |
NtMapViewOfSection = (func_NtMapViewOfSection)GetProcAddress | |
(GetModuleHandle("ntdll.dll"), "NtMapViewOfSection"); | |
status = NtMapViewOfSection(injection, process, &base,0, | |
size,&SectionOffset,&ViewSize,2,0,PAGE_EXECUTE_READ); | |
if(status!=0) | |
PrintError("Could inject file (%s (Error %d))", path, GetLastError()); | |
CloseHandle(f); | |
return base; | |
} | |
// PrintHelp | |
// prints manpage-like help message | |
// on invalid arguments or -h (-help) | |
void PrintHelp() | |
{ | |
printf( "NAME\n\trith - remote injection trough hooking\n\n" | |
"SYNOPSIS\n\trith [OPTION] ... [PROCESS] [FUNCDLL] [FUNC] [INJECTION]\n\n" | |
"DESCRIPTION\n\tHook FUNC in FUNCDLL in PROCESS to INJECTION.\n\n" | |
"\t-s, -silent\n\t\tRedirect output to file PROCESS.log\n\n" | |
"\t-q, -quiet\n\t\tNo feedback.\n\n" | |
"\t-h, -help\n\t\tDisplay this manpage.\n\n" | |
"\t-l, -local\n\t\tSearch the libary local.\n\n" | |
"ARGUMENTS\n\t[PROCESS]\n\t\tMay be a process id or process name.\n\n" | |
"\t[FUNCDLL]\n\t\tLibary containing the function to be hooked.\n" | |
"\t\tfor instance kernel32.dll or ntdll.dll\n\n" | |
"\t[FUNC]\n\t\tFunction to be hooked.\n" | |
"\t\tfor instance Sleep or NtMapSectionOfView.\n\n" | |
"\t[INJECTION]\n\t\tFull path to the libary to be injected.\n"); | |
exit(0); | |
} | |
int main(int argc, char** argv) | |
{ | |
int i, pid; | |
HANDLE process, function, injected; | |
struct BackupFunction oldfunc; | |
BOOL syslevel = FALSE; | |
char* hookdll; | |
char* hookfunc; | |
char* injection; | |
if (argc == 1) | |
return 0; | |
if (argc < 5) | |
PrintHelp(); | |
hookdll = argv[argc-3]; | |
hookfunc = argv[argc-2]; | |
injection = argv[argc-1]; | |
// Parse passed arguments | |
for(i = 1; i < argc; i++) | |
{ | |
if(!strcmp(argv[i], "-silent") || !strcmp(argv[i], "-s")) | |
freopen (strcat(argv[argc-4], ".log"),"w",stdout); | |
else if(!strcmp(argv[i], "-quiet") || !strcmp(argv[i], "-q")) | |
fclose (stdout); | |
else if(!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h")) | |
PrintHelp(); | |
else if(!strcmp(argv[i], "-local") || !strcmp(argv[i], "-l")) | |
{ | |
injection = malloc(MAX_PATH*sizeof(char)); | |
GetCurrentDirectory(MAX_PATH,injection); | |
sprintf(injection, "%s\\%s", injection, argv[argc-1]); | |
} | |
else if(!strcmp(argv[i], "-x")) | |
syslevel = TRUE; | |
} | |
pid = atoi(argv[argc-4]); | |
if(!pid) | |
{ | |
pid = GetProcessIDByName(argv[argc-4]); | |
if(!pid) | |
PrintError("Process %s not found", argv[argc-4]); | |
} | |
// Start | |
process = OpenTargetProcess(pid); | |
printf("Attached to Process %d\n", pid); | |
if(process==NULL) | |
PrintError("Could open process %d", pid); | |
function = GetProcAddress(GetModuleHandle(hookdll), hookfunc); | |
if(hookfunc==0) | |
PrintError("Could not find %s\n", hookfunc); | |
printf("Found %s at %p\n", hookfunc, function); | |
printf("Backing up %s: ", hookfunc); | |
oldfunc = DumpFunction(process, function); | |
for(i = 0; i < JUMPSIZE; i++) | |
printf("%.2x ", oldfunc.data[i]); | |
CreateInjection(oldfunc, injection, &i); | |
injected = MapFile(process, i); | |
printf("\nMapped injection to %p\n", injected); | |
WriteJmp(process, injected, function, syslevel); | |
printf("Wrote call to %p\n", function); | |
// Clean up | |
CloseHandle(process); | |
free(oldfunc.data); | |
remove(FILENAME); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment