第一次想解决一个软件问题,就拿仙剑98来开刀,可能我心太大了。不过还有点进展,我想希望求助高手一起解决这个问题。我说一下目前我研究出的部分,大家看看我卡的地方怎么解决。
问题症状:仙剑95/98在xp下,游戏中播放所有avi时,只能播放1秒,无论多长的文件,换成什么格式的,都只能播放1秒。
问题分析:
1、首先分析pal.exe,发现这个是一个vb4写的程序。很高兴。结果分析一看,里面什么都没有,import了一堆dll和函数(一看名字就知道是非api的),估计是个壳。后来在同一目录下找到了pal.dll。用w32dasm看了看,发现没有加密,连函数名都没改,很高兴。
2、w32dasm分析代码不爽,换IDA pro。发现里面有一个playavi函数。察看文件。原来我就怀疑是里面avi停止播放并关闭的时机有问题,发现其中对于avi关闭一段是这样写的:
.text:1000208F cmp dword_1000C068, 0
.text:10002096 jz loc_100020CD
……
.text:100020C8 jmp loc_1000208F
……
.text:100020CD loc_100020CD: ; CODE XREF: PlayAvi+3A4j
.text:100020CD ; PlayAvi+3BCj
.text:100020CD push 0 ; hwndCallback
.text:100020CF push 0 ; uReturnLength
.text:100020D1 push 0 ; lpstrReturnString
.text:100020D3 push offset aStopAviWait ; lpstrCommand
.text:100020D8 call ds:mciSendStringA *
.text:100020DE push 0 ; hwndCallback
.text:100020E0 push 0 ; uReturnLength
.text:100020E2 push 0 ; lpstrReturnString
.text:100020E4 push offset aCloseAviWait ; lpstrCommand
.text:100020E9 call ds:mciSendStringA *
重要变量中,aStopAviWait="stop avi wait"; aCloseAviWait="close avi wait";
解释一下。
两个*语句是mciSendString,其四个参数中最重要的是两个:lpstrCommand是一个文本的mci命令字,lpstrReturnString是执行这个命令字后存放返回参数的变量(地址)。
从上面语句段可以看出,由于mci播放avi时是把控制权交回程序的,因此上面一段代码的作用是检查dword_1000C068,为0的时候停止并关闭文件。
这样就看出dword_1000C068是一个重要的控制变量。
在整段程序中查找这个变量,于是……:
3、终于找到了这个变量,是在TimeProc函数中。熟悉c语言的都知道这个是一个被系统定时调用的函数。一般作为setTimer函数的参数传入。果然在TimeProc下面就看到了setTimer。现在看看TimeProc函数中的有关这个变量的部分:
……
.text:1000259D push 0 ; hwndCallback
.text:1000259F push 0Ah ; uReturnLength
.text:100025A1 lea eax, [ebp+strReturnString]
.text:100025A4 push eax ; lpstrReturnString
.text:100025A5 push offset aStatusAviMode ; lpstrCommand
.text:100025AA call ds:mciSendStringA
.text:100025B0 push offset aPlaying ; char *
.text:100025B5 lea eax, [ebp+strReturnString]
.text:100025B8 push eax ; char *
.text:100025B9 call _strcmp
.text:100025BE add esp, 8
.text:100025C1 test eax, eax
.text:100025C3 jz loc_100025D3
.text:100025C9 mov dword_1000C068, 0
.text:100025D3
.text:100025D3 loc_100025D3:
……
其中要说明的重要变量aStatusAviMode="status avi mode"; aPlaying="playing";
这个status avi mode的mci命令的含义就是返回当前avi播放状态。
从上面语句可以看出,返回的这个状态和字符串"playing"比较,相同的说明在播放,保持dword_1000C068原来的值(反正不会为0)。
整段程序在这个方面已经很清楚了,而且也可以看出,肯定是由于检测播放状态返回的不等于playing才会不正常地停止播放avi,造成错误。
现在面临两个可能:
1、xp下播放状态返回状态值不是playing了。
2、程序里的strcmp函数有问题。
于是我就逐个检测。
4、如果返回的状态值不是playing了,那会是什么呢?偶然的机会我把pal.dll中playing这个字符串替换为空串,程序居然可以继续播放avi了!
不过就是播放完了不会自己跳到下一个,要自己按一下。难道这个mci命令字在xp下被废了?什么时候返回的都是空串?
为了验证这个,我自己用vc写了一段功能相同的代码验证:
mciSendString("status avi mode",&modetext,10,0);
if (strcmp(modetext,"playing")==0) { a=1; }
调试的时候,没有问题。的确系统返回了一个playing。
我还察看了一下生成的程序的反汇编:
.text:0040166E push 0 ; hwndCallback
.text:00401670 push 0Ah ; uReturnLength
.text:00401672 push offset strReturnString ; lpstrReturnString
.text:00401677 push offset aStatusThevideo ; lpstrCommand
.text:0040167C call esi ; mciSendStringA
.text:0040167E mov esi, offset aPlaying ; "playing"
.text:00401683 mov eax, offset strReturnString
.text:00401688
.text:00401688 loc_401688: ; CODE XREF: sub_4014C0+1EAj
.text:00401688 mov dl, [eax]
.text:0040168A mov bl, [esi]
.text:0040168C mov cl, dl
.text:0040168E cmp dl, bl
.text:00401690 jnz short loc_4016B0
.text:00401692 test cl, cl
.text:00401694 jz short loc_4016AC
.text:00401696 mov dl, [eax+1]
.text:00401699 mov bl, [esi+1]
.text:0040169C mov cl, dl
.text:0040169E cmp dl, bl
.text:004016A0 jnz short loc_4016B0
.text:004016A2 add eax, 2
.text:004016A5 add esi, 2
.text:004016A8 test cl, cl
.text:004016AA jnz short loc_401688
.text:004016AC
.text:004016AC loc_4016AC: ; CODE XREF: sub_4014C0+1D4j
.text:004016AC xor eax, eax
.text:004016AE jmp short loc_4016B5
.text:004016B0 loc_4016B0: ; CODE XREF: sub_4014C0+1D0j
.text:004016B0 ; sub_4014C0+1E0j
.text:004016B0 sbb eax, eax
.text:004016B2 sbb eax, 0FFFFFFFFh
.text:004016B5
.text:004016B5 loc_4016B5: ; CODE XREF: sub_4014C0+1EEj
.text:004016B5 test eax, eax
.text:004016B7 jnz short loc_4016C3
.text:004016B9 mov dword_4069E4, 1
.text:004016C3
.text:004016C3 loc_4016C3:
和上面的代码没有什么区别。只不过把strcmp函数代码直接填进来了。难道真的是strcmp函数的问题?
5、这是pal.dll里的strcmp函数(函数太长,放在回帖里吧,影响阅读)
粗看了看,好像没有什么问题的样子?
这样看代码下去不现实,想使用动态跟踪的方法看看是怎么回事。
6、原来以为用了动态分析就可以找到答案,却没想到这时候真正的噩梦来了。
我是选用了OLLYDBG1.1进行的分析(softice在我机器上就是跑不起来,呼叫了什么反应都没有)。
结果……结果……
打开pal.exe的时候,我特意按alt+e呼出模块窗口找pal.dll,结果里面居然没有这个dll!
然后我尝试运行,结果提示一个某某dll在头部引用了一个外部的entry什么的,就停不下来了。单步跟踪跟踪到一定时候也执行不下去了。
这时候我才意识到,这程序肯定用了什么我弄不清楚的反跟踪方法。
原来查找整个pal.exe的时候,就没发现pal.dll这个串。vb4写的程序,怎么做这么强?
这样对于我加/解密刚开始学的人来说,光凭以前的知识就分析不下去了。
因此现在来求助各位大虾,我要怎么做才能分析下去?或者希望有经验的大虾帮我分析下去,告诉我这个程序反跟踪的原理是什么。
多谢了~
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!