破解步骤三——代码重建:
在此需要将限制的代码恢复(改变)成原来正常的代码,正常打印的Pascal代码应该如下所示:
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
根据Pascal代码可以很轻易的写出对应的汇编代码:
<!-- google_ad_client = "pub-5395599807454886"; /* 468x60, 创建于 08-12-15 */ google_ad_slot = "2456405239"; google_ad_width = 468; google_ad_height = 60; //-->
要把这段汇编代码填回原来的DCU里还存在两个问题:第一问题是,在DCU中,所有的Call XXXXX调用的机器码都是E8 00 00 00 00,也就是说Call的地址静态时全是00,在联编过程才将00替换成正确的地址写入到可执行文件中,如果直接修改了DCU文件,那么在联编过程中编译器依然试图进行地址替换,这将会破坏掉破解的代码段而产生不可知结果,所以编写破解代码时一方面必须绕开相应位置,避免被编译器破坏代码;另一方面,又需要充分利用编译器替换的结果,使破解代码能正确调用相应函数。于是便有了下面这段代码:
但是这段代码却多了19个字节,没法填回到DCU中。
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
第二个问题是,原来的汇编码中没有对TRMEndPages.GetCount的调用,编译器不知道要为可执行文件生成此函数的调用地址,因此必须另辟蹊径实现GetCount的功能。通过对GetCount函数的代码的分析(过程略),通过以下一段代码实现了GetCount函数的功能:
综合以上问题,我写下第三段破解代码:
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
这段代码中裁剪掉了逐份打印功能(实际中也很少有人使用)以便有足够的空间书写代码,而实际证明这段代码工作得相当好。剩下的事就是把破解代码转换成机器码、用十六进制编辑器(UtralEdit、HexEdit、或更旧的PCToools等),把rm_Class.dcu中的代码替换为破解代码(rm3.51的偏移值为48EE7),方法很多这里不作述说。
在此需要将限制的代码恢复(改变)成原来正常的代码,正常打印的Pascal代码应该如下所示:
- procedure_DoPrintReport;
- var
- i,j:integer;
- begin
- if逐份打印then
- begin
- i:=0;
- repeat
- ifCanPrint(i)thenPrintOnePage(i);
- i:=i+1;
- until((i>=TRMEndPages.GetCount-1)or(UserCancel));
- end
- elsebegin
- //逐页打印
- fori:=0toTRMEndPages.GetCount-1do
- forj:=0tocopies{打印份数}do
- begin
- ifCanPrint(i)thenPrintOnePage(i);
- ifUserCancelthenBreak;
- end;
- end;
- end;
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
根据Pascal代码可以很轻易的写出对应的汇编代码:
- //第一段
- asm
- moveax,[ebp+$08]
- cmpbyteptr[eax+$0C],$00//判断是否逐份打印
- jz@@579//不是转到逐页打印
- //`````````````````逐份打印`````````````````````````````
- xorebx,ebx//ebx保存当前打印份数
- jmp@@557
- @@506:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- moveax,[eax+$58]
- callTRMEndPages.GetCount//取总页数
- deceax//eax=页数-1
- movecx,eax//ecx保存循环次数
- xoreax,eax//eax保存当前打印页码
- @@1:
- pushecx
- pusheax
- pushebp
- call_CanPrint
- testal,al//判断是否要打印
- jz@@528
- popecx{pushebp}
- popeax
- pusheax
- pushebp
- call_PrintOnePage
- @@528:
- popecx{pushebp}
- popeax
- popecx
- inceax
- loop@@1
- incebx//打印份数加1
- @@557:
- moveax,[ebp+$08]
- cmpebx,[eax-$0c]//对比打印份数
- jnl@@679//打完退出
- moveax,[ebp+$08]
- moveax,[eax-$08]
- cmpbyteptr[eax+$30],$00//查看是否要终止
- jz@@506//不是继续
- jmp@@679//终止退出
- //`````````````````逐页打印`````````````````````````````
- @@579:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- moveax,[eax+$58]
- callTRMEndPages.GetCount//取总页数
- oreax,eax
- jz@@679//页数为0时直接退出
- deceax
- movecx,eax//ecx保存页数
- xoreax,eax//eax保存当前打印页码
- @@2Check_and_print:
- pushecx//保存循环次数(页数)
- pusheax//保存当前页码
- xorebx,ebx//ebx保存当前打印份数
- pushebp
- call_CanPrint//Tips:影响eax,ecx,edx
- popecx{pushebp}
- testal,al
- jz@@2Print_next_page
- @@2PrintOut:
- popeax//取当前页码
- pusheax
- pushebp
- call_PrintOnePage//Tips:影响eax,ecx
- popecx{pushebp}
- @@2Check_terminated_flag:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- cmpbyteptr[eax+$30],$00
- jnz@@2Exit
- @@2Check_copies:
- incebx//份数加1
- moveax,[ebp+$08]
- cmpebx,[eax-$0C]
- jl@@2PrintOut//继续打印
- @@2Print_next_page:
- popeax//取回页数和页码
- popecx
- inceax
- loop@@2Check_and_print
- jmp@@679
- @@2Exit:
- popeax//平衡堆栈
- popeax
- jmp@@679//退出
- @@679:
- nop
- end;
要把这段汇编代码填回原来的DCU里还存在两个问题:第一问题是,在DCU中,所有的Call XXXXX调用的机器码都是E8 00 00 00 00,也就是说Call的地址静态时全是00,在联编过程才将00替换成正确的地址写入到可执行文件中,如果直接修改了DCU文件,那么在联编过程中编译器依然试图进行地址替换,这将会破坏掉破解的代码段而产生不可知结果,所以编写破解代码时一方面必须绕开相应位置,避免被编译器破坏代码;另一方面,又需要充分利用编译器替换的结果,使破解代码能正确调用相应函数。于是便有了下面这段代码:
- //第二段
- asm
- moveax,[ebp+$08]
- cmpbyteptr[eax+$0C],$00//判断是否逐份打印
- jz@@579//不是转到逐页打印
- //`````````````````逐份打印`````````````````````````````
- xorebx,ebx//ebx保存当前打印份数
- jmp@@557
- {procnear}@@_IsCanPrint:
- nop
- nop
- pushebp
- db$E8,$00,$00,$00,$00//call_CanPrint
- popedx
- ret
- {endproc}
- {procnear}@@_Pages_GetCount:
- moveax,[ebp+$08]
- jmp@@_Pages_GetCount_Block2
- //----------------------------
- {procnear}@@_PrintAPage:
- pushebp
- db$E8,$00,$00,$00,$00//call_PrintOnePage
- popecx
- ret
- {endproc}
- //-----------------------------
- @@_Pages_GetCount_Block2:
- moveax,[eax-$08]
- jmp@@_Pages_GetCount_Block3
- db$E8,$00,$00,$00,$00//第二处call_CanPrint,必须跳过
- @@_Pages_GetCount_Block3:
- moveax,[eax+$58]
- moveax,[eax+$08]
- moveax,[eax+$08]
- deceax
- ret
- {endproc}
- @@57:
- db$E8,$00,$00,$00,$00//第二处call_PrintOnePage,必须跳过
- @@506:
- call@@_Pages_GetCount
- //oreax,eax
- //jz@@679//页数为0时直接退出
- //deceax//eax=页数-1
- movecx,eax//ecx保存循环次数
- xoreax,eax//eax保存当前打印页码
- @@1:
- pushecx
- pusheax
- call@@_IsCanPrint
- testal,al//判断是否要打印
- jz@@528
- popeax
- pusheax
- jmp@@1_2
- nop
- nop
- nop
- db$E8,$00,$00,$00,$00//第三处call_CanPrint,必须跳过
- @@1_2:
- call@@_PrintAPage
- @@528:
- popeax
- popecx
- inceax
- loop@@1
- incebx//打印份数加1
- jmp@@1_3
- db$00,$00,$00,$00//第三处call_PrintOnePage,必须跳过
- @@1_3:
- @@557:
- moveax,[ebp+$08]
- cmpebx,[eax-$0c]//对比打印份数
- jnl@@679//打完退出
- //moveax,[ebp+$08]
- moveax,[eax-$08]
- cmpbyteptr[eax+$30],$00//查看是否要终止
- jz@@506//不是继续
- jmp@@679//终止退出
- //`````````````````逐页打印`````````````````````````````
- @@579:
- (*moveax,[ebp+$08]
- moveax,[eax-$08]
- moveax,[eax+$58]
- db$E8,$00,$00,$00,$00//callTRMEndPages.GetCount//取总页数
- oreax,eax
- jz@@679//页数为0时直接退出
- deceax
- *)
- call@@_Pages_GetCount//取总页数
- movecx,eax//ecx保存页数
- jmp@@2_1
- db$E8,$00,$00,$00,$00//第四处call_CanPrint,必须跳过
- @@2_1:
- xoreax,eax//eax保存当前打印页码
- @@2Check_and_print:
- pushecx//保存循环次数(页数)
- pusheax//保存当前页码
- xorebx,ebx//ebx保存当前打印份数
- (*pushebp
- db$E8,$00,$00,$00,$00//call_CanPrint//Tips:影响eax,ecx,edx
- popecx{pushebp}
- *)
- call@@_IsCanPrint
- testal,al
- jmp@@2_2
- db$E8,$00,$00,$00,$00//第四处call_PrintOnePage,必须跳过
- @@2_2:
- jz@@2Print_next_page
- @@2PrintOut:
- popeax//取当前页码
- pusheax
- (*pushebp
- db$E8,$00,$00,$00,$00//call_PrintOnePage//Tips:影响eax,ecx
- popecx{pushebp}
- *)
- call@@_PrintAPage
- @@2Check_terminated_flag:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- cmpbyteptr[eax+$30],$00
- jnz@@2Exit
- @@2Check_copies:
- incebx//份数加1
- moveax,[ebp+$08]
- cmpebx,[eax-$0C]
- jl@@2PrintOut//继续打印
- @@2Print_next_page:
- popeax//取回页数和页码
- popecx
- inceax
- loop@@2Check_and_print
- jmp@@679
- @@2Exit:
- popeax//平衡堆栈
- popeax
- jmp@@679//退出
- @@679:
- db$90,$90,$90
- end;
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
第二个问题是,原来的汇编码中没有对TRMEndPages.GetCount的调用,编译器不知道要为可执行文件生成此函数的调用地址,因此必须另辟蹊径实现GetCount的功能。通过对GetCount函数的代码的分析(过程略),通过以下一段代码实现了GetCount函数的功能:
- //GetCount
- moveax,[ebp+$08]
- moveax,[eax-$08]
- moveax,[eax+$58]
- moveax,[eax+$08]
- moveax,[eax+$08]
- //ret
- asm
- moveax,[ebp+$08]
- cmp[eax-$0C],$00
- je@@2Go679//份数为0时直接退出
- jmp@@579
- @@_IsCanPrint:
- pushebp
- db$E8,$00,$00,$00,$00//call_CanPrint//Tips:影响eax,ecx,edx
- popecx{pushebp}
- testal,al
- jz@@2Print_next_page
- @@_PrintAPage:
- popeax//取当前页码
- pusheax
- pushebp
- db$E8,$00,$00,$00,$00//call_PrintOnePage//Tips:影响eax,ecx
- popecx
- jmp@@2Check_terminated_flag
- dd0
- db$E8,$00,$00,$00,$00//跳过第二处call_CanPrint
- @@579:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- moveax,[eax+$58]
- jmp@@2Skip1
- db$E8,$00,$00,$00,$00//跳过第二处call_PrintOnePage
- @@2Skip1:
- moveax,[eax+$08]
- moveax,[eax+$08]//TRMEndPages.GetCount//取总页数
- oreax,eax
- jz@@2Go679//页数为0时直接退出
- deceax
- movecx,eax//ecx保存页数
- xoreax,eax//eax保存当前打印页码
- @@2Check_and_print:
- pushecx//保存循环次数(页数)
- pusheax//保存当前页码
- xorebx,ebx//ebx保存当前打印份数
- jmp@@_IsCanPrint
- dw0,0,0
- db$E8,$00,$00,$00,$00//跳过第三处call_CanPrint
- dd0,0,0
- db$E8,$00,$00,$00,$00//跳过第三处call_PrintOnePage
- @@2Check_terminated_flag:
- moveax,[ebp+$08]
- moveax,[eax-$08]
- cmpbyteptr[eax+$30],$00
- jnz@@2Exit
- @@2Check_copies:
- incebx//份数加1
- moveax,[ebp+$08]
- cmpebx,[eax-$0C]
- jl@@_PrintAPage//继续打印
- @@2Print_next_page:
- popeax//取回页数和页码
- popecx
- inceax
- loop@@2Check_and_print
- jmp@@2Go679
- db$E8,$00,$00,$00,$00//跳过第四处call_CanPrint
- @@2Exit:
- popeax//平衡堆栈
- popeax
- @@2Go679://通过此处跳到@@679,因编译器忽略jmpwordptr..
- nop//空指令,避免编译器优化代码
- jmp@@679//到@@679退出
- dd0,0,0,0,0,0,0,0,0
- db0
- @@679:
- inceax
- end;
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。
这段代码中裁剪掉了逐份打印功能(实际中也很少有人使用)以便有足够的空间书写代码,而实际证明这段代码工作得相当好。剩下的事就是把破解代码转换成机器码、用十六进制编辑器(UtralEdit、HexEdit、或更旧的PCToools等),把rm_Class.dcu中的代码替换为破解代码(rm3.51的偏移值为48EE7),方法很多这里不作述说。