diff --git a/230130-hookgamesendto/inc/fksendto.h b/230130-hookgamesendto/inc/fksendto.h new file mode 100644 index 0000000000000000000000000000000000000000..5bc0ef3646c4c91d754b265ba5f4a2df36497e27 --- /dev/null +++ b/230130-hookgamesendto/inc/fksendto.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include "inlinehook.h" + +void hook_sendto(); +void unhook_sendto(); diff --git a/230130-hookgamesendto/inc/inlinehook.h b/230130-hookgamesendto/inc/inlinehook.h new file mode 100644 index 0000000000000000000000000000000000000000..8b4c8585da320786e7903a4de179ed1e14eee897 --- /dev/null +++ b/230130-hookgamesendto/inc/inlinehook.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#define HOOK_JUMP_LEN 5 + +class InlineHook +{ +private: + void *old_entry; // 存放原来的代码和跳转回去的代码 + char hook_entry[HOOK_JUMP_LEN]; // hook跳转的代码 + void *func_ptr; // 被hook函数的地址 +public: + InlineHook(HMODULE hmodule, const char *name, void *fake_func, int entry_len); + ~InlineHook(); + void hook(); + void unhook(); + void *get_old_entry(); +}; diff --git a/230130-hookgamesendto/inc/platform.h b/230130-hookgamesendto/inc/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..86c6c3087e709969f465ba97da025ef881a0b8af --- /dev/null +++ b/230130-hookgamesendto/inc/platform.h @@ -0,0 +1,9 @@ +#pragma once + +#if defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) || defined(_M_AMD64) +#define _CPU_X64 +#elif defined(__i386__) || defined(_M_IX86) +#define _CPU_X86 +#else +#error "Unsupported CPU" +#endif diff --git a/230130-hookgamesendto/inc/sockqueue.h b/230130-hookgamesendto/inc/sockqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..e90cace8ab18c23e262cb802575952f790958b43 --- /dev/null +++ b/230130-hookgamesendto/inc/sockqueue.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MAX_SOCK_QUEUE 64 + +// Socket循环队列,使socket排队关闭而不是立即关闭 +class SockQueue +{ +private: + SOCKET socks[MAX_SOCK_QUEUE]; + int current; +public: + SockQueue(); + ~SockQueue(); + void add(SOCKET s); +}; diff --git a/230130-hookgamesendto/src/fksendto.cpp b/230130-hookgamesendto/src/fksendto.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac55fe8931a81ee3bda8f3a133b6bbf2b54428da --- /dev/null +++ b/230130-hookgamesendto/src/fksendto.cpp @@ -0,0 +1,91 @@ +#include "fksendto.h" +#include "sockqueue.h" +#include "platform.h" +#include + +#ifdef _CPU_X86 +#define SENDTO_ENTRY_LEN 5 +#endif +#ifdef _CPU_X64 +#define SENDTO_ENTRY_LEN 7 +#endif + +typedef int WINAPI(*sendto_func) (SOCKET, const char *, int, int, const sockaddr *, int); + +static InlineHook *sendto_hook = NULL; +static sendto_func _sendto = NULL; + +// 枚举当前所有可用网卡的IPv4地址 +static const std::vector &enum_addr() +{ + static std::vector list; + hostent *phost = gethostbyname(""); // 获取本机网卡 + if (phost) { + if (phost->h_length == list.size()) // 数量相同直接返回 + return list; + char **ppc = phost->h_addr_list; // 获取地址列表 + if (ppc) { + list.clear(); + // 遍历列表添加到容器 + while (*ppc) { + in_addr addr; + memcpy(&addr, *ppc, sizeof(in_addr)); + list.push_back(addr); + ppc++; + } + } + } + return list; +} + +// hook后替换的函数 +static int WINAPI fake_sendto(SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) +{ + static SockQueue sockqueue; + int result = -1; + sockaddr_in *toaddr = (sockaddr_in *)to; + if (toaddr->sin_addr.S_un.S_addr != INADDR_BROADCAST) { + result = _sendto(s, buf, len, flags, to, tolen); // 非广播直接原样发送 + } + else { + sockaddr_in addr_self; + int namelen = sizeof(sockaddr_in); + getsockname(s, (sockaddr *)&addr_self, &namelen); // 获取原sockaddr + if (addr_self.sin_port == 0) { + // 如果没有端口号,先原样发送,这样系统才会分配一个端口号 + result = _sendto(s, buf, len, flags, to, tolen); + getsockname(s, (sockaddr *)&addr_self, &namelen); // 重新获取 + } + const std::vector &list = enum_addr(); + // 向列表中的每一个地址转发广播 + for (int i = 0; i < list.size(); i++) { + addr_self.sin_addr = list[i]; // 把新的地址换上去,然后发送 + SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); + BOOL opt = TRUE; + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(BOOL)); // 广播 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(BOOL)); // 重用地址端口 + bind(sock, (sockaddr *)&addr_self, sizeof(sockaddr)); // 绑定到地址端口 + result = _sendto(sock, buf, len, flags, to, tolen); + sockqueue.add(sock); // 加到socket队列里 + } + } + return result; +} + +void hook_sendto() +{ + if (!sendto_hook) { + sendto_hook = new InlineHook(GetModuleHandleA("ws2_32.dll"), "sendto", (void *)fake_sendto, SENDTO_ENTRY_LEN); + _sendto = (sendto_func)sendto_hook->get_old_entry(); + sendto_hook->hook(); + } +} + +void unhook_sendto() +{ + if (sendto_hook) { + sendto_hook->unhook(); + delete sendto_hook; + sendto_hook = NULL; + } +} diff --git a/230130-hookgamesendto/src/hookdll.cpp b/230130-hookgamesendto/src/hookdll.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc3aac167ce73537c15752837a8e391edbf7168d --- /dev/null +++ b/230130-hookgamesendto/src/hookdll.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include "inlinehook.h" +#include "fksendto.h" + +#ifdef _MSC_VER +#pragma comment (lib, "ws2_32.lib") // 比较坑啊,在项目设置里加没用 +#endif + +BOOL APIENTRY DllMain(HINSTANCE hinstdll, DWORD reason, LPVOID reserved) +{ + if (reason == DLL_PROCESS_ATTACH) { + hook_sendto(); + } + else if (reason == DLL_PROCESS_DETACH) { + unhook_sendto(); + } + return TRUE; +} diff --git a/230130-hookgamesendto/src/inlinehook.cpp b/230130-hookgamesendto/src/inlinehook.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f4e8ec95ab6059ff6e604f8e6aebf8b4e63a9f4 --- /dev/null +++ b/230130-hookgamesendto/src/inlinehook.cpp @@ -0,0 +1,116 @@ +#include +#include "inlinehook.h" +#include "platform.h" + +#ifdef _CPU_X64 +static void *FindModuleTextBlankAlign(HMODULE hmodule) +{ + BYTE *p = (BYTE *)hmodule; + IMAGE_DOS_HEADER dosh; + ReadProcessMemory(GetCurrentProcess(), p, &dosh, sizeof(dosh), NULL); // 读取dos头 + p += dosh.e_lfanew + 4; // PE信息偏移量 + IMAGE_FILE_HEADER exeh; + ReadProcessMemory(GetCurrentProcess(), p, &exeh, sizeof(exeh), NULL); // 读取PE信息 + p += sizeof(exeh) + exeh.SizeOfOptionalHeader; // 跳过可选头 + for (int i = 0; i < exeh.NumberOfSections; i++) { + IMAGE_SECTION_HEADER sech; + ReadProcessMemory(GetCurrentProcess(), p, &sech, sizeof(sech), NULL); // 读取区段头 + if (memcmp(sech.Name, ".text", 5) == 0) { // 是否.text段 + BYTE *offset = (BYTE *)hmodule + sech.VirtualAddress + sech.Misc.VirtualSize; // 计算空白区域偏移量 + offset += 16 - (INT_PTR)offset % 16; // 对齐16字节 + long long buf[2]; + ReadProcessMemory(GetCurrentProcess(), offset, &buf, 16, NULL); + while (buf[0] != 0 || buf[1] != 0) { + offset += 16; + ReadProcessMemory(GetCurrentProcess(), offset, &buf, 16, NULL); + } + return offset; + } + } + return 0; +} +#endif + +InlineHook::InlineHook(HMODULE hmodule, const char *name, void *fake_func, int entry_len) +{ + func_ptr = (void *)GetProcAddress(hmodule, name); + // 范围检查 + if (entry_len < HOOK_JUMP_LEN) + entry_len = HOOK_JUMP_LEN; + if (entry_len > 27) + entry_len = 27; + // 允许func_ptr处最前面的5字节内存可读可写可执行 + VirtualProtect(func_ptr, HOOK_JUMP_LEN, PAGE_EXECUTE_READWRITE, NULL); + // 使用VirtualAlloc申请内存,使其可读可写可执行 + old_entry = VirtualAlloc(NULL, 32, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#ifdef _CPU_X64 + union + { + void *ptr; + struct + { + long lo; + long hi; + }; + } ptr64; + void *blank = FindModuleTextBlankAlign(hmodule); // 找到第一处空白区域 + VirtualProtect(blank, 14, PAGE_EXECUTE_READWRITE, NULL); // 可读写 + hook_entry[0] = 0xE9; // 跳转代码 + *(long *)&hook_entry[1] = (BYTE *)blank - (BYTE *)func_ptr - 5; // 跳转到空白区域 + ptr64.ptr = fake_func; + BYTE blank_jump[14]; + blank_jump[0] = 0x68; // push xxx + *(long *)&blank_jump[1] = ptr64.lo; // xxx,即地址的低4位 + blank_jump[5] = 0xC7; + blank_jump[6] = 0x44; + blank_jump[7] = 0x24; + blank_jump[8] = 0x04; // mov dword [rsp+4], yyy + *(long *)&blank_jump[9] = ptr64.hi; // yyy,即地址的高4位 + blank_jump[13] = 0xC3; // ret + // 写入真正的跳转代码到空白区域 + WriteProcessMemory(GetCurrentProcess(), blank, &blank_jump, 14, NULL); + // 保存原来的入口代码 + ReadProcessMemory(GetCurrentProcess(), func_ptr, old_entry, entry_len, NULL); + ptr64.ptr = (BYTE *)func_ptr + entry_len; + // 设置新的跳转代码 + BYTE *new_jump = (BYTE *)old_entry + entry_len; + new_jump[0] = 0x68; + *(long *)(new_jump + 1) = ptr64.lo; + new_jump[5] = 0xC7; + new_jump[6] = 0x44; + new_jump[7] = 0x24; + new_jump[8] = 0x04; + *(long *)(new_jump + 9) = ptr64.hi; + new_jump[13] = 0xC3; +#endif +#ifdef _CPU_X86 + hook_entry[0] = 0xE9; // 跳转代码 + *(long *)&hook_entry[1] = (BYTE *)fake_func - (BYTE *)func_ptr - 5; // 直接到hook的代码 + ReadProcessMemory(GetCurrentProcess(), func_ptr, old_entry, entry_len, NULL); // 保存入口 + BYTE *new_jump = (BYTE *)old_entry + entry_len; + *new_jump = 0xE9; // 跳回去的代码 + *(long *)(new_jump + 1) = (BYTE *)func_ptr + entry_len - new_jump - 5; +#endif +} + +InlineHook::~InlineHook() +{ + if (old_entry) + VirtualFree(old_entry, 0, MEM_RELEASE); +} + +void InlineHook::hook() +{ + WriteProcessMemory(GetCurrentProcess(), func_ptr, &hook_entry, HOOK_JUMP_LEN, NULL); +} + +void InlineHook::unhook() +{ + WriteProcessMemory(GetCurrentProcess(), func_ptr, old_entry, HOOK_JUMP_LEN, NULL); +} + +void *InlineHook::get_old_entry() +{ + return old_entry; +} + diff --git a/230130-hookgamesendto/src/sockqueue.cpp b/230130-hookgamesendto/src/sockqueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..865b76c7d809b51d315639aaf62a39e5feacb22c --- /dev/null +++ b/230130-hookgamesendto/src/sockqueue.cpp @@ -0,0 +1,23 @@ +#include "sockqueue.h" + +SockQueue::SockQueue() +{ + memset(&socks, 0, MAX_SOCK_QUEUE * sizeof(SOCKET)); + current = 0; +} + +SockQueue::~SockQueue() +{ + for (int i = 0; i < MAX_SOCK_QUEUE; i++) + if (socks[i] != 0) + closesocket(socks[i]); +} + +void SockQueue::add(SOCKET s) +{ + if (socks[current] != 0) + closesocket(socks[current]); + socks[current++] = s; + if (current == MAX_SOCK_QUEUE) + current = 0; +} diff --git a/230130-hookgamesendto/test/testhook/Makefile b/230130-hookgamesendto/test/testhook/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f7c7628d3460d53d2c54ecb89062a6ae74a5a517 --- /dev/null +++ b/230130-hookgamesendto/test/testhook/Makefile @@ -0,0 +1,50 @@ +cxx = g++ +cxxflags = -c -I ../../inc +cxx32prefix = i686-w64-mingw32- +cxx64prefix = x86_64-w64-mingw32- +target = testhook.exe +target32 = testhook32.exe +target64 = testhook64.exe +src = testhook.cpp inlinehook.cpp +objdir = ./obj/ +obj = $(patsubst %.cpp, $(objdir)%.o, $(src)) +obj32 = $(patsubst %.cpp, $(objdir)%_32.o, $(src)) +obj64 = $(patsubst %.cpp, $(objdir)%_64.o, $(src)) +targets = $(target) +vpath %.cpp ../../src + +ifeq ($(cpu32),1) +targets += $(target32) +endif +ifeq ($(cpu64),1) +targets += $(target64) +endif + +all: check $(targets) + +$(target): $(obj) + $(cxx) -o $@ $^ + +$(target32): $(obj32) + $(cxx32prefix)$(cxx) -o $@ $^ + +$(target64): $(obj64) + $(cxx64prefix)$(cxx) -o $@ $^ + +$(objdir)%.o: %.cpp + $(cxx) $(cxxflags) -o $@ $< + +$(objdir)%_32.o: %.cpp + $(cxx32prefix)$(cxx) $(cxxflags) -o $@ $< + +$(objdir)%_64.o: %.cpp + $(cxx64prefix)$(cxx) $(cxxflags) -o $@ $< + +.PHONY: check clean + +check: + @ if not exist obj md obj + +clean: + @ if exist obj del obj\*.o + @ del *.exe diff --git a/230130-hookgamesendto/test/testhook/testhook.cpp b/230130-hookgamesendto/test/testhook/testhook.cpp new file mode 100644 index 0000000000000000000000000000000000000000..00797bdc648986bb2af8cb7678b8658823629695 --- /dev/null +++ b/230130-hookgamesendto/test/testhook/testhook.cpp @@ -0,0 +1,34 @@ +#include +#include +#include "inlinehook.h" +#include "platform.h" + +#ifdef _CPU_X64 +#define MESSAGEBOXA_ENTRYLEN 7 // 64位的一般需要反汇编得出 +#endif +#ifdef _CPU_X86 +#define MESSAGEBOXA_ENTRYLEN 5 +#endif + +typedef int WINAPI(*MessageBoxA_func)(HWND, LPCSTR, LPCSTR, UINT); + +static InlineHook *hook; +static MessageBoxA_func _MessageBoxA; + +int WINAPI fk_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) +{ + printf("original caption: %s\noriginal text: %s\n", lpCaption, lpText); + return _MessageBoxA(hWnd, "Hook MessageBoxA", "test", MB_ICONERROR); +} + +int main() +{ + hook = new InlineHook(GetModuleHandleA("user32.dll"), "MessageBoxA", (void *)fk_MessageBoxA, MESSAGEBOXA_ENTRYLEN); + _MessageBoxA = (MessageBoxA_func)hook->get_old_entry(); + hook->hook(); + MessageBoxA(0, "testhook", "test", 0); // 被hook + _MessageBoxA(0, "never hook", "test", 0); // 不被hook + hook->unhook(); + delete hook; + MessageBoxA(0, "nohook", "test", 0); // 不被hook +}