吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6456|回复: 23
收起左侧

[原创] 某DBISAM文件型数据库管理小工具的试用版破解过程详细记录

  [复制链接]
solly 发表于 2020-6-27 18:01
本帖最后由 solly 于 2020-6-28 23:33 编辑

这个小工具软件是一个 DBISAM 文件型数据库的管理工具,可以查看 DBISAM 格式的文件数据,软件名称为 DBISAM Viewer。
这个软件的试用版有一些限制:
1、时间限制,过期后不可用;
2、每次打开文件时会弹出 NAG 窗口;
3、查询的记录条数有限制,超过100条及以上数据不显示;
4、条件过滤无效;
5、导出文件也有100条限制,还有一个水印显示,并且禁止在"Options"界面中取消水印;
6、主窗口显示“(UNREGISTERED)”,About 窗口显示“未注册/演示版/只能查询前100条记录”等信息。


首先查看文件信息:
00.png
显示是 Delphi 编译的,“Scan /t” 再查一下:
01.png
显示了具体的 Delphi 版本为 3.0。程序的节信息如下:
02.png
标准的 delphi 程序。我们用 DeDe Dark 打开程序,查看 Form 和 事件 等信息:
03.png
DeDe 分析完成,表单信息如下:
04.png

上图是 TfrmMain 主表单的信息,事件过程的地址指针数据。
05.png
如上图所示,对事件进行排序一下,可以看到最前面的几个事件是 Form 的事件,对我们有用的主要是 FormCreate 和 FormActivate 两个事件。


首先看看 Form 的  FormActivate 事件,如下图所示:
06.png
可以看到,这一段代码就是对时间进行限制的。下面再看看 FormCreate 事件:
07.png
继续:
08.png
主要是初始化一些界面显示,标题栏和状态栏字符串等。


现在开始跟踪调试,用 OD 载入程序,首先Ctrl+G跳转到 FormActivate 事件,如下图所示:
11.png

如下图所示,就是 TfrmMain 表单的 FormActivate 事件的代码,在 0x004B126B 处下一个断点,并按 F9 执行程序:
12.png
程序中断在所下的断点处,按 F8 执行代码到下图所示位置:
13.png
这里是一个时间转换函数,将 year, month, day 转换成 Delphi 的 TDateTime 格式(2021-08-07,实际上是一个双精度浮点数:44415.0)。


然后再取得系统时间,与转换后的时间比较:
14.png
如果当前系统时间大于 2021-08-07,则表示过期,程序会弹出如下提示:
10.png
然后程序会退出。


首先破解这个时间限制,如下图所示:
15.png

将 jbe 指令改成 jmp 指令即可。具体改为如下所示(修改的是最后一条指令):
[Asm] 纯文本查看 复制代码
004B129D   .  DC5D F8       fcomp   qword ptr [ebp-0x8]              ;  时间比较
004B12A0   .  DFE0          fstsw   ax
004B12A2   .  9E            sahf
004B12A3      EB 45         jmp     short 004B12EA                   ;  时间过期限制



跳过时间检查后,进入主界面,如下图所示:
16.png
按“Open File” 打开一个 *.DAT 文件,会弹出一个 NAG 窗口(其实就是 About 窗口),如下图所示:
17.png
在 DeDe Dark 中找到这个 "Open File" 事件,如下图所示:
18.png
该事件调用了另外一个 btnLoadClick() 事件,业务处理都是在这个btnLoadClick()事件中进行的。在 OD 中跳转到 "Open File" 事件,如下图所示:
19.png

我们在 btnLoadClick() 中下一个断点(在 0x004AAE2B 处),然后再次打开一个 *.DAT 文件,如下图所示:
20.png
点“打开”后,OD 会中断程序:
21.png
一路按 F8 执行,执行完整个函数,也没有弹出 NAG ,如下图所示,已退出 btnLoadClick() 函数:
22.png
看来不在此函数内处理的NAG显示,F9 运行,可以听到“咣”的一声后,NAG 如期弹出。还好有这个“咣”的一声,我们在 OD 的命令窗口下断点 "bp MessageBeep",然后再一次打开一个*.DAT文件,这次会断在如下位置,如下图所示:
23_messagebeep.png

这是中断在系统 MessageBeep 函数中了,一路 F8 退出函数,如下图所示:
24.png

再一次F8返回上一级函数,如下图所示:
25.png
具体代码如下:
[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:显示),对该处地址下一个硬件中断,如下图所示:
26.png

然后再一次打开一个 *.DAT 文件,首先中断在如下位置:

27.png

这里是初始化为 0,所以不是这里改的状态值,F9 继续执行,再一次硬件中断在如下位置:

28.png

这里是在 btnLoadClick() 事件中的代码,也是在这里决定是否显示  NAG 窗口。


再一次 F9 运行,再次来到了显示 NAG 代码处,如下所示:
29.png
这里是将此标志重新设置为 0,表示不再显示(这里其实是3个 delphi 的 StringGrid 显示刷新的公共事件,会执行3次,置 0 是保证只显示一次)。至此,就是 btnLoadClick() 中通过置 [ebx+0x378]的值来决定是否显示 NAG。

因此,只要将前面置1的指令修改成置0就可以了,如下图所示(同时可以删除硬件中断了):
30.png
具体修改位置如下:
[Asm] 纯文本查看 复制代码
004AB29D      C680 78030000 00    mov     byte ptr [eax+0x378], 0x0        ;  是否显示 NAG, 1-显示,0-不显示
004AB2A4   .  C3                  retn                                     ;  RET 用作跳转到 004AB2AC

这样修改后,就不会再弹出 NAG 窗口。

下面继续处理 About 窗口中的显示,点 "About " 按钮,会弹出 About 对话框,如下图所示(NAG显示也是这个窗口):
31_about.png
这里显示“未注册/演示版/试用版只能显示100条记录”,并且,在OK按钮左边还显示了 30 天试用期什么的。。。。。

通过 DeDe 查得 About 弹出事件,然后直接CTRL+G 跳转到事件中去,如下图所示:
32.png
事件的开始代码如下所示:
33.png
在 0x004B13D1 处下一个断点,再在软件界面点“About”,就会中断在上图所示位置。
34.png
F8执行代码,上面是修改标签显示一个大大的重影文本标签(实际上是两个 Label 移位叠加)。F8 继续往下走:
35.png
还是显示一些信息,继续往下走:
36.png
可以看到,这里是一个解码函数,eax 指向一条编码后的字符串,如中上图中的 OD 数据区所选的数据。F8 执行解码函数,如下图所示:
37.png
解码的内容为:
[Asm] 纯文本查看 复制代码
02484EE0  You may use shareware version for 30 days (TRIAL period).
F8 继续执行,下面 ecx 又指向了一条编码的字符串,如下图所示:
38.png
后面继续解码,如下图所示(edx指向的字符串):
39.png
解码得到:
[Asm] 纯文本查看 复制代码
02487BF8  After that period has ended, to continue using this product you
02487C38  have to purchase the Registered version.

就是显示在“OK” 按钮左边的内容,合并后如下图所示:
40.png
同时调用函数显示:
[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 掉,就不会显示了。

接下来还是解码字符串,如下图所示,解码“未注册/演示版/”等信息:
41.png
解码后:
42.png
生成 100 条限制字符串:
43.png
再次解码:
44.png
解码后为一个 Format() 函数的参数字符串,如下图所示,包含一个  %s 参数,就是代入前面的 100 生成的字符串 "100",如下图所示:
45.png
代入后如下图所示:
46.png
再次合并字符串,用于显示 About 中的红色字符串:
47.png
最后就是显示 About 窗口了:
48_showABout.png
如下所示,框框中显示的就是前面解码合并的两条字符串。
49.png
首先,我们把上图中的第(2)条显示去除,如下图所示,将 call setText() 指令 NOP 掉:
50.png
具体修改如下,将 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”,则显示如下:
51.png
另外一条显示(红色字符串),暂不处理,放后面再处理。


现在我们来处理最多只显示 100 条记录的限制,如下图所示,当多于100条记录时,只显示100条:
60.png
首先在 DeDe Dark 中查看事件,找到 StringGrid 控件的刷新事件,如下图所示:
61.png
在 OD 中跳转到该事件,并下一个断点,断点我下在  0x004BD435,如下图所示:
62.png

点一下主界面的记录,变动一下活动的记录号,马上会中断下来,如下图所示:
63.png
上图中的 [ESI+0x1EC] 就是指的 StringGrid 控件,这个在 DeDe Dark 中或者IDR中可以看到。
先切换 OD 数据区的内容到 StringGrid 对象实例内存区内,查找 Delphi 控件 StringGrid 的 rowCount 属性值,如下图所示:
64.png
用过 Delphi 的编程者就知道,StringGrid 的 RowCount 包含标题行,所以是 0x65(101)行,我们输入 "65 00" 查询,如下所示,就找到了存放 rowCount 属性存放位置,在这个位置下一个硬件写入断点,如下图所示:
65.png
把上面的 F2 断点禁用,按 F9 回到程序,重新打开一个数据文件,如软件自带 Orders.DAT,马上就会硬件中断下来,如下图所示:
66.png
这里是写入实际的条数,还没有限制为 100 条,F9 继续执行:
67.png

这次写入的是 0x65 条了,看看这个 ESI 的值是如何来的,往上回溯,如下图所示:
68.png
是通过 ECX 传递过来的,这个 ECX 在 Delphi 就是函数的参数,也就是説这个函数就应该是 Delphi 的修改 RowCount 值的函数,CTRL+F9 退出函数,进入如下所示代码区域:
69.png
可以看到,前面是对 RowCount 数据进行校验或检查,再调用函数修改数据,再次 CTRL+F9 返回,来到下面位置,可以看到,我们要找的地方找到了:
70.png
具体代码如下:
[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。破解也简单,直接跳过修改即可,如下图所示:
71.png
具体修改如下:
[Asm] 纯文本查看 复制代码
004AA6C9   .  83B8 58010000 65     cmp     dword ptr [eax+0x158], 0x65      ;  加上标题行,共 101 行
004AA6D0      EB 0A                jmp     short 004AA6DC                   ;  破解显示100条限制


我们先删除硬件写入中断,再 F9 回到软件,重新打开刚才打开的数据文件,这次 rowCount 可以超出 100 了,但还没有显示数据,只显示空的格子,如下图所示:
72.png
由上图可见,数据可以显示 101 条,即 0x65 条,所以,还需要解决数据记录条数不超过101条的问题。

再次回到 DeDe Dark,如下图所示:
73.png

可以看到一个 StringGrid 单元格自绘事件,双击进去看看:
74.png
再一个一个函数打开看看,如上图中红框中的函数,看看会有什么有用的发现,如下图所示:
75.png

手气好,可以看到,上图中有一条与  0x65 进行比较的指令,大于则跳转,与前面的 rowCount = 0x65 类似,再次转入 OD,来到该代码处,如下图所示:
76.png
在这里下一个断点,位置:0x004AA3AD,可以看到,大于 0x65 时,直接跳出函数了,因此我们把 jg 跳转指令先 NOP 掉,如下图所示:
77.png
具体改动如下:
[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条以上的记录数据了,这个就是运气好吧:
78.png


该工具有一个查询过滤(Filter)功能,但是试用版没有生效,过滤条件没有执行,先在 DeDe Dark 中找到这个事件(miFilterClick)位置,如下图所示:
80.png

然后我们执行一下过滤功能,如下图所示:
81.png
但现在点“OK”没有用,在事件中下一个断点,如下图所示:
82.png
问题出在这里,这里总是返回 0,然后就跳过后面的循环过滤代码了,我们修改代码,强行返回 StringGrid 的行数即可。如下图所示:
83.png
正好下面有两行读取 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


再一次执行过滤功能,就可以用了:
84.png
不过该软件实现得不是太好,就是隐藏一下不符合要求的记录。

下面来处理软件导出数据的限制,导出时有些格式也会有100条记录的限制,同时会有一条水印(注释或标题)导出时不能修改。
如下图所示,先在 DeDe 找到事件入口:
90.png
在 OD 中跳转到该事件,并下一个断点(0x004B66F3),在软件中点“File"->"Save As",就会断下来:
91.png

F8 来到如下图所示位置,下两个断点,因为中间那条 call 0x0042F428 是显示导出窗口的,按F8执行会卡死(Delphi程序的通病)只能 F9 执行
92.png

具体代码如下:
[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 后,弹出如下对话框:
93.png
先择一个保存的类型,按”确定“,如上图所示,我选择了 XML 格式。再次中断在前下的第2个断点,再 F8 执行到如下图所示位置:
94.png

这里的 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,来到如下位置,这里开始处理数据保存了:
95.png

在这里按 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),如下图所示,按不同的选择类型跳转:
96.png
跳转后,会来到如下位置:
97.png
这里就是处理 XML 格式导出的地方了。再次按 F7 进入 call 0x004B3364,一路 F8 来到如下位置,可以看到 100 条限制的指令:
98.png

上图中用红框框中的指令,就是其它类型导出时,限制100条记录的特征码,可以通过搜索这个特征码找到其它格式导出时的限制。
去除限制也简单,把 jg 指令 NOP 掉即可,如下图所示:
99.png

具体修改如下:
[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 文件后,打开看看,如下图所示:
100.png

选中的那一行就是所谓的水印,不过这条注释可以在软件的选项设置中修改,但由于是未注册版,禁止修改,强行附加并不允许修改设置,如下图所示:
101.png
所以,导出的文件中的总是会带有”Generated by DBISAM Viewer (Scalabium Software, http://www.scalabium.com/dbisam) “这一条水印信息,现在我们去除这个限制。

我们这次在 TfrmMain 的 FormCreate 事件中,找到这两个设置的初始化过程,如下图所示:
102.png

因为这个初始化是启动时处理的,我们先把前面对程序的修改保存下来,不然重启软件就没有了,如下图所示,选择右键菜单:
103.png
”复制到可执行文件“->”所有修改“,弹出如下确认框:
104.png
选择”全部复制“即可。
105.png

再右键”保存文件“。这样就把我们前面的修改保存下来了。

退出软件,重新用 OD 载入我们前面修改后保存的软件

前面説了,这条水印其实可以在Options中修改,只是因为试用版限制修改了而矣,我们先在 DeDe 中找到设置的事件入口:
106.png
事件名为:miOptionsClick(),入口地址是 0x004B7D64。
在 OD 中跳转到该事件函数中,如下图所示,我可以看到初始化显示界面的代码,如下图所示:
107.png

但是再往后,却没有找到保存该设置的代码:
108.png

因此,我们需要重新补上这段代码,不然,还是无法修改并保存这个设置。
首先我们先找到一段类似的保存设置的代码,在这个代码基础上进行修改,如下图所示中选中的代码就是我们需要的:
109.png

具体代码如下所示:
[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字节,如下图所示位置:
110.png
代码位置如下:
[Asm] 纯文本查看 复制代码
004B8399   .  33C0          xor     eax, eax
004B839B   ?  5A            pop     edx
004B839C   ?  59            pop     ecx
004B839D   ?  59            pop     ecx


正好5字节,也没有重定位,我们在该位置写入跳转指令:jmp 0x004BF660。跟随该指令,来到空白处,补上我们的代码,如下图所示:
111.png

具体代码如下:
[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"时,会跳转到这里,所以,我们还要修改如下所示位置的代码:
112.png

具体代码如下:
[Asm] 纯文本查看 复制代码
004B808E   .  48               dec     eax
004B808F      0F85 F9750000    jnz     004BF68E

现在,保存数据的代码可以了,但还有一个问题,界面的控件状态是 disabled 状态,不可更改。还是的 miOptionsClick() 事件中,有对这两个控件状态进行处理的代码,如下图所示:
113.png

将这里两个call调用的dl参数赋值为 0x01即可,如下图所示:
114.png
具体修改后的代码如下:
[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

现在我们先保存这次的修改,因为需要重启才能验证是否有效:
115.png
按前面的方式保存代码,重新在 OD 中载入程序,F9 运行,点菜单“View”->"Options"打开选项窗口:
116.png
可以看到,现在可以修改了。我们修改后,重新启动程序,发现修改的内容又恢复原状了,看来还有暗桩存在,在 TfrmMain.FormCreate 事件中,前面初始化控件后,在读取设置信息后,又强行将这两个信息处理了:
117.png

因此,我们需要跳过这段代码,去除暗桩,修改如下:注意要改动没有重定位的代码:
118.png

具体代码如下:
[Asm] 纯文本查看 复制代码
004AE53D   .  A1 884B4C00      mov     eax, dword ptr [4C4B88]
004AE542      EB 47            jmp     short 004AE58B
004AE544      90               nop
004AE545      90               nop


修改后,再次保存代码到文件:
119.png
再次重新在 OD 中载入保存的程序,并 F9 运行,这次可以保存修改值了:
120.png
再次重启程序,可以看到,能正确读取这些保存的数据了。

功能方面的问题基本处理完了,现在开始处理显示方面的问题,显示方面主要是修改数据节的数据,不需改动代码了。

首先我们修改主界面标题显示“(UNREGGISTERED)”的问题。在 TfrmMain.FormCreate() 事件中,有如下代码:
130.png

具体代码如下:
[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)")

中间调用了一个解码函数,该函数如下图所示:
131.png

函数主体是一个循环,在循环内调用了另外一解码函数,如下图所示:
132.png
再进入这个函数,如下图所示:
133.png

具体代码如下:
[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 下一个断点,如下图所示:
134.png

先不要执行解码函数,如上图所示,可以看到解码前的字符串(在 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,如下图所示:
135.png

如上图所示,改了长度,就是把所选部分(8字节)截掉了。现在我们执行解码函数,如下图所示:
136.png
可以看到,与预料的一样,解码后变成了 "REGISTERED"了,F9 运行,进入主界面。如下图所示,标题改过来了:
137.png


接下来我们修改前面 About 窗口留下的没有修改的红色“未注册/演示版/”等信息。
先在 DeDe Dark 中找到事件入口:miAboutClick(),地址为 0x004B13D0,如下图所示:
140.png
在 OD 中跳转到函数:
141.png
在函数内 0x004B1524 处下断点,然后点“About”按钮,就会断下来,如下图所示:
142.png
这里又是一个解码调用,解码后,得到一个字符串:
143.png

就是 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;
}

执行上面代码,就可以得到我们需要的字符串,不过编码后的长度不要大于原长度(可以加上字符串后面的对齐用的留空),如下图所示:
144.png

进行二进制替换,替换后如下图所示:
145.png
包括长度也需要根据实际长度来修改,如上图中圆圈中所示数据。

接着,我们还要修改那个显示100条记录限制的字符串,如下图所示:
146.png
将代码生成的字符串替换上图中所选的数据,如下图所示:
147.png
执行解码后,得到我们想要的字符串:
148.png
显示用户名等信息。按 F9 运行,得到界面如下所示:
149.png
因为第1条是解码完成后才替换,所以还是显示原来的字符串,关闭about对话框,重新点“About”按钮,就可以再次打开 About 窗口,这次就正常了:
150.png
是不是有点成就了???

修改完成后,我们将这些修改保存到可执行程序,需要先将要保存的数据选中,如下图是开始位置:
151.png
结束位置如下图所示:
152.png
就是从地址 0x00469A9C 到 0x00469D9C 间的 0x300 字节,包括了我们刚才改动的3处数据。
在选中的数据上点右键菜单”复制到可执行文件“,然后保存为一个新的执行文件。

这个文件就是最终版本了,如下所示,最终效果图:
155.png


下面附上原版程序包: DBISAMViewer_original.rar (609.6 KB, 下载次数: 34)

最后附上,原版与修改版字节对比: patch_bytes.txt (6.53 KB, 下载次数: 7)


完成!!!

免费评分

参与人数 8威望 +1 吾爱币 +27 热心值 +7 收起 理由
朱朱你堕落了 + 1 真有耐心!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wangyftr + 1 我很赞同!
生有涯知无涯 + 2 + 1 我很赞同!
天空藍 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Fris + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wapjltb + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Das-Fest + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| solly 发表于 2020-6-30 23:58
Hmily 发表于 2020-6-30 21:29
感觉还是idr导出map再导入od方便一些,那个100限制还我估计直接暴力搜常数来找了,卡死那个我遇到一般按f12 ...

IDR 解析好慢(其实通过代码地址值也可猜出哪些调用是delphi控件的,哪些作者写的),暂停会报Delphi全局异常,虽不会退出程序,但也恢复不了现场,这个大概是VCL框架消息循环处理方面的问题。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
Hmily + 1 + 1 用心讨论,共获提升!

查看全部评分

Hmily 发表于 2020-6-30 21:29
感觉还是idr导出map再导入od方便一些,那个100限制还我估计直接暴力搜常数来找了,卡死那个我遇到一般按f12暂停再f9就活过来了,这文章对离开说太简单,就不多加分了
一丝风 发表于 2020-6-27 18:06
doctrinist 发表于 2020-6-27 18:20
谢谢分享,很棒
阿基米德的微笑 发表于 2020-6-27 18:52
膜拜大佬,光截图就100多张,收藏研究研究
lxhwan100 发表于 2020-6-27 19:32
好详细啊,一步步的看,一步步的学
suifeng165 发表于 2020-6-27 20:56
纯技术贴,每个帖子都值得学习!!
xyl52p 发表于 2020-6-27 21:14
有水平的长篇,不过在下水平不照,看的云里雾里的。
pizazzboy 发表于 2020-6-27 21:48
楼主好耐心,收藏一个。谢谢分享。
qaz003 发表于 2020-6-28 01:29
哈哈,感觉是吃完饭之后尸气上来了,就懒得打字了。。。
wapjltb 发表于 2020-6-28 07:07
很好很详细的教程,请问楼主DeDe Dark这个工具哪里有下载?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 22:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表