[Asm] 纯文本查看 复制代码
004AC278 . 53 push ebx
004AC279 . 8BD8 mov ebx, eax
004AC27B . 80BB 78030000 00 cmp byte ptr [ebx+0x378], 0x0 ; 检查是否要显示 NAG
004AC282 . 74 19 je short 004AC29D
004AC284 . 8BC3 mov eax, ebx
004AC286 . E8 9DE7FFFF call 004AAA28 ; beep()
004AC28B . C683 78030000 00 mov byte ptr [ebx+0x378], 0x0
004AC292 . 8B83 38020000 mov eax, dword ptr [ebx+0x238]
004AC298 . 8B10 mov edx, dword ptr [eax]
004AC29A . FF52 2C call dword ptr [edx+0x2C] ; 显示 NAG
004AC29D > 5B pop ebx
004AC29E . C3 retn
可以看到,是通过判断 [ebx+0x378] 处的值来决定是否显示 NAG 窗口的(0:不显示;1:显示),对该处地址下一个硬件中断,如下图所示:
然后再一次打开一个 *.DAT 文件,首先中断在如下位置:
这里是初始化为 0,所以不是这里改的状态值,F9 继续执行,再一次硬件中断在如下位置:
这里是在 btnLoadClick() 事件中的代码,也是在这里决定是否显示 NAG 窗口。
再一次 F9 运行,再次来到了显示 NAG 代码处,如下所示:这里是将此标志重新设置为 0,表示不再显示(这里其实是3个 delphi 的 StringGrid 显示刷新的公共事件,会执行3次,置 0 是保证只显示一次)。至此,就是 btnLoadClick() 中通过置 [ebx+0x378]的值来决定是否显示 NAG。
因此,只要将前面置1的指令修改成置0就可以了,如下图所示(同时可以删除硬件中断了):
具体修改位置如下:
[Asm] 纯文本查看 复制代码
004AB29D C680 78030000 00 mov byte ptr [eax+0x378], 0x0 ; 是否显示 NAG, 1-显示,0-不显示
004AB2A4 . C3 retn ; RET 用作跳转到 004AB2AC
这样修改后,就不会再弹出 NAG 窗口。
下面继续处理 About 窗口中的显示,点 "About " 按钮,会弹出 About 对话框,如下图所示(NAG显示也是这个窗口):
这里显示“未注册/演示版/试用版只能显示100条记录”,并且,在OK按钮左边还显示了 30 天试用期什么的。。。。。
通过 DeDe 查得 About 弹出事件,然后直接CTRL+G 跳转到事件中去,如下图所示:
事件的开始代码如下所示:
在 0x004B13D1 处下一个断点,再在软件界面点“About”,就会中断在上图所示位置。
F8执行代码,上面是修改标签显示一个大大的重影文本标签(实际上是两个 Label 移位叠加)。F8 继续往下走:
还是显示一些信息,继续往下走:
可以看到,这里是一个解码函数,eax 指向一条编码后的字符串,如中上图中的 OD 数据区所选的数据。F8 执行解码函数,如下图所示:
解码的内容为:
[Asm] 纯文本查看 复制代码
02484EE0 You may use shareware version for 30 days (TRIAL period).
F8 继续执行,下面 ecx 又指向了一条编码的字符串,如下图所示:
后面继续解码,如下图所示(edx指向的字符串):
解码得到:
[Asm] 纯文本查看 复制代码
02487BF8 After that period has ended, to continue using this product you
02487C38 have to purchase the Registered version.
就是显示在“OK” 按钮左边的内容,合并后如下图所示:
同时调用函数显示:
[Asm] 纯文本查看 复制代码
004B1513 |. 8B55 F8 mov edx, dword ptr [ebp-0x8]
004B1516 |. 8B45 FC mov eax, dword ptr [ebp-0x4]
004B1519 |. 8B80 FC010000 mov eax, dword ptr [eax+0x1FC]
004B151F |. E8 90FAF6FF call <SetText()>
我们把最后一条指令 nop 掉,就不会显示了。
接下来还是解码字符串,如下图所示,解码“未注册/演示版/”等信息:
解码后:
生成 100 条限制字符串:
再次解码:
解码后为一个 Format() 函数的参数字符串,如下图所示,包含一个 %s 参数,就是代入前面的 100 生成的字符串 "100",如下图所示:
代入后如下图所示:
再次合并字符串,用于显示 About 中的红色字符串:
最后就是显示 About 窗口了:
如下所示,框框中显示的就是前面解码合并的两条字符串。
首先,我们把上图中的第(2)条显示去除,如下图所示,将 call setText() 指令 NOP 掉:
具体修改如下,将 0x004B151F 处指令 NOP 处理:
[Asm] 纯文本查看 复制代码
004B1513 |. 8B55 F8 mov edx, dword ptr [ebp-0x8]
004B1516 |. 8B45 FC mov eax, dword ptr [ebp-0x4]
004B1519 |. 8B80 FC010000 mov eax, dword ptr [eax+0x1FC]
004B151F 90 nop
004B1520 90 nop
004B1521 90 nop
004B1522 90 nop
004B1523 90 nop
再次点“About”,则显示如下:
另外一条显示(红色字符串),暂不处理,放后面再处理。
现在我们来处理最多只显示 100 条记录的限制,如下图所示,当多于100条记录时,只显示100条:
首先在 DeDe Dark 中查看事件,找到 StringGrid 控件的刷新事件,如下图所示:
在 OD 中跳转到该事件,并下一个断点,断点我下在 0x004BD435,如下图所示:
点一下主界面的记录,变动一下活动的记录号,马上会中断下来,如下图所示:
上图中的 [ESI+0x1EC] 就是指的 StringGrid 控件,这个在 DeDe Dark 中或者IDR中可以看到。
先切换 OD 数据区的内容到 StringGrid 对象实例内存区内,查找 Delphi 控件 StringGrid 的 rowCount 属性值,如下图所示:
用过 Delphi 的编程者就知道,StringGrid 的 RowCount 包含标题行,所以是 0x65(101)行,我们输入 "65 00" 查询,如下所示,就找到了存放 rowCount 属性存放位置,在这个位置下一个硬件写入断点,如下图所示:
把上面的 F2 断点禁用,按 F9 回到程序,重新打开一个数据文件,如软件自带 Orders.DAT,马上就会硬件中断下来,如下图所示:
这里是写入实际的条数,还没有限制为 100 条,F9 继续执行:
这次写入的是 0x65 条了,看看这个 ESI 的值是如何来的,往上回溯,如下图所示:
是通过 ECX 传递过来的,这个 ECX 在 Delphi 就是函数的参数,也就是説这个函数就应该是 Delphi 的修改 RowCount 值的函数,CTRL+F9 退出函数,进入如下所示代码区域:
可以看到,前面是对 RowCount 数据进行校验或检查,再调用函数修改数据,再次 CTRL+F9 返回,来到下面位置,可以看到,我们要找的地方找到了:
具体代码如下:
[Asm] 纯文本查看 复制代码
004AA6C3 . 8B83 EC010000 mov eax, dword ptr [ebx+0x1EC]
004AA6C9 . 83B8 58010000 65 cmp dword ptr [eax+0x158], 0x65 ; 加上标题行,共 101 行
004AA6D0 . 7E 0A jle short 004AA6DC ; 破解显示100条限制
004AA6D2 . BA 65000000 mov edx, 0x65 ; 限制显示行数
004AA6D7 . E8 FC90F9FF call 004437D8
004AA6DC > A1 B44B4C00 mov eax, dword ptr [0x4C4BB4]
在这里检查如果 rowCount 大于 0x65 则将 rowCount 改成 0x65。破解也简单,直接跳过修改即可,如下图所示:
具体修改如下:
[Asm] 纯文本查看 复制代码
004AA6C9 . 83B8 58010000 65 cmp dword ptr [eax+0x158], 0x65 ; 加上标题行,共 101 行
004AA6D0 EB 0A jmp short 004AA6DC ; 破解显示100条限制
我们先删除硬件写入中断,再 F9 回到软件,重新打开刚才打开的数据文件,这次 rowCount 可以超出 100 了,但还没有显示数据,只显示空的格子,如下图所示:
由上图可见,数据可以显示 101 条,即 0x65 条,所以,还需要解决数据记录条数不超过101条的问题。
再次回到 DeDe Dark,如下图所示:
可以看到一个 StringGrid 单元格自绘事件,双击进去看看:
再一个一个函数打开看看,如上图中红框中的函数,看看会有什么有用的发现,如下图所示:
手气好,可以看到,上图中有一条与 0x65 进行比较的指令,大于则跳转,与前面的 rowCount = 0x65 类似,再次转入 OD,来到该代码处,如下图所示:
在这里下一个断点,位置:0x004AA3AD,可以看到,大于 0x65 时,直接跳出函数了,因此我们把 jg 跳转指令先 NOP 掉,如下图所示:
具体改动如下:
[Asm] 纯文本查看 复制代码
004AA3AD . 837D 10 65 cmp dword ptr [ebp+0x10], 0x65 ; 限制读取记录数
004AA3B1 90 nop
004AA3B2 90 nop
004AA3B3 90 nop
004AA3B4 90 nop
004AA3B5 90 nop
004AA3B6 90 nop
004AA3B7 . C645 FF 00 mov byte ptr [ebp-0x1], 0x0
回到软件,再次打开一个数据文件(orders.DAT),可以看到,可以显示101条以上的记录数据了,这个就是运气好吧:
该工具有一个查询过滤(Filter)功能,但是试用版没有生效,过滤条件没有执行,先在 DeDe Dark 中找到这个事件(miFilterClick)位置,如下图所示:
然后我们执行一下过滤功能,如下图所示:
但现在点“OK”没有用,在事件中下一个断点,如下图所示:
问题出在这里,这里总是返回 0,然后就跳过后面的循环过滤代码了,我们修改代码,强行返回 StringGrid 的行数即可。如下图所示:
正好下面有两行读取 StringGrid 的 RowCount 的代码,对照修改一下就可以了,具体修改如下所示:
[Asm] 纯文本查看 复制代码
004BD30C 8B80 EC010000 mov eax, dword ptr [eax+0x1EC] ; [eax+0x1EC]
004BD312 8BB0 58010000 mov esi, dword ptr [eax+0x158]
004BD318 . 85F6 test esi, esi
004BD31A . 7E 67 jle short 004BD383
再一次执行过滤功能,就可以用了:
不过该软件实现得不是太好,就是隐藏一下不符合要求的记录。
下面来处理软件导出数据的限制,导出时有些格式也会有100条记录的限制,同时会有一条水印(注释或标题)导出时不能修改。
如下图所示,先在 DeDe 找到事件入口:
在 OD 中跳转到该事件,并下一个断点(0x004B66F3),在软件中点“File"->"Save As",就会断下来:
F8 来到如下图所示位置,下两个断点,因为中间那条 call 0x0042F428 是显示导出窗口的,按F8执行会卡死(Delphi程序的通病)只能 F9 执行。
具体代码如下:
[Asm] 纯文本查看 复制代码
004B6A40 . 8B45 E0 mov eax, dword ptr [ebp-20]
004B6A43 . E8 E089F7FF call 0042F428
004B6A48 . 48 dec eax
004B6A49 . 0F85 DA010000 jnz 004B6C29
F9 后,弹出如下对话框:
先择一个保存的类型,按”确定“,如上图所示,我选择了 XML 格式。再次中断在前下的第2个断点,再 F8 执行到如下图所示位置:
这里的 al 就是我们在界面上选择的类型值,al==3 表示选择的第4项:XML格式。具体代码如下:
[Asm] 纯文本查看 复制代码
004B6B6B . 8A80 24010000 mov al, byte ptr [eax+124]
004B6B71 . 8845 DF mov byte ptr [ebp-21], al
004B6B74 . 8B45 E0 mov eax, dword ptr [ebp-20]
004B6B77 . 8B80 60020000 mov eax, dword ptr [eax+260]
004B6B7D . E8 3E15F8FF call 004380C0
再一路F8,来到如下位置,这里开始处理数据保存了:
在这里按 F7 进入函数,具体代码如下:
[Asm] 纯文本查看 复制代码
004B6C64 . 33C0 xor eax, eax
004B6C66 . 8A45 DF mov al, byte ptr [ebp-21]
004B6C69 . 8B15 4C4A4C00 mov edx, dword ptr [4C4A4C] ; datview.004C4664
004B6C6F . 8B0C82 mov ecx, dword ptr [edx+eax*4]
004B6C72 . 8B55 F4 mov edx, dword ptr [ebp-C]
004B6C75 . 8B45 FC mov eax, dword ptr [ebp-4]
004B6C78 . E8 CFF4FFFF call 004B614C
进入 0x004B614C 函数后,会来到一个分支跳转处(switch),如下图所示,按不同的选择类型跳转:
跳转后,会来到如下位置:
这里就是处理 XML 格式导出的地方了。再次按 F7 进入 call 0x004B3364,一路 F8 来到如下位置,可以看到 100 条限制的指令:
上图中用红框框中的指令,就是其它类型导出时,限制100条记录的特征码,可以通过搜索这个特征码找到其它格式导出时的限制。
去除限制也简单,把 jg 指令 NOP 掉即可,如下图所示:
具体修改如下:
[Asm] 纯文本查看 复制代码
004B3660 > \8BC6 mov eax, esi
004B3662 . 2B45 10 sub eax, dword ptr [ebp+10]
004B3665 . 83F8 65 cmp eax, 65 ; 限制导出XML行数
004B3668 90 nop
004B3669 90 nop
004B366A 90 nop
004B366B 90 nop
004B366C 90 nop
004B366D 90 nop
004B366E . C645 E6 01 mov byte ptr [ebp-1A], 1
其它几种格式的限制指令位置如下,方便大家定位:
[Asm] 纯文本查看 复制代码
004B25BC cmp eax, 65 限制导出txt行数
004B2DE7 cmp eax, 65 限制导出html行数
004B3665 cmp eax, 65 限制导出XML行数
004B4064 cmp eax, 65 限制导出JSON行数
004B4D77 cmp eax, 65 限制导出为XLS行数
004B9521 cmp eax, 65 限制导出SQL行数
我们导出成 XML 文件后,打开看看,如下图所示:
选中的那一行就是所谓的水印,不过这条注释可以在软件的选项设置中修改,但由于是未注册版,禁止修改,强行附加并不允许修改设置,如下图所示:
所以,导出的文件中的总是会带有”Generated by DBISAM Viewer (Scalabium Software, http://www.scalabium.com/dbisam) “这一条水印信息,现在我们去除这个限制。
我们这次在 TfrmMain 的 FormCreate 事件中,找到这两个设置的初始化过程,如下图所示:
因为这个初始化是启动时处理的,我们先把前面对程序的修改保存下来,不然重启软件就没有了,如下图所示,选择右键菜单:
”复制到可执行文件“->”所有修改“,弹出如下确认框:
选择”全部复制“即可。
再右键”保存文件“。这样就把我们前面的修改保存下来了。
退出软件,重新用 OD 载入我们前面修改后保存的软件。
前面説了,这条水印其实可以在Options中修改,只是因为试用版限制修改了而矣,我们先在 DeDe 中找到设置的事件入口:
事件名为:miOptionsClick(),入口地址是 0x004B7D64。
在 OD 中跳转到该事件函数中,如下图所示,我可以看到初始化显示界面的代码,如下图所示:
但是再往后,却没有找到保存该设置的代码:
因此,我们需要重新补上这段代码,不然,还是无法修改并保存这个设置。
首先我们先找到一段类似的保存设置的代码,在这个代码基础上进行修改,如下图所示中选中的代码就是我们需要的:
具体代码如下所示:
[Asm] 纯文本查看 复制代码
004B819F . 8B45 F8 mov eax, dword ptr [ebp-8]
004B81A2 . 8B80 88020000 mov eax, dword ptr [eax+288]
004B81A8 . E8 4B10F8FF call 004391F8
004B81AD . 8843 5A mov byte ptr [ebx+5A], al
004B81B0 . 8D55 E8 lea edx, dword ptr [ebp-18]
004B81B3 . 8B45 F8 mov eax, dword ptr [ebp-8]
004B81B6 . 8B80 8C020000 mov eax, dword ptr [eax+28C]
004B81BC . E8 C38DF6FF call 00420F84
004B81C1 . 8B55 E8 mov edx, dword ptr [ebp-18]
004B81C4 . 8D43 5C lea eax, dword ptr [ebx+5C]
004B81C7 . E8 70B8F4FF call 00403A3C
我们就是要在这段代码的基础上来操作。我们需要在程序代码节的最后面,找一段空白处,依据上面代码,写入我们的代码,在 0x004BF660 处找到一处可以保存代码的空白空间。
同时,由于有代码重定位,我们在这个函数内要找一处不含重定位的5个字节代码写入跳转指令,跳转到我们的代码处,在函数后面保存数据最后处,找到了5字节,如下图所示位置:
代码位置如下:
[Asm] 纯文本查看 复制代码
004B8399 . 33C0 xor eax, eax
004B839B ? 5A pop edx
004B839C ? 59 pop ecx
004B839D ? 59 pop ecx
正好5字节,也没有重定位,我们在该位置写入跳转指令:jmp 0x004BF660。跟随该指令,来到空白处,补上我们的代码,如下图所示:
具体代码如下:
[Asm] 纯文本查看 复制代码
004BF660 8B45 F8 mov eax, dword ptr [ebp-8]
004BF663 8B80 4C020000 mov eax, dword ptr [eax+24C]
004BF669 E8 8A9BF7FF call 004391F8
004BF66E 8843 51 mov byte ptr [ebx+51], al
004BF671 8D55 E8 lea edx, dword ptr [ebp-18]
004BF674 8B45 F8 mov eax, dword ptr [ebp-8]
004BF677 8B80 50020000 mov eax, dword ptr [eax+250]
004BF67D E8 0219F6FF call 00420F84
004BF682 8B55 E8 mov edx, dword ptr [ebp-18]
004BF685 8D43 54 lea eax, dword ptr [ebx+54]
004BF688 E8 AF43F4FF call 00403A3C
004BF68D 90 nop
004BF68E 33C0 xor eax, eax
004BF690 5A pop edx
004BF691 59 pop ecx
004BF692 59 pop ecx
004BF693 90 nop
004BF694 ^ E9 058DFFFF jmp 004B839E
004BF699 90 nop
另外,我们刚才在 0x004B839B 是函数退出代码,当在设置界面选择”Cancel"时,会跳转到这里,所以,我们还要修改如下所示位置的代码:
具体代码如下:
[Asm] 纯文本查看 复制代码
004B808E . 48 dec eax
004B808F 0F85 F9750000 jnz 004BF68E
现在,保存数据的代码可以了,但还有一个问题,界面的控件状态是 disabled 状态,不可更改。还是的 miOptionsClick() 事件中,有对这两个控件状态进行处理的代码,如下图所示:
将这里两个call调用的dl参数赋值为 0x01即可,如下图所示:
具体修改后的代码如下:
[Asm] 纯文本查看 复制代码
004B7EFE . 8B45 F8 mov eax, dword ptr [ebp-8]
004B7F01 . 8B80 4C020000 mov eax, dword ptr [eax+24C]
004B7F07 B2 01 mov dl, 1
004B7F09 . E8 0E90F6FF call 00420F1C
004B7F0E . 8B45 F8 mov eax, dword ptr [ebp-8]
004B7F11 . 8B80 50020000 mov eax, dword ptr [eax+250]
004B7F17 B2 01 mov dl, 1
004B7F19 . E8 FE8FF6FF call 00420F1C
现在我们先保存这次的修改,因为需要重启才能验证是否有效:
按前面的方式保存代码,重新在 OD 中载入程序,F9 运行,点菜单“View”->"Options"打开选项窗口:
可以看到,现在可以修改了。我们修改后,重新启动程序,发现修改的内容又恢复原状了,看来还有暗桩存在,在 TfrmMain.FormCreate 事件中,前面初始化控件后,在读取设置信息后,又强行将这两个信息处理了:
因此,我们需要跳过这段代码,去除暗桩,修改如下:注意要改动没有重定位的代码:
具体代码如下:
[Asm] 纯文本查看 复制代码
004AE53D . A1 884B4C00 mov eax, dword ptr [4C4B88]
004AE542 EB 47 jmp short 004AE58B
004AE544 90 nop
004AE545 90 nop
修改后,再次保存代码到文件:
再次重新在 OD 中载入保存的程序,并 F9 运行,这次可以保存修改值了:
再次重启程序,可以看到,能正确读取这些保存的数据了。
功能方面的问题基本处理完了,现在开始处理显示方面的问题,显示方面主要是修改数据节的数据,不需改动代码了。
首先我们修改主界面标题显示“(UNREGGISTERED)”的问题。在 TfrmMain.FormCreate() 事件中,有如下代码:
具体代码如下:
[Asm] 纯文本查看 复制代码
004AE4C7 . FFB5 60FEFFFF push dword ptr [ebp-1A0] ; "Direct DBISAM table reading"
004AE4CD . 68 24F74A00 push 004AF724 ; (
004AE4D2 . 8D95 6CFEFFFF lea edx, dword ptr [ebp-194]
004AE4D8 . A1 1C4D4C00 mov eax, dword ptr [4C4D1C]
004AE4DD . 8B00 mov eax, dword ptr [eax]
004AE4DF . E8 8CA3FFFF call <DecodeString()> ; Decode
004AE4E4 . FFB5 6CFEFFFF push dword ptr [ebp-194]
004AE4EA . 68 FCF64A00 push 004AF6FC ; )
004AE4EF . 8D85 64FEFFFF lea eax, dword ptr [ebp-19C]
004AE4F5 . BA 04000000 mov edx, 4
004AE4FA . E8 2558F5FF call 00403D24
004AE4FF . 8B95 64FEFFFF mov edx, dword ptr [ebp-19C]
004AE505 . 8B45 FC mov eax, dword ptr [ebp-4]
004AE508 . E8 A72AF7FF call <SetText()> ; setText("(UNREGISTERED)")
中间调用了一个解码函数,该函数如下图所示:
函数主体是一个循环,在循环内调用了另外一解码函数,如下图所示:
再进入这个函数,如下图所示:
具体代码如下:
[Asm] 纯文本查看 复制代码
004A8848 /$ B1 01 mov cl, 1
004A884A |. B2 07 mov dl, 7
004A884C |. E8 1BFEFFFF call 004A866C ; swapbit()
004A8851 |. B1 02 mov cl, 2
004A8853 |. B2 03 mov dl, 3
004A8855 |. E8 12FEFFFF call 004A866C
004A885A |. B1 04 mov cl, 4
004A885C |. B2 05 mov dl, 5
004A885E |. E8 09FEFFFF call 004A866C
004A8863 |. B1 06 mov cl, 6
004A8865 |. 33D2 xor edx, edx ; 0
004A8867 |. E8 00FEFFFF call 004A866C
004A886C \. C3 retn
一看代码,我就猜出,这应该是一个bit交换函数,1<->7,2<->3,4<->5,6<->0,将一个字节8位进行4组交换。
我们现在先来看看解码过程,在 0x004AE4C7 下一个断点,如下图所示:
先不要执行解码函数,如上图所示,可以看到解码前的字符串(在 OD 的数据区)。这里説明一下 delphi 的字符串格式:
[C++] 纯文本查看 复制代码
struct String {
DWORD refCount;
DWORD length;
char buffer[length];
};
其中当 refCount = 0xFFFFFFFF时,表示是常量字符串,不需要释放内存。大于或等于0时,表示是变量字符串,当为 0 时表示不再需要保留,可以释放内存了,而大于0时,表示还有引用,不能释放。
可以看到这个字符串的信息如下:
[Shell] 纯文本查看 复制代码
refCount = 0xFFFFFFFF
length = 0x00000029
buffer[] = "9 73 161 73 41 225 69 201 73 161 141 105 "
字符串通过空格分开的数字,数字的个数与 "unregistered"相同,其实这个字符串中的数字,就是字符的 ASCII 码通过前面的 swapbits() 交换后的 ASCII 码的十进制值。
并且顺序是反过来的,因此,我们只要把字符串后面两个数字去掉就变成 “REGISTERED”了,而去除字符串后面一截,只要修改长度就可以了。我们在内存中把长度改成 0x21,如下图所示:
如上图所示,改了长度,就是把所选部分(8字节)截掉了。现在我们执行解码函数,如下图所示:
可以看到,与预料的一样,解码后变成了 "REGISTERED"了,F9 运行,进入主界面。如下图所示,标题改过来了:
接下来我们修改前面 About 窗口留下的没有修改的红色“未注册/演示版/”等信息。
先在 DeDe Dark 中找到事件入口:miAboutClick(),地址为 0x004B13D0,如下图所示:
在 OD 中跳转到函数:
在函数内 0x004B1524 处下断点,然后点“About”按钮,就会断下来,如下图所示:
这里又是一个解码调用,解码后,得到一个字符串:
就是 about 窗口显示的“Unregistered /demo version/”,我们先要替换这条字符串。
因为这次需要修改字符串数据,所以需要先编码数据,因为是 bit 对称交换,编码/解码是同一个函数,所以我写了一个编码的代码,如下所示:
[C++] 纯文本查看 复制代码
#include <iostream>
#include <string.h>
typedef unsigned char UCHAR;
const int MAX_LEN = 1000;
int encode(char * msg);
UCHAR swapBit(UCHAR ch, int n1, int n2);
int main(int argc, char** argv) {
char msg[] = "Unregistered /demo version/";
int len = encode(msg);
printf("%d\n", len);
char msg2[] = "Cracked by [url=mailto:solly@52pojie.cn]solly@52pojie.cn[/url]";
int len2 = encode(msg2);
printf("%d\n", len2);
char msg3[] = "The trial version allow to load only the first %s records from table.";
int len3 = encode(msg3);
printf("%d\n", len3);
char msg4[] = "Username: solly\nOrganization: 52pojie\nProductID: DBISAM%s1-CRKED-OK";
int len4 = encode(msg4);
printf("%d\n", len4);
return 0;
}
int encode(char * msg) {
int n = strlen(msg);
char * rev_msg = strrev(msg);
//printf("%s\n", rev_msg);
UCHAR buff[MAX_LEN];
char encodedMsg[MAX_LEN*4];
int len = 0;
for(int i=0; i<n; i++) {
UCHAR ch = (UCHAR)rev_msg[i];
//printf("%c ==>", ch);
ch = swapBit(ch, 0, 6);
ch = swapBit(ch, 1, 7);
ch = swapBit(ch, 2, 3);
ch = swapBit(ch, 4, 5);
//printf("%02X ", ch);
//printf("\n");
buff[len++] = ch;
if(len==MAX_LEN) {
break;
}
}
buff[len] = '\0';
int pos = 0;
for(int i=0; i<len; i++) {
sprintf(&encodedMsg[pos], "%d ", buff[i]);
pos = strlen(encodedMsg);
}
printf("%s\n", encodedMsg);
return pos;
}
UCHAR swapBit(UCHAR ch, int n1, int n2) {
UCHAR b1 = (n1!=0)?((ch>>n1) & 0x01):(ch & 0x01);
UCHAR b2 = (n2!=0)?((ch>>n2) & 0x01):(ch & 0x01);
//printf("b1 = %02X, b2 = %02X\n", b1, b2);
if(n1 != 0) {
ch = (UCHAR)((((ch>>n1) & 0xFE) | b2 ) << (n1)) | ((UCHAR)(ch<<(8-n1))>>(8-n1));
} else {
ch = (ch & 0xFE) | b2;
}
//printf(" ---> %02X ", ch);
if(n2 != 0) {
ch = (UCHAR)((((ch>>n2) & 0xFE) | b1 ) << (n2)) | ((UCHAR)(ch<<(8-n2))>>(8-n2));
} else {
ch = (ch & 0xFE) | b1;
}
//printf(" ---> %02X, ", ch);
return ch;
}
执行上面代码,就可以得到我们需要的字符串,不过编码后的长度不要大于原长度(可以加上字符串后面的对齐用的留空),如下图所示:
进行二进制替换,替换后如下图所示:
包括长度也需要根据实际长度来修改,如上图中圆圈中所示数据。
接着,我们还要修改那个显示100条记录限制的字符串,如下图所示:
将代码生成的字符串替换上图中所选的数据,如下图所示:
执行解码后,得到我们想要的字符串:
显示用户名等信息。按 F9 运行,得到界面如下所示:
因为第1条是解码完成后才替换,所以还是显示原来的字符串,关闭about对话框,重新点“About”按钮,就可以再次打开 About 窗口,这次就正常了:
是不是有点成就了???
修改完成后,我们将这些修改保存到可执行程序,需要先将要保存的数据选中,如下图是开始位置:
结束位置如下图所示:
就是从地址 0x00469A9C 到 0x00469D9C 间的 0x300 字节,包括了我们刚才改动的3处数据。
在选中的数据上点右键菜单”复制到可执行文件“,然后保存为一个新的执行文件。
这个文件就是最终版本了,如下所示,最终效果图:
下面附上原版程序包:
DBISAMViewer_original.rar
(609.6 KB, 下载次数: 34)
最后附上,原版与修改版字节对比:
patch_bytes.txt
(6.53 KB, 下载次数: 7)
完成!!!