Created
September 3, 2017 07:08
-
-
Save anonymous/cc1f6d087029d2f6a16f24bbe77a9956 to your computer and use it in GitHub Desktop.
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 "stdafx.h" | |
#include <string.h> | |
#include <keystone\keystone.h> | |
#include <windows.h> | |
#include <time.h> | |
#pragma comment(lib, "keystone.lib") | |
void DCI64(); | |
// separate assembly instructions by ; or \n | |
char CODE[65535*2] = { 0 }; | |
typedef struct argList { | |
BOOLEAN isString; | |
char* ptr; | |
uint64_t value; | |
BOOLEAN isRegister; | |
} argList; | |
char tmp[256] = { 0 }; | |
uint64_t ep_addr = 0; | |
void setEntrypoint(uint64_t addr) { | |
ep_addr = addr; | |
} | |
__declspec(noinline) char* mystrcat(char* dst, char* src) { | |
char* result; | |
result = strcat(dst, src); | |
if (strlen(src) > 7 && src[strlen(src)-2] == ';') { | |
DCI64(); | |
} | |
return result; | |
} | |
void resetShellCode() { | |
memset(CODE, 0, 65535); | |
} | |
void RS() { //Random byte array for data entropy | |
int myrand = rand(); | |
int len = (rand() % 48) + 16; | |
sprintf(tmp, "\tJMP skip%d%d;\n", myrand, len); | |
strcat(CODE, tmp); | |
sprintf(tmp, "random%d%d: db ", myrand,len); | |
strcat(CODE, tmp); | |
for (int i = 0; i < len; i++) { | |
unsigned char c = rand()%253; | |
if (i == len - 1) | |
sprintf(tmp, "0x%02x", c); | |
else | |
sprintf(tmp, "0x%02x,", c); | |
strcat(CODE, tmp); | |
} | |
sprintf(tmp, ";\nskip%d%d:\n\tmov rax, rax;\n", myrand, len); | |
strcat(CODE, tmp); | |
} | |
void DCI64() { | |
int myRegister = (rand() % 15) + 1; | |
int myCase = (rand() % 10) + 1; | |
char Registers[16][4] = { | |
{ "RAX" }, | |
{ "RBX" }, | |
{ "RCX" }, | |
{ "RDX" }, | |
{ "RBP" }, | |
{ "RSP" }, | |
{ "RSI" }, | |
{ "RDI" }, | |
{ "R8" }, | |
{ "R9" }, | |
{ "R10" }, | |
{ "R11" }, | |
{ "R12" }, | |
{ "R13" }, | |
{ "R14" }, | |
{ "R15" } | |
}; | |
switch (myCase) { | |
case 1: | |
sprintf(tmp, "\tMOV %s, %s;\n", Registers[myRegister], Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
case 2: | |
sprintf(tmp, "\tCMP %s, %s;\n", Registers[myRegister], Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
case 3: | |
sprintf(tmp, "\tAND %s, %s;\n", Registers[myRegister], Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
case 5: | |
sprintf(tmp, "\tXOR %s, 0;\n", Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
case 4: | |
sprintf(tmp, "\tADD %s, 0;\n", Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
//or and xor | |
case 7: | |
sprintf(tmp, "\tSUB %s, 0;\n", Registers[myRegister]); | |
strcat(CODE, tmp); | |
break; | |
case 8: | |
sprintf(tmp, "\tNOP;\n"); | |
strcat(CODE, tmp); | |
break; | |
case 9: | |
RS(); | |
break; | |
default: | |
RS(); | |
break; | |
} | |
if (rand() % 10 > 8) | |
DCI64(); | |
} | |
#define OBFUSCATE 1 | |
#ifdef OBFUSCATE | |
#define strcat mystrcat | |
#endif | |
void AC(char* line, char* tag) { | |
if (tag != 0) { | |
strcat(CODE, tag); | |
strcat(CODE, ":\n"); | |
} | |
sprintf(tmp, "\t%s;\n", line); | |
strcat(CODE, tmp); | |
} | |
void PV(uint64_t value) { //push parameter to stack, 32/64bits (if5+ param) | |
sprintf(tmp, "\tPUSH 0x%I32x;\n", value); | |
strcat(CODE, tmp); | |
} | |
void PV(uint64_t value, int nArg) { | |
sprintf(tmp, "\tmov rax, 0x%02x;\n\tmov [rsp+0x%02x], rax;\n", value, (nArg - 1) * 0x08); | |
strcat(CODE, tmp); | |
} | |
void PV64(uint64_t value, char* REG) { //Push parameter, 64bit | |
sprintf(tmp, "\tmov %s, 0x%I64x;\n", REG, value); | |
strcat(CODE, tmp); | |
} | |
void PD(char* symbol) { //Push parameter, 64bit | |
sprintf(tmp, "\tpush %s;\n", symbol); | |
strcat(CODE, tmp); | |
} | |
void PD64(char* symbol, char* REG) { //Push parameter, 64bit | |
sprintf(tmp, "\tcall get%srip;\n\t add rax, 0x6\n\tmov %s, rax;\n", symbol, REG); | |
strcat(CODE, tmp); | |
} | |
void MOV(char* REG, char* REG2) { //Push parameter, 64bit | |
sprintf(tmp, "\tmov %s, %s;\n", REG, REG2); | |
strcat(CODE, tmp); | |
} | |
void PS(char* value, char* REG) { //Push String | |
//We could use a dynamically generated db 's', 't', 'r','i', 'n','g' within to add even more entropy to the shell randomness | |
auto ptr = (uint64_t)VirtualAlloc(0, strlen(value)+1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); //We never free this cause i'm lazy and this was hacked together on my birthday at like midnight | |
memcpy((void*)ptr, value, strlen(value) + 1); | |
if (REG != 0) | |
PV64(ptr, REG); //64bit | |
else | |
PV(ptr); //32bit | |
} | |
void SA(char* OP, int value) { //Stack Align | |
sprintf(tmp, "\t%s rsp, 0x%I32x;\n", OP, value); | |
strcat(CODE, tmp); | |
} | |
void FC(char* fname, uint64_t ptr) { //Function call 32bit | |
sprintf(tmp, "\tCALL 0x%I32x;\n", ptr); | |
strcat(CODE, tmp); | |
} | |
void FC64(char* fname, uint64_t ptr) { //Function call 64bit | |
if (ptr <= 0xFFFFFFFF) | |
return FC(fname, ptr); | |
int myRandID = rand(); | |
sprintf(tmp, "\tJMP skip%d;\n", myRandID); | |
strcat(CODE, tmp); | |
sprintf(tmp, "\n%s: dq 0x%I64x ; Variable declaration\n\n", fname, ptr); | |
strcat(CODE, tmp); | |
sprintf(tmp, "skip%d:\n", myRandID); | |
strcat(CODE, tmp); | |
sprintf(tmp, "\tMOV rax, [%s];\n", fname); | |
strcat(CODE, tmp); | |
AC("CALL rax", 0); | |
} | |
void WM(uint64_t ptr, unsigned char* data, size_t len) { | |
int myrand = rand(); | |
sprintf(tmp, "\tmov rdi, 0x%I64x;\n", ptr); | |
strcat(CODE, tmp); | |
#undef strcat | |
sprintf(tmp, "\tcall getrip;\n\tadd rax, 0xE;\n\tmov rsi, RAX;\n", ptr); | |
strcat(CODE, tmp); | |
sprintf(tmp, "\tJMP skip%d;\n", myrand); | |
strcat(CODE, tmp); | |
sprintf(tmp, "getrip:\n\tMOV RAX, [RSP];\n\tRET;\n"); | |
strcat(CODE, tmp); | |
sprintf(tmp, "data%d: db ",rand()); | |
strcat(CODE, tmp); | |
for (int i = 0; i < len; i++) { | |
unsigned char c = *(data + i); | |
if (i == len - 1) | |
sprintf(tmp, "0x%02x", c); | |
else | |
sprintf(tmp, "0x%02x,", c); | |
strcat(CODE, tmp); | |
} | |
#define strcat mystrcat | |
sprintf(tmp, ";\nskip%d:\n\tmov rcx, %d;\n\tREP MOVSB;\n", myrand, len); | |
strcat(CODE, tmp); | |
} | |
void WA(char* label, uint64_t ptr, int nArg, argList* args) { //WinAPI | |
SA("SUB", (nArg+1) * 0x08); | |
for (int i = nArg; i > 0; i--) { | |
argList arg = *(args + (i -1)); | |
if (arg.isString) { | |
switch (i) { | |
case 1: | |
PS(arg.ptr, "RCX"); | |
break; | |
case 2: | |
PS(arg.ptr, "RDX"); | |
break; | |
case 3: | |
PS(arg.ptr, "R8"); | |
break; | |
case 4: | |
PS(arg.ptr, "R9"); | |
break; | |
default: | |
printf("not supported yet. (need to push to stack)"); | |
} | |
} | |
else if (!arg.isRegister && arg.ptr != NULL) { | |
switch (i) { | |
case 1: | |
PD64(arg.ptr, "RCX"); | |
break; | |
case 2: | |
PD64(arg.ptr, "RDX"); | |
break; | |
case 3: | |
PD64(arg.ptr, "R8"); | |
break; | |
case 4: | |
PD64(arg.ptr, "R9"); | |
break; | |
default: | |
PD(arg.ptr); | |
break; | |
} | |
} | |
else if (arg.isRegister && arg.ptr != NULL) { | |
switch (i) { | |
case 1: | |
MOV("RCX", arg.ptr); | |
break; | |
case 2: | |
MOV( "RDX", arg.ptr); | |
break; | |
case 3: | |
MOV("R8", arg.ptr); | |
break; | |
case 4: | |
MOV("R9", arg.ptr); | |
break; | |
default: | |
PD(arg.ptr); | |
break; | |
} | |
} | |
else { | |
switch (i) { | |
case 1: | |
PV64(arg.value, "RCX"); | |
break; | |
case 2: | |
PV64(arg.value, "RDX"); | |
break; | |
case 3: | |
PV64(arg.value, "R8"); | |
break; | |
case 4: | |
PV64(arg.value, "R9"); | |
break; | |
default: | |
PV(arg.value, i); | |
break; | |
} | |
} | |
} | |
FC64(label, ptr); | |
SA("ADD", (nArg+1) * 0x08); | |
} | |
void initShellCode1() { //Creates a shellcode that simply shows a messagebox | |
resetShellCode(); | |
struct argList args[] = { | |
{ false, 0 , 0, false}, | |
{ true, "Text1", 0, false }, | |
{ true, "Text2", 0, false }, | |
{ false, 0, MB_OK, false } | |
}; | |
WA("f2", (uint64_t)MessageBoxA, sizeof(args) / sizeof(argList), args); | |
AC("RET", "end"); | |
//AC("RET", 0); | |
} | |
void Shellcodethread1() { | |
struct argList args2[] = { | |
{ false, 0 , 100, false }, | |
{ false, 0, 100, false } | |
}; | |
WA("beep", (uint64_t)Beep, sizeof(args2) / sizeof(argList), args2); | |
struct argList args3[] = { | |
{ false, 0 , 10000, false } | |
}; | |
WA("sleep", (uint64_t)Sleep, sizeof(args3) / sizeof(argList), args3); | |
struct argList args4[] = { | |
{ false, 0 , 0, false }, | |
{ true, "Text1", 0, false }, | |
{ true, "Text2", 0, false }, | |
{ false, 0, MB_OK, false } | |
}; | |
WA("msgboxA", (uint64_t)MessageBoxA, sizeof(args4) / sizeof(argList), args4); | |
} | |
void Fix64Keystone(char* tag) { | |
sprintf(tmp, "get%srip:\n\tCALL get%srip2;\n\tRET;\nget%srip2:\n\tMOV RAX, [RSP];\n\tRET;\n", tag, tag, tag); | |
#undef strcat | |
strcat(CODE, tmp); | |
#define strcat mystrcat | |
} | |
void AB(char* name, size_t len) { //Allocate len bytes and creates a tag to access it | |
int myrand = rand(); | |
sprintf(tmp, "\tJMP skip%d;\n", myrand); | |
strcat(CODE, tmp); | |
Fix64Keystone(name); | |
sprintf(tmp, "%s: db ", name); | |
strcat(CODE, tmp); | |
for (int i = 0; i < len; i++) { | |
unsigned char c = 0; | |
if (i == len - 1) | |
sprintf(tmp, "0x%02x", c); | |
else | |
sprintf(tmp, "0x%02x,", c); | |
strcat(CODE, tmp); | |
} | |
sprintf(tmp, ";\nskip%d:\n", myrand); | |
strcat(CODE, tmp); | |
} | |
void Shellcodethread2() { | |
struct argList args2[] = { | |
{ false, 0 , 100, false }, | |
{ false, 0, 100, false } | |
}; | |
WA("beep", (uint64_t)Beep, sizeof(args2) / sizeof(argList), args2); | |
AB("my256variable", MAX_PATH); //Array allocation + tag | |
AB("myTextBuffer", 512); //Array allocation + tag | |
struct argList gmfa[] = { | |
{ false, 0, 0, false }, | |
{ false, "my256variable", 0 , false }, | |
{ false, 0, MAX_PATH, false } | |
}; | |
WA("getmodulefilenamea", (uint64_t)GetModuleFileNameA, sizeof(gmfa) / sizeof(argList), gmfa); | |
//sprintf(myTextBuffer, "GetModulefilenameA result : %s and return : %d", my256variable) - Let's translate this to our shellcode | |
struct argList spra[] = { | |
{ false, "myTextBuffer", 0, false }, | |
{ true, "GetModulefilenameA : %s (len:%d)", 0, false }, | |
{ false, "my256variable", 0, false }, | |
{ false, "RAX" , 0, true } //gmfa return value | |
}; | |
WA("sprintf", (uint64_t)sprintf, sizeof(spra) / sizeof(argList), spra); | |
struct argList args3[] = { | |
{ false, 0 , 100, false } | |
}; | |
WA("sleep", (uint64_t)Sleep, sizeof(args3) / sizeof(argList), args3); | |
struct argList args4[] = { | |
{ false, 0 , 0 , false }, | |
{ false, "myTextBuffer", 0, false }, | |
{ false, "my256variable", 0 , false }, | |
{ false, 0, MB_OK, false } | |
}; | |
WA("msgboxA", (uint64_t)MessageBoxA, sizeof(args4) / sizeof(argList), args4); | |
} | |
void Shellcode2(uint64_t ptrToTrampoline) { //After a hook jump to this shellcode, will remove hook, create his own thread and resume execution of main program | |
unsigned char originalBytecode[12]; | |
memcpy(originalBytecode, (char*)ptrToTrampoline, 12); | |
resetShellCode(); | |
WM(ptrToTrampoline, originalBytecode, 12); //Restore the hooked function, we did our thread hijack. We can now execute whatever we want. - TODO: Make sure to restore context and go back to the hooked function | |
struct argList args[] = { | |
{ false, 0, 0 }, //arg1 | |
{ false, 0, 0 }, //arg2 | |
{ false, "loop", 0 }, //arg3 | |
{ false, 0, 0 }, //arg4 | |
{ false, 0, 0 }, //arg5 - pushed to stack | |
{ false, 0, 0 } // arg6 - pushed to stack - finally got that shitty win x64 logic to work | |
}; | |
WA("createthread", (uint64_t)CreateThread, sizeof(args) / sizeof(argList), args); | |
sprintf(tmp, "\tmov rax, 0x%I64x;\n\tjmp rax;\n", ptrToTrampoline); | |
strcat(CODE, tmp); | |
//Trick to get loop fct ptr dynamically because keystone is bugged with symbolic name on 64bit atm. | |
//When you call a fct, return RIP goes into [RSP], this function allows us to get the offset dynamically of getlooprip then add +x06 to it to get the thread start | |
//Thread Loop | |
Fix64Keystone("loop"); // loop ptr is referenced, keystone can't do 64bit relative addressive. Fuck it ghetto solution | |
AC("xor rax, rax", "loop"); | |
Shellcodethread2(); | |
AC("jmp loop", 0); | |
AC("RET", 0); | |
} | |
#undef strcat | |
void HookInstall(uint64_t ptrToShellCode) { | |
resetShellCode(); | |
sprintf(tmp, "\tMOV RAX, 0x%I64x;\n", ptrToShellCode); | |
strcat(CODE, tmp); | |
sprintf(tmp, "\tJMP RAX;\n"); | |
strcat(CODE, tmp); | |
} | |
__declspec(noinline) void NormalGameFunction() { //Function that we are targetting | |
int r = printf("I am a function called by the game every frame\n"); | |
printf("%d\n", r); | |
} | |
void NormalThread() { | |
Sleep(5000); | |
while (1) { | |
printf("I am a normal thread inside a game\n"); | |
NormalGameFunction(); | |
Sleep(1000/60); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
srand(44); | |
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)NormalThread, NULL, NULL, NULL); | |
//Sleep(500); //Install hook while thread is running already at 60fps without pausing it. | |
ks_engine *ks; | |
ks_err err; | |
size_t count; | |
unsigned char *encode; | |
size_t size; | |
err = ks_open(KS_ARCH_X86, KS_MODE_64, &ks); | |
ks_option(ks, KS_OPT_SYNTAX, KS_OPT_SYNTAX_NASM); //important for relative symbol name | |
if (err != KS_ERR_OK) { | |
printf("ERROR: failed on ks_open(), quit\n"); | |
return -1; | |
} | |
ep_addr = (uint64_t)VirtualAlloc(0, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // VirtualAllocEx for remote process | |
Shellcode2((uint64_t)NormalGameFunction); //initiate shellcode with NormalGameFunction as the caller | |
printf("shellcode asm code : \n%s\n", CODE); | |
if (ks_asm(ks, CODE,ep_addr, &encode, &size, &count) != KS_ERR_OK) { //Generate opcode for the shellcode | |
printf("ERROR: ks_asm() failed & count = %lu, error = %u\n", | |
count, ks_errno(ks)); | |
} | |
else { | |
size_t i; | |
DWORD oldProtect; | |
memcpy((void*)ep_addr, encode, size); //(OR WriteProcessMemory for remote process) | |
HookInstall(ep_addr); //We generate a MOV RAX, JMP RAX (12bytes) assembly to ep_addr | |
if (ks_asm(ks, CODE, ep_addr, &encode, &size, &count) != KS_ERR_OK) { //Generate the opcode for the hook | |
printf("ERROR: ks_asm() failed & count = %lu, error = %u\n", | |
count, ks_errno(ks)); | |
goto end; | |
} | |
ep_addr = (uint64_t)NormalGameFunction; //The function we want to hook | |
VirtualProtect(NormalGameFunction, 100, PAGE_EXECUTE_READWRITE, &oldProtect); //Dirty memory hook | |
memcpy(NormalGameFunction, encode, size); //Install hook - rest of execution is in shellcode2 | |
} | |
end: | |
// NOTE: free encode after usage to avoid leaking memory | |
ks_free(encode); | |
// close Keystone instance when done | |
ks_close(ks); | |
Sleep(100000); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment