RMReport3.51破解手札㈢

来源:互联网 发布:base64转化为json格式 编辑:程序博客网 时间:2024/05/21 09:01
破解步骤三——代码重建:
  在此需要将限制的代码恢复(改变)成原来正常的代码,正常打印的Pascal代码应该如下所示:
  1. procedure _DoPrintReport;
  2. var
  3.     i,j: integer;
  4. begin
  5.     if 逐份打印 then
  6.     begin
  7.         i := 0;
  8.         repeat
  9.             if CanPrint(i) then PrintOnePage(i);
  10.             i := i+1;
  11.         until ((i>=TRMEndPages.GetCount-1or (UserCancel));
  12.     end 
  13.     else begin
  14.         //逐页打印
  15.         for i:=0 to TRMEndPages.GetCount-1 do
  16.             for j:=0 to copies{打印份数} do 
  17.             begin
  18.                 if CanPrint(i) then PrintOnePage(i);
  19.                 if UserCancel then Break;
  20.             end;
  21.     end;
  22. end;
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。

      根据Pascal代码可以很轻易的写出对应的汇编代码:
    1. //第一段
    2. asm
    3.     mov eax, [ebp + $08]
    4.     cmp byte ptr [eax + $0C], $00   //判断是否逐份打印
    5.     jz @@579                        //不是转到逐页打印
    6. //`````````````````逐份打印`````````````````````````````
    7.     xor ebx, ebx                    //ebx保存当前打印份数
    8.     jmp @@557
    9. @@506:
    10.     mov eax, [ebp + $08]
    11.     mov eax, [eax - $08]
    12.     mov eax, [eax + $58]
    13.     call TRMEndPages.GetCount    //取总页数
    14.     dec eax                         //eax=页数-1
    15.     mov ecx, eax                    //ecx保存循环次数
    16.     xor eax, eax                    //eax保存当前打印页码
    17. @@1:
    18.     push ecx
    19.     push eax
    20.     push ebp
    21.     call _CanPrint
    22.     test al, al                     //判断是否要打印
    23.     jz @@528
    24.     pop ecx {push ebp}
    25.     
    26.     pop eax
    27.     push eax
    28.     push ebp
    29.     call _PrintOnePage
    30. @@528:
    31.     pop ecx {push ebp}
    32.     pop eax
    33.     pop ecx
    34.     
    35.     inc eax
    36.     loop @@1
    37.     inc ebx                         //打印份数加1
    38. @@557:
    39.     mov eax, [ebp + $08]
    40.     cmp ebx, [eax - $0c]            //对比打印份数
    41.     jnl @@679                       //打完退出
    42.     mov eax, [ebp + $08]
    43.     mov eax, [eax - $08]
    44.     cmp byte ptr [eax + $30], $00   //查看是否要终止
    45.     jz @@506                        //不是继续
    46.     jmp @@679                       //终止退出
    47. //`````````````````逐页打印`````````````````````````````
    48. @@579:
    49.     mov eax, [ebp + $08]
    50.     mov eax, [eax - $08]
    51.     mov eax, [eax + $58]
    52.     call TRMEndPages.GetCount       //取总页数
    53.     or eax, eax
    54.     jz @@679                        //页数为0时直接退出
    55.     dec eax
    56.     mov ecx, eax                    //ecx保存页数
    57.     xor eax, eax                    //eax保存当前打印页码
    58. @@2Check_and_print:
    59.     push ecx                        //保存循环次数(页数)
    60.     push eax                        //保存当前页码
    61.     xor ebx, ebx                    //ebx保存当前打印份数
    62.     push ebp
    63.     call _CanPrint                  //Tips:影响eax, ecx, edx
    64.     pop ecx {push ebp}
    65.     test al, al
    66.     jz @@2Print_next_page
    67. @@2PrintOut:
    68.     pop eax                         //取当前页码
    69.     push eax
    70.     push ebp
    71.     call _PrintOnePage              //Tips:影响eax, ecx
    72.     pop ecx {push ebp}
    73. @@2Check_terminated_flag:
    74.     mov eax, [ebp + $08]
    75.     mov eax, [eax - $08]
    76.     cmp byte ptr [eax + $30], $00
    77.     jnz @@2Exit
    78. @@2Check_copies:
    79.     inc ebx                         //份数加1
    80.     mov eax, [ebp + $08]
    81.     cmp ebx, [eax - $0C]
    82.     jl @@2PrintOut                  //继续打印
    83. @@2Print_next_page:
    84.     pop eax                         //取回页数和页码
    85.     pop ecx
    86.     inc eax
    87.     loop @@2Check_and_print
    88.     jmp @@679
    89. @@2Exit:
    90.     pop eax                         //平衡堆栈
    91.     pop eax
    92.     jmp @@679                       //退出
    93. @@679:
    94.     nop
    95. end;
    <script type="text/javascript"><!--google_ad_client = "pub-5395599807454886";/* 468x60, 创建于 08-12-15 */google_ad_slot = "2456405239";google_ad_width = 468;google_ad_height = 60;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
      要把这段汇编代码填回原来的DCU里还存在两个问题:第一问题是,在DCU中,所有的Call XXXXX调用的机器码都是E8 00 00 00 00,也就是说Call的地址静态时全是00,在联编过程才将00替换成正确的地址写入到可执行文件中,如果直接修改了DCU文件,那么在联编过程中编译器依然试图进行地址替换,这将会破坏掉破解的代码段而产生不可知结果,所以编写破解代码时一方面必须绕开相应位置,避免被编译器破坏代码;另一方面,又需要充分利用编译器替换的结果,使破解代码能正确调用相应函数。于是便有了下面这段代码:
    1. //第二段
    2. asm
    3.     mov eax, [ebp + $08]
    4.     cmp byte ptr [eax + $0C], $00   //判断是否逐份打印
    5.     jz @@579                        //不是转到逐页打印
    6. //`````````````````逐份打印`````````````````````````````
    7.     xor ebx, ebx                    //ebx保存当前打印份数
    8.     jmp @@557
    9. {proc near }@@_IsCanPrint:
    10.     nop
    11.     nop
    12.     push ebp
    13.     db $E8$00$00$00$00      //call _CanPrint
    14.     pop edx
    15.     ret
    16. {end proc}
    17. {proc near}@@_Pages_GetCount:
    18.     mov eax, [ebp + $08]
    19.     jmp @@_Pages_GetCount_Block2
    20. //----------------------------
    21. {proc near }@@_PrintAPage:
    22.     push ebp
    23.     db $E8$00$00$00$00      //call _PrintOnePage
    24.     pop ecx
    25.     ret
    26. {end proc}
    27. //-----------------------------
    28. @@_Pages_GetCount_Block2:
    29.     mov eax, [eax - $08]
    30.     jmp @@_Pages_GetCount_Block3
    31.     db $E8$00$00$00$00      //第二处call _CanPrint,必须跳过
    32. @@_Pages_GetCount_Block3:
    33.     mov eax, [eax + $58]
    34.     mov eax, [eax + $08]
    35.     mov eax, [eax + $08]
    36.     dec eax
    37.     ret
    38. {end proc}
    39. @@57:
    40.     db $E8$00$00$00$00      //第二处call _PrintOnePage,必须跳过
    41. @@506:
    42.     call @@_Pages_GetCount
    43.     //or eax, eax
    44.     //jz @@679                        //页数为0时直接退出
    45.     //dec eax                         //eax=页数-1
    46.     mov ecx, eax                    //ecx保存循环次数
    47.     xor eax, eax                    //eax保存当前打印页码
    48. @@1:
    49.     push ecx
    50.     push eax
    51.     call @@_IsCanPrint
    52.     test al, al                     //判断是否要打印
    53.     jz @@528
    54.     pop eax
    55.     push eax
    56.     jmp @@1_2
    57.     nop
    58.     nop
    59.     nop
    60.     db $E8$00$00$00$00      //第三处call _CanPrint,必须跳过
    61. @@1_2:
    62.     call @@_PrintAPage
    63. @@528:
    64.     pop eax
    65.     pop ecx
    66.     inc eax
    67.     loop @@1
    68.     inc ebx                         //打印份数加1
    69.     jmp @@1_3
    70.     db $00$00$00$00           //第三处call _PrintOnePage,必须跳过
    71. @@1_3:
    72. @@557:
    73.     mov eax, [ebp + $08]
    74.     cmp ebx, [eax - $0c]            //对比打印份数
    75.     jnl @@679                       //打完退出
    76.     //mov eax, [ebp + $08]
    77.     mov eax, [eax - $08]
    78.     cmp byte ptr [eax + $30], $00   //查看是否要终止
    79.     jz @@506                        //不是继续
    80.     jmp @@679                       //终止退出
    81. //`````````````````逐页打印`````````````````````````````
    82. @@579:
    83. (*    mov eax, [ebp + $08]
    84.     mov eax, [eax - $08]
    85.     mov eax, [eax + $58]
    86.     db $E8, $00, $00, $00, $00      //call TRMEndPages.GetCount //取总页数
    87.     or eax, eax
    88.     jz @@679                        //页数为0时直接退出
    89.     dec eax
    90. *)
    91.     call @@_Pages_GetCount          //取总页数
    92.     mov ecx, eax                    //ecx保存页数
    93.     jmp @@2_1
    94.     db $E8$00$00$00$00      //第四处call _CanPrint,必须跳过
    95. @@2_1:
    96.     xor eax, eax                    //eax保存当前打印页码
    97. @@2Check_and_print:
    98.     push ecx                        //保存循环次数(页数)
    99.     push eax                        //保存当前页码
    100.     xor ebx, ebx                    //ebx保存当前打印份数
    101. (*    push ebp
    102.     db $E8, $00, $00, $00, $00      //call _CanPrint    //Tips:影响eax, ecx, edx
    103.     pop ecx {push ebp}
    104. *)
    105.     call @@_IsCanPrint
    106.     test al, al
    107.     jmp @@2_2
    108.     db $E8$00$00$00$00      //第四处call _PrintOnePage,必须跳过
    109. @@2_2:
    110.     jz @@2Print_next_page
    111. @@2PrintOut:
    112.     pop eax                         //取当前页码
    113.     push eax
    114. (*    push ebp
    115.     db $E8, $00, $00, $00, $00      //call _PrintOnePage//Tips:影响eax, ecx
    116.     pop ecx {push ebp}
    117. *)
    118.     call @@_PrintAPage
    119. @@2Check_terminated_flag:
    120.     mov eax, [ebp + $08]
    121.     mov eax, [eax - $08]
    122.     cmp byte ptr [eax + $30], $00
    123.     jnz @@2Exit
    124. @@2Check_copies:
    125.     inc ebx                         //份数加1
    126.     mov eax, [ebp + $08]
    127.     cmp ebx, [eax - $0C]
    128.     jl @@2PrintOut                  //继续打印
    129. @@2Print_next_page:
    130.     pop eax                         //取回页数和页码
    131.     pop ecx
    132.     inc eax
    133.     loop @@2Check_and_print
    134.     jmp @@679
    135. @@2Exit:
    136.     pop eax                         //平衡堆栈
    137.     pop eax
    138.     jmp @@679                       //退出
    139. @@679:
    140.     db $90$90$90
    141. end;
      但是这段代码却多了19个字节,没法填回到DCU中。
    文章出自:《编程手札》http://blog.csdn.net/nhconch
    作者:狂歌痛饮
    请从《编程手札》阅读原文,引用或转载可能导致内容不全。

        第二个问题是,原来的汇编码中没有对TRMEndPages.GetCount的调用,编译器不知道要为可执行文件生成此函数的调用地址,因此必须另辟蹊径实现GetCount的功能。通过对GetCount函数的代码的分析(过程略),通过以下一段代码实现了GetCount函数的功能:
      1. //GetCount
      2.     mov eax, [ebp + $08]
      3.     mov eax, [eax - $08]
      4.     mov eax, [eax + $58]
      5.     mov eax, [eax + $08]
      6.     mov eax, [eax + $08]
      7. //ret
        综合以上问题,我写下第三段破解代码:
      1. asm
      2.     mov eax, [ebp + $08]
      3.     cmp [eax - $0C], $00
      4.     je @@2Go679                     //份数为0时直接退出
      5.     jmp @@579
      6. @@_IsCanPrint:
      7.     push ebp
      8.     db $E8$00$00$00$00      //call _CanPrint    //Tips:影响eax, ecx, edx
      9.     pop ecx {push ebp}
      10.     test al, al
      11.     jz @@2Print_next_page
      12. @@_PrintAPage:
      13.     pop eax                         //取当前页码
      14.     push eax
      15.     push ebp
      16.     db $E8$00$00$00$00      //call _PrintOnePage//Tips:影响eax, ecx
      17.     pop ecx
      18.     jmp @@2Check_terminated_flag
      19.     dd 0
      20.     db $E8$00$00$00$00      //跳过第二处call _CanPrint
      21. @@579:
      22.     mov eax, [ebp + $08]
      23.     mov eax, [eax - $08]
      24.     mov eax, [eax + $58]
      25.     jmp @@2Skip1
      26.     db $E8$00$00$00$00      //跳过第二处call _PrintOnePage
      27. @@2Skip1:
      28.     mov eax, [eax + $08]
      29.     mov eax, [eax + $08]            //TRMEndPages.GetCount //取总页数
      30.     or eax, eax
      31.     jz @@2Go679                     //页数为0时直接退出
      32.     dec eax
      33.     mov ecx, eax                    //ecx保存页数
      34.     xor eax, eax                    //eax保存当前打印页码
      35. @@2Check_and_print:
      36.     push ecx                        //保存循环次数(页数)
      37.     push eax                        //保存当前页码
      38.     xor ebx, ebx                    //ebx保存当前打印份数
      39.     jmp @@_IsCanPrint
      40.     dw 000
      41.     db $E8$00$00$00$00      //跳过第三处call _CanPrint
      42.     dd 000
      43.     db $E8$00$00$00$00      //跳过第三处call _PrintOnePage
      44. @@2Check_terminated_flag:
      45.     mov eax, [ebp + $08]
      46.     mov eax, [eax - $08]
      47.     cmp byte ptr [eax + $30], $00
      48.     jnz @@2Exit
      49. @@2Check_copies:
      50.     inc ebx                         //份数加1
      51.     mov eax, [ebp + $08]
      52.     cmp ebx, [eax - $0C]
      53.     jl @@_PrintAPage                //继续打印
      54. @@2Print_next_page:
      55.     pop eax                         //取回页数和页码
      56.     pop ecx
      57.     inc eax
      58.     loop @@2Check_and_print
      59.     jmp @@2Go679
      60.     db $E8$00$00$00$00      //跳过第四处call _CanPrint
      61. @@2Exit:
      62.     pop eax                         //平衡堆栈
      63.     pop eax
      64. @@2Go679:                           //通过此处跳到@@679,因编译器忽略jmp word ptr ..
      65.     nop                             //空指令,避免编译器优化代码
      66.     jmp @@679                       //到@@679退出
      67.     dd 000000000
      68.     db 0
      69. @@679:
      70.     inc eax
      71. end;
      文章出自:《编程手札》http://blog.csdn.net/nhconch
      作者:狂歌痛饮
      请从《编程手札》阅读原文,引用或转载可能导致内容不全。

          这段代码中裁剪掉了逐份打印功能(实际中也很少有人使用)以便有足够的空间书写代码,而实际证明这段代码工作得相当好。剩下的事就是把破解代码转换成机器码、用十六进制编辑器(UtralEdit、HexEdit、或更旧的PCToools等),把rm_Class.dcu中的代码替换为破解代码(rm3.51的偏移值为48EE7),方法很多这里不作述说。

        RMReport3.51破解手札㈠   RMReport3.51破解手札㈡