solly 发表于 2019-5-5 14:10

160个CrackMe之89 -- PhrekTech (16位NE)的详细跟踪和追码过程

本帖最后由 solly 于 2019-5-6 23:22 编辑

160个CrackMe之89--PhrekTech是一个16位的NE格式的win16执行程序,这个程序只能在Win3.x和Win9x系统下运行,需要装一个Win98SE来跟踪。
我是在VMWare中装了一个Win98SE的虚拟机来跟踪这个程序的,并且是通过Win9x下经典的调试工具 TRW2000 来完成跟踪和追码。
为了在虚拟机中调试该CrackMe,需要进行以下设置或操作,不然无法调出TRW2k的调试界面,但这样设置后就无法使用鼠标了,只能通过键盘操作。

1、修改虚拟机vmx配置文件:
vmmouse.present = "FALSE"
svga.maxFullscreenRefreshTick = "5"
2、将TRW2000的所有文件及子目录放置到C:\盘的根目录下。如下图所示:

由于只能键盘操作,有几个快捷键要用到:
1、ALT+SpaceBar:调用出系统菜单,可以用方向键调整窗口大小,以及移动窗口位置;
2、ALT+F4:关闭窗口;
3、ALT+TAB:切换窗口;
4、TAB键:在窗口内各个控件中移动焦点。
5、CTRL + ALT:从虚拟机中切回到Window10主机;
6、TRW2000加载后,CTRL+N和CTRL+M,调出TRW2000的调试界面。

下面简单介绍在TRW中需要用到了几个调试命令:
1、s 0,FFFFFFFF ’solly‘在内存中搜索字符串”solly"
2、bpm address 内存下断点,内存地址格式:segment:offset,如 es:0528
3、bl 查看断点
4、bd 编号禁用某个编号的断点,如 bd 04 禁用04断点,编号可以通过上面的bl查到
5、be 编号启用某个编号的断点,如 be 04 启用04断点,编号可以通过前面的bl查到
6、bpx 代码下断点,如 bpx cs:0208 ,在代码段中偏移0208处下断点。
7、g 运行,退出调试界面
8、按F8键是跟入,可以进入函数,F10是单步跟踪(不进入函数)
注意,有时从TRW2000退回Win98后,键盘不响应,按任何键虚拟机都是发出“嘀”声时,可以先按"CTRL+ALT"返回Window10,再点击虚拟机的桌面进去,就可以再次使用键盘了。

16位的NE程序与DOS程序一样,是分段的,所有的代码和数据的访问是通过 segment:offset 的地址格式来访问的,如 cs:0208 是访问代码,ds:0528、ss:sp、es:si、es:di 是访问数据等


该CrackMe程序是Delphi 1.x编写的,通过IDA反汇编后,可以看到其资源和内部函数,我们可以通过IDA找到内部函数,在TRW2000中就不需要进入这些内部函数跟踪了。

启动TRW2000的Loader程序 trw2000.exe:

点击“Browse”找到我们要跟踪的CrackMe程序。

然后再点击“Load"加载程序,TRW加载程序后,会弹出调试界面,停留在 INITTASK 位置。

不管它,输入 g, 加回车,返回桌面:

继续运行程序,如下:

TRW2000的 Message Output 内显示加载成功,并且CrackMe也正常进入界面,如下:

按钮上面的标签显示”Not tested“,表示还没有进行过注册码验证。
输入假码,如下图:

然后按CTRL+N调出TRW2000调试界面,如果没有出来,则多按几次。

输入” S 0, ffffffff 'solly'   “,搜索刚才输入的用户名,如果搜到,则会在后面显示地址,同时,数据窗口会显示搜索到的数据,如上图所示。
搜到地址,可以下内存断点:bpm 80386d68,这样下完断点后,只要程序读取这个用户名,就会断下来。
如果没有断下来,説明这个数据在内存中有多个,如上图,我们可以重新搜索,如 S 80386d69,ffffffff 'solly' 来重新指定搜索范围,搜索刚才没有搜索过的地址空间,直到搜索到能够断下来的地址,如果搜索结果太多,一直断不下来,则可以下g关闭调试窗口,重新输入一个没有使用过的用户名再来重新搜索。如下图,我就把用户用重新改成了”username9977",再重新搜索。

下完新的断点,再g回到windows,按CrackMe的 Test 按钮。这回断了下来,如上图,TRW2000显示“Break on BP1",代码区显示,我们断在了 KERNEL.DLL中的 HMEMCPY 函数中,这是一个内存拷贝系统函数,这里 es:si指向源字符串,为系统空间,es:di为目的字符串,指向了应用程序的空间,如下图:

在指令区下 d es:di-4,就可以看到,系统正向程序传送用户名数据,如上图,已经传送了一部分,下图则是全部传送完成了,代码也返回了应用空间的状态:

目的地址为: es:5128,这是应用程序保存用户名的缓冲区。
这个时候为了我们可以下命令 bd 01,禁用刚才的内存中断,也可以下 bc * ,禁用所有的断点,并重新下一个新的断点:bpx HMEMCPY, 这样,等一下读取序列号时就可以断下来了。
一路按F10,返回多个 retf 后,我们来到了CrackMe的指令空间,如下图:

这里就是CrackMe进行序列号验证的地方。同时,我们用IDA反编译程序,按地址3907:0237在IDA找到相应代码位置,另外,段地址3907不用管,只要在IDA搜索偏移地址0237就可以,然后跟据TRW2000中显示的汇编代码就可以在IDA中找到相应的汇编代码范围。
这样,我们可以在IDA中看到Delphi的内部函数,如下所示,是上图对应的汇编代码:
cseg01:0237               call    @TControl@GetText$qv ; 读取用户名:username9977
cseg01:023C               call    @Concat$qm6Stringt1 ; 连接字符串:"g3df" + "username9977"
cseg01:0241               lea   di,
cseg01:0245               push    ss
cseg01:0246               push    di            ; es:di 输出结果缓冲区
cseg01:0247               push    0FFh
cseg01:024A               call    @$basg$qm6Stringt14Byte ; 生成Delphi字符串:"g3dfusername9977"
cseg01:024F               lea   di, ; es:di ===> 用户名:username9977
cseg01:0253               push    ss
cseg01:0254               push    di
cseg01:0255               lea   di, ; es:di ===>连接后的字符串:"g3dfusername9977"
cseg01:0259               push    ss
cseg01:025A               push    di
cseg01:025B               call    @$basg$qm6Stringt1 ; 生成字符串:"g3dfusername9977"
cseg01:0260               mov   di, offset aH9cf8 ; "h9cf8"
cseg01:0263               push    cs
可以看到刚才执行了Delphi 控件的 GetText 函数,取得了用户名。
一路往下跟踪执行,来到了 3907:02D9 处,执行完这条指令,就生成了一个序列号,如下图所示:

使用 d es:4F28 就会在数据区显示出来,这个地址是前面push到栈中的 = 0x4F28,在TRW2000中的Stack窗口可以看到。
同时在IDA中可以看到,马上要执行一个字符比较函数: bsub(),这是一个delphi的字符串比较函数,如果相等则返回0,不相等则返回非0,按F8进入函数内部,其进行字符串比较的代码细节如下图所示:


先比较长度,再比较字符串。
执行到 0FA7:2DCD LODSB 时,可以通过 d es:si 和 d es:di 来显示进行比较的两个字符串,如下图:



到这里,真正的序列号已经出来了,其生成规则是:
1、输入的用户名不能为空;
2、在用户名"username9977"前面加上"g3df",形成字符串1:"g3dfusername9977";
3、在前面生成的字符串1的后面加上"h9cf8",形成字符串2:"g3dfusername9977h9cf8";
4、再把前面生成的两个字符串连接起来,形成序列号:"g3dfusername9977g3dfusername9977h9cf8";
这样,只要我们返回CrackMe,输入以上序列号就可以注册通过了。
先禁用断点:

禁用断点后,输入 g 返回应用。输入正确的序列号,再按“Test”:

这时,按钮上面的标签显示“YEAH,Good work!”,表示注册成功!
下面是完整的汇编代码的验证过程,我加了注释:
cseg01:0208 sub_208         proc far                ; DATA XREF: cseg01:010E↑o
cseg01:0208
cseg01:0208 var_500         = byte ptr -500h
cseg01:0208 var_400         = byte ptr -400h
cseg01:0208 var_300         = byte ptr -300h
cseg01:0208 var_200         = byte ptr -200h
cseg01:0208 var_100         = byte ptr -100h
cseg01:0208 arg_0         = dword ptr6
cseg01:0208
cseg01:0208               push    bp
cseg01:0209               mov   bp, sp
cseg01:020B               mov   ax, 500h
cseg01:020E               call    @__StackCheck$q4Word               ; __StackCheck(Word)
cseg01:0213               sub   sp, 500h
cseg01:0217               lea   di,
cseg01:021B               push    ss
cseg01:021C               push    di
cseg01:021D               mov   di, offset aG3df                   ; "g3df"
cseg01:0220               push    cs
cseg01:0221               push    di
cseg01:0222               call    @$basg$qm6Stringt1               ; 生成Delphi字符串:"g3df"
cseg01:0227               lea   di,                    ; es:di 保存用户名的缓冲区
cseg01:022B               push    ss
cseg01:022C               push    di
cseg01:022D               les   di,
cseg01:0230               les   di, es:
cseg01:0235               push    es
cseg01:0236               push    di                                 ; this
cseg01:0237               call    @TControl@GetText$qv               ; 读取用户名:username9977
cseg01:023C               call    @Concat$qm6Stringt1                ; 连接字符串:"g3df" + "username9977"
cseg01:0241               lea   di,
cseg01:0245               push    ss
cseg01:0246               push    di                                 ; es:di 输出结果缓冲区
cseg01:0247               push    0FFh
cseg01:024A               call    @$basg$qm6Stringt14Byte            ; 生成Delphi字符串:"g3dfusername9977"
cseg01:024F               lea   di,                    ; es:di ===> 用户名:username9977
cseg01:0253               push    ss
cseg01:0254               push    di
cseg01:0255               lea   di,                    ; es:di ===>连接后的字符串:"g3dfusername9977"
cseg01:0259               push    ss
cseg01:025A               push    di
cseg01:025B               call    @$basg$qm6Stringt1               ; 生成字符串:"g3dfusername9977"
cseg01:0260               mov   di, offset aH9cf8                  ; "h9cf8"
cseg01:0263               push    cs
cseg01:0264               push    di
cseg01:0265               call    @Concat$qm6Stringt1                ; 连接字符串:"g3dfusername9977" + "h9cf8"
cseg01:026A               lea   di,                    ; es:di ===> 字符串缓冲区
cseg01:026E               push    ss
cseg01:026F               push    di
cseg01:0270               push    0FFh
cseg01:0273               call    @$basg$qm6Stringt14Byte            ; 生成Delphi字符串:"g3dfusername9977h9cf8"
cseg01:0278               lea   di,                    ; es:di ==> delphi字符串数据区
cseg01:027C               push    ss
cseg01:027D               push    di
cseg01:027E               les   di,
cseg01:0281               les   di, es:
cseg01:0286               push    es
cseg01:0287               push    di                                 ; this
cseg01:0288               call    @TControl@GetText$qv               ; 再次取用户名
cseg01:028D               add   sp, 4
cseg01:0290               cmp   , 0                  ; 字符串长度不等于0?
cseg01:0295               jnz   short loc_2AD
cseg01:0297               mov   di, offset aNaahWrong            ; "Naah! Wrong!"
cseg01:029A               push    cs
cseg01:029B               push    di                                 ; __int32
cseg01:029C               les   di,
cseg01:029F               les   di, es:
cseg01:02A4               push    es                                 ; int
cseg01:02A5               push    di                                 ; TControl *
cseg01:02A6               call    @TControl@SetText$q8TCaption       ; TControl::SetText(TCaption)
cseg01:02AB               jmp   short locret_30F
cseg01:02AD ; ---------------------------------------------------------------------------
cseg01:02AD
cseg01:02AD loc_2AD:                                                   ; CODE XREF: sub_208+8D↑j
cseg01:02AD               lea   di,                    ; es:di ==> delphi字符串数据区,准备保存序列号
cseg01:02B1               push    ss
cseg01:02B2               push    di
cseg01:02B3               les   di,
cseg01:02B6               les   di, es:
cseg01:02BB               push    es
cseg01:02BC               push    di                                 ; this
cseg01:02BD               call    @TControl@GetText$qv               ; 读取序列号
cseg01:02C2               lea   di,
cseg01:02C6               push    ss
cseg01:02C7               push    di
cseg01:02C8               lea   di,                    ; es:di ===> "g3dfusername9977"
cseg01:02CC               push    ss
cseg01:02CD               push    di
cseg01:02CE               call    @$basg$qm6Stringt1               ; 生成字符串:"g3dfusername9977"
cseg01:02D3               lea   di,                    ; es:di ===> "g3dfusername9977h9cf8"
cseg01:02D7               push    ss
cseg01:02D8               push    di
cseg01:02D9               call    @Concat$qm6Stringt1                ; 连接字符:"g3dfusername9977" + "g3dfusername9977h9cf8"
cseg01:02DE               call    @$bsub$qm6Stringt1               ; 比较上面连接的字符串是否与输入的序列号相等,不相等返回非0,相等返回0
cseg01:02E3               jnz   short loc_2FB
cseg01:02E5               mov   di, offset aYeahGoodWork         ; "YEAH! Good work!"
cseg01:02E8               push    cs
cseg01:02E9               push    di                                 ; __int32
cseg01:02EA               les   di,
cseg01:02ED               les   di, es:
cseg01:02F2               push    es                                 ; int
cseg01:02F3               push    di                                 ; TControl *
cseg01:02F4               call    @TControl@SetText$q8TCaption       ; TControl::SetText(TCaption)
cseg01:02F9               jmp   short locret_30F
cseg01:02FB ; ---------------------------------------------------------------------------
cseg01:02FB
cseg01:02FB loc_2FB:                                                   ; CODE XREF: sub_208+DB↑j
cseg01:02FB               mov   di, offset aNaahWrong            ; "Naah! Wrong!"
cseg01:02FE               push    cs
cseg01:02FF               push    di                                 ; __int32
cseg01:0300               les   di,
cseg01:0303               les   di, es:
cseg01:0308               push    es                                 ; int
cseg01:0309               push    di                                 ; TControl *
cseg01:030A               call    @TControl@SetText$q8TCaption       ; TControl::SetText(TCaption)
cseg01:030F
cseg01:030F locret_30F:                                                ; CODE XREF: sub_208+A3↑j
cseg01:030F                                                            ; sub_208+F1↑j
cseg01:030F               leave
cseg01:0310               retf    8
cseg01:0310 sub_208         endp

分析完华!

淫神 发表于 2019-5-6 13:06

学习了 共享精神伟大

92013 发表于 2019-5-7 23:09

厉害里楼主

明世嘤嘤嘤 发表于 2019-5-30 20:47

大神真的很牛,我找了好久才找到。值得转播。

明世嘤嘤嘤 发表于 2019-5-30 20:47

大神真的很牛,我找了好久才找到。值得分享。

安尼大大 发表于 2019-7-26 11:25

谢谢分享出来

cpj1203 发表于 2019-11-5 12:01

內容很詳細
页: [1]
查看完整版本: 160个CrackMe之89 -- PhrekTech (16位NE)的详细跟踪和追码过程