首页
社区
课程
招聘
[求助]想解决仙剑98版在xp下不能正常播放avi的故障,现在卡住了~
发表于: 2004-12-9 19:51 5554

[求助]想解决仙剑98版在xp下不能正常播放avi的故障,现在卡住了~

2004-12-9 19:51
5554
第一次想解决一个软件问题,就拿仙剑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写的程序,怎么做这么强?
这样对于我加/解密刚开始学的人来说,光凭以前的知识就分析不下去了。

因此现在来求助各位大虾,我要怎么做才能分析下去?或者希望有经验的大虾帮我分析下去,告诉我这个程序反跟踪的原理是什么。
多谢了~

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (9)
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
以下是反编译出来的pal.dll里的strcmp函数,不知道有没有什么问题啊~

.text:10005580 ; int __cdecl strcmp(const char *,const char *)
.text:10005580 _strcmp         proc near               ; CODE XREF: TimerProc+17Ep
.text:10005580
.text:10005580 arg_0           = dword ptr  4
.text:10005580 arg_4           = dword ptr  8
.text:10005580
.text:10005580                 mov     edx, [esp+arg_0]
.text:10005584                 mov     ecx, [esp+arg_4]
.text:10005588                 test    edx, 3
.text:1000558E                 jnz     short loc_100055CC
.text:10005590
.text:10005590 loc_10005590:                           ; CODE XREF: _strcmp+3Cj
.text:10005590                                         ; _strcmp+66j ...
.text:10005590                 mov     eax, [edx]
.text:10005592                 cmp     al, [ecx]
.text:10005594                 jnz     short loc_100055C4
.text:10005596                 or      al, al
.text:10005598                 jz      short loc_100055C0
.text:1000559A                 cmp     ah, [ecx+1]
.text:1000559D                 jnz     short loc_100055C4
.text:1000559F                 or      ah, ah
.text:100055A1                 jz      short loc_100055C0
.text:100055A3                 shr     eax, 10h
.text:100055A6                 cmp     al, [ecx+2]
.text:100055A9                 jnz     short loc_100055C4
.text:100055AB                 or      al, al
.text:100055AD                 jz      short loc_100055C0
.text:100055AF                 cmp     ah, [ecx+3]
.text:100055B2                 jnz     short loc_100055C4
.text:100055B4                 add     ecx, 4
.text:100055B7                 add     edx, 4
.text:100055BA                 or      ah, ah
.text:100055BC                 jnz     short loc_10005590
.text:100055BE                 mov     eax, eax
.text:100055C0
.text:100055C0 loc_100055C0:                           ; CODE XREF: _strcmp+18j
.text:100055C0                                         ; _strcmp+21j ...
.text:100055C0                 xor     eax, eax
.text:100055C2                 retn
.text:100055C2 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
.text:100055C3                 align 4
.text:100055C4
.text:100055C4 loc_100055C4:                           ; CODE XREF: _strcmp+14j
.text:100055C4                                         ; _strcmp+1Dj ...
.text:100055C4                 sbb     eax, eax
.text:100055C6                 shl     eax, 1
.text:100055C8                 inc     eax
.text:100055C9                 retn
.text:100055C9 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
.text:100055CA                 align 4
.text:100055CC
.text:100055CC loc_100055CC:                           ; CODE XREF: _strcmp+Ej
.text:100055CC                 test    edx, 1
.text:100055D2                 jz      short loc_100055E8
.text:100055D4                 mov     al, [edx]
.text:100055D6                 inc     edx
.text:100055D7                 cmp     al, [ecx]
.text:100055D9                 jnz     short loc_100055C4
.text:100055DB                 inc     ecx
.text:100055DC                 or      al, al
.text:100055DE                 jz      short loc_100055C0
.text:100055E0                 test    edx, 2
.text:100055E6                 jz      short loc_10005590
.text:100055E8
.text:100055E8 loc_100055E8:                           ; CODE XREF: _strcmp+52j
.text:100055E8                 mov     ax, [edx]
.text:100055EB                 add     edx, 2
.text:100055EE                 cmp     al, [ecx]
.text:100055F0                 jnz     short loc_100055C4
.text:100055F2                 or      al, al
.text:100055F4                 jz      short loc_100055C0
.text:100055F6                 cmp     ah, [ecx+1]
.text:100055F9                 jnz     short loc_100055C4
.text:100055FB                 or      ah, ah
.text:100055FD                 jz      short loc_100055C0
.text:100055FF                 add     ecx, 2
.text:10005602                 jmp     short loc_10005590
.text:10005602 _strcmp         endp
2004-12-9 19:53
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
为什么没有大虾愿意帮一下呢?
是因为大家都没有这个游戏吗?
这样经典的游戏,值得大家研究一下啊
2004-12-10 11:03
0
雪    币: 216
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
只能播放一秒??但我玩这个游戏在xp下没这个现象啊。老大,你水平够高的,我还看不懂你分析的代码呢
2004-12-10 11:33
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我这里是只能播放一秒,XP SP2下。
请问你用的是哪个版本仙剑啊?avi文件完整吗?(现在网上很多都是没动画的)
能把pal.exe和pal.dll打个包放在什么地方给我下一下试试好吗?

其实我觉得这段代码没有什么啊,主要是最基本的跳转和mciSendStringA函数API的运用。
那个API的格式如下(VB格式的声明):

Declare Function mciSendString Lib "winmm.dll" lias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long,ByVal hwndCallback As Long) As Long

其中主要的就是那个lpstrCommand参数,是一个mci命令字
2004-12-10 16:21
0
雪    币: 216
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
我玩的是新仙剑,就是那个在98下玩的那个,老大如果想要,我周一上传给你
2004-12-10 17:31
0
雪    币: 216
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
忘了说了,avi文件绝对是完整的,正版的。你的那个版本是从网上下载的吧?
2004-12-10 17:33
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8

我说的不是新仙剑.新仙剑的技术自然成熟多了
是"仙剑98柔情篇"
就是DOS仙剑的WIN95复刻版
不信你下一个来研究一下,avi就是放不了
不过要下完整版带AVI的,200M这样的
2004-12-10 20:26
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
oh my god
真的没有朋友能帮我么~
就是帮我分析一下pal.exe是怎么调用pal.dll的都可以啊~~~~~
2004-12-11 19:29
0
雪    币: 216
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
看起来确实不是,和你说的不是一个版本的。我没找到pal.exe,只有palgame.exe
2004-12-13 08:15
0
游客
登录 | 注册 方可回帖
返回
//