欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

移花接木:破解闪客精灵 MX 2002演示版

移花接木:破解闪客精灵 MX 2002演示版

移花接木:破解闪客精灵 MX 2002演示版

为了破解一个Flash动画,下载了几个工具,觉得这个最好,可以
导出所有的资源,包括脚本。可惜是个演示版。从其文档看,如
果要买,会提供另外一个下载URL。估计是个真正的演示版。
安装完后,没有提供任何注册方式。从FileMon,RegMon的结果看,
也没发现什么可疑之处。演示版有何限制?
试用版的限制:
  * 30天试用期
  * 只能分析动画的前5帧
  * 只支持导出每组资源文件中的前2个文件
?!
看来正式版与演示版并无质的不同,只有量的差别,完全可能
做出一个自己的"正式版"。
对于这种需要到处打补丁的程序,最好每做一次patch,就备份一次,
以便需要的时候退回去。下面的文字忽略了退回去的过程。
程序用MFC写的,没有加壳。
 
1. 解除试用期限制
   这个比较简单,从对GetLocalTime的调用就可以找到。也没有对
   系统时间回调的检测(应该学学Flexlm)。:-)
   .text:0042E3B3    call    f_GetDaysUsed
   .text:0042E3B8    mov     edieax       ;->xor edi,edi(0x33 0xFF)
   .text:0042E3BA    test    ediedi
   .text:0042E3BC    jl      short loc_42E3F2
   .text:0042E3BE    cmp     edi, 1Eh        ; here,继续试用
   关闭对应Nag Screen(还有多少天可用)
 
   .text:00432AAB    call    sub_47DF8F  ;关闭
   .text:00432AB0    cmp     eax, 1
   .text:00432AB3    jz      short loc_432B2B ;jmp

2. 解除对动作分析(即脚本)的数目限制
   既然上面用edi作比较,先偷个懒,直接在IDA中查找:
   cmp edi,2h
   找到多处。挑几个可疑的一试,还行:-)
  
  .text:00436737 loc_436737:
  .text:00436737     mov     ecx, [esi+1Ch]
  .text:0043673A     push    eax
  .text:0043673B     push    2
  .text:0043673D     push    110Ah
  .text:00436742     push    ecx
  .text:00436743     inc     edi             ; <-NOP
  .text:00436744     call    ebx ; SendMessageA ; 注意,是以SendMessage来做的
  .text:00436744                                ; 且均用edi做比较
  .text:00436746     test    eaxeax
  .text:00436748     jz      short loc_436751
  .text:0043674A     cmp     edi, 2          ; 只能导出前两个动作脚本.
  .text:0043674D     jl      short loc_436737

3.解除只分析影片前五帧限制(实际上右侧CTreeCtrl上几乎每个item
  都多了很多东西):-)
  用和上一步同样的办法,这次找"cmp edi,5h",没有。转念一想,也
  许是在0-4间取值呢?试试"cmp edi,4h",Bingo!运气不错啊。
 .text:0041E131 loc_41E131:                  ; CODE XREF: sub_41DF50+1C5
 .text:0041E131      mov     edi, [esp+50h+var_4]
 .text:0041E135      push    ebp
 .text:0041E136      lea     ecx, [esp+54h+var_38]
 .text:0041E13A      push    edi
 .text:0041E13B      push    ecx
 .text:0041E13C      mov     ecxesi
 .text:0041E13E      call    sub_415F90
 .text:0041E143      inc     edi ;不要NOP这句,会导致动画帧资源命名错误
 .text:0041E143             ;(总为"frame 1")。这是后面发现问题后改的
 .text:0041E144      cmp     edi, 4
 .text:0041E147      mov     [esp+50h+var_4], edi
 .text:0041E14B      jbe     loc_41E688     ;改这里为jmp
  不要急于关闭"演示版限制: 只分析影片的前五帧。"和
  "演示版限制每类资源中只有前两个资源可以被导出。"
  两个Nag Screen。这个程序有用的代码和Nag Screen搅在
  一起,修改的位置不当,冒然去掉Nag Screen会造成问题,
  而且留着Nag可以帮助寻找线索。等所有的功能实现以后再
  去除Nag。
 
4.解除每种资源只能导出两个文件的限制
  这回运气用完了,前面的花招不管用了。得读读代码了。
  注意到在导出资源时,如果在用于显示可用资源的树型控件上
  只选择前两项之外的item,会报没有选择资源。以此为线索,可
  找到下面的代码。共9种资源(矢量图,图片,声音,文字等等),
  在导出时对每种资源调用一次。返回值为该类型的资源被
  选中的item数。如果没有选前两项,返回0。
  函数传入的参数是指向第1级节点(矢量图,图片...)TreeItem的指针。
  TreeItem结构中有对应item的显示文本,在调试时可以用来确认判断。
  可以看到这里用了硬编码(而不是循环),只会返回0-2间的值。
  或许是为了隐蔽,代码在不少地方没有直接使用CTreeCtrl的成员
  函数,而是用兵SendMessage的方式来完成对应的操作。
 .text:00437E40 f_GetNumOfItems proc near    ; CODE XREF: f_GetNumOfEntry+C
 .text:00437E40                              ; sub_437EE0+32
 .text:00437E40 
 .text:00437E40 arg_nIndex      = dword ptr  4
 .text:00437E40 
 .text:00437E40      mov     eax, [esp+arg_nIndex]
 .text:00437E44      push    ebp
 .text:00437E45      push    esi
 .text:00437E46      mov     esiecx
 .text:00437E48      and     eax, 0FFh
 .text:00437E4D      push    edi
 .text:00437E4E      xor     ebpebp
 .text:00437E50      mov     edi, [esi+eax*4+60h] ; 这里存的是第1级TREEITEM
 .text:00437E50                                   ; 的句柄(9项)
 .text:00437E54      test    ediedi
 .text:00437E56      jnz     short loc_437E60
 .text:00437E58      pop     edi
 .text:00437E59      pop     esi
 .text:00437E5A      xor     eaxeax
 .text:00437E5C      pop     ebp
 .text:00437E5D      retn    4
 .text:00437E60 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 .text:00437E60 
 .text:00437E60 loc_437E60:                  ; CODE XREF: f_GetNumOfItems+16
 .text:00437E60      push    ebx
 .text:00437E61      push    edi             ; 指向第1级的节点:
 .text:00437E61                              ; 矢量图,图片...
 .text:00437E62      mov     ecxesi        ; 用__thiscall,this指针从
 .text:00437E62                              ; ecx
 .text:00437E64      call    CTreeCtrl::ItemHasChildren(_TREEITEM *) ; 有子item?
 .text:00437E69      test    eaxeax
 .text:00437E6B      jz      short loc_437EB7
 .text:00437E6D      mov     ecx, [esi+1Ch]
 .text:00437E70      mov     ebxds:SendMessageA
 .text:00437E76      push    edi             ; lParam
 .text:00437E77      push    4               ; wParam
 .text:00437E79      push    110Ah           ; Msg
 .text:00437E79                              ; 
 .text:00437E79                              ; 应该是等价于CTreeCtrl::GetChildItem
 .text:00437E7E      push    ecx             ; hWnd 
 .text:00437E7E                              ; 
 .text:00437E7E                              ; 向TreeView控件发消息
 .text:00437E7F      call    ebx ; SendMessageA
 .text:00437E81      mov     edieax        ; 这里会返回一个指向子TreeItem
 .text:00437E81                              ; 的句柄,第1个子item
 .text:00437E83      test    ediedi
 .text:00437E85      jz      short loc_437EB7
 .text:00437E87      push    edi
 .text:00437E88      mov     ecxesi
 .text:00437E8A      call    f_IsItemChecked ; 节点是否被选中
 .text:00437E8F      test    eaxeax
 .text:00437E91      jz      short loc_437E98
 .text:00437E93      mov     ebp, 1          ; ebp计数
 .text:00437E93                              ; 
 .text:00437E93                              ; 可以从这里开始patch
 .text:00437E93                               ; (最后是在437EAA处patch的)
 .text:00437E98 
 .text:00437E98 loc_437E98:                  ; CODE XREF: f_GetNumOfItems+51
 .text:00437E98      mov     edx, [esi+1Ch]
 .text:00437E9B      push    edi             ; lParam
 .text:00437E9C      push    1               ; wParam 这个值与上一个不同
 .text:00437E9E      push    110Ah           ; Msg
 .text:00437EA3      push    edx             ; hWnd
 .text:00437EA4      call    ebx ; SendMessageA ; 等价于CTreeCtrl::GetNextSiblingItem
 .text:00437EA4                                 ; 
 .text:00437EA4                                 ; 查看pTreeItem指向的内容,
 .text:00437EA4                                 ; 两次SendMessage分别(对矢量图
 .text:00437EA4                                 ; 节点)返回"shape1","shape2"
 .text:00437EA6      test    eaxeax
 .text:00437EA8      jz      short loc_437EB7
 .text:00437EAA      push    eax             ; 指向子TreeItem的句柄,
 .text:00437EAA                              ; 第2个子item
 .text:00437EAA                              ; 
 .text:00437EAA                              ; 可见只能输出两项是硬
 .text:00437EAA                              ; 编码实现的,没有用循环
 .text:00437EAA                              ; 
 .text:00437EAA                              ; 要加循环代码,这里空间不够
 .text:00437EAB      mov     ecxesi
 .text:00437EAD      call    f_IsItemChecked
 .text:00437EB2      test    eaxeax
 .text:00437EB4      jz      short loc_437EB7
 .text:00437EB6      inc     ebp             ; ebp计数
 .text:00437EB6                              ; 
 .text:00437EB6                              ; 如果在这里硬返回到
 .text:00437EB6                              ; 437E98,需要使edi指向
 .text:00437EB6                              ; 当前item
 .text:00437EB7 
 .text:00437EB7 loc_437EB7:                  ; CODE XREF: f_GetNumOfItems+2B
 .text:00437EB7                              ; f_GetNumOfItems+45
 .text:00437EB7                              ; f_GetNumOfItems+68
 .text:00437EB7                              ; f_GetNumOfItems+74
 .text:00437EB7      pop     ebx
 .text:00437EB8      pop     edi
 .text:00437EB9      mov     eaxebp
 .text:00437EBB      pop     esi
 .text:00437EBC      pop     ebp
 .text:00437EBD      retn    4
 .text:00437EBD f_GetNumOfItems endp
  这里的代码很明显,直接用了前两个item,不存在循环。的确是个
  演示版。我们只要修改一下代码,加上循环,使其访问对应资源item
  下的所有item即可。
  这里空间有限,只好把代码加到.text section的末尾。从PEditor可以
  看到:
  .text section:
  VirtualSize=0x98A04
  RawSize=0x99000
  可用空间=RawSize - VirtualSize=0x99000 - 0x98A04=0x5FC
          =1532字节,足够了。
  VirtualOffset=0x1000
  
  可用的起始地址=ImageBase + VitualOffset + VirtualSize
                =0x400000 + 0x1000 + 0x98A04
                =0x499A04
  对应的RawOffset=0x99A04
  在SoftIce中用a命令输入代码,即可得到需要的opcode。需要注意
  的是,我一开始从437E98开始改,在输入
  
  push 110Ah 
 
  时,在SoftIce中生成的opcode有误,总是把立即数110A作为一个WORD
  而不是DWORD来压栈,导致堆栈被破坏而崩溃,不知什么原因。这句不
  能用a命令,必须直接编辑内存把opcode输进去。后来干脆把代码改
  到437EAA处(即找到该资源下的第2个item后)。
  
  改为:
 .text:00437EAA       jmp 499A04
  在499A04处增加代码:
 .text:00499A04      push    eax  ;保存eax即指向当前item的TreeItem
                                                   ;结构的指针
    
                     push    eax
     mov     ecxesi
     call      426A00
    test     eax,eax ;是否选中
                     pop      edi  ;弹出保存值,下一轮使用
    jz        437E98
    inc       ebp  ;增加计数
    jmp     437E98
  此时当仅选前两项之后的item时,不再报"未选择资源...",上述函数
  正确返回了选择的所有资源的总item数目。结束循环的出口为437EA8,即
  没有剩余的同级item。
  
5.解除最后的限制:每类只能输出2个文件
  可以从f_IsItemChecked(426A00)入手,每种资源都应该会用这个
  函数判断item是否被选中。在IDA中检查其caller,注意附近是否
  有类似计数动作的代码。
  (4385C0处的函数有点象,实际上只是检查矢量图资源的格式是否
  符合"闪客巫师"的要求,下面会用到)。"闪客巫师"为另一个搭配
  使用的软件。
  很容易地定位到:
 .text:00437DA0 f_ExportResource proc near    ; CODE XREF: sub_437EE0+8C
 .text:00437DA0 
 .text:00437DA0 arg_0           = dword ptr  4
 .text:00437DA0 arg_4           = dword ptr  8
 .text:00437DA0 
 .text:00437DA0       mov     eax, [esp+arg_4]
 .text:00437DA4       push    ebx
 .text:00437DA5       push    ebp
 .text:00437DA6       push    esi
 .text:00437DA7       mov     esiecx
 .text:00437DA9       and     eax, 0FFh
 .text:00437DAE       push    edi
 .text:00437DAF       mov     ecx, [esi+eax*4+84h]
 .text:00437DB6       mov     edi, [esi+eax*4+60h]
 .text:00437DBA       test    ediedi
 .text:00437DBC       lea     edx, [ecx+eax*2]
 .text:00437DBF       mov     eax, dword_4B65A0[edx*4]
 .text:00437DC6       mov     [esp+10h+arg_4], eax
 .text:00437DCA       jz      short loc_437E32
 .text:00437DCC       push    edi
 .text:00437DCD       mov     ecxesi
 .text:00437DCF       call    CTreeCtrl::ItemHasChildren(_TREEITEM *)
 .text:00437DD4       test    eaxeax
 .text:00437DD6       jz      short loc_437E32
 .text:00437DD8       mov     ecx, [esi+1Ch]
 .text:00437DDB       mov     ebxds:SendMessageA
 .text:00437DE1       push    edi             ; lParam
 .text:00437DE2       push    4               ; wParam
 .text:00437DE4       push    110Ah           ; Msg
 .text:00437DE9       push    ecx             ; hWnd
 .text:00437DEA       call    ebx             ; SendMessageA
 .text:00437DEC       mov     edieax
 .text:00437DEE       test    ediedi
 .text:00437DF0       jz      short loc_437E32
 .text:00437DF2       push    edi
 .text:00437DF3       mov     ecxesi
 .text:00437DF5       call    f_IsItemChecked
 .text:00437DFA       mov     ebp, [esp+10h+arg_0]
 .text:00437DFE       test    eaxeax
 .text:00437E00       jz      short loc_437E0A
 .text:00437E02       push    edi
 .text:00437E03       push    ebp
 .text:00437E04       mov     ecxesi
 .text:00437E06       call    [esp+18h+arg_4]
 .text:00437E0A 
 .text:00437E0A loc_437E0A:                   ; CODE XREF: f_ExportResource+60
 .text:00437E0A       mov     edx, [esi+1Ch]
 .text:00437E0D       push    edi             ; lParam
 .text:00437E0E       push    1               ; wParam
 .text:00437E10       push    110Ah           ; Msg
 .text:00437E15       push    edx             ; hWnd
 .text:00437E16       call    ebx             ; SendMessageA
 .text:00437E18       mov     edieax
 .text:00437E1A       test    ediedi
 .text:00437E1C       jz      short loc_437E32;是否找到下一个同级的item
 .text:00437E1E       push    edi
 .text:00437E1F       mov     ecxesi
 .text:00437E21       call    f_IsItemChecked
 .text:00437E26       test    eaxeax
 .text:00437E28       jz      short loc_437E32
 .text:00437E2A       push    edi
 .text:00437E2B       push    ebp
 .text:00437E2C       mov     ecxesi
 .text:00437E2E       call    [esp+18h+arg_4] ;传入的是函数指针
 .text:00437E32 
 .text:00437E32 loc_437E32:                    ; CODE XREF: f_ExportResource+2A
 .text:00437E32                                ; f_ExportResource+36
 .text:00437E32                                ; f_ExportResource+50
 .text:00437E32                                ; f_ExportResource+7C
 .text:00437E32                                ; f_ExportResource+88
 .text:00437E32       pop     edi
 .text:00437E33       pop     esi
 .text:00437E34       pop     ebp
 .text:00437E35       pop     ebx
 .text:00437E36       retn    8
 .text:00437E36 f_ExportResource endp
 与前面判断有多少item被选中的代码如出一辙。如果在此设断点,
 在按下"导出"后中断9次,每种资源一次。很明显,传入的2个参数,
 一个是指向第1级节点TVITEM的指针,另一个是用于导出对应类型资
 源的函数指针。
  
 更好的一点是,在437E18处,取到与当前item同一层的下一个item(类
 似CTreeCtrl::GetNextSiblingItem)指针后,有
 .text:00437E18       mov     edieax
 这正是重复执行437E16访问下一个item需要的。这样方便了我们建
 立循环。而437E39处还有因为对齐边界而剩余的8个字节:
 这次不必跳到.text的末尾去写代码了,只要简单地修改几个字节:
 .text:00437E32       jmp 437E0A  ;循环
 .text:00437E34       pop edi   ;原来的出口代码顺序后移
                      pop esi
                                 pop ebp
                                 pop ebx
                                 ret 8
 另外,修改437E1C的跳转:
 .text:00437E1C       jz  437E34  ;这是唯一的出口
 运行一下,这次导出的文件很多了。可仍然有问题:
 对照在CTreeCtrl控件中显示的数据和实际得到的文件:
 
           显示的数据       得到的文件数
 ____________________________________________
  
 矢量图      28         26(缺1,7)
 图片     2                   2
 声音         9            9
 字体     5            5
 文字  135        135
 动画     8             8  
 按钮    72          72
 动画帧  187        187 
 动作   73          73
 
 上面提到过,4385C0处的函数检查矢量图资源的格式是否符
 合"闪客巫师"的要求。当按"导出"按钮时,报了shape 1不能
 输出为指定格式。显然,这里还缺一个。
 在这里:
 .text:00438720        mov     eax, [esi+1Ch]
 .text:00438723        push    edi             ; lParam
 .text:00438724        push    1               ; wParam
 .text:00438726        push    110Ah           ; Msg
 .text:0043872B        push    eax             ; hWnd
 .text:0043872C        call    ds:SendMessageA
 .text:00438732        inc     ebx
 .text:00438733        mov     edieax
 .text:00438735        cmp     ebx, 1          ;只检查前2项    
 .text:00438738        jg      short loc_438742 ; 
 .text:0043873A        test    ediedi
 .text:0043873C        jnz     loc_438682
 关闭438732的inc指令。这次在"导出"时报shape1,shape7不符合
 格式要求。
  
6.解除两个Nag Screen
  去掉"演示版限制: 只分析影片的前五帧。"
 .text:004361AB        push    0FFFFFFFFh
 .text:004361AD        push    40h
 .text:004361AF        push    0F2A5h
 .text:004361B4        call    sub_481725 ;关闭 
  
 "演示版限制每类资源中只有前两个资源可以被导出。"
 .text:00428BEE        push    0FFFFFFFFh ;函数的第1个参数
  ......
 .text:00428C0F        push    41h
 .text:00428C11        push    12Eh          
 .text:00428C16        call    sub_481725 ; 
 .text:00428C1B        cmp     eax, 1
 .text:00428C1E        jnz     loc_428D9B ;
 NOP掉上述全部代码。
7.OK,终于结束了。打开卜桦的《猫》试试,在程序的窗口中,
  点击item,看不到有图形显示?!导出来瞧瞧,可以。虚惊
  一场...... :-)


注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功