Skip to content

Instantly share code, notes, and snippets.

@Nordwald
Last active July 11, 2020 13:11
Show Gist options
  • Save Nordwald/9d0cd13df6ac039d21df4fbdea36d28f to your computer and use it in GitHub Desktop.
Save Nordwald/9d0cd13df6ac039d21df4fbdea36d28f to your computer and use it in GitHub Desktop.
Code Injection
/*
* 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;
}
#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