能力值:
(RANK: )
|
-
-
254 楼
不要着急, 我只会在晚上出现.
我这里分析的结果是这个程序每隔15000(毫秒?)会向外部服务器发送HTTP请求, 服务器的域名有四个:
1d1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0
90cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3N6S2L8h3g2Q4x3X3c8@1L8$3!0D9i4K6u0W2j5$3!0E0
2c6K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3N6S2L8h3g2Q4x3X3c8W2P5s2m8Q4x3X3g2U0L8$3@1`.
d5eK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3N6S2L8h3g2Q4x3X3c8U0L8s2g2T1i4K6u0W2j5$3!0E0i4K6u0W2K9r3D9`.
请求的URL有两种格式:
/Reg/Member/Check3.asp?A=xxx&L=xxx&B=xxx&G=xxx&V=xxx
/Reg/Member/Check3.asp?A=xxx&L=xxx&I=xxx&G=xxx&V=xxx
合起来完整的URL类似这样
aa4K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0i4K6u0r3f1X3g2Y4i4K6u0r3e0h3g2E0j5X3g2J5i4K6u0r3b7$3S2W2j5$3D9K6i4K6u0W2j5i4y4H3i4K6y4r3b7g2)9K6c8s2S2^5P5q4)9J5y4V1I4Q4x3@1c8^5P5s2S2Q4x3U0k6n7i4K6y4p5P5s2S2^5i4K6t1$3c8#2)9K6c8s2S2^5P5q4)9J5y4W2k6Q4x3@1c8^5P5s2R3`.
所以应该可以确认是认证是否是注册用户的请求包.
每一轮请求最多重试三次, 三次不成功则等下一个15000后再试. 我让你把15000这个数改大目的是把这个发包的间隔加大. 这样还不行的话, 说明程序里还有地方另外判断时间.
你再试试看: 1. 能不能抓到这类包; 2. 把数值改大后发包的时间间隔是否也相应加大.
我自己会再看看这个函数设置了什么标志.
还有, 现在那个网站上又更新了版本, 你打算继续用0124还是0215? 我要选一个来处理, 要不然对不上号.
|
能力值:
(RANK: )
|
-
-
258 楼
还是先讲解一下我的思路吧. 原来我担心先把思路讲出来可能会误导你们, 所以打算在确认方案可行后再详细说明, 不过现在看来有必要先介绍一下, 以方便有条件的同学跟进详细研究.
根据先前的各贴说明, 这个程序用到了网络认证, 那么肯定会有网络通讯模块, 也应该会用到与网络相关的API, 所以要先找出哪个程序用到了与网络相关的API
用Total Commander的FileInfo逐一检查(用有类似功能其他的工具也行), 发现只有DS.DLL里用了WinINet.DLL和WSock32.DLL, 网络通讯模块肯定是在这个DLL中.
用IDA打开DS.DLL(本文以0215版本为例), 等IDA分析完, 直接在Import里找从WinINet.DLL(这个API的封装比WSock32.DLL的高级)里的导入函数:
.idata:10021344 ;
.idata:10021344 ; Imports from WININET.dll
.idata:10021344 ;
.idata:10021344 extrn InternetCloseHandle:dword
.idata:10021344 ; CODE XREF: sub_1000BAC0+17C
.idata:10021344 ; sub_1000BAC0+240
.idata:10021344 ; sub_1000BAC0+243
.idata:10021348 extrn InternetOpenUrlA:dword
.idata:10021348 ; CODE XREF: sub_1000BAC0+16F
.idata:1002134C extrn InternetCanonicalizeUrlA:dword
.idata:1002134C ; CODE XREF: sub_1000BAC0+158
.idata:10021350 extrn InternetGetConnectedState:dword
.idata:10021350 ; CODE XREF: sub_1000BAC0+4E
.idata:10021354 extrn InternetOpenA:dword
.idata:10021354 ; CODE XREF: sub_1000BAC0+A0
.idata:10021354 ; sub_1000BAC0+B5
.idata:10021358 extrn InternetReadFile:dword
.idata:10021358 ; CODE XREF: sub_1000BAC0+1C3
.idata:10021358 ; sub_1000BAC0+22F
真运气, 居然有这么高级的API调用, 而且都在同一个函数.....
别客气, 直接跳到sub_1000BAC0
对这个函数没必要详细分析, 有兴趣自己慢慢看, 总之, 这是一个完整的连接/请求/接收/关闭的HTTPIO过程, 所以我先把它命名成HTTPIO.
既然sub_1000BAC0只是个HTTPIO, 那确切的URL和内容处理肯定都在调用者那边, 因此我们关心的是谁调用它.
.text:1000BAC0 HTTPIO proc near ; CODE XREF: StartAddress+6D
.text:1000BAC0 ; .text:1000C35C
.text:1000BAC0 ; sub_1000CCA0+E9
.text:1000BAC0 ; sub_1000CCA0+456
有四个函数, 其中sub_1000CCA0调用两次, 那就先看看它吧
.text:1000CD7A push edx
.text:1000CD7B push ecx
.text:1000CD7C push ecx
.text:1000CD7D mov ecx, esp
.text:1000CD7F mov [esp+0D3Ch+var_D1C], esp
.text:1000CD83 push eax ; Src
.text:1000CD84 call sub_1001A2CE
.text:1000CD89 call HTTPIO
.text:1000CD8E add esp, 0Ch
.text:1000CD91 xor edx, edx
.text:1000CD93 cmp al, 1
.text:1000CD95 setz dl
.text:1000CD98 mov eax, edx
.text:1000CD9A pop edi
.text:1000CD9B pop esi
.text:1000CD9C pop ebp
.text:1000CD9D pop ebx
.text:1000CD9E add esp, 0D20h
.text:1000CDA4 retn
看起来不象有什么价值的东西, 再看另一处
.text:1000D0E3 push 0BB8h
.text:1000D0E8 push ecx
.text:1000D0E9 push ecx
.text:1000D0EA mov ecx, esp
.text:1000D0EC mov [esp+0D3Ch+var_D14], esp
.text:1000D0F0 push edx ; Src
.text:1000D0F1 call sub_1001A2CE
.text:1000D0F6 call HTTPIO
.text:1000D0FB add esp, 0Ch
.text:1000D0FE cmp al, 1
.text:1000D100 jnz loc_1000D3DF
.text:1000D106 lea eax, [esp+0D30h+Str]
.text:1000D10D push offset SubStr ; "DB ERROR"
.text:1000D112 push eax ; Str
.text:1000D113 call _strstr
等等, DB ERROR? 有点门路, 上下左右随便看一下(这个看和下面的翻都是指用PgUp/PgDn上下浏览附近的代码)
.text:1000CFE8 mov edi, offset aI ; "&I="
.text:1000D048 mov edi, offset aG ; "&G="
.text:1000D090 mov edi, offset aV ; "&V="
Hmmmm.... 我好象已经闻到了什么, 继续翻
.text:1000CDE8 mov ecx, [esp+0D30h+arg_C]
.text:1000CDEF mov edx, [esp+0D30h+var_D18]
.text:1000CDF3 push ecx
.text:1000CDF4 push ebx
.text:1000CDF5 lea eax, [esp+0D38h+var_D10]
.text:1000CDF9 push edx
.text:1000CDFA push eax
.text:1000CDFB call sub_1000C7B0
.text:1000CE00 mov eax, [esp+0D40h+arg_C]
.text:1000CE07 add esp, 10h
.text:1000CE0A cmp eax, 3
.text:1000CE0D lea edx, [esp+0D30h+var_D10]
.text:1000CE11 mov edi, offset a?a ; "?A="
源头好象在这, 到sub_1000C7B0里看一下
.text:1000C7D7 mov edi, offset aHt ; "ht"
.text:1000C807 mov edi, offset aTpW ; "tp://w"
.text:1000C82F mov edi, offset aWw_g ; "ww.g"
.text:1000C854 mov edi, offset aAme ; "ame-"
.text:1000C87C mov edi, offset aClub_co ; "club.co"
.text:1000C8A4 mov edi, offset aM_hk ; "m.hk/"
呵呵, 现在知道 fbaK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3N6S2L8h3g2Q4x3X3c8U0L8s2g2T1i4K6u0W2j5$3!0E0i4K6u0W2K9r3E0Q4x3U0k6F1j5Y4y4H3i4K6y4n7i4@1f1$3i4K6V1^5i4@1q4r3i4@1f1$3i4K6R3H3i4K6S2q4i4@1f1@1i4@1t1&6i4K6R3^5i4@1f1$3i4K6W2p5i4@1p5#2i4@1f1%4i4K6W2m8i4K6R3@1i4@1f1@1i4@1u0m8i4K6R3$3i4@1f1#2i4K6V1H3i4@1p5%4i4K6u0o6i4K6t1$3L8X3u0K6M7q4)9K6b7W2!0q4y4g2)9^5c8g2)9&6c8W2!0q4y4W2)9&6c8q4!0m8y4g2!0q4z5q4!0m8x3W2!0m8b7W2!0q4y4g2)9^5b7#2)9&6y4W2!0q4y4W2)9&6y4g2!0n7y4q4!0q4y4q4!0n7z5q4!0n7b7g2!0q4z5g2)9&6b7W2!0n7y4W2!0q4y4q4!0n7b7g2)9^5y4W2)9J5b7#2)9J5y4X3&6T1M7%4m8Q4x3@1u0Q4c8e0S2Q4b7V1k6Q4z5e0m8Q4c8e0S2Q4b7e0q4Q4z5p5y4Q4c8e0k6Q4z5e0N6Q4b7U0k6Q4c8e0k6Q4z5o6W2Q4z5p5c8Q4c8e0N6Q4b7V1u0Q4z5o6c8Q4c8e0S2Q4b7e0y4Q4z5o6g2Q4c8e0S2Q4b7U0g2Q4b7U0N6Q4c8e0k6Q4z5f1c8Q4b7e0g2Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0g2Q4z5e0g2Q4z5p5q4Q4x3X3f1`.
对sub_1000C7B0也不再详细说明了, 大家不要对里面的一堆repne scasb/movsb吓倒, 其实那只是个inline展开后的strcat, 整个函数就是根据某个参数(大概是用户的分类吧)的值来决定去哪个Site做认证.
sub_1000C7B0返回的认证的URL最终是会是这个样子:
0f9K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0i4K6u0r3f1X3g2Y4i4K6u0r3e0h3g2E0j5X3g2J5i4K6u0r3b7$3S2W2j5$3D9K6i4K6u0W2j5i4y4H3
调用返回到1000CE00后再根据同一个参数决定是加上
?A=xxx&L=xxx&B=xxx&G=xxx&V=xxx
还是
?A=xxx&L=xxx&I=xxx&G=xxx&V=xxx
合起来就是
4b0K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0i4K6u0r3f1X3g2Y4i4K6u0r3e0h3g2E0j5X3g2J5i4K6u0r3b7$3S2W2j5$3D9K6i4K6u0W2j5i4y4H3i4K6y4r3b7g2)9K6c8s2S2^5P5q4)9J5y4V1I4Q4x3@1c8^5P5s2S2Q4x3U0k6n7i4K6y4p5P5s2S2^5i4K6t1$3c8#2)9K6c8s2S2^5P5q4)9J5y4W2k6Q4x3@1c8^5P5s2R3`.
OK, 回到调用HTTPIO的地方, 刚才我们已经看到了,
.text:1000D0F6 call HTTPIO
.text:1000D0FB add esp, 0Ch
.text:1000D0FE cmp al, 1
.text:1000D100 jnz loc_1000D3DF
也就是说, 如果HTTPIO出错, 则跳到loc_1000D3DF
.text:1000D3DF mov eax, [esp+0D30h+var_D18]
.text:1000D3E3 inc eax
.text:1000D3E4 cmp eax, 3
.text:1000D3E7 mov [esp+0D30h+var_D18], eax
.text:1000D3EB jle loc_1000CDE8
再去看看loc_1000CDE8
.text:1000CDE0 mov [esp+0D30h+var_D18], 1
.text:1000CDE8 loc_1000CDE8: ; CODE XREF: sub_1000CCA0+74B
.text:1000CDE8 mov ecx, [esp+0D30h+arg_C]
我不知道你明不明白, 但我肯定看得出来, 这其实就是
for(i = 1, i <= 3; i ++) /// i = esp+0D30h+var_D18
很显然, 这个请求会重试三次, 如果都不成功
.text:1000D3F1 mov edx, ds:dword_100363EC
.text:1000D3F7 mov eax, [esp+0D30h+var_D1C]
.text:1000D3FB add edx, 15000
.text:1000D401 mov ds:dword_10146D40, edx
.text:1000D407 pop edi
.text:1000D408 pop esi
.text:1000D409 pop ebp
.text:1000D40A pop ebx
.text:1000D40B add esp, 0D20h
.text:1000D411 retn
显然直接就返回了.
等等, 这是什么?
dword_10146D40 = dword_100363EC + 15000 ?!
dword_10146D40和dword_100363EC好象在哪见过? 赶快到处翻
.text:1000CDA5 mov eax, ds:dword_100363EC
.text:1000CDAA mov ecx, ds:dword_10146D40
.text:1000CDB0 cmp eax, ecx
.text:1000CDB2 jb loc_1000D412
.text:1000CDB8 cmp ds:dword_1013FCBC, 0C8h
.text:1000CDC2 jge loc_1000D412
.text:1000CDC8 mov ebp, [esp+0D30h+arg_14]
.text:1000CDCF mov ebx, [esp+0D30h+arg_8]
.text:1000CDD6 mov ds:dword_10146D40, 0FFFFFFFFh
看明白什么意思了吗?
如果 dword_100363EC < dword_10146D40, 则去到loc_1000D412
.text:1000D412 pop edi
.text:1000D413 pop esi
.text:1000D414 pop ebp
.text:1000D415 xor eax, eax
.text:1000D417 pop ebx
.text:1000D418 add esp, 0D20h
.text:1000D41E retn
显然是返回0
也就是说, 1000CDA5到1000D41E这段代码相当于:
if( dword_100363EC < dword_10146D40 )
return 0;
for( i=0; i <= 3; i ++)
HTTPIO的相关处理
dword_10146D40 = dword_100363EC + 15000
return [esp+0D30h+var_D1C]
dword_100363EC是什么内容呢? 我可以先告诉大家, 这个值就是系统的TickCount(至于具体分析我下回再讲), 那么dword_10146D40的意义就很明显了.
到此为止, 我们应该确认已经找到了网络认证部分以及认证的方式和频度.
.text:1000D106 lea eax, [esp+0D30h+Str]
.text:1000D10D push offset SubStr ; "DB ERROR"
.text:1000D112 push eax ; Str
.text:1000D113 call _strstr
从这里开始就是对认证的结果进行处理的部分, 大家先自行分析一下, 我明天有事不上来, 后天再跟大家继续交流.
TIPS:
.text:1000CDB8 cmp ds:dword_1013FCBC, 0C8h
.text:1000CDC2 jge loc_1000D412
从这里可以看到, 如果dword_1013FCBC >= 200 也会跑到loc_1000D412, 如果不想研究认证结果的处理, 不妨研究一下dword_1013FCBC到底起了什么作用.
谢谢您的捧场.
|
能力值:
(RANK: )
|
-
-
265 楼
忘记了你对汇编还不太熟悉.
250楼的修改方案的思路是: 如果它的计时器是根据这个发认证包的频率来计算的话, 把发认证包的频率加大, 则计时器更新的时间自然也加大, 从而达到延长使用时间的效果. 另外, 这个修改是针对0124版本的.
既然你已经试过没有效果, 说明时间限制另有一个计时器, 我们得另试其他办法.
|
能力值:
(RANK: )
|
-
-
266 楼
我们前面已经知道了, 认证包是类似这样的一个URL
2b8K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0i4K6u0r3f1X3g2Y4i4K6u0r3e0h3g2E0j5X3g2J5i4K6u0r3b7$3S2W2j5$3D9K6i4K6u0W2j5i4y4H3i4K6y4r3b7g2)9K6c8s2S2^5P5q4)9J5y4V1I4Q4x3@1c8^5P5s2S2Q4x3U0k6n7i4K6y4p5P5s2S2^5i4K6t1$3c8#2)9K6c8s2S2^5P5q4)9J5y4W2k6Q4x3@1c8^5P5s2R3`.
那么服务器的返回是怎么样的呢? 实时不能抓包, 但并不等于不能直接访问, 我们在IE的URL输入试试
913K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4S2A6N6$3q4F1k6%4N6Y4i4K6u0W2j5$3!0E0i4K6u0r3f1X3g2Y4i4K6u0r3e0h3g2E0j5X3g2J5i4K6u0r3b7$3S2W2j5$3D9K6i4K6u0W2j5i4y4H3i4K6y4r3b7g2)9K6c8r3N6S2L8h3g2Q4x3U0k6x3i4K6y4p5N6X3W2H3i4K6t1$3b7W2)9K6c8o6p5I4x3U0q4Q4x3U0k6s2i4K6y4p5M7$3q4K6j5g2)9J5y4W2k6Q4x3@1b7I4x3e0p5K6
OK, IE返回了这样的内容:
==2006/01/01 12:00:01,5,1,583
就是说, 无效的账号认证的结果是这个样子的, 接下来, 我们从前面找到的认证处理部分开始跟下去
.text:1000D106 lea eax, [esp+0D30h+Str]
.text:1000D10D push offset SubStr ; "DB ERROR"
.text:1000D112 push eax ; Str
.text:1000D113 call _strstr
处理的流程我就不详细说了, 现在知道的是, 程序使用了一种0x90(144)字节的数据结构, 共0xC8(200)个, 前面提到的dword_1013FCBC是已经使用了其中的多少个(所以超过200个就不再发请求).
每次认证服务器返回的数据会存放在这个结构中(可以是以前发过的数据, 数据结构中有一个字节是用来做标识的), 处理的流程只负责将服务器返回的数据填到这个数据结构中, 而对这些数据的处理是在DLL之外进行的.
所以, 从网络通讯模块入手的路被堵死了. 
等周末我再看看有没有其他路子可行吧.
|
能力值:
(RANK: )
|
-
-
272 楼
我再解释一下我的思路吧.
基本的想法跟你是一样的, 就是说要解除这30分钟的限制, 只不过你是从定时器入手, 我是从网络认证入手.
一开始, 我发现它会定时向认证服务器发认证请求, 于是猜想是否当认证请求失败的次数累计到一定程度时程序会退出, 这样的话, 请求失败的总次数就是限制值, 那么如果我改了它发包的频率, 在总数不变的情况下, 频率间隔越大, 达到总数的时间就越长, 无形中也就延长了使用的时间. 我已经找到发包的频率值并让你修改进行测试, 这就是250楼提到的地方. 你的测试结果表明, 这个猜想是错的.
接下来, 我想能否简单的通过修改认证返回的结果把非注册用户当成注册用户, 但分析认证处理部分后发现, 它只负责将服务器返回的数据填到某个数据结构中, 而对这些数据的处理是在DLL之外进行的, 这样即使能骗过程序说当前用户是注册用户, 但后来程序要使用那些数据时还是会出错, 因此我在266楼说, 此路不通.
至于271楼的修改办法, 灵感来源于对认证处理部分的分析. 266楼里提到认证服务器返回的包是这个样子的
==2006/01/01 12:00:01,5,1,583
其中第一个逗号后面的数字(5)要先乘上1000后才跟当前的TickCount进行运算比较, 然后再存到数据结构中, 因此我猜想这个数的单位一定是秒, 那么把这个数字加大后会有什么效果呢? 那要你测试过才能知道.
在266楼最后说: "从网络通讯模块入手的路被堵死了", 其实并非完全堵死, 只不过当前的条件下确是被堵死了.
如果我们有条件, 能够抓到程序向认证服务器发送的具体认证请求和服务器实际返回的认证数据, 通过分析, 应该还是可以找出一点线索的. 所以, 接下来的问题就是: 我们能否抓到这些认证用的数据包?
其实答案应该是肯定的, 既然我们已经知道它的认证部分使用的是HTTP协议, 那么Sniffer一下就可以解决问题; 可惜你的条件不允许使用Sniffer.
那么变通一下, 既然知道它是通过InternetOpenUrlA发请求, InternetReadFile取数据, 那么用个Hook把这两个函数接管一下或许行得通(希望NP不会监视这么偏门的API).
如果还是行不通, 那干脆直接在HTTPIO上挂个Hook. 反正目的就是为了抓包.
总之, 在当前的条件下, 除非271楼的办法成功, 或者你能抓到程序与认证服务器间的通讯包, 否则的话我只能帮你帮到这个程度了.
|