windows dll注入修改函数

背景

之前分析windows异常捕获时接触到一个hook api 重定向了SetUnhandledExceptionFilter函数,当时并不熟悉,现在刚好抽点时间可以研究下,并自己手动实现了一个远程dll注入,并修改指定exe函数的实现。

效果

  • hooktest.exe 里面的某个函数被修改为其他函数实现,如下是hooktest的源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void testOutPut()
{
printf("%s\n","hook test");
}

int main()
{
while (1)
{
char a;
std::cin >> a;
testOutPut();
}
return 0;
}

当输入键盘任意按钮时,输出”hook test”,但是当我注入dll后,按下按键输出”dealHookFunc succ”,效果如下

1
2
3
4
5
6
7
8
a
hook test
hook in
a
dealHookFunc succ
hook out
a
hook test

过程

因为是远程注入,需要一个load.exe(loaddll.exe) 、一个 hook.dll(xxxx.dll)、一个被注入程序hooktest.exe

三个项目的源码如下

loaddll.exe

该程序主要功能主要是注入xxxx.dll 到指定目标中

  • 找到指定exe的pid

  • 判断是否有重复注入

  • 打开目标进程

  • 获取LoadLibrary地址

  • CreateRemoteThread 动态加载xxxx.dll

xxxx.dll

  • 触发dllmain DLL_PROCESS_ATTACH 时,修改进程的内存机器码

  • DLL_PROCESS_DETACH 时,还原进程的机器码

testOutPut 函数地址

  • 通过 ida 工具分析hooktest.exe 的信息,很容易找到testOutPut 函数的偏移地址为 0x80

修改汇编代码

1
2
3
4
5
6
7
8
    hooktest!testOutPut:
00cf1080 55 push ebp
00cf1081 8bec mov ebp, esp
00cf1083 681831cf00 push offset hooktest!GS_ExceptionPointers+0x8 (00cf3118)
00cf1088 682431cf00 push offset hooktest!GS_ExceptionPointers+0x14 (00cf3124)
00cf108d e8aeffffff call hooktest!printf (00cf1040)
00cf1092 83c408 add esp, 8
00cf1095 5d pop ebp

因为是无条件跳转,所以只要把 00cf1080 开始之后的地址修改为

1
2
jmp 目标地址  // 5个字节
rent // 1个字节

1
2
3
4
5
6
7
BYTE hookCode[32] = { 0 };  
hookCode[nsize++] = 0xE9; // // 0xE9 0x12345678 : jmp 0x12345678 5个字节
*(DWORD*)&hookCode[nsize++] = (DWORD)dealHookFunc - hookAddress - 5; // 5个字节偏移 jmp call 都是相对地址
nsize++;
nsize++;
nsize++;
hookCode[nsize++] = 0xC3; // 0xC3 retn

修改后的汇编代码

1
2
3
4
5
6
7
8
9
10
11
 hooktest!testOutPut:
00cf1080 e9fbff7c54 jmp xxxx!dealHookFunc (554c1080)
00cf1085 c3 ret
00cf1086 cf iretd
00cf1087 006824 add byte ptr [eax+24h], ch
00cf108a 31cf xor edi, ecx
00cf108c 00e8 add al, ch
00cf108e ae scas byte ptr es:[edi]
00cf108f ff ???
00cf1090 ff ???
00cf1091 ff83c4085dc3 inc dword ptr [ebx-3CA2F73Ch]

跳转地址

jmp指令所指向的地址是个相对地址,所以真实的目标地址为

1
hook函数的地址 - 被hook函数的地址 - 5 ;  // 5是指指令(jmp 0x12345678)的长度

被hook函数地址

1
模块基础地址 + virtual address + 偏移地址

修改内存内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DWORD baseAddress = (DWORD)GetModuleHandle(NULL);
DWORD hookAddress = baseAddress + OUTPUT_FUNC_ADDR_OFFSET;

//jmp 0x12345678
//retn

// 如上共6个字节
BYTE hookCode[32] = { 0 };
hookCode[nsize++] = 0xE9; // // 0xE9 0x12345678 : jmp 0x12345678 5个字节
*(DWORD*)&hookCode[nsize++] = (DWORD)dealHookFunc - hookAddress - 5; // 5个字节偏移 jmp call 都是相对地址
nsize++;
nsize++;
nsize++;
hookCode[nsize++] = 0xC3; // 0xC3 retn

BOOL bRet = ReadProcessMemory(GetCurrentProcess(), (LPVOID)hookAddress, oldCode, nsize, 0);

DWORD dwOldFlag, dwTempFlag;
(void)VirtualProtect((void*)hookAddress, nsize, PAGE_READWRITE, &dwOldFlag);
bRet = WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookAddress, hookCode, nsize, 0);
(void)VirtualProtect((void*)hookAddress, nsize, dwOldFlag, &dwTempFlag);

总结

这个程序只是简单的说明,希望对你有帮助。