So currently i have the following code which is just a simple trampoline type hook for ws2_32 to hook connect in order to eventually add a proxy to an application. The problematic area is inside int __stdcall connect_FUNC(SOCKET s, const struct sockaddr* name, int namelen). Whilst i don't need to understand or call anything after 'connect_ORI(s, name, namelen)` when proxying (all the work is done before the original function is called); it'd be nice to understand the following incorrect/weird behaviour:
Calls to functions like printf, MessageBox, even std::cout before connect_ORI(s, name, namelen) work fine and the connect is successful, However, moving the same call (printf etc) after connect_ORI(s, name, namelen) results in the detour'd function returning a different (incorrect) result (-1). It might be important to also add that the injected process does not crash in both test cases.
I know that it can't be the Patch/RemovePatch functions, I tried MinHook and it had the same weird behaviour. Also, patching other functions, inside user32.dll worked fine. Is there some special case or optimization in ws2_32.dll that i have to look out for? I'm thinking there's some sort of stack or frame pointer / register corruption but unsure if that is the case and unsure on how to fix it. I included all of the code for completeness. Could anyone explain this behaviour specifically using code?
I am using MinGW-w64 x86_64-720-posix-sjlj-rt_v5-rev1. 32Bit DLL injecting into a 32bit process. Using -Os optimization.
Thanks in advance.
typedef int (__stdcall *connect_DEF)(SOCKET, const struct sockaddr*, int);
connect_DEF connect_ORI = NULL;
void* jmpOffset = NULL;
std::uint8_t* Patch(std::uint8_t* OrigFunc, std::uint8_t* HookFunc)
{
DWORD dwProtect = 0;
const static std::uint8_t jmp[] = {0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0};
const static std::int8_t jmp_size = sizeof(jmp) / sizeof(std::uint8_t);
static std::uint8_t HookJump[jmp_size + 1] = {jmp_size};
VirtualProtect(OrigFunc, jmp_size, PAGE_EXECUTE_READWRITE, &dwProtect);
memcpy(&HookJump[1], OrigFunc, jmp_size);
memcpy(OrigFunc, jmp, jmp_size);
memcpy(OrigFunc + 1, &HookFunc, sizeof(void*));
VirtualProtect(OrigFunc, jmp_size, dwProtect, &dwProtect);
return HookJump;
}
void RemovePatch(std::uint8_t* OrigFunc, std::uint8_t* HookJump)
{
DWORD dwProtect = 0;
VirtualProtect(OrigFunc, HookJump[0], PAGE_EXECUTE_READWRITE, &dwProtect);
memcpy(OrigFunc, &HookJump[1], HookJump[0]);
VirtualProtect(OrigFunc, HookJump[0], dwProtect, &dwProtect);
}
int __stdcall connect_FUNC(SOCKET s, const struct sockaddr* name, int namelen)
{
int Result = -1;
printf("HERE\n"); // printf works here, connect func works with printf here.
if (jmpOffset)
{
RemovePatch((uint8_t*)connect_ORI, (uint8_t*)jmpOffset);
jmpOffset = NULL;
Result = connect_ORI(s, name, namelen);
jmpOffset = (void*)Patch((uint8_t *)connect_ORI, (uint8_t*)connect_FUNC);
}
//printf("HERE\n"); // stack gets corrupted or something happens when printf is here. connect returns -1.
return Result;
}
BOOL __stdcall DllMain(HMODULE DLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
HMODULE WS2_32DLL = GetModuleHandle("ws2_32.dll");
if (WS2_32DLL)
{
connect_ORI = (connect_DEF)GetProcAddress(WS2_32DLL, "connect");
if (connect_ORI)
jmpOffset = (void*)Patch((uint8_t*)connect_ORI, (uint8_t*)connect_FUNC);
}
}
break;
case DLL_PROCESS_DETACH:
{
if (jmpOffset)
{
RemovePatch((uint8_t*)connect_ORI, (uint8_t*)jmpOffset);
}
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
asm of differences in both testcases (connect_FUNC)
printf before calling original function (works correctly):
LC0:
.ascii "HERE\12\0"
.text
.globl __Z12connect_FUNCjPK8sockaddri@12
.def __Z12connect_FUNCjPK8sockaddri@12; .scl 2; .type 32; .endef
__Z12connect_FUNCjPK8sockaddri@12:
pushl %ebp
movl %esp, %ebp
pushl %ebx
orl $-1, %ebx
subl $20, %esp
movl $LC0, (%esp)
call __Z6printfPKcz
movl _jmpOffset, %eax
testl %eax, %eax
je L7
movl %eax, 4(%esp)
movl _connect_ORI, %eax
movl %eax, (%esp)
call __Z11RemovePatchPhS_
movl 16(%ebp), %eax
movl $0, _jmpOffset
movl %eax, 8(%esp)
movl 12(%ebp), %eax
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call *_connect_ORI
movl %eax, %ebx
movl _connect_ORI, %eax
subl $12, %esp
movl $__Z12connect_FUNCjPK8sockaddri@12, 4(%esp)
movl %eax, (%esp)
call __Z5PatchPhS_
movl %eax, _jmpOffset
L7:
movl %ebx, %eax
movl -4(%ebp), %ebx
leave
ret $12
.section .rdata,"dr"
printf after calling original function (doesn't work correctly):
LC0:
.ascii "HERE\12\0"
.text
.globl __Z12connect_FUNCjPK8sockaddri@12
.def __Z12connect_FUNCjPK8sockaddri@12; .scl 2; .type 32; .endef
__Z12connect_FUNCjPK8sockaddri@12:
pushl %ebp
movl %esp, %ebp
pushl %ebx
orl $-1, %ebx
subl $20, %esp
movl _jmpOffset, %eax
testl %eax, %eax
je L8
movl %eax, 4(%esp)
movl _connect_ORI, %eax
movl %eax, (%esp)
call __Z11RemovePatchPhS_
movl 16(%ebp), %eax
movl $0, _jmpOffset
movl %eax, 8(%esp)
movl 12(%ebp), %eax
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call *_connect_ORI
movl %eax, %ebx
movl _connect_ORI, %eax
subl $12, %esp
movl $__Z12connect_FUNCjPK8sockaddri@12, 4(%esp)
movl %eax, (%esp)
call __Z5PatchPhS_
movl %eax, _jmpOffset
L8:
movl $LC0, (%esp)
call __Z6printfPKcz
movl %ebx, %eax
movl -4(%ebp), %ebx
leave
ret $12
.section .rdata,"dr"