【转帖】Windows Hook (3) API Hook 初探

上一篇 / 下一篇  2010-01-20 12:04:59 / 个人分类:C++

 

B+K:|%G@PO6qL0.....51Testing软件测试网.} e5iA oB5j

u9G1gws0Q0上一期,我们讲了用HOOK技术实现远程线程插入,相信大家还记忆犹新.51Testing软件测试网9Rb*R5G%Sy] UO

51Testing软件测试网!Qf6K0wnv$Gq

这一期我们来谈谈 API HOOK

d2E,@ d,l|y#U8uN ^*m051Testing软件测试网 A Ur)[jI qzv

    API Hook技术应用广泛,常用于屏幕取词,网络防火墙,病毒木马,加壳软件,串口红外通讯,游戏外

W+Z%{@zp^C?0

2TVR8_ 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'py W4b2U2z'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"cm o3|+Y7y0

*pl0Lit{,Z~3m$yq0的可执行文件)的文件格式,这种文件格式

p3n]$Gimw0

G bhu3AE2}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\7ru-aA M0      |       PE 标志 ("PE")            |51Testing软件测试网4GZ6db cU
      +-------------------------------+
t}G z~+T0      |              .text              |      - 模块代码51Testing软件测试网 x vS6R1?{z
      |            程序代码             |51Testing软件测试网1t8b(MuW{1t&z&r&q9G
      |                                |51Testing软件测试网6l b[ _4]NX
      +-------------------------------+51Testing软件测试网 m9w^+` Ws4l+b
      |              .data              |      - 已初始化的(全局静态)数据51Testing软件测试网3M%D'p3ba.R G
      |           已初始化的数据        |51Testing软件测试网E*X8tk-]l
      |                                |
OX/\4j l.{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 YUz TOE
      |            调试符号             |
{f7DN0be2U0      +-------------------------------+51Testing软件测试网s/G D"E|

(i(L)A6Nm^0    
o B] CqU s|Y051Testing软件测试网 tGd2O vm
    这里对我们比较重要的是.idata部分的导入地址表(IAT)。这个部分包含了导入的相关信息和导51Testing软件测试网 FuPVl

3XE;i!vZ'q/lv0入函数的地址。有一点很重要的是我们必须知道PE文件是如何创建的。当在编程语言里间接调用任

:U:ul0Zr;wO5t0

4h0G*RN2hd0意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并不直接把调用连接到

o5w)T2fb0

K 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指令并重定向代码到我们的地址。完

*ou{4h\;a051Testing软件测试网 d%@SNM

成之后每次调用都会执行我们的代码了。
p Y&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(换长数据)元素都描述一个函数。此数组是输入地址表的一部分,并且可以改变。

qr9F yh;G0

Xz2Lg:{'C \#r0   51Testing软件测试网)Z'@&a8QC { A2|p
    如果我们找到了就必须用VirtualProtectEx函数来改变内存页面的保护属性,然后就可以通过51Testing软件测试网 Z(@r/jD8L3u F
WriteProcessMemory在内存中的这些部分写入代码了。在改写了地址之后我们要把保护属性改回来
9J9w]~`x0。在调用VirtualProtectEx之前我们还要先知道有关页面的信息,这通过VirtualQueryEx来实现。51Testing软件测试网6}#E]u MOj/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{NXlK b
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&\#Ijm6_

51Testing软件测试网`(D&F"R*s"J6bp

51Testing软件测试网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 rX T

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;K4rlR U(T

当作我留给大家的问题吧,呵呵.               51Testing软件测试网Na+~"hq+Nok]Ea1G3d

k*Hg+Z:k| [` h0制作目标:挂勾ExitWindowsEx,使关机无效.51Testing软件测试网B Y;lx5N!_

#z@T)MZ%Z-N0分析:只要求关机无效,即ExitWindowsEx无效就可以了,我们不必那么麻烦去写跳转指令,可以直接在
/Y}(xr G-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 AB0

J0W2i9N4@ 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软件测试网#BkDFa2cQLE Y$A$f
API = GetProcAddress(GetModuleHandleA(“user32.dll”), “ExitWindowsEx”)51Testing软件测试网;i,oX9C F-uP

51Testing软件测试网%w+b9qz { Dv}*O

51Testing软件测试网"gnV%R!B!zM
API_BAK = 指针到字节集 (API, 1)   '为了以后可以还原,我们先备份一下

p.|$oR%w+];p0

p tPi!YM0'用OpenProcess打开其他进程前先提升进程权限,这样可以打开几乎所有的非内核程序51Testing软件测试网6r9K _`yU] n

_8RjR L.yfr^0提升进程权限 ()   '这个模块在附件里

%eYvo"K0

1HI^V@6DV0
'na7_s8sZ:^8K0==============================修改API首地址()=================

u;A7t B/]7I;[-i EJ r0e0

5X"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软件测试网Z6k6TCRwb
.局部变量 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[hE f9]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软件测试网4Zs egCQ
返回 (结果)51Testing软件测试网3[4^*G7RpD

*\N OGm!Ax8x;a,z;zI051Testing软件测试网g1^:MN[
========================51Testing软件测试网6n5[ lt;c/SfI gs

51Testing软件测试网#d @l L J L3HS

我们通过以下几个API就可以举出系统所有进程,得到它们的进程ID51Testing软件测试网(d c"Ef @:l.n(}

51Testing软件测试网?h)L:m8lW6A

*CreateToolhelp32Snapshot,创建进程快照

qSnqiv cY0

6O:W`3kO3Qu0*Process32First,开始进程快照  

`h-q Ip6y e051Testing软件测试网_8t8s9Q2eka_

*Process32Next, 继续进程快照51Testing软件测试网S,^ p+n']4L

f uYIAZ0看代码:

odgRb2rqL T9u0

/z;C8dE-}0=========================刷新进程信息==============
!BV9u0Df&VF*? ~u0.子程序 刷新进程信息
#|r2Y p$z#qWs0|d0.参数 进程信息输出, 进程信息输出, 数组
gyZ9N,w0.局部变量 临时变量, 整数型51Testing软件测试网+}\q#Z?~
.局部变量 临时变量B, 整数型51Testing软件测试网 D ?B7I ~y3t*Q
.局部变量 线程基本优先级, 整数型, , "0"
n N+Ba:t'doc{B0.局部变量 临时进程信息, 进程信息输出

8PFI |`.s/J-g0

P;{l$AYZ,t!y\0临时变量 = 创建进程快照 (2, 0)
1UT(D)A\Mc0临时进程信息.结构大小 = 296
q8L1jy3^uQ0临时变量B = 开始进程快照 (临时变量, 临时进程信息)51Testing软件测试网N1k3{S\"Py.]
.判断循环首 (临时变量B ≠ 0)
] f2x`w:D em+Q(r0     加入成员 (进程信息输出, 临时进程信息)
9{"B&{t&|j0     临时变量B = 继续进程快照 (临时变量, 临时进程信息)51Testing软件测试网h,fBuuD
.判断循环尾 ()
/B9DK3]E7}8_0关闭内核对象 (临时变量)51Testing软件测试网/p1m#] @MV-[J

.N.i!Qf;E*F k0============================HOOK_API()================================

e@+_@*O/d3w0

:`CJ&vQPx)~0.子程序 HOOK_API51Testing软件测试网/s6w U)S`X
.参数 是否HOOK, 逻辑型, , 真,表示HOOK,假,表示还原
!Y)w8{ q0p8\J0.局部变量 i, 整数型51Testing软件测试网&p"b2^0g$b h)L

51Testing软件测试网0f+vE.t9v1\ t&_] Y%U

刷新进程信息 (进程信息)51Testing软件测试网iNG)} O1I%\
.计次循环首 (取数组成员数 (进程信息), i)51Testing软件测试网^j4CnE ft~
     进程句柄 = 打开进程 (2035711, 0, 进程信息 [i].进程标识)
&{.G&t1@g/wb%g0     .如果 (是否HOOK)
6E*N3[Nn;W0         修改API首地址 (进程句柄, API, { 195 })   ' 写入返回命令{195}51Testing软件测试网%s4v'gk9W#oXp;q$P
     .否则51Testing软件测试网1S/v/M So&Fe
         修改API首地址 (进程句柄, API, API_BAK)51Testing软件测试网GS8sAWG
     51Testing软件测试网[8|6|DP7o)\B
      关闭内核对象 (进程句柄)
P`4K9a w d'tf/J0.计次循环尾 ()

h8g&l.mp;v |%B051Testing软件测试网$W a$VD)P9y

=================================================================51Testing软件测试网wS+b'kO8xws{B

Ex,df;G pRo0e0最后我们通过调用HOOK_API(真)就可以挂勾ExitWindowsEx,

,Og#T ^:l051Testing软件测试网~{8g;IfF

调用HOOK_API (假)就可以还原ExitWindowsEx.

z)Z"e9p.J051Testing软件测试网 }q~2T3]J]!q}

继续看代码:51Testing软件测试网M4yR b-H)d4v4l j@A

51Testing软件测试网Ym0M yLWu,@

======================================51Testing软件测试网2s7`;n3o&\

51Testing软件测试网(d/nI*h)}nr

.子程序 _选择框_挂勾API_被单击51Testing软件测试网9Oy f'TN3i*R

51Testing软件测试网o$~+Vx/FVYMQ

.如果 (选择框_挂勾API.标题 = “挂勾API”)51Testing软件测试网GUs rg?$b(@z
     HOOK_API (真)51Testing软件测试网(mN [\pN f e
     选择框_挂勾API.标题 = “还原”51Testing软件测试网,x/P2g6XE7Q'b
     信息框 (“恭喜,挂勾ExitWindowsEx成功,你现在不能关闭系统了,不信你可以试试!”, 64, )51Testing软件测试网)L,[x{l
.否则51Testing软件测试网v?@It7{?.URq
     HOOK_API (假)
G1\0F} Rb,f hS$U0     选择框_挂勾API.标题 = “挂勾API”51Testing软件测试网-A8{ HA2G3ho ~V6V
     信息框 (“提示:还原ExitWindowsEx成功,你现在可以关闭系统了!”, 48, “提示”)51Testing软件测试网 |S"Ba.l$L G
     结束 ()

U I&j'Q ^7yQ051Testing软件测试网7}/^r#L4I sM-l*Pq

=======================================

+a%FL"Yj(r*b051Testing软件测试网&M| mM{#v$Z `

题外话:

#G&DZ8U L&X3_0

Lhm YF(MK$s0   有了这个程序,再结合API函数ShowWindow,控制网管软件窗口的可见性,就可以破解几乎所有网管

;trX7p"C'};O9S;f0

0a B1EeKq0软件的限制,不过,我不同意你用它拿来破解网管软件,我之所以这么说,只是想告诉大家一个道理:51Testing软件测试网LWt` o @2F

4F{8D M0o%w(ltS0思维不要只限制到一个很狭隘的空间中去......51Testing软件测试网 Bec3R4EA ~'I

51Testing软件测试网*S$Q'Nln+O

==========================================
p6SGf-lv!I0总结:51Testing软件测试网L?IGw#q6j/K+z

_@&f1@8acJ0   怎么样,是不是很简单啊,呵呵,通过本期的学习,我们的Windows Hook 易核心编程也就告一段落了51Testing软件测试网O+R+j[%{Y&j

51Testing软件测试网pb)jn(mE

希望对你有所帮助,如果你想了解上期的内容,请在论坛里搜索我的名字.

9U$lp7AjcX8I0

\%u nUf0自由 平等 随意 突破......

'H8pj^&J0hs vb051Testing软件测试网8n5Jgifj~'T

此文献给所有易的支持者51Testing软件测试网Z/d;i1i9P+r

.[`[.R!q ]0下次再见.51Testing软件测试网cOy;\q)z1A{


TAG:

 

评分:0

我来说两句

Open Toolbar