提交 2a71e0a8 编写于 作者: Peacoor Zomboss's avatar Peacoor Zomboss

First step to implement x86 and x64 sendto hook

上级 382b1d66
#pragma once
#include <winsock2.h>
#include "inlinehook.h"
void hook_sendto();
void unhook_sendto();
#pragma once
#include <windows.h>
#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();
};
#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
#pragma once
#include <winsock2.h>
#define MAX_SOCK_QUEUE 64
// Socket循环队列,使socket排队关闭而不是立即关闭
class SockQueue
{
private:
SOCKET socks[MAX_SOCK_QUEUE];
int current;
public:
SockQueue();
~SockQueue();
void add(SOCKET s);
};
#include "fksendto.h"
#include "sockqueue.h"
#include "platform.h"
#include <vector>
#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<in_addr> &enum_addr()
{
static std::vector<in_addr> 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<in_addr> &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;
}
}
#include <winsock2.h>
#include <windows.h>
#include <vector>
#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;
}
#include <windows.h>
#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;
}
#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;
}
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
#include <windows.h>
#include <stdio.h>
#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
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册