WoW的服务器会不定时发送SMSG_WARDEN_DATA(0x2E6),它包含了一段加密了的程序,客户端收到后解密执行,然后返回CMSG_WARDEN_DATA(0x2E5)包含了执行的结果。
这段程序的codename叫warden,主要是针对客户端修改和外挂的检测,网上老外有点研究,都藏着当宝贝,最近没事就稍微跟了一下,写点粗略的分析。
首先OD加载WOW.EXE,查找常量2E6,发现有三处,逐个分析:
006D0740 $ B9 EC18E100 mov ecx, 00E118EC ; ASCII "x?"
006D0745 . E8 568CF7FF call 006493A0
006D074A . E8 91FEFFFF call 006D05E0
006D074F . E8 CCFEFFFF call 006D0620
006D0754 . A1 DC18E100 mov eax, dword ptr [E118DC]
006D0759 . 85C0 test eax, eax
006D075B . 74 12 je short 006D076F
006D075D . 6A 00 push 0
006D075F . 68 5C020000 push 25C
006D0764 . 68 6C0A8D00 push 008D0A6C ; ASCII "./WardenClient.cpp"
006D0769 . 50 push eax
006D076A . E8 C13AF7FF call 00644230
006D076F > 68 E6020000 push 2E6
006D0774 . C705 DC18E100>mov dword ptr [E118DC], 0
006D077E . C705 E018E100>mov dword ptr [E118E0], 0
006D0788 . E8 5335EEFF call 005B3CE0
006D078D . 83C4 04 add esp, 4
006D0790 . B9 EC18E100 mov ecx, 00E118EC ; ASCII "x?"
006D0795 .^ E9 168CF7FF jmp 006493B0
跟入,
005B3CE0 /$ 55 push ebp
005B3CE1 |. 8BEC mov ebp, esp
005B3CE3 |. 8B45 08 mov eax, dword ptr [ebp+8]
005B3CE6 |. 50 push eax
005B3CE7 |. E8 94FEFFFF call 005B3B80
005B3CEC |. 8BC8 mov ecx, eax
005B3CEE |. E8 2DB7FAFF call 0055F420
005B3CF3 |. 5D pop ebp
005B3CF4 /. C3 retn
再跟,
005B3B80 /$ A1 2C33D400 mov eax, dword ptr [D4332C]
005B3B85 /. C3 retn
这个地方咋一看是个全局变量,也许以后用的到,做个记号。
第二个call,
0055F420 /$ 55 push ebp
0055F421 |. 8BEC mov ebp, esp
0055F423 |. 8B45 08 mov eax, dword ptr [ebp+8]
0055F426 |. 33D2 xor edx, edx
0055F428 |. 895481 74 mov dword ptr [ecx+eax*4+74], edx
0055F42C |. 899481 041100>mov dword ptr [ecx+eax*4+1104], edx
0055F433 |. 5D pop ebp
0055F434 /. C2 0400 retn 4
这是什么?我当时看到第一个反应就是,DISPATCH TABLE
再回想一下,前面那个全局,在这里是调度表基址。
再看第二个常量参考,
006D0BA0 /. 55 push ebp
006D0BA1 |. 8BEC mov ebp, esp
006D0BA3 |. 51 push ecx
006D0BA4 |. 817D 0C E6020>cmp dword ptr [ebp+C], 2E6
006D0BAB |. 74 06 je short 006D0BB3
006D0BAD |. 33C0 xor eax, eax
006D0BAF |. 8BE5 mov esp, ebp
006D0BB1 |. 5D pop ebp
006D0BB2 |. C3 retn
006D0BB3 |> 56 push esi
006D0BB4 |. B9 EC18E100 mov ecx, 00E118EC ; ASCII "x?"
006D0BB9 |. E8 E287F7FF call 006493A0
006D0BBE |. 8B4D 14 mov ecx, dword ptr [ebp+14]
006D0BC1 |. 8B71 10 mov esi, dword ptr [ecx+10]
006D0BC4 |. 2B71 14 sub esi, dword ptr [ecx+14]
006D0BC7 |. 8D45 FC lea eax, dword ptr [ebp-4]
006D0BCA |. 56 push esi
006D0BCB |. 50 push eax
006D0BCC |. E8 0F58D5FF call 004263E0
006D0BD1 |. 833D D418E100>cmp dword ptr [E118D4], 0
006D0BD8 |. C745 0C 00000>mov dword ptr [ebp+C], 0
006D0BDF |. 74 1B je short 006D0BFC
006D0BE1 |. 8B0D D818E100 mov ecx, dword ptr [E118D8]
006D0BE7 |. 8B11 mov edx, dword ptr [ecx]
006D0BE9 |. 8B52 08 mov edx, dword ptr [edx+8]
006D0BEC |. 8D45 0C lea eax, dword ptr [ebp+C]
006D0BEF |. 50 push eax
006D0BF0 |. 8B45 FC mov eax, dword ptr [ebp-4]
006D0BF3 |. 56 push esi
006D0BF4 |. 50 push eax
006D0BF5 |. FFD2 call edx
006D0BF7 |. E8 84FAFFFF call 006D0680
006D0BFC |> B9 EC18E100 mov ecx, 00E118EC ; ASCII "x?"
006D0C01 |. E8 AA87F7FF call 006493B0
006D0C06 |. B8 01000000 mov eax, 1
006D0C0B |. 5E pop esi
006D0C0C |. 8BE5 mov esp, ebp
006D0C0E |. 5D pop ebp
006D0C0F /. C3 retn
现在一时还不好说,先放着,看第三个
006D0C56 |. 6A 00 push 0
006D0C58 |. 68 A00B6D00 push 006D0BA0
006D0C5D |. 68 E6020000 push 2E6
006D0C62 |. A3 E418E100 mov dword ptr [E118E4], eax
006D0C67 |. E8 5430EEFF call 005B3CC0
这里,我们已经知道这个call是写调度表,那么,handler的信息在哪呢?看看这个参数006D0BA0,是不是很熟悉?他正好是我们刚才找到的第二个参考的函数起始,原来那个就是handler。
于是一切都明了了,我们在handler上下断点,从call stack找到了这里:
0055F440 /$ 55 push ebp
0055F441 |. 8BEC mov ebp, esp
0055F443 |. 8305 B898D200>add dword ptr [D298B8], 1
0055F44A |. 53 push ebx
0055F44B |. 8B5D 0C mov ebx, dword ptr [ebp+C]
0055F44E |. 56 push esi
0055F44F |. 57 push edi
0055F450 |. 8D45 0C lea eax, dword ptr [ebp+C]
0055F453 |. 8BF1 mov esi, ecx
0055F455 |. 50 push eax
0055F456 |. 8BCB mov ecx, ebx
0055F458 |. E8 036DECFF call 00426160
0055F45D |. 0FB77D 0C movzx edi, word ptr [ebp+C]
0055F461 |. 8B16 mov edx, dword ptr [esi]
0055F463 |. 8B42 40 mov eax, dword ptr [edx+40]
0055F466 |. 57 push edi
0055F467 |. 8BCE mov ecx, esi
0055F469 |. FFD0 call eax
0055F46B |. 8B44BE 74 mov eax, dword ptr [esi+edi*4+74]
0055F46F |. 85C0 test eax, eax
0055F471 |. 74 1A je short 0055F48D
0055F473 |. 8B4D 08 mov ecx, dword ptr [ebp+8]
0055F476 |. 8B94BE 041100>mov edx, dword ptr [esi+edi*4+1104]
0055F47D |. 53 push ebx
0055F47E |. 51 push ecx
0055F47F |. 57 push edi
0055F480 |. 52 push edx
0055F481 |. FFD0 call eax
0055F483 |. 83C4 10 add esp, 10
0055F486 |. 5F pop edi
0055F487 |. 5E pop esi
0055F488 |. 5B pop ebx
0055F489 |. 5D pop ebp
0055F48A |. C2 0800 retn 8
0055F48D |> 8B03 mov eax, dword ptr [ebx]
0055F48F |. 8B50 18 mov edx, dword ptr [eax+18]
0055F492 |. 8BCB mov ecx, ebx
0055F494 |. FFD2 call edx
0055F496 |. 5F pop edi
0055F497 |. 5E pop esi
0055F498 |. 5B pop ebx
0055F499 |. 5D pop ebp
0055F49A /. C2 0800 retn 8
很明显,一个查表跳转,注意这个mov edx, dword ptr [esi+edi*4+1104],这是一个传给handler的全局参数,经常带的是DISPATCH TABLE的基址,用来在handler里继续注册handler。
至于怎么对付它的检测,就变得简单了,有了DISPATCH TABLE,有了handler,由于这个函数本身没有seh,可以利用dr断点实现纯ring3的过检测。
005B3B80 Get handler table base
BASE+MSG_ID*4+74 handler address
与国内的反外挂系统相比起来,这种方法的框架结构相对好点,灵活性也强,可以在handler里重新定义所有的handler,甚至可以跳出这个体系重新建立新的DISPATCH函数。目前这段服务器下发的stub还只是简单的做hash效验,处于起步阶段。