【文章标题】: 突破封锁线:第四章--代码篇
【文章作者】: Austin
【作者邮箱】: austiny.cn@gmail.com
【软件名称】: 神州数码网络客户登陆程序
【软件大小】: 不重要
【下载地址】: 自己搜索下载
【加壳方式】: UltraProtect 1.x
【保护方式】: 加壳 网络封禁
【编写语言】: VC6.0
【使用工具】: OllyDbg VC6.0 LordPE
【操作平台】: Windows2003
【软件介绍】: 神州数码小区宽带网络登陆客户端,诸多限制
【作者声明】: 技术交流中。。。
--------------------------------------------------------------------------------
【详细过程】
一、(停电)蜡烛 + 收音机 = 前情回顾
上一回说到,我们找到了DIGI的弱点,我们来突破它。。。
二、(热身)第一次的亲密接触
我们的计划是,截获DIGI发包的函数,用我们自己的发包函数来替代它发出伪造过的包。
(据说在欧洲,穿假名牌是要脱光光的。[口水],真是好制度啊)
用OllyDbg打开DIGI,停在入口处:
0040FB74 >/$ 68 A82A4100 push 00412AA8
0040FB79 |. 68 D4FC4000 push <jmp.&msvcrt._except_handler3> ; SE 处理程序安装
0040FB7E |. 64:A1 0000000>mov eax, fs:[0]
网络应用程序发送数据包,最后都要用到wsock32.dll中的sendto函数。找到这个函数,就能找到调用它的发包函数。
在CPU窗口中点击Ctrl+N查看当前模块中的名称,果然发现了对sendto函数的引用:
...
00411538 .rdata 输入 ( user32.SendMessageA
00411598 .rdata 输入 ( wsock32.sendto
00411064 .rdata 输入 ( kernel32.SetEvent
...
光标移到sendto项上,回车查看输入函数参考:
参考位于 digi_dum:.text 到 wsock32.sendto
地址 反汇编 注释
00403A10 call <jmp.&wsock32.sendto>
00403A31 call <jmp.&wsock32.sendto>
00403A52 call <jmp.&wsock32.sendto>
00403A73 call <jmp.&wsock32.sendto>
00403CC3 call <jmp.&wsock32.sendto>
00403CE8 call <jmp.&wsock32.sendto>
00403D07 call <jmp.&wsock32.sendto>
00403ECF call <jmp.&wsock32.sendto>
004040F9 call <jmp.&wsock32.sendto>
0040542F call <jmp.&wsock32.sendto>
004083F4 call <jmp.&wsock32.sendto>
0040FD50 jmp [<&wsock32.sendto>] WS2_32.sendto
出去最后一项是一个跳转,我们来挨个考察到底哪个调用是发包函数:
在第一项上回车,在cpu窗口中来到00403A10处:
004039FF . 8BF0 mov esi, eax
00403A01 . 8D4424 24 lea eax, [esp+24]
00403A05 . 6A 10 push 10 ; /ToLength = 10 (16.)
00403A07 . 50 push eax ; |pTo
00403A08 . 55 push ebp ; |Flags
00403A09 . 8D4C24 40 lea ecx, [esp+40] ; |
00403A0D . 56 push esi ; |DataSize
00403A0E . 51 push ecx ; |Data
00403A0F . 52 push edx ; |Socket => 0
00403A10 . E8 3BC30000 call <jmp.&wsock32.sendto> ; \sendto
00403A15 . 68 4A010000 push 14A
00403A1A . FFD3 call ebx
00403A1C . 8B15 386C4100 mov edx, [416C38]
00403A22 . 8D4424 24 lea eax, [esp+24]
00403A26 . 6A 10 push 10 ; /ToLength = 10 (16.)
00403A28 . 50 push eax ; |pTo
00403A29 . 55 push ebp ; |Flags
00403A2A . 8D4C24 40 lea ecx, [esp+40] ; |
00403A2E . 56 push esi ; |DataSize
00403A2F . 51 push ecx ; |Data
00403A30 . 52 push edx ; |Socket => 0
00403A31 . E8 1AC30000 call <jmp.&wsock32.sendto> ; \sendto
00403A36 . 68 4A010000 push 14A
00403A3B . FFD3 call ebx
00403A3D . 8B15 386C4100 mov edx, [416C38]
00403A43 . 8D4424 24 lea eax, [esp+24]
00403A47 . 6A 10 push 10 ; /ToLength = 10 (16.)
00403A49 . 50 push eax ; |pTo
00403A4A . 55 push ebp ; |Flags
00403A4B . 8D4C24 40 lea ecx, [esp+40] ; |
00403A4F . 56 push esi ; |DataSize
00403A50 . 51 push ecx ; |Data
00403A51 . 52 push edx ; |Socket => 0
00403A52 . E8 F9C20000 call <jmp.&wsock32.sendto> ; \sendto
00403A57 . 68 4A010000 push 14A
00403A5C . FFD3 call ebx
00403A5E . 8B15 386C4100 mov edx, [416C38]
00403A64 . 8D4424 24 lea eax, [esp+24]
00403A68 . 6A 10 push 10 ; /ToLength = 10 (16.)
00403A6A . 50 push eax ; |pTo
00403A6B . 55 push ebp ; |Flags
00403A6C . 8D4C24 40 lea ecx, [esp+40] ; |
00403A70 . 56 push esi ; |DataSize
00403A71 . 51 push ecx ; |Data
00403A72 . 52 push edx ; |Socket => 0
00403A73 . E8 D8C20000 call <jmp.&wsock32.sendto> ; \sendto
00403A78 . A1 386C4100 mov eax, [416C38]
00403A7D . 5F pop edi
00403A7E . 3BC5 cmp eax, ebp
00403A80 . 74 0C je short 00403A8E
00403A82 . 50 push eax ; /Socket => 0
00403A83 . E8 C2C20000 call <jmp.&wsock32.closesocket> ; \closesocket
一眼扫去,不免多看了几行,发现这里有连续四次对sendto的调用,也就是说连续发出了四个数据包。
回忆一下上一章的内容,。。。,对了,只有在断开连接的时候才会发出4个包的,所以前4个调用都可以排除。
看看第5个,00403CC3:
00403CB5 |. 8BF0 mov esi, eax
00403CB7 |. 6A 10 push 10 ; /ToLength = 10 (16.)
00403CB9 |. 52 push edx ; |pTo
00403CBA |. 6A 00 push 0 ; |Flags = 0
00403CBC |. 8D4424 34 lea eax, [esp+34] ; |
00403CC0 |. 56 push esi ; |DataSize
00403CC1 |. 50 push eax ; |Data
00403CC2 |. 51 push ecx ; |Socket => 0
00403CC3 |. E8 88C00000 call <jmp.&wsock32.sendto> ; \sendto
00403CC8 |. 8B3D 7C104100 mov edi, [<&kernel32.Sleep>] ; kernel32.Sleep
00403CCE |. 6A 64 push 64 ; /Timeout = 100. ms
00403CD0 |. FFD7 call edi ; \Sleep
00403CD2 |. 8B0D 386C4100 mov ecx, [416C38]
00403CD8 |. 8D5424 18 lea edx, [esp+18]
00403CDC |. 6A 10 push 10 ; /ToLength = 10 (16.)
00403CDE |. 52 push edx ; |pTo
00403CDF |. 6A 00 push 0 ; |Flags = 0
00403CE1 |. 8D4424 34 lea eax, [esp+34] ; |
00403CE5 |. 56 push esi ; |DataSize
00403CE6 |. 50 push eax ; |Data
00403CE7 |. 51 push ecx ; |Socket => 0
00403CE8 |. E8 63C00000 call <jmp.&wsock32.sendto> ; \sendto
00403CED |. 6A 64 push 64 ; /Timeout = 100. ms
00403CEF |. FFD7 call edi ; \Sleep
00403CF1 |. 8B0D 386C4100 mov ecx, [416C38]
00403CF7 |. 8D5424 18 lea edx, [esp+18]
00403CFB |. 6A 10 push 10 ; /ToLength = 10 (16.)
00403CFD |. 52 push edx ; |pTo
00403CFE |. 6A 00 push 0 ; |Flags = 0
00403D00 |. 8D4424 34 lea eax, [esp+34] ; |
00403D04 |. 56 push esi ; |DataSize
00403D05 |. 50 push eax ; |Data
00403D06 |. 51 push ecx ; |Socket => 0
00403D07 |. E8 44C00000 call <jmp.&wsock32.sendto> ; \sendto
00403D0C |. 5F pop edi
00403D0D |. B8 01000000 mov eax, 1
00403D12 |. 5E pop esi
00403D13 |. 81C4 20020000 add esp, 220
00403D19 \. C3 retn
习惯性的又多看了几行,这里发了三个包,中间用Sleep函数作了0.1秒的技术暂停。
(理发师:很明显我就是这样的人)
(包租公包租婆:很明显,你不是)
既然不是,又排除三个,我们再往下看,00403ECF:
00403EB1 . BF 01000000 mov edi, 1
00403EB6 . 52 push edx ; /ToLength
00403EB7 . 8D5424 40 lea edx, [esp+40] ; |
00403EBB . 52 push edx ; |pTo
00403EBC . 53 push ebx ; |Flags
00403EBD . 50 push eax ; |DataSize
00403EBE . 8D8424 700100>lea eax, [esp+170] ; |
00403EC5 . 50 push eax ; |Data
00403EC6 . 51 push ecx ; |Socket => 0
00403EC7 . 894C24 78 mov [esp+78], ecx ; |
00403ECB . 897C24 74 mov [esp+74], edi ; |
00403ECF . E8 7CBE0000 call <jmp.&wsock32.sendto> ; \sendto
00403ED4 . A1 386C4100 mov eax, [416C38]
00403ED9 . 8D4C24 34 lea ecx, [esp+34]
00403EDD . 51 push ecx ; /pTimeout
00403EDE . 53 push ebx ; |Exceptfds
00403EDF . 8D5424 64 lea edx, [esp+64] ; |
00403EE3 . 53 push ebx ; |Writefds
00403EE4 . 40 inc eax ; |
00403EE5 . 52 push edx ; |Readfds
00403EE6 . 50 push eax ; |nfds
00403EE7 . E8 7CBE0000 call <jmp.&wsock32.select> ; \select
00403EEC . 83F8 FF cmp eax, -1
00403EEF . 74 65 je short 00403F56
00403EF1 . 8B15 386C4100 mov edx, [416C38]
00403EF7 . 8D4C24 5C lea ecx, [esp+5C]
00403EFB . 51 push ecx
00403EFC . 52 push edx
00403EFD . E8 60BE0000 call <jmp.&wsock32.__WSAFDIsSet>
00403F02 . 85C0 test eax, eax
00403F04 . 74 50 je short 00403F56
00403F06 . 8D4424 14 lea eax, [esp+14]
00403F0A . 8D4C24 4C lea ecx, [esp+4C]
00403F0E . 50 push eax ; /pFromLen
00403F0F . A1 386C4100 mov eax, [416C38] ; |
00403F14 . 51 push ecx ; |pFrom
00403F15 . 53 push ebx ; |Flags
00403F16 . 8D9424 6C0300>lea edx, [esp+36C] ; |
00403F1D . 68 00020000 push 200 ; |BufSize = 200 (512.)
00403F22 . 52 push edx ; |Buffer
00403F23 . 50 push eax ; |Socket => 0
00403F24 . E8 33BE0000 call <jmp.&wsock32.recvfrom> ; \recvfrom
00403F29 . 8BF0 mov esi, eax
这个很完整了,call完sendto,又call了select和recvfrom,有嫌疑。
(靠,call这么多人来,斧头帮聚会啊)
先不着急,再看下一个,004040F9:
004040DA . BF 01000000 mov edi, 1
004040DF . 52 push edx ; /ToLength
004040E0 . 8D5424 40 lea edx, [esp+40] ; |
004040E4 . 52 push edx ; |pTo
004040E5 . 6A 00 push 0 ; |Flags = 0
004040E7 . 50 push eax ; |DataSize
004040E8 . 8D8424 700100>lea eax, [esp+170] ; |
004040EF . 50 push eax ; |Data
004040F0 . 51 push ecx ; |Socket => 0
004040F1 . 894C24 78 mov [esp+78], ecx ; |
004040F5 . 897C24 74 mov [esp+74], edi ; |
004040F9 . E8 52BC0000 call <jmp.&wsock32.sendto> ; \sendto
004040FE . A1 386C4100 mov eax, [416C38]
00404103 . 8D4C24 2C lea ecx, [esp+2C]
00404107 . 51 push ecx ; /pTimeout
00404108 . 6A 00 push 0 ; |Exceptfds = NULL
0040410A . 8D5424 64 lea edx, [esp+64] ; |
0040410E . 6A 00 push 0 ; |Writefds = NULL
00404110 . 40 inc eax ; |
00404111 . 52 push edx ; |Readfds
00404112 . 50 push eax ; |nfds
00404113 . E8 50BC0000 call <jmp.&wsock32.select> ; \select
00404118 . 3BC7 cmp eax, edi
0040411A . 75 78 jnz short 00404194
0040411C . 8B15 386C4100 mov edx, [416C38]
00404122 . 8D4C24 5C lea ecx, [esp+5C]
00404126 . 51 push ecx
00404127 . 52 push edx
00404128 . E8 35BC0000 call <jmp.&wsock32.__WSAFDIsSet>
0040412D . 85C0 test eax, eax
0040412F . 74 68 je short 00404199
00404131 . 8D4424 14 lea eax, [esp+14]
00404135 . 8D4C24 4C lea ecx, [esp+4C]
00404139 . 50 push eax ; /pFromLen
0040413A . A1 386C4100 mov eax, [416C38] ; |
0040413F . 51 push ecx ; |pFrom
00404140 . 6A 00 push 0 ; |Flags = 0
00404142 . 8D9424 6C0300>lea edx, [esp+36C] ; |
00404149 . 68 00020000 push 200 ; |BufSize = 200 (512.)
0040414E . 52 push edx ; |Buffer
0040414F . 50 push eax ; |Socket => 0
00404150 . E8 07BC0000 call <jmp.&wsock32.recvfrom> ; \recvfrom
00404155 . 8BF0 mov esi, eax
00404157 . 85F6 test esi, esi
00404159 . 7E 2C jle short 00404187
0040415B . 8B0D A86E4100 mov ecx, [416EA8]
00404161 . 51 push ecx ; /pAddr => 00034460
00404162 . E8 EFBB0000 call <jmp.&wsock32.inet_addr> ; \inet_addr
00404167 . 394424 50 cmp [esp+50], eax
0040416B . 75 1A jnz short 00404187
0040416D . 8D9424 600300>lea edx, [esp+360]
00404174 . 56 push esi
00404175 . 52 push edx
00404176 . E8 651A0000 call 00405BE0
0040417B . 8BE8 mov ebp, eax
0040417D . 83C4 08 add esp, 8
00404180 . 83FD 02 cmp ebp, 2
00404183 . 74 73 je short 004041F8
00404185 . EB 12 jmp short 00404199
00404187 > 68 88130000 push 1388 ; /Timeout = 5000. ms
0040418C . FF15 7C104100 call [<&kernel32.Sleep>] ; \Sleep
00404192 . EB 05 jmp short 00404199
00404194 > BD 21000000 mov ebp, 21
00404199 > 43 inc ebx
0040419A . 3BEF cmp ebp, edi
0040419C . 75 0D jnz short 004041AB
0040419E . 68 30750000 push 7530 ; /Timeout = 30000. ms
004041A3 . 33DB xor ebx, ebx ; |
004041A5 . FF15 7C104100 call [<&kernel32.Sleep>] ; \Sleep
004041AB > 393D 6C6E4100 cmp [416E6C], edi
004041B1 .^ 0F84 D3FEFFFF je 0040408A
004041B7 . EB 72 jmp short 0040422B
004041B9 > A1 386C4100 mov eax, [416C38]
004041BE . 33F6 xor esi, esi
004041C0 . 50 push eax ; /Socket => 0
004041C1 . 8935 6C6E4100 mov [416E6C], esi ; |
004041C7 . E8 7EBB0000 call <jmp.&wsock32.closesocket> ; \closesocket
004041CC . 8935 386C4100 mov [416C38], esi
004041D2 . E8 6DBB0000 call <jmp.&wsock32.WSACleanup> ; [WSACleanup
004041D7 . 56 push esi
(这个call不但完整,而且小小年纪竟然就有一副横练的筋骨,真是百年难得一遇的武学奇才。)
注意到有一个30秒的sleep,回忆上一章的内容,没错,每隔三十秒发包,这个就是用来保持连接的。
考虑到上一个嫌疑调用就在这儿不远处,极有可能就是发包函数。
在斧头帮聚会的00403ECF处,下一个断点,跑起来。
然后连接,果然,程序乖乖的断在了00403ECF处。就是这里啦。(记下来)
当然,给sendto的调用下全局断点,然后运行看停在哪里,也是一样的。只是--
(只是,只是我当时没有想到而已。有时觉得自己真是聪明的可以)
三、(再热)第二次的亲密接触
(如果把整个太平洋的水倒出来。。。 再热的话,太平洋里就没有水了)
还记得整容的时候给DIGI加的"高级"按钮吗?现在我们来给它加上处理函数。
由于DIGI是一个MFC程序,所以突破口在CCmdTarget::OnCmdMsg(此函数是mfc对windows消息处理的封装)
先找到主对话框的消息表:
重新加载DIGI,Ctrl+N打开名称列表,找到CCmdTarget::OnCmdMsg:
...
00411374 .rdata 输入 mfc42.#4407_CWnd::OnChildNotify
00411138 .rdata 输入 mfc42.#4424_CCmdTarget::OnCmdMsg
...
在数据窗口跟随至 00411138:
00411138 >6BC4223C mfc42.#4424_CCmdTarget::OnCmdMsg
0041113C >6BCB87C6 mfc42.#3738_CWinApp::GetRuntimeClass
00411140 >6BC575A1 mfc42.#815_CWinApp::~CWinApp
00411144 >6BC4AFAF mfc42.#561_CWinApp::CWinApp
00411148 >6BC48D34 mfc42.#641_CDialog::~CDialog
0041114C >6BC5019D mfc42.#609_CButton::~CButton
00411150 >6BC544E5 mfc42.#795_CStatic::~CStatic
00411154 >6BC56AB1 mfc42.#2514_CDialog::DoModal
可知CCmdTarget::OnCmdMsg的地址在6BC4223C,在6BC4223C处下断,点击界面中高级按钮,断点如约而至:
6BC4223C > 55 push ebp
6BC4223D 8BEC mov ebp, esp
6BC4223F 8B45 0C mov eax, [ebp+C]
6BC42242 53 push ebx
6BC42243 56 push esi
6BC42244 57 push edi
6BC42245 83F8 FE cmp eax, -2
6BC42248 8BF9 mov edi, ecx
6BC4224A 0F84 21C30500 je 6BC9E571
6BC42250 83F8 FD cmp eax, -3
6BC42253 0F84 39C30500 je 6BC9E592
6BC42259 83F8 FF cmp eax, -1
6BC4225C 75 29 jnz short 6BC42287
6BC4225E BB 11010000 mov ebx, 111
6BC42263 8B07 mov eax, [edi]
6BC42265 8BCF mov ecx, edi
6BC42267 FF50 30 call [eax+30]
6BC4226A 8BF0 mov esi, eax
6BC4226C 85F6 test esi, esi
6BC4226E 74 40 je short 6BC422B0
6BC42270 FF75 08 push dword ptr [ebp+8]
6BC42273 FF75 0C push dword ptr [ebp+C]
6BC42276 53 push ebx
6BC42277 FF76 04 push dword ptr [esi+4]
6BC4227A E8 09FEFFFF call #1145_AfxFindMessageEntry
将光标停至 6BC4227A E8 09FEFFFF call #1145_AfxFindMessageEntry 这句,
F4运行到此处,前一句 6BC42277 FF76 04 push dword ptr [esi+4] 压入栈中的就指向消息映射表的地址。
看看堆栈:
0012EBF8 00411814 digi.00411814
0012EBFC 00000111
0012EC00 FFFFFFFF
0012EC04 0000800B
0012EC08 FFFFFFFF
可知我们要找的消息表地址就在00411814里,在数据窗口中跟随(地址显示方式):
00411814 00411818 digi.00411818
注意00411814指向的00411818即为消息表,内容如下:
00411818 00000112 <-- 消息号
0041181C 00000000 <-- Code
00411820 00000000 <-- 控件ID
00411824 00000000 <-- 最后一个空间ID
00411828 00000012 <-- 响应函数类型
0041182C 00402730 digi.00402730 <-- 响应函数
00411830 0000000F
00411834 00000000
...
00411A4C 00000000
00411A50 0000000D
00411A54 00404E90 digi.00404E90 <-- 消息表结束于此
00411A58 00000000
00411A5C 00000000
00411A60 00000000
00411A64 00000000
00411A68 00000000
00411A6C 00000000
00411A70 0040F612 <jmp.&mfc42.#3597_CDialog::GetRuntimeClass>
要处理我们自己的按钮消息,要在表中加入新项,注意到表结束后紧跟其他数据,故没有足够的空间来加入新项。
没关系,我们将消息处理表整个移到别处。
回忆一下Digi整容的成果:
地址 长度
.text 00410000 10000
.rdata 00411000 4000
.data 00415000 204000
.oldrsrc 00619000 3000
.perplex 0061C000 1D000
.newimt 00639000 1000
.rsrc 0063A000 3000
对了,.perplex是壳代码所在的段,现在正好用来放我们要加入的东西。
在数据窗口中选中从 00411818 到 00411A54 的所有内容,然后选择二进制复制。
将00411814的内容改为 0061C518,这就是新消息表的地址。
在数据窗口中右键,选择复制到可执行文件,保存下来。
在数据窗口中来到0061C518处,选择二进制粘贴。
来到消息表的末尾0061C784处:
0061C778 00000000
0061C77C 00000000
0061C780 0000000C
0061C784 0061CD06 digi.0061CD06
从0061C758开始填充自己的消息:
0061C758 00000111 <-- 0x111消息,WM_COMMAND
0061C75C 00000000
0061C760 000003ED <-- 3ED即我们加入的"高级"按钮的资源ID
0061C764 000003ED
0061C768 0000000C
0061C76C 00000000 <-- 此处应为响应函数地址,先留空
0061C770 00000110 <-- 0x110消息,WM_INITDIALOG
0061C774 00000000
0061C778 00000000
0061C77C 00000000
0061C780 0000000C
0061C784 0061CD06 digi.0061CD06 <-- 我们的窗体初始化消息响应写在此处
0061C788 00000111 <-- 0x111消息,WM_COMMAND
0061C78C 00000000
0061C790 0000800B <-- 800B为我们加入的"高级"菜单项的资源ID
0061C794 0000800B
0061C798 0000000C
0061C79C 00000000 <-- 响应函数,留空
0061C7A0 00000000
0061C7A4 00000000
0061C7A8 00000000
0061C7AC 00000000
0061C7B0 00000000
0061C7B4 00000000
选中从0061C518到0061C7B4,复制到可执行文件,保存。
四、(脱水)第三类接触
(热到脱水的时候,轻舞飞扬也会变成外星人。注:美女就是美女,永远也变不成恐龙。反之亦然)
下面我们来加入一个窗体初始化处理函数,来完成Digi主窗口初始化的时候,加载我们自己的dll和取得函数地址。
在代码窗口中来到0061CD06处,开始写汇编代码:
0061CD07 68 10CC6100 push 0061CC10 ; ASCII "dycdll.dll"
0061CD0C E8 4F511E7C call kernel32.LoadLibraryA
0061CD11 50 push eax
0061CD12 68 20CC6100 push 0061CC20 ; ASCII "_Mysendto@24"
0061CD17 50 push eax
0061CD18 E8 A4F2207C call kernel32.GetProcAddress
0061CD1D BB 40CC6100 mov ebx, 0061CC40
0061CD22 8903 mov [ebx], eax
0061CD24 58 pop eax
0061CD25 50 push eax
0061CD26 68 30CC6100 push 0061CC30 ; ASCII "Mysetting"
0061CD2B 50 push eax
0061CD2C E8 90F2207C call kernel32.GetProcAddress
0061CD31 BB 6CC76100 mov ebx, 0061C76C
0061CD36 8903 mov [ebx], eax
0061CD38 BB 9CC76100 mov ebx, 0061C79C
0061CD3D 8903 mov [ebx], eax
0061CD3F 58 pop eax
0061CD40 50 push eax
0061CD41 68 60CC6100 push 0061CC60 ; ASCII "Getnotest"
0061CD46 50 push eax
0061CD47 E8 75F2207C call kernel32.GetProcAddress
0061CD4C BB 50CC6100 mov ebx, 0061CC50
0061CD51 8903 mov [ebx], eax
0061CD53 58 pop eax
0061CD54 90 nop
0061CD55 90 nop
0061CD56 90 nop
0061CD57 61 popad
0061CD58 - E9 5354DEFF jmp 004021B0 ;跳回到原处理函数
其中用到的变量有:
0061CC10 - dll文件名
0061CC20 - 我们的sendto函数名
0061CC30 - 我们的按钮处理函数名
0061CC40 - 保存sendto函数地址
0061CC50 - 保存Getnotest函数地址
0061CC60 - 我们的Getnotest函数名
0061CC10 dycdll.dll......_Mysendto@24....
0061CC30 Mysetting.......?.............
0061CC50 P.............Getnotest.......
注意到按钮处理函数的地址已经被写到0061C76C和0061C79C的消息表项中。
将汇编和变量的修改复制到可执行文件,保存。
(和所有的动作游戏一样,请养成随时存档的习惯)
五、(晕了)真的晕了
(欢迎来到代码篇)
下面我们来修改登陆时调用的sendto函数,来到00403ECF处:
00403ECF . E8 7CBE0000 call <jmp.&wsock32.sendto> ; \sendto
修改为
00403ECF E8 E88D2100 call 0061CCCB ; 0061CCCB为我们自己的处理函数
复制到可执行文件,保存。
来到 0061CCCB处,开始汇编:
0061CCCB A1 40CC6100 mov eax, [61CC40] ;其中有我们的sendto函数地址
0061CCD0 83F8 00 cmp eax, 0
0061CCD3 74 0B je short 0061CCE0
0061CCD5 90 nop
0061CCD6 90 nop
0061CCD7 90 nop
0061CCD8 - FF25 40CC6100 jmp [61CC40] ; dycdll.Mysendto
0061CCDE 90 nop
0061CCDF 90 nop
0061CCE0 - E9 D4AA5471 jmp WS2_32.sendto
完成的功能就是如果我们自己的sendto函数已经加载,就用我们的sendto函数工作,否则用系统的sendto函数工作。
复制到可执行文件,保存。
(喝口水,成功在即了)
六、(编程)如果上帝是个程序员
脏活累活干得差不多了,写点优雅的代码吧。
打开VC6.0,新建一个MFC的DLL工程。
加入如下变量和函数定义。
//下列变量保存sendto发出的包内容
int len_authorize=0;
int flags_authorize=0;
struct sockaddr to_authorize={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char buf_authorize[100]={0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0
};
int tolen_authorize=0;
//这三个变量保存程序运行方式
BOOL NoTest=TRUE; //是否禁止代理检测等
BOOL SAVE=FALSE; //是否保存登陆包
BOOL LOAD=FALSE; //是否调入保存的登陆包(路由器穿透时用)
void ReadIni() //读配置文件
{
NoTest=GetPrivateProfileInt("Setting","NoTest",1,".\\dyc.ini");
SAVE=GetPrivateProfileInt("Setting","SAVE",0,".\\dyc.ini");
LOAD=GetPrivateProfileInt("Setting","LOAD",0,".\\dyc.ini");
}
void WriteIni() //写配置文件
{
CString tmp;
tmp.Format("%d",NoTest);
WritePrivateProfileString("Setting","NoTest",tmp,".\\dyc.ini");
tmp.Format("%d",SAVE);
WritePrivateProfileString("Setting","SAVE",tmp,".\\dyc.ini");
tmp.Format("%d",LOAD);
WritePrivateProfileString("Setting","LOAD",tmp,".\\dyc.ini");
}
void Load() //调入保存的包
{
HANDLE t=CreateFile("auth.dat",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);
if(t != INVALID_HANDLE_VALUE)
{
unsigned long xlen;
ReadFile(t,&len_authorize,1,&xlen,NULL);
ReadFile(t,&flags_authorize,1,&xlen,NULL);
ReadFile(t,&to_authorize,15,&xlen,NULL);
ReadFile(t,&buf_authorize,100,&xlen,NULL);
ReadFile(t,&tolen_authorize,1,&xlen,NULL);
CloseHandle(t);
}
}
void Save() //保存包
{
HANDLE t=CreateFile("auth.dat",GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,NULL,NULL);
if(t != INVALID_HANDLE_VALUE)
{
unsigned long xlen;
WriteFile(t,&len_authorize,1,&xlen,NULL);
WriteFile(t,&flags_authorize,1,&xlen,NULL);
WriteFile(t,&to_authorize,15,&xlen,NULL);
WriteFile(t,&buf_authorize,100,&xlen,NULL);
WriteFile(t,&tolen_authorize,1,&xlen,NULL);
CloseHandle(t);
}
}
extern "C" __declspec(dllexport) BOOL Getnotest() //导出函数,得到是否不检测
{
return NoTest;
}
extern "C" __declspec(dllexport) void Mysetting() //导出函数,"高级"按钮处理函数,显示一个对话框
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CAdForm p;
p.DoModal();
// MessageBox(NULL,"Ha","ha",MB_OK);
}
extern "C" __declspec(dllexport) int WINAPI Mysendto( //导出函数,我们自己的sendto函数
SOCKET s,
const char FAR * buf,
int len,
int flags,
const struct sockaddr FAR * to,
int tolen
)
{
int result;
for(int tt=0;tt<len;tt++)
{
*(buf_authorize+tt)=*(buf+tt);
}
len_authorize=len;
tolen_authorize=tolen;
flags_authorize=flags;
strcpy(to_authorize.sa_data,to->sa_data);
to_authorize.sa_family=to->sa_family;
if(SAVE)Save();
if(LOAD)Load();
result=::sendto(s,buf_authorize,len_authorize,flags_authorize,&to_authorize,tolen_authorize);
return result;
}
然后加入一个对话框,在其中处理对程序工作的设置等。
编译得到的dll文件就可以使用了。
通过自己的sendto函数,我们可以截获正常情况下的登陆数据包,然后在路由器后使用,就可以穿透路由器,正常登陆了。
七、(点歌)我点一首歌,不要对唱的
下面我们来解决最后一个检测代理和BT的问题。
先看看登陆代码:
0040402C . 8B15 4C8D6100 mov edx, [618D4C]
00404032 . 893D 6C6E4100 mov [416E6C], edi
00404038 . 891D 706E4100 mov [416E70], ebx
0040403E . 53 push ebx ; /lParam
0040403F . 8B82 70030400 mov eax, [edx+40370] ; |
00404045 . 66:C705 026F4>mov word ptr [416F02], 3 ; |
0040404E . A3 746F4100 mov [416F74], eax ; |
00404053 . 8B4D 20 mov ecx, [ebp+20] ; |
00404056 . 57 push edi ; |wParam
00404057 . 68 8C040000 push 48C ; |Message = MSG(48C)
0040405C . 51 push ecx ; |hWnd
0040405D . FF15 24154100 call [<&user32.PostMessageA>] ; \PostMessageA
00404063 > A1 506C4100 mov eax, [416C50]
登陆成功后一直执行到0040405D出会有一个对PostMessageA的调用,发送窗口消息,消息代码是48C。
初步估计这个消息的响应函数中会有检测的代码。(后来我们才知道,要相信男人的直觉)
来到消息表0061c518,往下看直到:
0061C660 0000000A
0061C664 00404250 digi_new.00404250
0061C668 0000048C <-- 就是我们要找的48C
0061C66C 00000000
0061C670 00000000
0061C674 00000000
0061C678 0000000A
0061C67C 00403AC0 digi_new.00403AC0 <-- 这个就是消息响应函数了
0061C680 00000485
不多说,在代码窗口中跟到00403AC0:
00403AC0 . 64:A1 0000000>mov eax, fs:[0]
00403AC6 . 6A FF push -1
00403AC8 . 68 30024100 push 00410230
00403ACD . 50 push eax
00403ACE . 64:8925 00000>mov fs:[0], esp
00403AD5 . 83EC 64 sub esp, 64
00403AD8 . 66:C705 026F4>mov word ptr [416F02], 3
00403AE1 . C705 DC6E4100>mov dword ptr [416EDC], 1
00403AEB . 6A 00 push 0
00403AED . 6A 00 push 0
00403AEF . 6A 00 push 0
00403AF1 . 6A 00 push 0
00403AF3 . 51 push ecx
00403AF4 . 68 109C4000 push 00409C10
00403AF9 . E8 E0BB0000 call <jmp.&mfc42.#1105_AfxBeginThread>
00403AFE . A1 306E4100 mov eax, [416E30]
00403B03 . 8B48 F8 mov ecx, [eax-8]
00403B06 . 85C9 test ecx, ecx
...
00403B56 . C2 0800 retn 8
注意到00403AF9处的AfxBeginThread函数启动了一个线程,线程函数地址为00409C10,跟进去。
00409C10 . 6A FF push -1
00409C12 . 68 4C094100 push 0041094C ; SE 处理程序安装
00409C17 . 64:A1 0000000>mov eax, fs:[0]
00409C1D . 50 push eax
00409C1E . 64:8925 00000>mov fs:[0], esp
...
00409CC6 . A3 606F4100 mov [416F60], eax
00409CCB . E8 A0150000 call 0040B270 ;<-- 有call,跟进去发现很短,不是
00409CD0 . 83C4 04 add esp, 4
00409CD3 . 8BF0 mov esi, eax
00409CD5 . E8 264B0000 call 0040E800 ;<-- 又有,跟进去也不是,再往下看
00409CDA . 2B05 44704100 sub eax, [417044]
...
00409D8D . 8D5424 30 lea edx, [esp+30]
00409D91 . 891D 346E4100 mov [416E34], ebx
00409D97 . 52 push edx ; /pThreadId
00409D98 . 57 push edi ; |CreationFlags
00409D99 . 68 346E4100 push 00416E34 ; |pThreadParm = digi_dum.00416E34
00409D9E . 68 10964000 push 00409610 ; |ThreadFunction = digi_dum.00409610
00409DA3 . 57 push edi ; |StackSize
00409DA4 . 57 push edi ; |pSecurity
00409DA5 . 891D E06E4100 mov [416EE0], ebx ; |
00409DAB . FF15 54104100 call [<&kernel32.CreateThread>] ; \CreateThread
又建了一个线程,太可疑,线程函数时00409610,跟进去:
00409610 . 53 push ebx
00409611 . 56 push esi
00409612 . 57 push edi
00409613 . E8 38FDFFFF call 00409350 ;<-- 有call了,跟进去
00409618 . 83F8 01 cmp eax, 1
0040961B . 75 17 jnz short 00409634
跟到00409350:
00409350 /$ 6A FF push -1
00409352 |. 68 8B084100 push 0041088B ; SE 处理程序安装
00409357 |. 64:A1 0000000>mov eax, fs:[0]
...
00409384 |. E8 27610000 call <jmp.&mfc42.#540_CString::CString>
00409389 |. 56 push esi ; /ProcessID
0040938A |. 6A 02 push 2 ; |Flags = TH32CS_SNAPPROCESS
0040938C |. 89B424 440100>mov [esp+144], esi ; |
00409393 |. E8 7C690000 call <jmp.&kernel32.CreateToolhelp32Snapshot>; \CreateToolhelp32Snapshot
00409398 |. 8BF8 mov edi, eax
...
004093C5 |. 8D4C24 0C lea ecx, [esp+C]
004093C9 |. E8 DC600000 call <jmp.&mfc42.#860_CString::operator=>
004093CE |. 68 F05D4100 push 00415DF0 ; ASCII "SyGate.exe"
004093D3 |. 8D4C24 0C lea ecx, [esp+C]
004093D7 |. E8 70640000 call <jmp.&mfc42.#2764_CString::Find>
004093DC |. 3BC6 cmp eax, esi
004093DE |. 0F8F C1010000 jg 004095A5
004093E4 |> 68 E45D4100 /push 00415DE4 ; ASCII "Sygate.exe"
004093E9 |. 8D4C24 0C |lea ecx, [esp+C]
004093ED |. E8 5A640000 |call <jmp.&mfc42.#2764_CString::Find>
004093F2 |. 3BC6 |cmp eax, esi
...
这样一看就很清楚了,这里创建了系统进程列表,逐项比对,来确定没有使用代理和BT程序的。
所以没错,之前受怀疑的00403AC0就是检测函数,我们来动个小小手术:
从:
00403AEB . 6A 00 push 0
00403AED . 6A 00 push 0
00403AEF . 6A 00 push 0
00403AF1 . 6A 00 push 0
00403AF3 . 51 push ecx
00403AF4 . 68 109C4000 push 00409C10
00403AF9 . E8 E0BB0000 call <jmp.&mfc42.#1105_AfxBeginThread>
改为:
00403AEB .- E9 10932100 jmp 0061CE00
00403AF0 90 nop
00403AF1 . 6A 00 push 0
00403AF3 . 51 push ecx
00403AF4 . 68 109C4000 push 00409C10
00403AF9 . E8 E0BB0000 call <jmp.&mfc42.#1105_AfxBeginThread>
然后在 0061CE00处汇编:
0061CE00 /EB 13 jmp short 0061CE15
0061CE02 |6A 00 push 0
0061CE04 |6A 00 push 0
0061CE06 |6A 00 push 0
0061CE08 |6A 00 push 0
0061CE0A |51 push ecx
0061CE0B |68 109C4000 push 00409C10
0061CE10 |E8 C928DFFF call <jmp.&mfc42.#1105_AfxBeginThread>
0061CE15 -\E9 E46CDEFF jmp 00403AFE
0061CE1A 8B1D 50CC6100 mov ebx, [61CC50] ;61CC50就是我们的Getnotest函数
0061CE20 83FB 00 cmp ebx, 0 ;返回为0就执行检测,否则不执行
0061CE23 ^ 74 DD je short 0061CE02
0061CE25 FFD3 call ebx
0061CE27 83F8 00 cmp eax, 0
0061CE2A ^ 74 D6 je short 0061CE02
0061CE2C ^ EB E2 jmp short 0061CE10
0061CE2E 90 nop
复制到可执行文件,保存。至此,代理和BT检测也被破除。
八、(R&B)最后的战役
最后,最后我们还要干嘛呢? 嗯,发现没,Digi的体重已经变到了惊人的2.23MB!
(虽然我一再说Size不重要,可是拥有姚明的脸蛋和李宇春的身材也不是那么的令人茶饭不思吧)
减肥一下,用LordPE打开修改好的Digi,点击"Rebuild PE",对话框选择刚刚修改过的Digi,小灯从红色变为绿色,成功。
瘦身后的Digi回复完美身材207KB。至此,封锁线完全突破成功!!
([手捧小金人] 感谢CCTV,MTV,Channel-V,感谢我的家人和长期以来支持我的扇子们,我爱你们!!)
--------------------------------------------------------------------------------
【经验总结】
至此,我们已经彻底的突破了客户端对我们的封锁,天接着很蓝,网继续很宽,资源可以共享,BT随便可以下。。。
代码编写中的关键点有:
1. 网络发包离不开 wsock32.sendto函数
2. 窗体消息的突破口在 CCmdTarget::OnCmdMsg函数
3. 注意消息表的结构,那就是mfc的MESSAGE_MAP宏做的事情
4. 加入自己的dll用 kernel32.LoadLibraryA函数载入文件和用 kernel32.GetProcAddress函数取得函数地址
5. 懂一点汇编
6. 再懂一点C
7. Size matters, kinda hard to admit though.
突破封锁线:第四章--代码篇 [完]
谢谢您耐心的看到这里,在下一章--外围篇中,将讲述如何不攻入程序内部而改变程序行为,以及dll注入技术。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年05月13日 2:42:37
[注意]APP应用上架合规检测服务,协助应用顺利上架!