正在将线程重定向到另一个函数



这是我开始的内容,但我在某个随机地址处收到了执行冲突,该地址来自我无法在反汇编中查看的内存区域(不是从分配的页面内(

// ThreadCallbackParameter = void*
// ThreadCallback = void __stdcall (void*)
// suspend thread
SuspendThread(Thread->Handle);
// get the threads full context
CONTEXT c;
c.ContextFlags = CONTEXT_FULL;
GetThreadContext(Thread->Handle, &c);
// shellcode
// push ThreadCallbackParameter
// call ThreadCallback
// ret
unsigned char shell[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xC3 };
*(unsigned int*)(shell + 1) = ThreadCallbackParameter;
*(unsigned int*)(shell + 6) = ThreadCallback;
// allocate executable page for shellcode
void* mem = VirtualAlloc(0, sizeof(shell), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// copy shellcode to page
memcpy(mem, shell, sizeof(shell));
// redirect thread to shellcode
c.Eip = mem;
SetThreadContext(Thread->Handle, &c);
// resume thread
ResumeThread(Thread->Handle);
return 0;
  • 分配的页面是可执行的
  • 该应用程序是32位的,因此程序集说明应正确
  • 我想调用的函数是stdcall,所以推送应该是合适的
  • 我可以重定向到另一个函数,通过将Eip更改为函数的地址这些调用也应该是正确的(我要求shell代码能够也将参数传递给它(

一定是一些简单的代码/思维错误我不知道

OpCode0xE8是x86CALL指令的相对版本。您将要跳转到的回调函数的绝对内存地址传递给它,但您需要给它一个相对于该函数的偏移量,其中该偏移量是相对于跟随CALL指令本身的指令的。在这种情况下,您需要获取RET(0xC3(指令的地址,并从ThreadCallback所指向的地址中减去该地址,例如:

PBYTE mem = (PBYTE) VirtualAlloc(0, sizeof(shell), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// copy shellcode to page
memcpy(mem, shell, sizeof(shell));
*(PDWORD)(mem + 1) = ThreadCallbackParameter;
*(PINT32)(mem + 6) = ((INT_PTR)ThreadCallback) - ((INT_PTR)(mem + 10));
// redirect thread to shellcode
...

也就是说,在执行之前,你需要在分配的内存上调用FlushInstructionCache()。你还应该从内存中删除READ/WRITE标志,以避免恶意代码劫持你的shell代码来做其他事情。

为了更好的可读性,我还建议在shell代码中使用struct而不是原始字节。

#pragma pack(push, 1)
struct myByteCode
{
BYTE push;
DWORD pushValue;
BYTE call;
INT32 callOffset;
BYTE ret;
};
#pragma pack(pop)
// allocate executable page for shellcode
struct myByteCode* mem = (struct myByteCode*) VirtualAlloc(0, sizeof(struct myByteCode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mem)
{
// error handling...
}
// copy shellcode to page
mem->push = 0x68;
mem->pushValue = (DWORD) ThreadCallbackParameter;
mem->call = 0xE8;
mem->callOffset = ((INT_PTR)ThreadCallback) - ((INT_PTR)(&mem->ret));
mem->ret = 0xC3;
DWORD ignored;
if (!VirtualProtect(mem, sizeof(struct myByteCode), PAGE_EXECUTE, &ignored))
{
// error handling...
}
FlushInstructionCache(GetCurrentProcess(), mem, sizeof(struct myByteCode));
// redirect thread to shellcode
...

感谢remy lebeau,我已经解决了我的问题,这是我最初发布的代码的工作版本:

// suspend thread
SuspendThread(Thread->Handle);
// get the threads full context
CONTEXT c;
c.ContextFlags = CONTEXT_FULL;
GetThreadContext(Thread->Handle, &c);
// allocate executable page for shellcode
unsigned char shell[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xC3 };
void* mem = VirtualAlloc(0, sizeof(shell), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// shellcode
// push ThreadCallbackParameter
// call ThreadCallback
// ret
*(unsigned int*)(shell + 1) = ThreadCallbackParameter;
*(unsigned int*)(shell + 6) = (unsigned int)ThreadCallback - (unsigned int)((char*)mem + 10);
// copy shellcode to page
memcpy(mem, shell, sizeof(shell));
// redirect thread to shellcode
c.Eip = mem;
SetThreadContext(Thread->Handle, &c);
// resume thread
ResumeThread(Thread->Handle);
return 0;

最新更新