active player 是北京网动(http://www.iactive.com.cn/)出品的多媒体课件播放器。很好用。在国内的教育行业有较为广泛的应用。时代光华、华图网校等课件都是用这种播放器来播放的。
手中的课件使用了网络验证。二话不说先抓包。结果如下:
代码
User-Agent: ACPlayer
Host: http://www.******.net/
Cache-Control: no-cache
HTTP/1.1 200 OK
Date: Wed, 23 Dec 2009 04:24:20 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Length: 137
Content-Type: text/html
Cache-control: private
ID:36ca8f6009ba4d8481f13d4a77a0c41f33fe43cdaf494710a542988b38dca2990cb7d371183241019ad2d5dc167eaa89be3cfc70aacd45ddac49ed82dbdf3a58;INFO:
http 包很简单。应答包中,有一个ID字串,可能这就是 key 了。
用错误的用户名和密码试试:
User-Agent: ACPlayer
Host: www.htexam.net
Cache-Control: no-cache
Cookie: 92o_smile=1D1D0D1; rtime=3; ltime=1259506761000; cnzz_eid=52227509-1258510252-; 92o_cookietime=2592000; ASPSESSIONIDSARSBCTD=BAIGHELANAJMNENJDHCFJPHC; xsxscdb_cookietime=2592000; DOYOO_USER_ID=c114da7eaa134aaa99fbccc26c548645; DVC_25924=1
HTTP/1.1 200 OK
Date: Wed, 23 Dec 2009 04:37:19 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Length: 30
Content-Type: text/html
Cache-control: private
ID:0;INFO:....................
以上,info 是一个二进制字串。其 hex值为:
共 20个字节。
对应的提示信息是 10个中文字符:
故以上info的两个字节代码一个汉字字节。应该是 gbk 编码吧。
所以,关键的返回的信息应该就是id 串了。 在上面的字串中,id 是一个 128 位的字串,其字符范围是 [0-9][a-f]。估计是一些字节的 16进制视图。那么,这个id 应该是一个 64byte的二进制串。
af 49 47 10 a5 42 98 8b 38 dc a2 99 0c b7 d3 71 18 32 41 01
9a d2 d5 dc 16 7e aa 89 be 3c fc 70 aa cd 45 dd ac 49 ed 82
db df 3a 58
祭出 ida。载入程序后 debug 起来:
呵呵,看到程序中就这一个 ok 按钮。当然要在这里下断点了。
右键,"add breakpoint"。运行后,程序果然断了下来。
回到程序领空。
0044600F . 6A 00 push 0
00446011 . 68 02000084 push 84000002
00446016 . 6A 01 push 1
00446018 . 8B85 58FFFFFF mov eax, dword ptr [ebp-A8]
0044601E . 50 push eax
0044601F . 8B4D DC mov ecx, dword ptr [ebp-24]
00446022 . E8 CB8E0300 call <jmp.&MFC42.#5207_CInternetSession::OpenURL> ; 连接网络
00446027 . 8985 54FFFFFF mov dword ptr [ebp-AC], eax ; // CHttpFile*
openurl 的第一个参数就是 我们上面截获的 get 串。往上查找 get 字串的组织过程:
......
004474E2 . 52 push edx ; edx是开机到现在的秒数
004474E3 . 68 44034A00 push 004A0344 ; ASCII "%d"
004474E8 . 8D45 C8 lea eax, dword ptr [ebp-38]
004474EB . 50 push eax
004474EC . E8 7BF90300 call <jmp.&MFC42.#2818_CString::Format> ; [ebp-38]指向开机秒数的字串
......
00447524 . 52 push edx
00447525 . 8B85 60FFFFFF mov eax, dword ptr [ebp-A0]
0044752B . 8B88 8C000000 mov ecx, dword ptr [eax+8C] ; [00100fb8+8c]
00447531 . 51 push ecx ; guid
00447532 . 8B15 C0334A00 mov edx, dword ptr [4A33C0] ; 用户名
00447538 . 52 push edx
00447539 . A1 C4334A00 mov eax, dword ptr [4A33C4] ; 密码
0044753E . 50 push eax
0044753F . 8B4D 08 mov ecx, dword ptr [ebp+8]
00447542 . E8 79DCFBFF call 004051C0 ; **[ebp+8]
00447547 . 50 push eax ; [ebp-14] 带形参的 get字串
00447548 . 8D4D EC lea ecx, dword ptr [ebp-14] ; [ebp-14] 组织好的 get 字串
0044754B . 51 push ecx
0044754C . E8 1BF90300 call <jmp.&MFC42.#2818_CString::Format> ; 填充 get 字串中的形参
经过多次跟踪发现,get 串中,只有 enc_seed 字段的值是变的。而从上面的代码分析知,enc_seed 的值是首地址保存在 [ebp-38] 中的一个字串。这个字串是开机到现在的秒数的 string 表达。而其 int 值保存在了 [ebp-1c]中。
向下查看函数中与 [ebp-38] 或 [ebp-1c] 相关的代码:
0044789F . 52 push edx
004478A0 . E8 3CFC0200 call 004774E1 ; 对 id 进行计算。提取到一个整数。关键算法?
004478A5 . 83C4 04 add esp, 4
004478A8 . 8985 0CFFFFFF mov dword ptr [ebp-F4], eax
004478AE . 8B85 0CFFFFFF mov eax, dword ptr [ebp-F4]
004478B4 . 8945 E0 mov dword ptr [ebp-20], eax ; [ebp -20] ID 的运算值
004478B7 . 8B4D E0 mov ecx, dword ptr [ebp-20]
004478BA . 334D E4 xor ecx, dword ptr [ebp-1C]
004478BD . 894D E0 mov dword ptr [ebp-20], ecx
以上代码中,首地址为 edx 的 id 字串,经函数 004774e1解密后,与保存在 [ebp-1c] 中的时间函数做 xor 运算。得到一个整数值保存在 [ebp-20]中。再往下看这个函数知,函数只将 [ebp-20] 作为函数返回值返回。因为这个函数中没有涉及栈帧以外的变量来处理 id 值,所以我们推测,id 字串已经浓缩为 [ebp-20]。原字串已经不再有用。
程序从函数 返回到 acplayer.004469c6:
00446D75 |. 8B8D 78FBFFFF mov ecx, dword ptr [ebp-488]
00446D7B |. 8981 18020000 mov dword ptr [ecx+218], eax
00446D81 |. 8B95 78FBFFFF mov edx, dword ptr [ebp-488]
00446D87 |. 83BA 18020000>cmp dword ptr [edx+218], 0 ; [00100fb8+ 218] id xor time 的值
00446D8E |. 75 2A jnz short 00446DBA
返回值保存到了栈帧以外的变量中 [00100fb8+ 218] 。这就是解密的 key 了吧?
在 00100fb8+ 218 处下内存访问断点。f9,程序一路小跑。停下来了:
004463D4 . 8B85 1CFBFFFF mov eax, dword ptr [ebp-4E4]
004463DA . 83B8 1C020000>cmp dword ptr [eax+21C], 0
004463E1 . 74 12 je short 004463F5
004463E3 . 8B8D 1CFBFFFF mov ecx, dword ptr [ebp-4E4]
004463E9 . 8B55 EC mov edx, dword ptr [ebp-14]
004463EC . 3391 18020000 xor edx, dword ptr [ecx+218]
此处 [00100fb8+ 218] 的操作是:解密字串 "LENR"。
查找程序中所有对此处的引用。一种办法是下内存访问断点。另一种办法是使用 ida 的脚本。
祭出《加密与解密》, 124 页。
“一个简单并且有效的办法,是在整个代码里搜索访问 0xxxxxxx--0xxxxxxx 这段缓存区的 mov 指令。这时 IDA 强大就体现出来了。用IDA 打开如下脚本,就可将读取指定内存的代码列出了”。
书中提供了一个脚本 getasm.idc
// code by DarkNessOut
static getasm( from, to, range1, range2 )
{
auto ea, cmd, fp,deta, opcode;
fp = fopen( "c:\\code.txt", "w" );
for( ea= from;ea < to; )
{
cmd = GetMnem( ea );
if ( strstr( cmd, "mov" ) == 0 || strstr( cmd, "lea" ) == 0 )
{
opcode = Dword( NextNotTail( ea ) - 4 );
if ( opcode< 0 )
{
opcode = ~opcode;
opcode++;
}
Message( "-> %08X %08X\n", ea, opcode );
if( opcode >= range1 && opcode <= range2 )
{
deta = opcode - range1;
fprintf( fp, "%08X %s // + 0x%04X\n ", ea, GetDisasm( ea ), deta );
MakeComm( ea, form( " // +0x%04X",deta ) );
}
}
ea = NextNotTail( ea );
}
}
用法如下:
其中,第一个参数和第二个参数是主程序 acplayer.exe 镜像的内存范围。后两个参数是 http 消息 id 的内存范围。
但是没有找到结果。
73DDEDCC F8 0D DC 73 99 CC D8 73 71 D1 D8 73 35 D2 D8 73 ?躶櫶豷q沿s5邑s
73DDEDDC 93 B1 D4 73 65 C4 D4 73 6C C9 D8 73 FD 3D DA 73 摫詓e脑sl韶s?趕
73DDEDEC 87 39 DA 73 03 51 DA 73 CC 3D DA 73 4E 3A DA 73 ?趕Q趕?趕N:趕
73DDEDFC 77 3B DA 73 6B FC DB 73 6B FC DB 73 w;趕ksks?趕
766996DF F3:A5 rep movs dword ptr es:[edi], dword ptr [esi] ; 内存断点
F3 A5是汇编的原始二进制代码
rep movs dword ptr [edi],dword ptr [esi]是个串处理指令
是把esi所指向的内存中的内容复制到edi所指向的内存中
长度是在ecx寄存器中指定的
t-IIS/6.0
EAX 001E2AA0
ECX 00000022
EDX 00000089
EBX 00204480
ESP 000FFFEC
EBP 0010032C
ESI 002110A3 ASCII 49,"D:c10f085d0b4f4217a75e17ac2c97b89e82b4b96c1546468299b140a9d89ec88cad8fe51"
EDI 0165B8A0
EIP 766996DF WININET.766996DF
C 0 ES 0023 32位 0(FFFFFFFF)
P 1 CS 001B 32位 0(FFFFFFFF)
A 0 SS 0023 32位 0(FFFFFFFF)
Z 0 DS 0023 32位 0(FFFFFFFF)
S 0 FS 003B 32位 7FFDF000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_SUCCESS (00000000)
EFL 00210206 (NO,NB,NE,A,NS,PE,GE,G)
MM0 0000 0000 0000 0000
MM1 00FF 00FF 00FF 00FF
MM2 0000 0000 0000 0000
MM3 0000 0000 0000 0000
MM4 A5AC 15FF FFA4 F000
MM5 A6AA AAAA AAAA A800
MM6 A5A9 7B55 54FA 4000
MM7 0000 0000 0000 0000
73DA3CC0 FF15 14E1E073 call dword ptr [73E0E114] ; WININET.InternetReadFile
0165B848 3A 31 33 34 33 33 13433
0165B858 32 30 63 30 62 64 31 34 61 66 33 31 34 34 30 36 20c0bd14af314406
0165B868 36 63 35 64 37 31 33 34 39 39 34 34 63 36 63 31 6c5d71349944c6c1
0165B878 63 37 63 33 31 62 61 34 65 34 61 30 35 35 30 37 c7c31ba4e4a05507
0165B888 61 35 34 31 39 30 38 39 38 33 39 34 33 32 34 63 a54190898394324c
0165B898 66 38 35 66 39 36 61 34 39 34 36 61 34 65 38 32 f85f96a4946a4e82
0165B8A8 31 64 64 37 38 66 33 33 31 39 38 66 38 30 39 30 1dd78f33198f8090
0165B8B8 32 66 34 64 32 65 61 34 66 64 61 62 31 61 63 61 2f4d2ea4fdab1aca
0165B8C8 31 64 37 37 62 32 37 63 32 35 39 3B 49 4E 46 4F 1d77b27c259;INFO
0165B8D8 3A :
EAX 00000089
ECX 01651238 ASCII "ID:1343320c0bd14af3144066c5d71349944c6c1c7c31ba4e4a05507a54190898394324cf85f96a4946a4e821dd78f33198f80902f4d2ea4fdab1aca1d77b27c259;INFO:"
EDX 01651239 ASCII "D:1343320c0bd14af3144066c5d71349944c6c1c7c31ba4e4a05507a54190898394324cf85f96a4946a4e821dd78f33198f80902f4d2ea4fdab1aca1d77b27c259;INFO:"
EBX 001005B8
ESP 001004B8
EBP 001004C8
ESI 016512B7 ASCII "c259;INFO:"
EDI 00000080
EIP 73DA3D8B MFC42.73DA3D8B
C 0 ES 0023 32位 0(FFFFFFFF)
P 1 CS 001B 32位 0(FFFFFFFF)
A 0 SS 0023 32位 0(FFFFFFFF)
Z 0 DS 0023 32位 0(FFFFFFFF)
S 1 FS 003B 32位 7FFDF000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_SUCCESS (00000000)
EFL 00200286 (NO,NB,NE,A,S,PE,L,LE)
MM0 0000 0000 0000 0000
MM1 00FF 00FF 00FF 00FF
MM2 0000 0000 0000 0000
MM3 0000 0000 0000 0000
MM4 0000 0000 0000 0000
MM5 8000 0000 0000 0000
MM6 0000 0000 0000 0000
MM7 0000 0000 0000 0000
004461CE . E8 938B0300 call <jmp.&MFC42.#4277_CString::Mid>
001005BC 48 13 65 01 38 7F 65 01 D8 7F 65 01 00 16 D3 17 He8e?e.?
001005CC 37 08 00 00 1C 52 47 00 78 B4 65 01 FF FF FF FF 7..RG.x磂
001005DC 20 46 5C 00 02 00 00 00 28 11 65 01 D4 04 10 00 F\....(e?.
001005EC B0 0A 10 00 18 30 48 00 07 00 00 00 BC 0A 10 00 ?.0H....?.
001005FC 85 57 44 00 2C AE 49 00 00 00 BC 00 F0 01 15 00 匴D.,甀...??.
0010060C 48 B8 65 01 50 06 10 00 50 06 10 00 00 06 10 00 H竐P.P...
0010061C 58 06 10 00 58 06 10 00 00 06 10 00 60 06 10 00 X.X...`.
0010062C 60 06 10 00 00 06 10 00 70 23 10 00 38 B3 65 01 `...p#.8砮
0010063C 88 04 00 00 50 B8 65 01 30 74 1F 00 01 00 00 00 ?..P竐0t....
0010064C 50 B8 65 01 D4 86 E0 73 00 06 10 00 18 71 BC 00 P竐詥鄐..q?
0010065C 00 06 10 00 88 7F 65 01 00 06 10 00 D8 06 10 02 ..?e..?
0010066C E8 F8 98 7C 00 00 BC 00 84 77 94 7C AC 06 10 00 桫榺..?剋攟?.
0010067C DF E5 98 7C 00 00 BC 00 00 00 00 00 70 B3 65 01 咤榺..?....p砮
0010068C 00 00 BC 00 78 B3 65 01 A8 06 10 00 0C 07 10 00 ..?x砮?...
00444F38 . 68 046A4400 push 00446A04
00444F3D . E8 A2A10300 call <jmp.&MFC42.#1105_AfxBeginThread> ; 解码完成。开始播放
00444F38 . 68 046A4400 push 00446A04
00446016 . 6A 01 push 1
找到音频函数:
00448939 . 55 push ebp ; // 解码的关键函数。挂起此函数导致音频中断
EBP ==> >/0467FF80
EBP+4 >|73DE0BBD 返回到 MFC42.73DE0BBD
EBP+8 >|00100FB8
EBP+C >|0456FB30
EBP+10 >|00B0C270
EBP+14 >|00B0C5F0
EBP+18 >|00B0C5F0
EBP+1C >|73E7764C MFC42.73E7764C
EBP+20 >|00000001
EBP+24 >|00000000