完成 madchook 库驱动的逆向工程, 驱动内的逻辑是这样的:
用 PsSetCreateProcessNotifyRoutine 函数挂接一个 "进程创建回调", 当有进程创建时, 针对目标进程, 这个回调函数里干了这些事情:
[list=1]
[*] 查看驱动有无创建一块内存, 没有则打开名为 "\\BaseNamedObjects\\mchInjDrvMap" 的文件映射, 如果打开成功后, 则创建一块分页内存, 并将文件映射读出来, 读取的长度在文件映射的第一个 DWORD 里保存着, 读出的数据写到新创建的内存里. 我读出的内容是:
kd> dd e145f688
e145f688 00000971 00000089 000000ba 00000115
e145f698 0000002c 00000000 00000000 00000000
e145f6a8 7c92e8b8 000103b8 0300ba00 000000e8
e145f6b8 00255800 e8fffff0 0000010d 53e58955
e145f6c8 000000e8 e3815b00 fffff000 0875ff53
e145f6d8 0005e8e8 00f88300 75ff0b74 ed93ff08
e145f6e8 eb000000 0022b805 5d5bc000 ca0004c2
e145f6f8 867c9361 007c9365 867c8000 487c92d5
经过分析,
第 1 个 DWORD 代表了这块数据本身的长度,
第 2, 3, 4 个 DWORD 代表了在 ntdll.dll 里导出的 ring 3 函数
ZwProtectVirtualMemory,
NtReadVirtualMemory,
NtWriteVirtualMemory
的系统调用号, 因为 ntoskrnl.exe 里并没有导出这三个函数, 因此采用这种晦涩的方式以便可以在内核里调用它们.
第 5 个 DWORD 好像是可执行代码的偏移量,
第 6, 7, 8 三个 DWORD 好像没有使用,
第 9 个 DWORD 保存的是目标进程的 ntdll.dll 导出的函数 ZwTestAlert 的基址.
第 10, 11 两个 DWORD 共 8 个字节, 用于存储 ntdll.ZwTestAlert 函数的原始头 8 个字节.
第 12 个 DWORD, 从此开始, 就是可执行代码了, 此后用字节计数了, 一直到第 0x971 字节结束这段 shellcode
[*] 针对目标进程, 在其内部分配一块内存, 指定固定基址是 0x71700000 (我不知道是为什么), 将步骤 1 读出的所有内容写到这块内存里.
[*] 读取步骤 1 里创建的内存里的数据偏移的第 9 个 DWORD 的值, 我得到的是 ox7c92e8b8, 读取目标进程的以这个值作为偏移量的内存里的 8 个字节 (有点绕), 我对这些字节的反汇编如下:
0:001> u 7c92e8b8
ntdll!ZwTestAlert:
7c92e8b8 b803010000 mov eax,103h
7c92e8bd ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92e8c2 ff12 call dword ptr [edx]
7c92e8c4 c3 ret
查看 [url]http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/APC/NtTestAlert.html[/url] 网页, 说:
You can use NtTestAlert to empty APC queue for current thread. If APC queue was empty before call, NtTestAlert has no efect.
NtTestAlert is typical ntcall kernel routine, accessable via int 2Eh. It check thread APC queue, and call KiUserApcDispatcher.
看来还是与 APC 有关, 牛皮糖一样, 扯都扯不脱.
[*] 将步骤 3 里读到的 8 个字节写到步骤 2 里分配到目标进程的内存的可执行代码起始偏移之前的 8 个字节处 (还是有点绕).
[*] 装配汇编代码, 共 8 个字节:
push eax;
push eax;
push eax;
jmp [xxxxxxx];
其中 [xxxxxxx] 代表从 (ZwTestAlert + 8) 跳转到我们的代码的偏移量(带符号数)
[*] 将装配好的汇编代码 8 个字节写入 ZwTestAlert 函数的开头.
[/list]
到了这里, 我们可以总结一下了, 在 win2000/xp 下, 往目标进程的内存写入我们的 shellcode 代码, 修改 Ntdll.NtTestAlert 的头 8 个字节让程序跳到我们的 shellcode 处执行, 在执行的过程中恢复 Ntdll.NtTestAlert 函数头部被改了的代码, 同时加载我们自己注入的链接库. 本质上还是一个 detours.
下面的任务, 就是逆向这段运行于 ring 3 下的 shellcode 了.
posted on 2008-06-11 00:06
free2000fly 阅读(1127)
评论(0) 编辑 收藏 引用