B+K:|%G@PO6qL0.....51Testing软件测试网.}e5iAoB5j
u9G1gws0Q0上一期,我们讲了用HOOK技术实现远程线程插入,相信大家还记忆犹新.51Testing软件测试网9Rb*R5G%Sy]UO
51Testing软件测试网!Qf6K0wnv$Gq这一期我们来谈谈 API HOOK
d2E,@d,l|y#U8uN ^*m051Testing软件测试网
AUr)[jI qzv API Hook技术应用广泛,常用于屏幕取词,网络防火墙,病毒木马,加壳软件,串口红外通讯,游戏外
W+Z%{@zp^C?02TVR8_ h5{@%S-{}0挂,internet通信等领域API HOOK的中文意思就是钩住API,对API进行预处理,先执行我们的函数,例
0B-}f3O(Y!R051Testing软件测试网*x4k
?i]5?~如我们用API Hook技术挂接ExitWindowsEx API函数,使关机失效,挂接ZwOpenProcess函51Testing软件测试网/uO)CB2O7}I
#A P/^
c3d:a0z.L|0数,隐藏进程等等......51Testing软件测试网pHt+F
t'pyW4b2U2z'a
3H8whm9{yrA"p0 51Testing软件测试网8o_GHn$W4~(MJ
总的来说,常用的挂钩API方法有以下两种:
we L6t-Y051Testing软件测试网9\E3l;q!v:{<一>改写IAT导入表法51Testing软件测试网4K%L
\FG9q
]L kel)O0 我们知道,Windows下的可执行文档的文件格式是一种叫PE(“portable executable”,可移植
G"cmo3|+Y7y0*pl0Lit{,Z~3m$yq0的可执行文件)的文件格式,这种文件格式
p3n]$Gimw0Gbhu3AE2}0是由微软设计的,接下来这张图描述了PE文件的结构:51Testing软件测试网8Y[PnG6N'd.j
51Testing软件测试网A1}(nrBU +-------------------------------+ - offset 051Testing软件测试网D
j D[_4o!fbm#S:}
| MS DOS标志("MZ") 和 DOS块 |
$?}
q5@ }0 +-------------------------------+
n\7r u-aA
M0 | PE 标志 ("PE") |51Testing软件测试网4GZ6db
cU
+-------------------------------+
t}G
z~+T0 | .text | - 模块代码51Testing软件测试网xvS6R1?{z
| 程序代码 |51Testing软件测试网1t8b(MuW{1t&z&r&q9G
| |51Testing软件测试网6l
b[
_4]NX
+-------------------------------+51Testing软件测试网 m9w^+`Ws4l+b
| .data | - 已初始化的(全局静态)数据51Testing软件测试网3M%D'p3ba.RG
| 已初始化的数据 |51Testing软件测试网E*X8tk-]l
| |
OX/\4jl.{MRKg0 +-------------------------------+
v8b(zt;h0 | .idata | - 导入函数的信息和数据51Testing软件测试网r ic!j"M+s l-V
| 导入表 | 51Testing软件测试网7cm(B%X$H5r.J7D&E+c
| |51Testing软件测试网*oM8]1Jr9xHD| V6l
+-------------------------------+51Testing软件测试网5? dAM;^8k$P
| .edata | - 导出函数的信息和数据
1B3za!@r!y0 | 导出表 |
]{*SsQW*p;u0 | |
#u`gD/S&y0 +-------------------------------+51Testing软件测试网O Y Uz TOE
| 调试符号 |
{f7DN0be2U0 +-------------------------------+51Testing软件测试网s/G D"E|
(i(L)A6Nm ^0
oB]
Cq Us|Y051Testing软件测试网tGd2O
vm
这里对我们比较重要的是.idata部分的导入地址表(IAT)。这个部分包含了导入的相关信息和导51Testing软件测试网FuPVl
3XE;i!vZ'q/lv0入函数的地址。有一点很重要的是我们必须知道PE文件是如何创建的。当在编程语言里间接调用任
:U:ul0Zr;w O5t04h0G*RN2hd0意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并不直接把调用连接到
o5w)T2fb0K r#b+d&x*A0模块,而是用jmp指令连接调用到IAT,IAT在系统把进程调入内存时时会由进程载入器填满。这就是
^(L&~*d\#C W051Testing软件测试网4r+I6].{!s#sa我们可以在两个不同版本的Windows里使用相同的二进制代码的原因,虽然模块可能会加载到不同的51Testing软件测试网%arg0j0i)G~'` T4^
51Testing软件测试网G!s"A(~F地址。进程载入器会在程序代码里调用所使用的IAT里填入直接跳转的jmp指令。所以我们能在IAT里
kvX3|sX2a-W0@Jf9x]/@D
u0找到我们想要挂钩的指定函数,我们就能很容易改变那里的jmp指令并重定向代码到我们的地址。完
*o u{4h\;a051Testing软件测试网 d%@SNM成之后每次调用都会执行我们的代码了。
pY&sOkHX0 51Testing软件测试网5R uYd.Iyj%U
我们通过使用imagehlp.dll里的ImageDirectoryEntryToData来很容易地找到IAT。
0u tD*t
P1`5A051Testing软件测试网,lZYO9R;] .DLL命令 ImageDirectoryEntryToData, 整数型, "imagehlp", , , 返回IMAGE_IMPORT_DEscrīptOR数组的首地址51Testing软件测试网|G)u0G|4h
.参数 Base, 整数型, , 模块句柄51Testing软件测试网+pWYa4LY
.参数 MappedAsImage, 逻辑型, , 真
z!K%@
uA9@0 .参数 DirectoryEntry, 整数型, , 恒量:IMAGE_DIRECTORY_ENTRY_IMPORT,1
(Kx5_^H*C]x0 .参数 Size, 整数型, 传址, IMAGE_IMPORT_DEscrīptOR数组的大小
;z$Hs j
?051Testing软件测试网J
@aq]W
uO7w.M6};\%A7A0 .数据类型 IMAGE_IMPORT_DEscrīptOR, , 输入描述结构51Testing软件测试网YQ)?3@0E
.成员 OriginalFirstThunk, 整数型, , , 它是一个RVA(32位),指向一个以0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数 组,其每个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组永不改变。51Testing软件测试网"_f{hq!i(Wa)?
.成员 TimeDateStamp, 整数型, , , 它是一个具有好几个目的的32位的时间日期戳.
%g"Q@AY!h!Z0 .成员 ForwarderChain, 整数型, , , 它是输入函数列表中第一个中转的、32位的索引。
1iI4G0wW:dY1@!Y9t0 .成员 Name, 整数型, , , 它是一个DLL文件的名称(0结尾的ASCII码字符串)的、32位的RVA。51Testing软件测试网(ia^7e~
.成员 FirstThunk, 整数型, , , 它也是一个RVA(32位),指向一个0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数组,其每 个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组是输入地址表的一部分,并且可以改变。
q r9Fyh;G0Xz2Lg:{'C\#r0 51Testing软件测试网)Z'@&a8QC{ A2|p
如果我们找到了就必须用VirtualProtectEx函数来改变内存页面的保护属性,然后就可以通过51Testing软件测试网
Z(@r/jD8L3u F
WriteProcessMemory在内存中的这些部分写入代码了。在改写了地址之后我们要把保护属性改回来
9J9w ] ~`x0。在调用VirtualProtectEx之前我们还要先知道有关页面的信息,这通过VirtualQueryEx来实现。51Testing软件测试网6}#E]uM Oj/M
jswN)Y#t.|h(k0 这种方法的好处是比较稳定,但有漏API的可能,因为并不是所有的API调用都是通过IAT的,可能易
w)u\:{+{;F
|)W#TG{0程序就是这种情况,我当初也是想用这种方法,但是在易里面调试时总不能在IAT里得到正确的程序调51Testing软件测试网
J{'C+sv-EY?
用的API,常常是些无关的API(可能是易的核心支持库在做怪),不得不放弃.51Testing软件测试网!~3s+K{NXlKb
51Testing软件测试网4\ _@Lb
51Testing软件测试网mWf+l(x<二>直接改写API函数入口点.(改写内存地址JMP法)51Testing软件测试网:f7D^*N*vR
C
H|{xH$[%}051Testing软件测试网
Rh
N.H N~T
改写函数入口点开始的一些字节这种方法相当简单,这也是本文采用的方法,就象改变IAT里的地址一样,我们也要先修改页面属性。
1@8HZ~q~V*@051Testing软件测试网%Dp(V0lz.DLL命令 返回页面虚拟信息, 整数型, "kernel32", "VirtualQueryEx"
L'_e A
Y.fpQ+o"^l0 .参数 hProcess, 整数型, , 对象的进程句柄,可以使用函数 OpenProcess() 返回。
9| {*o#c#r7aa0 .参数 lpAddress, 整数型, , 对象指针地址51Testing软件测试网+]"Mf-D!`
.参数 lpBuffer, 虚拟信息, , 返回的虚拟信息
k`!G8A"s$i0 .参数 dwLength, 整数型, , 信息长度,已知 2851Testing软件测试网Q&\#Ij m6_
51Testing软件测试网`(D&F"R*s"J6bp51Testing软件测试网G1WBpH*BNu
.DLL命令 修改页面虚拟保护, 逻辑型, "kernel32", "VirtualProtectEx"51Testing软件测试网5E3Ft1k V-{&}sw
.参数 hProcess, 整数型, , 对象的进程句柄,可以使用函数 OpenProcess() 返回。51Testing软件测试网$Gzdu/P#^
^Uy
.参数 lpAddress, 整数型, , 虚拟信息.BaseAddress51Testing软件测试网(R2fv/oe;z#j-pM^ j
.参数 dwSize, 整数型, , 修改虚拟保护的长度.
9F C/Y5{M}F%`0 .参数 flNewProtect, 整数型, , 修改类型,#PAGE_EXECUTE_READWRITE 64为可读写模式51Testing软件测试网J6Q
?Oe*J;JTF8v
.参数 lpflOldProtect, 整数型, 传址, 虚拟信息.Protect51Testing软件测试网{ ?#A7xW8v+on2[u
51Testing软件测试网/P^s3U&iQ 通过调用VirtualQueryEx,VirtualProtectEx这两个API就可以修改我们要挂勾的API所在的页面属性为可读写模式,接着就可以调用API函数
`.\#\/R%s"O;i-FwB0 ltb\d6iktk.k0WriteProcessMemory写内存字节了.51Testing软件测试网4R|'o rXT
51Testing软件测试网D v5I&~"M(e5d$z 到这里,也就没有什么难点了,我们拿API函数ExitWindowsEx来简单说明下,如果要实现自定API函
K3|6nYQ~@4@0-jdLv8^"gV0数,可以写入一个跳转指令,JMP OX00000(其中OX00000为自定API函数地址),请参照论坛上的教程,也
yP9m2_qK}
{D
c051Testing软件测试网#W)I'O$|m;K4rlRU(T当作我留给大家的问题吧,呵呵. 51Testing软件测试网Na+~"hq+Nok]Ea1G3d
k*Hg+Z:k|[`h0制作目标:挂勾ExitWindowsEx,使关机无效.51Testing软件测试网B Y;lx5N!_
#z@T)MZ%Z-N0分析:只要求关机无效,即ExitWindowsEx无效就可以了,我们不必那么麻烦去写跳转指令,可以直接在
/Y}(xrG-s.x7}
P)e&|0 51Testing软件测试网2?i|uL9q"ZG
ExitWindowsEx首地址写入一个垃圾指令,使它后面的代码无法执行就可以了,我们可以写入
2fu%}@O.^0 {104}→汇编 PUSH 或者 {195}→汇编 retn→返回命令(在易里面调用这个API会造成错误)这51Testing软件测试网 DyvPV
51Testing软件测试网%IpH!u#KG
样就可以达到目地了.
Y"z;]JMhDK2k051Testing软件测试网4IEy e]i_K看下面的代码:
5{[3p`+D
AB0J0W2i9N4@ z\;u'wq6F0===================51Testing软件测试网@3ao}d1\
si7X
51Testing软件测试网 ~)Sv5D-c
GWs]'通过API函数GetProcAddress和GetModuleHandleA很容易得到API函数ExitWindowsEx的首地址.
9_-R+Yr]ZOT0]Be;s/FD
y(ev051Testing软件测试网#BkDFa2cQLEY$A$f
API = GetProcAddress(GetModuleHandleA(“user32.dll”), “ExitWindowsEx”)51Testing软件测试网;i,oX9CF-uP
51Testing软件测试网%w+b9qz
{ Dv}*O51Testing软件测试网"gnV%R!B!zM
API_BAK = 指针到字节集 (API, 1) '为了以后可以还原,我们先备份一下
p.|$oR%w+];p0p
tPi!YM0'用OpenProcess打开其他进程前先提升进程权限,这样可以打开几乎所有的非内核程序51Testing软件测试网6r9K
_`yU] n
_8RjR L.yfr^0提升进程权限 () '这个模块在附件里
%eY vo"K01HI^V@6DV0
'na7_s8sZ:^8K0==============================修改API首地址()=================
u;A7tB/]7I;[-i EJr0e05X"S9F${1DC&d)hQ-{0.子程序 修改API首地址, 逻辑型
!rh+Hd(i0.参数 Process, 整数型, , 目标进程句柄51Testing软件测试网+tN cV0N;I^
.参数 Papi, 整数型, , 要修改的API函数地址
_u'r9W2fXR9a0.参数 type, 字节集, , 要改写的内容,字节集
H a0X0U0g*b:Y0.局部变量 mbi, 虚拟信息
6rApD$~?*a0.局部变量 结果, 逻辑型
Gt8Nv,Uw^G0.局部变量 MyAPI, 整数型51Testing软件测试网Z6k6T CRwb
.局部变量 Ptype, 字节集
U}7P7s K[z0%NC(B/k'dA*U!`0.如果真 (Papi = 0)
[#u8je ^tL/U0 返回 (假)51Testing软件测试网3Lk)Z1[@"cQ
.如果真结束
$iF~$N3lgK0.如果真 (返回虚拟信息 (Process, Papi, mbi, 28) = 0)
y'Zi%B"pi0G?\0 返回 (假)51Testing软件测试网%q'\G8L'B-]Kb
.如果真结束51Testing软件测试网*s[hEf9]q;G(X
.如果真 (修改虚拟保护 (Process, mbi.BaseAddress, 取字节集长度 (type) + 1, #PAGE_EXECUTE_READWRITE, mbi.Protect) = 假)
*s&GZ
PT0 返回 (假)
gL"tG$H/ws0.如果真结束51Testing软件测试网&LN0g
Uo
结果 = 写内存字节 (Process, Papi, type, 取字节集长度 (type), 0)
Z$R@u
Dir+CLj0修改虚拟保护 (Process, mbi.BaseAddress, 取字节集长度 (type) + 1, #PAGE_EXECUTE_READ, mbi.Protect) ' 改回只读模式51Testing软件测试网4ZsegCQ
返回 (结果)51Testing软件测试网3[4^*G7RpD
*\NOGm!Ax8x;a,z;zI051Testing软件测试网g1^:MN[
========================51Testing软件测试网6n5[ l t;c/SfIgs
51Testing软件测试网#d
@lLJL3HS我们通过以下几个API就可以举出系统所有进程,得到它们的进程ID51Testing软件测试网(d c"Ef
@:l.n(}
51Testing软件测试网?h)L:m8lW6A*CreateToolhelp32Snapshot,创建进程快照
qSnqiv cY06O:W`3kO3Qu0*Process32First,开始进程快照
`h-q Ip6y e051Testing软件测试网_8t8s9Q2eka_*Process32Next, 继续进程快照51Testing软件测试网S,^ p+n']4L
f
uYIAZ0看代码:
od gRb2rqL
T9u0/z;C8dE-}0=========================刷新进程信息==============
!BV9u0Df&V