目标
使用学习版XYplorer时,打开新标签页和设置菜单,很大几率会出现咖啡图案和提示“There is a problem with your license key!”错误并程序退出。很烦人,今天我们来解决这个问题。
开始
基本工具
XYplorer 23.80
x64dbg
VB Decompiler v11.5 pro(XYplorer是VB写的)
切入点
由于是程序退出,先在x64dbg里下了ExitProcess断点,发现追不回去。在网上发现了这个帖子,他帖子里是跳出对话框,和我们现在的情况不一样,但是帖子里有个截图,如下图
图中弹出对话框CALL前面有一个vbaStrCopy调用引起了我的注意,那就下个bp __vbaStrCopy
断点试试。
启动程序,下bp __vbaStrCopy
断点,发现被断下来的次数也太多了,需要做条件筛选。
观察x64dbg寄存器,发现EDX总是字符串,ESP是调用返回地址
那么编辑vbaStrCopy断点条件如下图
现在点新标签页,直到出现咖啡图案和错误提示。出现咖啡图案后,程序会自动退出。我们再看看x64dbg的日志窗口
“There is a problem with your license key!”字符串已经出现了。按说我们要去追踪这个82A4E地址,但别着急,之前提到的帖子截图里,有个UNICODE字符串"31180B...."和"nep",我们日志这里也出现了,那按照他帖子截图的代码,应该是出现这两个字符串后调用对话框,对话框代码再复制"There is a problem"字符串。因此我们也先追踪717566地址的"31180B...."字符串
看看。
717566地址
删除vbaStrCopy断点,重启程序,按CTRL+G来到717566地址,上下翻动查看,发现这段代码还挺长。不要紧,我们换用VB Decompiler软件。
打开VB Decompiler,载入XYplorer程序,也按CTRL+G来到717566地址,发现717566地址属于switch case 14分支。
代码段中有coffee、picList1.Refresh、Global.Unload字样,那这很明显,这段代码段的功能就是显示咖啡图案并且程序退出。因此我们不能让这段代码运行。
switch分支
看看switch分支选择在哪里。VB Decompiler 717566代码段往上拉到顶,有一句
loc_00716D74: Select Case Me
这句就是分支选择语句。切换到x64dbg,按CTRL+G跳转到716D74地址
00716D62 | 8B46 3C | mov eax,dword ptr ds:[esi+0x3C] |
00716D65 | 8945 D0 | mov dword ptr ss:[ebp-0x30],eax |
00716D68 | 8D48 FF | lea ecx,dword ptr ds:[eax-0x1] |
00716D6B | 83F9 1B | cmp ecx,0x1B |
00716D6E | 0F87 790C0000 | ja xyplorer.7179ED |
00716D74 | FF248D 807A7100 | jmp dword ptr ds:[ecx*4+0x717A80] |
ecx的值是由eax传过来的。在716D62行下断点,CTRL+F2重启程序并运行。
先得知道正常的switch分支段。经我随便尝试,取消最大化时,会触发断点。被断下来时[esi+0x3C]=0x18。0x18的十进制是24,对应switch 24分支,地址是007177BF。
loc_007177BF: Case 24
在Xyplorer里点新建标签页加号,多点几次直至被断下。此时[esi+0x3C]=0xE。E的十进制是14,也就对应switch 14分支。
由于我们不能让14分支代码执行,可以用一个jmp指令强制跳转14分支到24分支。
14分支开头代码:
007174AD | 8B16 | mov edx,dword ptr ds:[esi] |
007174AF | 56 | push esi |
007174B0 | FF92 58170000 | call dword ptr ds:[edx+0x1758] |
007174B6 | 8B06 | mov eax,dword ptr ds:[esi] |
007174B8 | 56 | push esi |
007174B9 | FF90 F0040000 | call dword ptr ds:[eax+0x4F0] |
007174BF | 50 | push eax |
直接修改7174AD行,改为jmp强制跳转到24分支去。
007174AD | E9 0D030000 | jmp xyplorer.7177BF |
007174B2 | 90 | nop |
007174B3 | 90 | nop |
007174B4 | 90 | nop |
007174B5 | 90 | nop |
007174B6 | 8B06 | mov eax,dword ptr ds:[esi] |
007174B8 | 56 | push esi |
007174B9 | FF90 F0040000 | call dword ptr ds:[eax+0x4F0] |
007174BF | 50 | push eax |
好,现在我们重启XY程序运行试试。XY程序目前我们点击新标签页加号,程序已经不会出错。但经常会点击后不打开新标签页。这实际上就是程序从14分支跳到24分支,还不是正常的程序流程。
那么接下来我们从根源上追踪[esi+0x3C]这个代码分支选择数值是哪里来的。
[esi+0x3C] 写入追踪
在
00716D62 | 8B46 3C | mov eax,dword ptr ds:[esi+0x3C] |
行上下断点,XY程序里点几次新标签页,程序断下来。转储跟随[esi+0x3C]地址,如下图
我这里是FFC1FC地址,这个地址每次重启XY程序都不一样。
在FFC1FC地址下断点-硬件写入-字节,发现会被多次断下。那我们再用条件断点筛选一下。我们需要当FFC1FC地址数值为E时断下,因此可以如图设置
设置好后,让XY程序运行,继续点新标签页直至断下
虽然断在了716B8B行,但其实是716B88行修改的数值,ESI+3C就是FFC1FC,EDI=E。
切换到VB Decompiler的716B8B地址,整个函数如下
Private Sub Proc_0_1046_716A10(arg_C, arg_10, arg_14, arg_18, arg_1C) '716A10
loc_00716A5A: If arg_14 Then
loc_00716A68: If global_60 = arg_C Then GoTo loc_00716B59
loc_00716A70: If global_60 = 0 Then GoTo loc_00716B59
loc_00716A79: If arg_C = 24 Then GoTo loc_00716B59
loc_00716A82: var_8004 = frmMain.tmrDelay_Timer
loc_00716AA6: If global_60 Then
loc_00716ABC: ReDim var_30()
loc_00716B1C: var_801C = Proc_3_153_7D66F0("setDelayedAction (" & CStr(arg_C) & "): could not trigger queued because " & CStr(global_60), var_30, 0)
loc_00716B51: GoTo loc_00716C41
loc_00716B56: End If
loc_00716B59: End If
loc_00716B5F: If global_00716C42.Reset <> 23 Then
loc_00716B68: If global_00716C42.Reset <> 19 Then
loc_00716B72: If arg_18 Then
loc_00716B76: If global_00716C42.Reset Then GoTo loc_00716C05
loc_00716B7C: End If
loc_00716B7F: var_8020 = .Proc_0_1047_717AF0(0)
loc_00716BA7: frmMain.tmrDelay.Interval = arg_10
loc_00716BE1: frmMain.tmrDelay.Enabled = True
loc_00716C05: End If
loc_00716C05: End If
loc_00716C0A: GoTo loc_00716C41
loc_00716C40: Exit Sub
loc_00716C41: ' Referenced from: 00716B51
loc_00716C41: ' Referenced from: 00716C0A
End Sub
虽然看不太明白,但大概是设置了一个Delay_Timer,之后设置了frmMain.tmrDelay的值。
这里我尝试将00716B5F行强行jmp,发现无效,因此只有继续回溯看是哪个函数调用了这个代码段。
loc_00716B5F: If global_00716C42.Reset <> 23 Then
00716B5F | 0F84 A0000000 | je xyplorer.716C05 |
谁调用了716A10
删除硬件断点,在716B88行上下条件断点(中断条件EDI==E),继续点XY的新标签页直至断下。
断下后,F8单步运行,直至返回调用函数,可以看到是5C1FF2调用的。
005C1FF2 | FF90 50170000 | call dword ptr ds:[eax+0x1750] |
切换到VB Decompiler,CTRL+G来到5C1FF2地址,代码如下
Private Sub Proc_0_455_5C1F10(arg_C, arg_10, arg_14, arg_18, arg_1C, arg_20, arg_24, arg_28, arg_2C, arg_30, arg_34) '5C1F10
loc_005C1FAC: var_28 = arg_10
loc_005C1FBC: If global_00BCF088 Then
loc_005C1FD5: var_8004 = frmMain.Proc_0_617_5FC560(0, 0, 0, 0, 0, )
loc_005C1FE3: If Proc_12_4_829930(, , ) Then
loc_005C1FF2: var_800C = frmMain.Proc_0_1046_716A10(14, 100, 0, 0, var_5C)
loc_005C1FFE: GoTo loc_005C2FA3
loc_005C2003: End If
loc_005C2003: End If
很明显,只要屏蔽loc_005C1FE3: If Proc_12_4_829930(, , ) Then
即可。
切换到x64dbg,CTRL+G到005C1FE3地址,直接将
005C1FE3 | 74 1E | je xyplorer.5C2003 |
改为jmp跳转
005C1FE3 | EB 1E | jmp xyplorer.5C2003 |
测试效果
此时之前我们设置的断点00716B88 | 897E 3C | mov dword ptr ds:[esi+0x3C],edi |
仍然有效,如果jmp修改有效,则这个断点不会再触发。
现在继续点击XY程序的新建标签页,点了十几次,都没有触发716B88断点,也没有出现点击却不打开新标签页。说明修改成功。
解决XY配置选项错误
新标签页的问题解决了,但打开XY的工具-配置选项时,仍然会出现咖啡图案。
确保上面提到的716B88行条件断点启用,然后打开XY的配置选项,如果成功打开配置选项了,那就关了重新打开,直至断下。
仍然断在716B88行,并且EDI=E,F8单步运行到上级函数(或者CTRL+F9运行到返回也可以),返回到了
006D7A8F | FF91 50170000 | call dword ptr ds:[ecx+0x1750] |
>>>006D7A95 | 68 E87B6D00 | push xyplorer.6D7BE8 |
同样,切换到VB Decompiler,CTRL+G到6D7A95看看。
Private Sub Proc_0_912_6D7890
loc_006D78FE: If global_00BD2468 Then
loc_006D790B: If global_00BD246A = 0 Then
loc_006D7962: var_800C = "Please unlock the trial version under the Help menu." & vbCrLf & Chr(13) & Chr(10) & "If you haven't purchased a license yet: Click OK to open the online order page."
loc_006D79D5: var_8014 = Proc_113_34_B374F0(Proc_70_15_969FC0("Trial Version Expired", 0, var_58, 0, 0), global_00464718, Proc_70_15_969FC0(var_800C, 0, var_5C, 0, 0), 6, 3, -1, 0, var_60, Form())
loc_006D7A17: If var_8014 - 1 + 1 = 0 Then GoTo loc_006D7B9D
loc_006D7A27: var_58 = &H16
loc_006D7A36: If frmMain.menHelpWeb_Click(var_58) >= 0 Then GoTo loc_006D7B9D
loc_006D7A48: var_8018 = CheckObj(Me, global_0046F344, 4776)
loc_006D7A58: End If
loc_006D7A58: End If
loc_006D7A5A: var_801C = Proc_83EC20(global_006D7BE8, 0, 0)
loc_006D7A62: If var_801C = 0 Then
loc_006D7A6F: If global_00BD13CC = 0 Then
loc_006D7A7D: If Proc_12_4_829930(0, 0, 0) Then
loc_006D7A8F: var_8024 = frmMain.Proc_0_1046_716A10(14, 100, 0, 0, var_58)
loc_006D7A9A: GoTo loc_006D7BD5
loc_006D7A9F: End If
loc_006D7AB4: If var_14 <= 7 Then
loc_006D7AD6: var_8028 = frmMain.Proc_0_622_5FDD10(var_14, var_94)
loc_006D7AF6: var_14 = var_14(1)
loc_006D7AF9: GoTo loc_006D7AAA
loc_006D7AFB: End If
loc_006D7AFD: var_802C = Proc_0_913_6D7C00(Me)
loc_006D7B5E: var_8034 = frmConfig.Show 3, var_40
loc_006D7B8F: Set global_00BD3348 = {DDA30E3B-B946-4670-B887C909445C231F}()
loc_006D7B98: var_8038 = Proc_0_913_6D7C00(Me)
loc_006D7B9D: End If
loc_006D7B9D: End If
loc_006D7BA2: GoTo loc_006D7BD5
loc_006D7BD4: Exit Sub
loc_006D7BD5: ' Referenced from: 006D7A9A
loc_006D7BD5: ' Referenced from: 006D7BA2
End Sub
很明显这是先判断、验证序列号,之后执行frmConfig.Show。
修改同样,屏蔽loc_006D7A7D: If Proc_12_4_829930(0, 0, 0) Then
。
x64dbg里CTRL+G跳到006D7A7D地址,je直接修改为jmp即可。
006D7A7D | 74 20 | je xyplorer.6D7A9F |
修改后,打开XY的配置选项,再也不会出现咖啡图案了。
Proc_12_4_829930(0, 0, 0)
是判断序列号是否有问题的函数,有兴趣的同学可以研究一下。
总结
本文没什么难度,XYplorer没加壳,用VB Decompiler几乎和读源代码没什么区别。
修改的地方
由于论坛禁止发成品和补丁,我贴一下特征码应该没问题吧?
x64dbg补丁:
标签页特征码:
E8 ?? ?? ?? ?? 66 85 C0 74 1E 8B 06
改为
E8 ?? ?? ?? ?? 66 85 C0 EB 1E 8B 06
配置选项特征码:
E8 ?? ?? ?? ?? 66 85 C0 74 ?? 8B ?? ?? 8D ?? ?? 52 53
改为
E8 ?? ?? ?? ?? 66 85 C0 EB ?? 8B ?? ?? 8D ?? ?? 52 53