Skip to content

Instantly share code, notes, and snippets.

Created September 3, 2017 07:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/cc1f6d087029d2f6a16f24bbe77a9956 to your computer and use it in GitHub Desktop.
Save anonymous/cc1f6d087029d2f6a16f24bbe77a9956 to your computer and use it in GitHub Desktop.
#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