支付宝COM组件浅析
/**************************************
/* 作者:半斤八兩
/* 博客:http://cnblogs.com/bjblcracked
/* 日期:2014-01-08 12:01
/**************************************
只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
工具: ie 6.0 od1.0 支付宝
环境:xp sp3
在Ddv群里面有个朋友问我怎样给淘宝的密码框发送诸如wm_settext之类的消息. 心想现在也没工作了,反正闲着也是闲着,就看看.
首先直接打开www.支付宝.com 可以看到,密码框处会提示 请先安装控件. 然后浏览器上方会显示 需要下载的文件.
我们只要点击下载文件然后安装,安装后,直接刷新或者重新打开浏览器即可.
印象中,安装控件有的时候下载是exe文件,有的时候是直接下载com组件(非exe)
安装好了之后,我们就可以正常的登录了.我们来写一个发消息的,看看是否如那位朋友所说.
1 Private Sub Command1_Click() 2 3 SendMessage &H10B90, WM_SETTEXT, 0, "bjbl" 4 5 End Sub
点了按钮之后果真没有反映.
我们首先要确定消息是否有到达.
可以打开spy来看看.
通过spy回显,我们可以知道消息是已经到达了.
但是 fSucceeded:False 的提示.我们现在知道消息是到达的.
只是支付宝处理(过滤掉)了.
那么我们就来看看支付宝做了哪些”手脚”.
我们大家都知道支付宝是b/s框架的.(好像也有c/s的,本文只讲b/s).
我们来打开一个browser 然后看一下他用了哪些模块.
这里browser我用的ie 6.0 模块查看工具用的 xuetr(它支持直接显示非系统模块)
我们可以直观的看出目录为 3.6.0.0 的模块是可疑的.
既然是b/s那这些模块肯定就是com程序.
下面我们直接用od 加载 ie . 看看它的导出函数.
我们随便找个aliedit.dll 模块看看.
通过那四个 DLLxxxxx 的导出函数名,我们就能基本判定这个是com了.
Com的程序怎么分析? 看雪有一篇贴子有详细的介绍如何分析.
链接:跟踪调试COM组件的接口
文中的方法大致就是对oleaut32.dispcallfunc 下断点.然后com里面调用了哪些函数就都会断下来.
我自己写了一个com程序,用了这个方法试了一下,并没有断下来 :(
而支付宝断是会断下来,但并不是响应我们想要他断下来的消息.当我们输入密码的时候.它并不会断下来. 所以这个贴子的方法,对于我们来说,是没有用的 :(
这里给出另一种可用的方法.
首先go到 user32.getwindowthreadprocessid 这个函数下.
1 77D18A80 user32.> $ 8BFF mov edi,edi 2 77D18A82 . 55 push ebp 3 77D18A83 . 8BEC mov ebp,esp 4 77D18A85 . 56 push esi 5 77D18A86 . FF75 08 push dword ptr ss:[ebp+8] 6 77D18A89 . E8 38000000 call user32.77D18AC6 7 77D18A8E . 8BF0 mov esi,eax 8 77D18A90 . 85F6 test esi,esi 9 77D18A92 . 74 28 je short user32.77D18ABC 10 77D18A94 . E8 BCFBFFFF call user32.77D18655 11 77D18A99 . 3BF0 cmp esi,eax 12 77D18A9B .^ 0F85 49FCFFFF jnz user32.77D186EA 13 77D18AA1 . 8B4D 0C mov ecx,dword ptr ss:[ebp+C] 14 77D18AA4 . 85C9 test ecx,ecx 15 77D18AA6 . 74 0B je short user32.77D18AB3 16 77D18AA8 . 64:A1 1800000>mov eax,dword ptr fs:[18] 17 77D18AAE . 8B40 20 mov eax,dword ptr ds:[eax+20] 18 77D18AB1 . 8901 mov dword ptr ds:[ecx],eax 19 77D18AB3 > 64:A1 1800000>mov eax,dword ptr fs:[18] 20 77D18AB9 . 8B40 24 mov eax,dword ptr ds:[eax+24] 21 77D18ABC > 5E pop esi 22 77D18ABD . 5D pop ebp 23 77D18ABE . C2 0800 retn 8
其中
1 77D18A9B .^\0F85 49FCFFFF jnz user32.77D186EA
这里就是关键.
我们在go到 77D186EA 处
1 77D186EA > /8B75 0C mov esi,dword ptr ss:[ebp+C] 2 77D186ED . |85F6 test esi,esi 3 77D186EF . |74 0C je short user32.77D186FD 4 77D186F1 . |6A 00 push 0 5 77D186F3 . |FF75 08 push dword ptr ss:[ebp+8] 6 77D186F6 . |E8 E0FFFFFF call user32.77D186DB 7 77D186FB . |8906 mov dword ptr ds:[esi],eax 8 77D186FD > |6A 01 push 1 9 77D186FF . |FF75 08 push dword ptr ss:[ebp+8] 10 77D18702 . |E8 D4FFFFFF call user32.77D186DB 11 77D18707 . |E9 B0030000 jmp user32.77D18ABC 12 77D1870C $ |55 push ebp 13 77D1870D . |8BEC mov ebp,esp 14 77D1870F . |56 push esi 15 77D18710 . |57 push edi 16 77D18711 . |53 push ebx 17 77D18712 . |68 CDABBADC push DCBAABCD 18 77D18717 . |56 push esi 19 77D18718 . |FF75 18 push dword ptr ss:[ebp+18] 20 77D1871B . |FF75 14 push dword ptr ss:[ebp+14] 21 77D1871E . |FF75 10 push dword ptr ss:[ebp+10] 22 77D18721 . |FF75 0C push dword ptr ss:[ebp+C] 23 77D18724 . |64:A1 1800000>mov eax,dword ptr fs:[18] 24 77D1872A . |8088 B40F0000>or byte ptr ds:[eax+FB4],1 25 77D18731 . |FF55 08 call near dword ptr ss:[ebp+8] 26 77D18734 . |64:8B0D 18000>mov ecx,dword ptr fs:[18] 27 77D1873B . |80A1 B40F0000>and byte ptr ds:[ecx+FB4],0 28 77D18742 . |817C24 04 CDA>cmp dword ptr ss:[esp+4],DCBAABCD 29 77D1874A . |0F85 607C0200 jnz user32.77D403B0 30 77D18750 > |83C4 08 add esp,8 31 77D18753 . |5B pop ebx 32 77D18754 . |5F pop edi 33 77D18755 . |5E pop esi 34 77D18756 . |5D pop ebp 35 77D18757 . |C2 1400 retn 14
其中下面这句就是com的毕竟桥梁.我们在这里下个f2断点即可.
1 77D18731 . |FF55 08 call near dword ptr ss:[ebp+8]
我们还可以用另外一个方法.直接搜索这个数值: 0xDCBAABCD.
1 References in user32:.text to constant DCBAABCD 2 Address Disassembly 3 77D186F3 push dword ptr ss:[ebp+8] 4 77D18712 push DCBAABCD 5 77D18742 cmp dword ptr ss:[esp+4],DCBAABCD 6 77D403B0 cmp dword ptr ss:[esp],DCBAABCD
会有上面四个结果, 双击 cmp dword ptr ss:[esp+4],DCBAABCD 这一句进去.
这样就会看到和上面一个方法一样的结果了.
我们在刚刚 77D18731 com必经桥梁下好断点后.现在就可以去输入密码.
我们会发现还没有等我们输入密码,od就已经断下来了.
断下来我们可以看信息窗口显示.
Stack ss:[0013EA90]=06740FB0
我们go到 06740FB0 处.
从图中我们可以看出,当前是主线程.
而在06740FB8 处 可以看到,将要跳向flash32 这个com.
那么我们就可以认为这个是和我们无关的,我们直接f9.
又断下来了,我们接着看信息窗口.
Stack ss:[0013E464]=75F4348B (BROWSEUI.75F4348B)
这下,我们直接从信息窗口中可以看出是 browseui 这个模块.
我们一直f9 会发现一直中断在这几个无关的模块下.
那么我们可以用条件断点过滤掉.
我们首先看一下我们判断的那几个模块的地址.
1 Executable modules 2 Base Size Entry Name 3 07BF0000 00098000 07C852D0 Alidcp.dll 4 07A30000 000BC000 07AE90E0 aliedit.dll 5 07070000 0001D000 0707820C itrusenroll.dll 6 10000000 000C5000 100C2640 npAliSecCtrl.dll 7 070A0000 00021000 070B19AB pta.dll 8 00400000 00019000 00402451 IEXPLORE.EXE
知道地址后,我们可以对着必经桥梁按下shift+f2 写入下面的条件断点.
因为这几个模块地址的跨度比较大,为了省时间就不写复杂的条件语句了.
我们就一个一个的试过去.
1 aliedit.dll 2 [ebp+8]>=7a30000&[ebp+8]<=7AEC000 3 4 Alidcp.dll 5 [ebp+8]>=7bf0000&[ebp+8]<=7C88000 6 7 itrusenroll.dll 8 [ebp+8]>=7070000&[ebp+8]<=708D000 9 10 npAliSecCtrl.dll 11 [ebp+8]>=10000000&[ebp+8]<=100C5000 12 13 pta.dll 14 [ebp+8]>=70A0000&[ebp+8]<=70C1000 15 16 IEXPLORE.EXE 17 [ebp+8]>=400000&[ebp+8]<=419000
全部试下来,发现都没有中断.这是哪里的问题?
大家还记得刚刚中断的时候,是在主线程里面吧?所以我们这样写是不行的.
应该像下面这样.
[ebp+8]!=06740FB0&[ebp+8]<=7a30000
断下来了,看信息窗口.
Stack ss:[0013E77C]=06660F90
GO过去看反汇编.
1 06660F90 C74424 04 104BE>mov dword ptr ss:[esp+4],2E44B10 2 06660F98 - E9 D8529A09 jmp npAliSec.10006275
可以看到这时才是jmp 到 支付宝的模块里面了.
用此方法可以方便的定位到支付宝的com组件处理流程里面.
此方法我也测试过我写的com demo,也是可以轻松定位到的 :)
剩下的我们就是直接到 10006275分析即可.
之前的条件断点取消掉,在 10006275处下断点. F9运行.
1 10006275 /. 55 push ebp 2 10006276 |. 8BEC mov ebp,esp 3 10006278 |. 83EC 24 sub esp,24 4 1000627B |. 56 push esi 5 1000627C |. 8B75 08 mov esi,[arg.1] 6 1000627F |. 85F6 test esi,esi 7 10006281 |. 0F84 C1000000 je npAliSec.10006348 8 10006287 |. 8B06 mov eax,dword ptr ds:[esi] 9 10006289 |. 85C0 test eax,eax 10 1000628B |. 0F84 B7000000 je npAliSec.10006348 11 10006291 |. 837E 1C 00 cmp dword ptr ds:[esi+1C],0 12 10006295 |. 0F84 AD000000 je npAliSec.10006348 13 1000629B |. 57 push edi 14 1000629C |. 6A 01 push 1 15 1000629E |. FF75 14 push [arg.4] 16 100062A1 |. 8D4D DC lea ecx,[local.9] 17 100062A4 |. FF75 10 push [arg.3] 18 100062A7 |. FF75 0C push [arg.2] 19 100062AA |. 50 push eax 20 100062AB |. E8 B8F4FFFF call npAliSec.10005768 21 100062B0 |. FF76 20 push dword ptr ds:[esi+20] 22 100062B3 |. 8B7E 24 mov edi,dword ptr ds:[esi+24] 23 100062B6 |. 8B4E 1C mov ecx,dword ptr ds:[esi+1C] 24 100062B9 |. 8D55 08 lea edx,[arg.1] 25 100062BC |. 52 push edx 26 100062BD |. FF75 14 push [arg.4] 27 100062C0 |. 8D45 DC lea eax,[local.9] 28 100062C3 |. FF75 10 push [arg.3] 29 100062C6 |. 8946 24 mov dword ptr ds:[esi+24],eax 30 100062C9 |. FF75 0C push [arg.2] 31 100062CC |. 8B01 mov eax,dword ptr ds:[ecx] 32 100062CE |. FF36 push dword ptr ds:[esi] 33 100062D0 |. FF10 call near dword ptr ds:[eax] 34 100062D2 |. 897E 24 mov dword ptr ds:[esi+24],edi 35 100062D5 |. 85C0 test eax,eax 36 100062D7 |. 75 69 jnz short npAliSec.10006342 37 100062D9 |. 53 push ebx 38 100062DA |. BB 82000000 mov ebx,82 39 100062DF |. 395D 0C cmp [arg.2],ebx 40 100062E2 |. 74 15 je short npAliSec.100062F9 41 100062E4 |. FF75 14 push [arg.4] 42 100062E7 |. 8BCE mov ecx,esi 43 100062E9 |. FF75 10 push [arg.3] 44 100062EC |. FF75 0C push [arg.2] 45 100062EF |. E8 B5F3FFFF call npAliSec.100056A9 46 100062F4 |. 8945 08 mov [arg.1],eax 47 100062F7 |. EB 48 jmp short npAliSec.10006341 48 100062F9 |> 8B06 mov eax,dword ptr ds:[esi] 49 100062FB |. 8B3D ECF20710 mov edi,dword ptr ds:[1007F2EC] 50 10006301 |. 6A FC push -4 51 10006303 |. 50 push eax 52 10006304 |. FFD7 call near edi 53 10006306 |. FF75 14 push [arg.4] 54 10006309 |. 8BCE mov ecx,esi 55 1000630B |. FF75 10 push [arg.3] 56 1000630E |. 8945 0C mov [arg.2],eax 57 10006311 |. 53 push ebx 58 10006312 |. E8 92F3FFFF call npAliSec.100056A9 59 10006317 |. 8945 08 mov [arg.1],eax 60 1000631A |. 8B46 18 mov eax,dword ptr ds:[esi+18] 61 1000631D |. 3B05 D4F20710 cmp eax,dword ptr ds:[1007F2D4] 62 10006323 |. 74 19 je short npAliSec.1000633E 63 10006325 |. 8B06 mov eax,dword ptr ds:[esi] 64 10006327 |. 6A FC push -4 65 10006329 |. 50 push eax 66 1000632A |. FFD7 call near edi 67 1000632C |. 3B45 0C cmp eax,[arg.2] 68 1000632F |. 75 0D jnz short npAliSec.1000633E 69 10006331 |. FF76 18 push dword ptr ds:[esi+18] 70 10006334 |. 6A FC push -4 71 10006336 |. FF36 push dword ptr ds:[esi] 72 10006338 |. FF15 E8F20710 call near dword ptr ds:[1007F2E8] 73 1000633E |> 8326 00 and dword ptr ds:[esi],0 74 10006341 |> 5B pop ebx 75 10006342 |> 8B45 08 mov eax,[arg.1] 76 10006345 |. 5F pop edi 77 10006346 |. EB 02 jmp short npAliSec.1000634A 78 10006348 |> 33C0 xor eax,eax 79 1000634A |> 5E pop esi 80 1000634B |. C9 leave 81 1000634C \. C2 1000 retn 10
立马中断, 再F9运行,又中断, 如是重复, 再通过堆栈观察不难看出是下面四个参数.
1 BOOL IsDealMsg( 2 3 LPMSG lpMsg, // message information 4 5 HWND hWnd, // handle to window 6 7 UINT wMsgFilterMin,// first message 8 9 UINT wMsgFilterMax // last message);
我们把里面的msg都纪录一下.大概有如下一些消息.
1 #define WM_SETFOCUS 0x0007 2 #define WM_KILLFOCUS 0x0008 3 #define WM_SETTEXT 0x000C 4 #define WM_GETTEXT 0x000D 5 #define WM_PAINT 0x000F 6 #define WM_ERASEBKGND 0x0014 7 #define WM_CANCELMODE 0x001F 8 #define WM_SETCURSOR 0x0020 9 #define WM_MOUSEACTIVATE 0x0021 10 #define WM_WINDOWPOSCHANGING 0x0046 11 #define WM_WINDOWPOSCHANGED 0x0047 12 #define WM_NCCALCSIZE 0x0083 13 #define WM_NCHITTEST 0x0084 14 #define WM_NCPAINT 0x0085 15 #define WM_NCMOUSEMOVE 0x00A0 16 #define WM_CTLCOLOREDIT 0x0133 17 #define WM_MOUSEFIRST 0x0200 18 #define WM_LBUTTONDOWN 0x0201 19 #define WM_LBUTTONUP 0x0202 20 #define WM_CAPTURECHANGED 0x0215 21 #define WM_IME_SETCONTEXT 0x0281 22 #define WM_IME_NOTIFY 0x0282 23 24 #define EM_GETSEL 0x00B0 25 #define EM_SETSEL 0x00B1
我们自己发的消息是wm_settext 对应的值是 0xc
那么我们直接在函数首下个条件断点 [esp+8]==0xc 即可
断下来后我们单步,走到这里的时候不该跳的,他却跳了.所以,我们只要把这句nop掉即可 :)
1 100062D7 |. /75 69 jnz short npAliSec.10006342
Nop 后, 我们来用工具测试一下.
测试是okay的.我们的目的就达到了 :)
但是大家应该能看出好像哪里还有问题.
没错,问题就出现在密码是明文的了.我们点登录测试,
也是不成功的,会提示 “系统忙,请稍候再试” 的字样.
当我们再在 10006275 处下好wm_settext 条件断点.
然后直接运行, 在密码框处 输入 “b”
然后中断看 lparam 这个参数. 里面直接显示的是 “*”
同样用工具测试的时候,查看 lparam 而是显示 “bjbl”
说明在wm_settext 之前还做了些手脚.就okay了.
考虑到有些童鞋拿去做坏事,剩下的,就不写了.后面的分析方法和前面讲的都差不多.
那位朋友又问我, 如果不patch 可以实现吗?
方法还是有很多的. 比如 patch com文件 ,(当然也可能会有Crc 文件完整性效验)
甚至我们重写一份 edit 的 com 程序,也是可以的.
本文主要还是讲解com组件的调试分析方法,大家不要做坏事~
com demo和pdf下载地址:<看雪学院>