RMReport3.51破解手札㈢

破解步骤三——代码重建:
  在此需要将限制的代码恢复(改变)成原来正常的代码,正常打印的Pascal代码应该如下所示:
  1. procedure_DoPrintReport;
  2. var
  3. i,j:integer;
  4. begin
  5. if逐份打印then
  6. begin
  7. i:=0;
  8. repeat
  9. ifCanPrint(i)thenPrintOnePage(i);
  10. i:=i+1;
  11. until((i>=TRMEndPages.GetCount-1)or(UserCancel));
  12. end
  13. elsebegin
  14. //逐页打印
  15. fori:=0toTRMEndPages.GetCount-1do
  16. forj:=0tocopies{打印份数}do
  17. begin
  18. ifCanPrint(i)thenPrintOnePage(i);
  19. ifUserCancelthenBreak;
  20. end;
  21. end;
  22. end;
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。

  根据Pascal代码可以很轻易的写出对应的汇编代码:
  1. //第一段
  2. asm
  3. moveax,[ebp+$08]
  4. cmpbyteptr[eax+$0C],$00//判断是否逐份打印
  5. jz@@579//不是转到逐页打印
  6. //`````````````````逐份打印`````````````````````````````
  7. xorebx,ebx//ebx保存当前打印份数
  8. jmp@@557
  9. @@506:
  10. moveax,[ebp+$08]
  11. moveax,[eax-$08]
  12. moveax,[eax+$58]
  13. callTRMEndPages.GetCount//取总页数
  14. deceax//eax=页数-1
  15. movecx,eax//ecx保存循环次数
  16. xoreax,eax//eax保存当前打印页码
  17. @@1:
  18. pushecx
  19. pusheax
  20. pushebp
  21. call_CanPrint
  22. testal,al//判断是否要打印
  23. jz@@528
  24. popecx{pushebp}
  25. popeax
  26. pusheax
  27. pushebp
  28. call_PrintOnePage
  29. @@528:
  30. popecx{pushebp}
  31. popeax
  32. popecx
  33. inceax
  34. loop@@1
  35. incebx//打印份数加1
  36. @@557:
  37. moveax,[ebp+$08]
  38. cmpebx,[eax-$0c]//对比打印份数
  39. jnl@@679//打完退出
  40. moveax,[ebp+$08]
  41. moveax,[eax-$08]
  42. cmpbyteptr[eax+$30],$00//查看是否要终止
  43. jz@@506//不是继续
  44. jmp@@679//终止退出
  45. //`````````````````逐页打印`````````````````````````````
  46. @@579:
  47. moveax,[ebp+$08]
  48. moveax,[eax-$08]
  49. moveax,[eax+$58]
  50. callTRMEndPages.GetCount//取总页数
  51. oreax,eax
  52. jz@@679//页数为0时直接退出
  53. deceax
  54. movecx,eax//ecx保存页数
  55. xoreax,eax//eax保存当前打印页码
  56. @@2Check_and_print:
  57. pushecx//保存循环次数(页数)
  58. pusheax//保存当前页码
  59. xorebx,ebx//ebx保存当前打印份数
  60. pushebp
  61. call_CanPrint//Tips:影响eax,ecx,edx
  62. popecx{pushebp}
  63. testal,al
  64. jz@@2Print_next_page
  65. @@2PrintOut:
  66. popeax//取当前页码
  67. pusheax
  68. pushebp
  69. call_PrintOnePage//Tips:影响eax,ecx
  70. popecx{pushebp}
  71. @@2Check_terminated_flag:
  72. moveax,[ebp+$08]
  73. moveax,[eax-$08]
  74. cmpbyteptr[eax+$30],$00
  75. jnz@@2Exit
  76. @@2Check_copies:
  77. incebx//份数加1
  78. moveax,[ebp+$08]
  79. cmpebx,[eax-$0C]
  80. jl@@2PrintOut//继续打印
  81. @@2Print_next_page:
  82. popeax//取回页数和页码
  83. popecx
  84. inceax
  85. loop@@2Check_and_print
  86. jmp@@679
  87. @@2Exit:
  88. popeax//平衡堆栈
  89. popeax
  90. jmp@@679//退出
  91. @@679:
  92. nop
  93. end;
<!-- 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文件,那么在联编过程中编译器依然试图进行地址替换,这将会破坏掉破解的代码段而产生不可知结果,所以编写破解代码时一方面必须绕开相应位置,避免被编译器破坏代码;另一方面,又需要充分利用编译器替换的结果,使破解代码能正确调用相应函数。于是便有了下面这段代码:
  1. //第二段
  2. asm
  3. moveax,[ebp+$08]
  4. cmpbyteptr[eax+$0C],$00//判断是否逐份打印
  5. jz@@579//不是转到逐页打印
  6. //`````````````````逐份打印`````````````````````````````
  7. xorebx,ebx//ebx保存当前打印份数
  8. jmp@@557
  9. {procnear}@@_IsCanPrint:
  10. nop
  11. nop
  12. pushebp
  13. db$E8,$00,$00,$00,$00//call_CanPrint
  14. popedx
  15. ret
  16. {endproc}
  17. {procnear}@@_Pages_GetCount:
  18. moveax,[ebp+$08]
  19. jmp@@_Pages_GetCount_Block2
  20. //----------------------------
  21. {procnear}@@_PrintAPage:
  22. pushebp
  23. db$E8,$00,$00,$00,$00//call_PrintOnePage
  24. popecx
  25. ret
  26. {endproc}
  27. //-----------------------------
  28. @@_Pages_GetCount_Block2:
  29. moveax,[eax-$08]
  30. jmp@@_Pages_GetCount_Block3
  31. db$E8,$00,$00,$00,$00//第二处call_CanPrint,必须跳过
  32. @@_Pages_GetCount_Block3:
  33. moveax,[eax+$58]
  34. moveax,[eax+$08]
  35. moveax,[eax+$08]
  36. deceax
  37. ret
  38. {endproc}
  39. @@57:
  40. db$E8,$00,$00,$00,$00//第二处call_PrintOnePage,必须跳过
  41. @@506:
  42. call@@_Pages_GetCount
  43. //oreax,eax
  44. //jz@@679//页数为0时直接退出
  45. //deceax//eax=页数-1
  46. movecx,eax//ecx保存循环次数
  47. xoreax,eax//eax保存当前打印页码
  48. @@1:
  49. pushecx
  50. pusheax
  51. call@@_IsCanPrint
  52. testal,al//判断是否要打印
  53. jz@@528
  54. popeax
  55. pusheax
  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. popeax
  65. popecx
  66. inceax
  67. loop@@1
  68. incebx//打印份数加1
  69. jmp@@1_3
  70. db$00,$00,$00,$00//第三处call_PrintOnePage,必须跳过
  71. @@1_3:
  72. @@557:
  73. moveax,[ebp+$08]
  74. cmpebx,[eax-$0c]//对比打印份数
  75. jnl@@679//打完退出
  76. //moveax,[ebp+$08]
  77. moveax,[eax-$08]
  78. cmpbyteptr[eax+$30],$00//查看是否要终止
  79. jz@@506//不是继续
  80. jmp@@679//终止退出
  81. //`````````````````逐页打印`````````````````````````````
  82. @@579:
  83. (*moveax,[ebp+$08]
  84. moveax,[eax-$08]
  85. moveax,[eax+$58]
  86. db$E8,$00,$00,$00,$00//callTRMEndPages.GetCount//取总页数
  87. oreax,eax
  88. jz@@679//页数为0时直接退出
  89. deceax
  90. *)
  91. call@@_Pages_GetCount//取总页数
  92. movecx,eax//ecx保存页数
  93. jmp@@2_1
  94. db$E8,$00,$00,$00,$00//第四处call_CanPrint,必须跳过
  95. @@2_1:
  96. xoreax,eax//eax保存当前打印页码
  97. @@2Check_and_print:
  98. pushecx//保存循环次数(页数)
  99. pusheax//保存当前页码
  100. xorebx,ebx//ebx保存当前打印份数
  101. (*pushebp
  102. db$E8,$00,$00,$00,$00//call_CanPrint//Tips:影响eax,ecx,edx
  103. popecx{pushebp}
  104. *)
  105. call@@_IsCanPrint
  106. testal,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. popeax//取当前页码
  113. pusheax
  114. (*pushebp
  115. db$E8,$00,$00,$00,$00//call_PrintOnePage//Tips:影响eax,ecx
  116. popecx{pushebp}
  117. *)
  118. call@@_PrintAPage
  119. @@2Check_terminated_flag:
  120. moveax,[ebp+$08]
  121. moveax,[eax-$08]
  122. cmpbyteptr[eax+$30],$00
  123. jnz@@2Exit
  124. @@2Check_copies:
  125. incebx//份数加1
  126. moveax,[ebp+$08]
  127. cmpebx,[eax-$0C]
  128. jl@@2PrintOut//继续打印
  129. @@2Print_next_page:
  130. popeax//取回页数和页码
  131. popecx
  132. inceax
  133. loop@@2Check_and_print
  134. jmp@@679
  135. @@2Exit:
  136. popeax//平衡堆栈
  137. popeax
  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. moveax,[ebp+$08]
  3. moveax,[eax-$08]
  4. moveax,[eax+$58]
  5. moveax,[eax+$08]
  6. moveax,[eax+$08]
  7. //ret
  综合以上问题,我写下第三段破解代码:
  1. asm
  2. moveax,[ebp+$08]
  3. cmp[eax-$0C],$00
  4. je@@2Go679//份数为0时直接退出
  5. jmp@@579
  6. @@_IsCanPrint:
  7. pushebp
  8. db$E8,$00,$00,$00,$00//call_CanPrint//Tips:影响eax,ecx,edx
  9. popecx{pushebp}
  10. testal,al
  11. jz@@2Print_next_page
  12. @@_PrintAPage:
  13. popeax//取当前页码
  14. pusheax
  15. pushebp
  16. db$E8,$00,$00,$00,$00//call_PrintOnePage//Tips:影响eax,ecx
  17. popecx
  18. jmp@@2Check_terminated_flag
  19. dd0
  20. db$E8,$00,$00,$00,$00//跳过第二处call_CanPrint
  21. @@579:
  22. moveax,[ebp+$08]
  23. moveax,[eax-$08]
  24. moveax,[eax+$58]
  25. jmp@@2Skip1
  26. db$E8,$00,$00,$00,$00//跳过第二处call_PrintOnePage
  27. @@2Skip1:
  28. moveax,[eax+$08]
  29. moveax,[eax+$08]//TRMEndPages.GetCount//取总页数
  30. oreax,eax
  31. jz@@2Go679//页数为0时直接退出
  32. deceax
  33. movecx,eax//ecx保存页数
  34. xoreax,eax//eax保存当前打印页码
  35. @@2Check_and_print:
  36. pushecx//保存循环次数(页数)
  37. pusheax//保存当前页码
  38. xorebx,ebx//ebx保存当前打印份数
  39. jmp@@_IsCanPrint
  40. dw0,0,0
  41. db$E8,$00,$00,$00,$00//跳过第三处call_CanPrint
  42. dd0,0,0
  43. db$E8,$00,$00,$00,$00//跳过第三处call_PrintOnePage
  44. @@2Check_terminated_flag:
  45. moveax,[ebp+$08]
  46. moveax,[eax-$08]
  47. cmpbyteptr[eax+$30],$00
  48. jnz@@2Exit
  49. @@2Check_copies:
  50. incebx//份数加1
  51. moveax,[ebp+$08]
  52. cmpebx,[eax-$0C]
  53. jl@@_PrintAPage//继续打印
  54. @@2Print_next_page:
  55. popeax//取回页数和页码
  56. popecx
  57. inceax
  58. loop@@2Check_and_print
  59. jmp@@2Go679
  60. db$E8,$00,$00,$00,$00//跳过第四处call_CanPrint
  61. @@2Exit:
  62. popeax//平衡堆栈
  63. popeax
  64. @@2Go679://通过此处跳到@@679,因编译器忽略jmpwordptr..
  65. nop//空指令,避免编译器优化代码
  66. jmp@@679//到@@679退出
  67. dd0,0,0,0,0,0,0,0,0
  68. db0
  69. @@679:
  70. inceax
  71. end;
文章出自:《编程手札》http://blog.csdn.net/nhconch
作者:狂歌痛饮
请从《编程手札》阅读原文,引用或转载可能导致内容不全。

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

VB控件 RMReport7 转:RMReport的使用方法及详解 1、不打印特定的MemoView,套打常用 a.页面设置-->其它-->不打印背景图 b.设置MemoView属性printable=False 2、 如何打印wwDBGrid? 修改rm.inc,如果想支持RX,GIF,JPEG,DimandAccess,Halcyon,DBISAM, EHLib,也需要修改rm.inc //{$DEFINE InfoPower} //修改这行,去掉"//" //{$Ehlib} 3.试用版安装方法(以下假设将文件释放到c:/rm目录中) (1)Tools->Environments Option->Libary->Libary Path中增加: c:/rm/souce c:/rm/bpl $(DELPHI)/Lib $(DELPHI)/Bin $(DELPHI)/Imports $(DELPHI)/Projects/Bpl (2)Component->Install Packages->Add,选bpl/rm_d70.bpl 4.在Delphi IDE中卸载以前的Report Machine版本,然后打开rm_r50.dpk,选"compile", 在打开rm_d50.dpk,选"Install". 包分成了Runtime package和Designer package,所以要安装顺序安装 5、单元格的变量格式用代码设置 t = TRMGridReportPage(RMGridReport1.Pages[0]).Grid.Cells[1, 1].View t = TRMMemoView(RMReport1.FindObject('memo1')); t.DisplayFormat := 'N0.001' //数字型 t.DisplayFormat := 'Dyyyy/mm/dd' //日期型 6、两遍报表如何用代码设置 GridReport1.DoublePass := True 7、用代码写数据字典: RMReport1.Dictionary.FieldAliases.Clear; RMReport1.Dictionary.FieldAliases['RMDBDataSet1'] := '动物'; RMReport1.Dictionary.FieldAliases['RMDBDataSet1."Name"'] := '姓名'; 这样在RM的设计器显示为自定义名称,为最终用户提供友好的显示 8、在报表中如何使用变量(或者如何给某个memoview赋值) a.RMVariables在RM_Class.pas中定义,是全局变量,这样定义后就可以在报表中使用变量"var1",例如: RMVariables['变量名称'] := Edit1.Text; b.用报表中数据字典,TRMReport.Dictionary.Variables,需要注意的是,如果变量是字符型的需要用AsString赋值,其他类型的用RMReport.Dictionary.Variables['var1'] := 1234,例如: RMReport1.LoadFromFile('1.rls'); RMReport1.Dictionary.Variables.AsString['变量名称'] := Edit1.Text; c. 直接对某个单元格赋值,例如: RMGridReport1.LoadFromFile('1.rls'); TRMGridReportPage(RMGridReport1.Pages[0]).Grid.Cells[1,1].Text := '值'; 如果是RMReportRMReport1.LoadFromFile('1.rmf'); t := RMReport1.FindObject('Memo1'); if t nil then // var t: TRMView t.Memo.Text := 'dsdsdsds'; d.脚本中直接引用Form的值 procedure Main; begin Memo1.Memo.Text := Form1.Edit1.Text; end; 9、自动换行 主项数据栏Stretched = true 文本框 Stretched = true WordWrap = true 10、RM内置变量(Script),增加中.... a.属性PrintAtAppendBlank=True CurReport.AppendBlanking=True时代表增加空行 在RM中,打印设置只能保存页面边距及打印份数、是否两遍打印以及
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值