• 标 题:修改指针法破解VB程序 腾图影视'97 (14千字)
  • 作 者:囚童
  • 时 间:2001-7-25 22:57:31
  • 链 接:http://bbs.pediy.com

修改指针法破解VB程序
腾图影视'97

作者: 囚童
课题: 解除"腾图影视'97"五分钟播映时间限制,清NAG
背景: VB4.0编程
下载:
工具: TRW2000
    Ultra Edit 32


  腾图影视'97是一个播放VCD等数字影视节目的工具软件,除了播放画面流畅、色
彩逼真、界面美观以外,还采用了独创的“无界面”操作技术,即在影片播放过程中
进行的任何操作,都不会有操作画面进行遮挡。此外,还增加了动态屏幕变焦(动态
的将局部画面进行放大、缩小)及漫游,多画面播放(产生电视墙的效果),动态连
续抓拍,还可以将窗口缩小后放在最上层,边干别的工作边看电影,互不干扰。在播
放卡拉-OK时,可将所有曲目的画面显示在屏幕上,从而进行屏幕选曲,操作十分方
便。播放多种格式的视频文件(*.FLI)、 (*.FLC)、(*.DAT)、(*.MPG)、 (*.AVI)、
(*.MOV)等。

腾图影视'97试用版有5分钟播映时间限制,并会弹出一个提示窗口。没有注册入口。

  腾图影视'97选择VB4.0编程.大家知道,VB程序的特点是既难跟又难改,这就给破解
带来相当的难度.


下面给出用修改指针法破解VB程序的例子,供参考.

启动TRW2000,点OK,TRW2000在屏幕右下角生成一个小图标.右击这个图标,选TRmewTCB.

将一张VCD影碟放入光驱,双击腾图影视'97图标,TRW截获这一新线程.

为了让大家了解什么是VB的指针,我们先做如下操作:

下指令:    BPX THROW
按F5,程序被TRW截获.
下指令:    BD*  ;清中断.
按F12三次,按F10一直来到:

109F:30B6 MOV  DS,BX
109F:30B8 CALL FAR [BP+0C] //按F7、F8进入
109F:30BB MOV  [BP-04],AX  //光标停在这里
109F:30BE MOV  [BP-02],DX
109F:30C1 MOV  CX,[BP+06]
109F:30C4 ADD  SP,CX
109F:30C6 ADD  SP,CX
109F:30C8 MOV  AX,[BP-04]
109F:30CB MOV  DX,[BP-02]
109F:30CE POP  SI
109F:30CF  POP  DI
109F:30D0 LEAVE
109F:30D1 RETF 0A

将光标上移到30B8一行,按F7、F8进入到:

418F:2CF0 MOV CX,C8   //光标停在这里
418F:2CF3 CMP AX,C933
418F:2CF6 MOV AX,2622
418F:2CF9 MOV DX,1ADF
418F:2CFC JMP `VB40016!METHCALLENGINE`  //将光标移在这里,按F7、F8进入
418F:2D01 ADD [BX+SI],AL
418F:2D03 ADD [BX+SI],AL
418F:2D05 DB  00
3EC7:2D06 MOV CX,D0
3EC7:2D09 CMP AX,C933
3EC7:2D0C MOV AX,1CB4
3EC7:2D0F MOV DX,3EDF
3EC7:2D12 JMP `VB40016!METHCALLENGINE`

这是典型的VB指针控制域的一个引导入口表。
按F8跟进去,来到:

VB40016!METHCALLENGINE
1A57:0E40 MOV  BX,SP     //光标停在这里
1A57:0E42 SUB  [SS:BX+04],CX
1A57:0E46 MOV  BX,CX
1A57:0E48 MOV  CX,3D0C
1A57:0E4B JMP  SHORT 0E50
1A57:0E4D MOV  CX,3CDD
1A57:0E50 PUSH BP
     ┇
     ┇
1ADF:0F7E MOV [ES:BX+0162],AX
1ADF:0F83 ADD SI,BYTE +02
1ADF:0F86 JMP NEAR [SI-02]   //将光标移在这里,按F7跟到这里

将光标下移到F86一行,按F7。
可以看到,AX的值经处理存入指针SI,DX置为DS段值。
按住F10,可以来到:

1C27:209B MOV   AX,[SI]
1C27:209D PUSH  SS
1C27:209E ADD   AX,BP
1C27:20A0 PUSH  AX
1C27:20A1 ADD   SI,BYTE +04
1C27:20A4 JMP   NEAR [SI-02]
     ┇
     ┇
1C27:20B9 MOV   DI,[SI]
1C27:20BB POP   AX
1C27:20BC MOV   [BP+DI],AL
1C27:20BE ADD   SI,BYTE +04
1C27:20C1 JMP   NEAR [SI-02]

  分析上面给出的209B和20B9两节指令,可以看出VB指针控制域代码结构的明显特
征:指针DS:SI指向一个混合装有指令入口和数据的指针列表,节首的传送指令通过指
针列表下载数据;节尾的跳转指令通过指针列表下载新的指令入口。这样就将事先按
死顺序编好在VB运行库里的指令代码和用户所给的数据通过指针列表盘活了。因此,
下面给出的带条件语句的TRW跟踪指令也反映了VB指针控制域代码结构的这一特征。

那么,怎样解除"腾图影视'97"五分钟播映时间限制呢?

  "腾图影视'97"有多个时间限制机制,一个是由窗口内的鼠标指针激活的与系统时
钟相关的5分钟计时,另七个是内建的以分钟为单位的5分钟计时,它们各自独立运行。

先看第一个:
A:

其指令引导表为:
1B1F:22DE MOV CX,0086
1B1F:22E1 CMP AX,C933
1B1F:22E4 MOV AX,2DA6
1B1F:22E7 MOV DX,1AC7
1B1F:22EA JMP `VB40016!METHCALLENGINE`

下指令:G 158E IF(SI==2),回车,将光标移入窗口,截获到:

15A7:158B MOV  DI,15AF
15A7:158E CALL FAR [SI]   //光标停在这里
15A7:1590 ADD  SI,BYTE +04
15A7:1593 JMP  DI
15A7:1595 MOV  DI,159A
15A7:1598 JMP  SHORT 158E
15A7:159A XOR  AH,AH
15A7:159C PUSH AX
15A7:159D ADD  SI,BYTE +02
15A7:15A0 JMP  NEAR [SI-02]
15A7:15A3 MOV  DI,15AE
15A7:15A6 JMP  SHORT 158E
15A7:15A8 MOV  DI,15AD
15A7:15AB SHORT 158E
15A7:15AD PUSH DX
15A7:15AE PUSH AX
15A7:15AF ADD  SI,BYTE +02
15A7:15B2 JMP  NEAR [SI-02]

  可以看到TRW反汇编窗口下面的横线中央标有VB40016(xx)+xxx,指出当前模块名
是VB40016,括号中是子模块序号,加号后是在模块中的偏移量.

这就是VB4的指针控制域.所谓VB难追,也是指在这个域里.

  为什么上面的TRW跟踪指令要附带条件语句呢?因为VB是解释语言,解释语言的
一节命令可能会被反复多次调用,不同的调用会有不同的指针。用指针做条件可以限
定在指定的调用环境下截获中断。特别在长调用返回时,若调用的节对自身再次调用,
无指针做条件会导致返回在下一个调用的返回处。

(若按F8跟进去,可以发现这是对函数RTCGetTimeBSTR的调用。)

下指令:D DS:0
得到指针列表(方扩号是作者加的。方扩号内是指令指针,方扩号外是数据指针):

1AC7:0000[15A8]0BCC 116F[402E]FF9A[2051]FF9A[3283]
1AC7:0010 0001 0000[332C]FF5C 0002[15A8]0AE0 3B6F
1AC7:0020[4025]FF58[394B]FF50[158B]1E18 116F[2051]
1AC7:0030 FF9A[3283]0004 0000[332C]FF30 0002[15A8]
1AC7:0040 0AE0[3B6F 4025]FF2C[394B]FF24[158B]1E18
1AC7:0050 116F[2BA0]0026 0000[321B 468C 381D]0006
1AC7:0060 005C[3283]0005 0000[3555 1876 2090]FF50
1AC7:0070[3213]003C[1869 3631 2090]FF24[3585 1965]
1AC7:0080[46E4 33B2 220B]0004 FF58 FF2C[21F7]0004
1AC7:0090 FF5C FF30[148B]000C 161E 0004 06AC 3BE0

将上表整理,可得指针、指令和数据的关系如下(每一序号包含一至多节):

节序        ①      ②      ③      ④      ⑤
指针SI     2       C          1C         40        5C
指令入口[SI-2]    15A8     2051      15A8     15A8     468C
数据内容[SI]  0BCC     FF9A      0AE0     0AE0     —
数据内容描述  函数地址偏移 数据地址偏移 函数地址偏移 函数地址偏移 — 
该节功能    获取当前时间 传送时间   处理小时   处理分钟    判小时


节序        ⑥      ⑦      ⑧
指针SI     5E      82      84
指令入口[SI-2]    381D     46E4     33B2
数据内容[SI]  0006     —      —
数据内容描述  浮点处理参数 —      —
该节功能    进入浮点处理 判分钟        终判、置标志

节① SI=2
1A8F:158E CALL FAR [SI]  //SI指向函数RTCGETTIMEBSTR入口,获取当前时间
1A8F:1590 ADD  SI,BYTE +04
1A8F:1593 JMP  DI
1A8F:1595 MOV  DI,159A
1A8F:1598 JMP  SHORT 158E
1A8F:159A XOR  AH,AH
1A8F:159C PUSH AX
1A8F:159D ADD  SI,BYTE +02
1A8F:15A0 JMP  NEAR [SI-02]
1A8F:15A3 MOV  DI,15AE
1A8F:15A6 JMP  SHORT 158E
1A8F:15A8 MOV  DI,15AD   //该节入口
1A8F:15AB JMP  SHORT 158E
1A8F:15AD PUSH DX
1A8F:15AE PUSH AX
1A8F:15AF ADD  SI,BYTE +02  //指向下一节
1A8F:15B2 JMP  NEAR [SI-02] //跳到下一节入口

节② SI=C
1A8F:2051 MOV  DI,[SI]
1A8F:2053 PUSH WORD [BP+DI+02] //将指向当前时间的地址压入堆栈
1A8F:2056 PUSH WORD [BP+DI]
1A8F:2058 ADD  SI,BYTE +04   //指向下一节
1A8F:205B JMP  NEAR [SI-02]  //跳到下一节入口

节③ SI=1C
同节①,SI指向函数RTCMIDCHARBSTR入口,取小时值供后续处理。

节④ SI=40
同节③,SI指向函数RTCMIDCHARBSTR入口,取分钟值供后续处理。

节⑤ SI=5C
1A8F:468C POP  DX     //弹出参照标量
1A8F:468D POP  CX     //弹出小时处理结果
1A8F:468E CMP  CX,DX    //判断
1A8F:4690 MOV  AX,00    //清小时超时标志
1A8F:4693 JNZ  4696     //未超时,跳
1A8F:4695 DEC  AX     //超时,置小时超时标志FFFF
1A8F:4696 PUSH AX      //压入堆栈
1A8F:4697 ADD  SI,BYTE +02 //指向下一节
1A8F:469A JMP  NEAR [SI-02]//跳到下一节入口

节⑥ SI=5E
1A8F:381D MOV  DI,[SI]
1A8F:381F LES  DI,[BP+DI]
1A8F:3821 MOV  AX,ES
1A8F:3823 OR   AX,AX
1A8F:3825 JZ   3870
1A8F:3827 ADD  DI,[SI+02]
1A8F:382A PUSH WORD [ES:DI+02]  //将浮点处理参数压入堆栈
1A8F:382E PUSH WORD [ES:DI]   //供浮点系统处理
1A8F:3831 ADD  SI,BYTE +06
1A8F:3834 JMP  NEAR [SI-02]

节⑦ SI=82
3A7F:46E4 CALL C777    //在浮点系统中先将十六进制分钟数据转换为80位的数
3A7F:46E7 JMP  SHORT 46F6  //据,再判断是否超时,将结果送入AH,再将AH中的内
3A7F:46E9 CALL C785    //容送标志位。
3A7F:46EC JMP  SHORT 46F6
3A7F:46EE CALL C74C
3A7F:46F1 JMP  SHORT 46F6
3A7F:46F3 CALL C732
3A7F:46F6 SBB  AX,AX    //根据标志位置分钟超时标志,0为未超时,FFFF为超时。
3A7F:46F8 PUSH AX      //压入堆栈
3A7F:46F9 ADD  SI,BYTE +02 //指向下一节
3A7F:46FC JMP  NEAR [SI-02] //跳到下一节入口

节⑧ SI=84
31B7:33B2 POP  AX     //弹出小时超时标志
31B7:33B3 POP  CX     //弹出分钟超时标志
31B7:33B4 AND  AX,CX    //终判,其一为超时则判为超时
31B7:33B6 PUSH AX     //置终判标志于栈顶
31B7:33B7 ADD  SI,BYTE +02 //指向下一节
31B7:33BA JMP  NEAR [SI-02]//跳到下一节入口

  可见,只要在节⑧位置向栈顶压入标志字0,就可以不去理会浮点运算有多么复
杂,使程序始终处于未超时状态。
      
  但是,上面提到的全部数据传输和处理过程都是在vb40016.dll中完成的,无法将
33B6处改写为例如PUSH BYTE +00这样的指令(改写公共DLL文件是对用户极不负责
的表现).

  而指针表是程序在编译过程中生成的,是可以改写的。只要记住这样的规则,就
不怕改写VB程序了:不要指望改写语句,要设法改变调用。

改变调用的方法就是修改指针表。

在当前指令代码段中找了一下,有下面2节大约可用于新的调用:


31D7:1746 SBB  AX,AX
31D7:1748 PUSH AX
31D7:1749 ADD  SI,BYTE +02
31D7:174C JMP  NEAR [SI-02]


31D7:321B PUSH BYTE +00
31D7:321D ADD  SI,BYTE +02
31D7:3220 JMP  NEAR [SI-02]

试选⑵,做一个新的指针方案:

节序        ⑧
指针SI     84
指令入口[SI-2]    321B
数据内容[SI]  —
数据内容描述  —
该节功能    强制设置未超时标志0

  抄下指针表中指针84前后的十六进制码,使用Ultra Edit 32,按下面的方法做第
一步的修改:

46 B2 33 0B
— 1B 32 —

下面来看另七个内建的以分钟为单位的5分钟计时。
B-1:

其指令引导表为:
346F:0F40 MOV CX,66
346F:0F43 CMP AX,C933
346F:0F46 MOV AX,370C
346F:0F49 MOV DX,346F
346F:0F4C JMP `VB40016!METHCALLENGINE`

下指令:G 3807 IF(SI==31F2),回车,来到:

1A8F:3807 MOV  DI,[SI]   //光标停在这里
1A8F:3809 LES  DI,[BP+DI]
1A8F:380B MOV  AX,ES
1A8F:380D OR   AX,AX
1A8F:380F JZ   3870
1A8F:3811 ADD  DI,[SI+02]
1A8F:3814 PUSH WORD [ES:DI]
1A8F:3817 ADD  SI,BYTE +06
1A8F:381A JMP  NEAR [SI-02]

下指令:D DS:31E0


得到指针列表(方扩号是作者加的。方扩号内是指令指针,方扩号外是数据指针):

346F:31E0[0F92]0004[0F92]0008[3D49]0512[0F92]0012
346F:31F0[3807]0006 0034[321B 47C5 148B]0018[0F92]
346F:3200 0014[3807]0006 0034[3223 359C 38BA]0006
346F:3210 0034[1474]04E4[0F92]0004[0F92]0034[321B]

将上表整理,可得指针、指令和数据的关系如下(每一序号包含一至多节):

节序        ①      ②     ③      ④  
指针SI     31F2     31F8    31FA     31FC 
指令入口[SI-2]    3807     321B     47C5     148B 
数据内容[SI]  0006     —     —      —  
数据内容描述  数据地址偏移 —     —      —  
该节功能    获取剩余分钟 给出参照值 比较、置标志 判断 

节序      ⑤      ⑥     ⑦     ⑧
指针SI     3204     320A    320C    320E
指令入口[SI-2]    3087     3223    359C    38BA
数据内容[SI]  0006     —     —     0006    
数据内容描述  数据地址偏移 —     —     非超时增跳节数
该节功能    获取剩余分钟 给出递减值 剩余分钟减1 回置存储单元

节① SI=31F2

1A8F:3807 MOV  DI,[SI]   //取数据地址偏移
1A8F:3809 LES  DI,[BP+DI]
1A8F:380B MOV  AX,ES
1A8F:380D OR   AX,AX    //校验地址有效性
1A8F:380F JZ   3870
1A8F:3811 ADD  DI,[SI+02] //取下一个数据地址偏移
1A8F:3814 PUSH WORD [ES:DI]//将剩余分钟压入堆栈
1A8F:3817 ADD  SI,BYTE +06 //指向下一节
1A8F:381A JMP  NEAR [SI-02]//跳到下一节入口

节② SI=31F8

1A8F:321B PUSH BYTE +00  //给出参照值,0为计时结束。
1A8F:321D ADD  SI,BYTE +02 //指向下一节
1A8F:3220 JMP  NEAR [SI-02]//跳到下一节入口

节③ SI=31FA

1A8F:47C5 POP  DX     //弹出参照值
1A8F:47C6 POP  CX     //弹出剩余分钟值
1A8F:47C7 XOR  AX,AX    //清超时标志
1A8F:47C9 CMP  CX,DX    //比较
1A8F:47CB JNG  47CE    //非超时,跳;标志为0
1A8F:47CD DEC  AX     //超时,不跳;标志为FFFF
1A8F:47CE PUSH AX     //保存标志
1A8F:47CF ADD  SI,BYTE +02 //指向下一节
1A8F:47D2 JMP  NEAR [SI-02]//跳到下一节入口

节④ SI=31FC

1A8F:148B POP CX      //弹出超时标志
1A8F:148C MOV AX,[SI]   //取非超时增跳节数
1A8F:148E OR  CX,CX    //判断
1A8F:1490 JNZ 1494     //超时,保持原节进程
1A8F:1492 ADD SI,AX    //非超时,增跳指定指令节数
1A8F:1494 ADD SI,BYTE +04 //指向下一节
1A8F:1497 JMP NEAR [SI-02] //跳到下一节入口

节⑤ SI=3204
同节①

节⑥ SI=320A

1A8F:3223 PUSH BYTE +01  //取递减值
1A8F:3225 ADD  SI,BYTE +02 //指向下一节
1A8F:3228 JMP  NEAR [SI-02]//跳到下一节入口

节⑦ SI=320C

1A8F:359C POP AX      //弹出递减值
1A8F:359D MOV BX,SP
1A8F:359F SUB [SS:BX],AX  //剩余分钟值减1
1A8F:35A2 JO  35BD
1A8F:35A4 ADD SI,BYTE +02 //指向下一节
1A8F:35A7 JMP NEAR [SI-02]//跳到下一节入口

节⑧ SI=320E

1A8F:38BA MOV DI,[SI]   //取数据地址偏移
1A8F:38BC LES DI,[BP+DI]
1A8F:38BE MOV AX,ES
1A8F:38C0 OR  AX,AX    //校验地址有效性
1A8F:38C2 JZ  3870
1A8F:38C4 ADD DI,[SI+02] //取下一个数据地址偏移
1A8F:38C7 POP WORD [ES:DI]//将更新的剩余分钟值回置原存储单元
1A8F:38CA ADD SI,BYTE +06 //指向下一节
1A8F:38CD JMP NEAR [SI-02]//跳到下一节入口

  从节④可以看出,在VB中,指令的跳转不是按指令字节数来跳转的,而是按指令
的节数来跳转的。

  从节⑦可以看出,若时间递减值为0,则剩余分钟值永不为0,程序就可以不受限
制地运行下去了。这可以通过改变节⑥的调用来实现。我们仍可以用前面用到过的321B
来替换节⑥。

新的调用:

31D7:321B PUSH BYTE +00
31D7:321D ADD  SI,BYTE +02
31D7:3220 JMP  NEAR [SI-02]

新的指针方案:

节序        ⑥
指针SI     320A
指令入口[SI-2]    321B
数据内容[SI]  —
数据内容描述  —
该节功能    强制设置时间递减值为0

另外6个计时器大同小异,但指针表的位置不一样,列表如下:

计时器号    B-1   B-2   B-3   B-4   B-5   B-6   B-7
入口引导表入口 F40   23BA  28F8  30B8  3560  389C  3A98
首节指针SI   31E2  56E   270A  374E  2C76  2    ADA
首节入口    F92   F92   F92   F92   F92   F92   F92
指令指针SI   31F8  57E   271A  375E  2C86  12   AEA
指令入口    3807  3807  3807  3807  3807  3807  3807
需要改值的指针 3208  594   2730  3774  2C9C  28   B00
UltraEdit32偏移 44440  45510  43970  449A0  43ED0  44F90  45A80

  抄下指针表中需要改值的指针前后的十六进制码(它们大致相同),使用Ultra
Edit 32,按下面的方法做最后的修改:

00 23 32 9C 35 BA 38
-- 1B -- -- -- -- --

  与此代码相同的不止7处,可参考上表最下面一行UltraEdit32偏移,它是一组行
号,需要改值的指针在此行内。

存盘。
试运行。哇,成功!
VB是以修改的!