首页
社区
课程
招聘
[原创]WinRAR623的破解小记
发表于: 2023-8-8 22:48 5416

[原创]WinRAR623的破解小记

2023-8-8 22:48
5416

WinRAR623的破解小记

最近看书的时候发现一个 20 年前的练手题——破解 WinRAR。
当年的版本是 3.42,现在的最新版已经是 6.23 了,源码层面应该有比较大的改动,于是手痒想尝试一下。

仅供个人参考学习,请不要拿作它用。

WinRAR官网链接

试用版激活

下载试用版之后,第一次打开会出现一个弹窗,提示用户需要购买。
如果不购买而直接试用,标题栏会带有提示 evaluation copy。

购买弹窗

打开 Help > About WinRAR... ,可以看见试用期是 40 天。

关于弹窗

选择 License > License and purchasing information > Purchasing information ,可以看到注册 WinRAR 的提示。
简单来说就是从官网购买授权,然后下载一个激活密钥 rarkey 放在程序目录下。

If you use WinRAR, you will need to copy the registration key file (rarreg.key) to a WinRAR folder or to %APPDATA%\WinRAR folder folder.
By default WinRAR folder is "C:\Program Files\WinRAR", but it can be changed by a user when installing WinRAR. You can also drag rarreg.key file and drop it to WinRAR window to register.

If the key is archived in a .rar or .zip file, please extract rarreg.key from the archive before copying it.
If archive name is rarkey.rar, another way to install the key file is to open such archive in WinRAR and answer "Yes" to confirmation prompt.

You can place rarreg.key to the same folder as WinRAR installer .exe file if you wish the installer to apply it automatically. Such rarreg.key file is used only if no previously installed key files are found.

IDA 打开 WinRAR.exe,首先看看这个字串的引用。
String 视图中没有它,直接在反汇编页面搜索 Text,找到 4 个函数

  • sub_45E7D0
  • sub_508040
  • sub_514830
  • sub_514E00

进入这几个函数检查字串周围,终于在 sub_514E00 找到这一段(函数名已经改过了)

1
2
3
v29 = (const WCHAR *)GetFileNameW(FileName);
if ( wstrcmp(v29, L"rarkey", 6) )
  sub_4D12A0(0, 0);

sub_4D12A0 最终触发一个模板为 "REMINDER" 的 DialogBox,其处理函数中有一个链接 www.rarlab.com/reminder.php
浏览器打开是 WinRAR 的购买页面(200 多大洋),显然这个 DialogBox 就是第一次打开时的购买弹窗。
根据检测是否是第一次打开的逻辑,可以将代码还原如下

1
2
3
4
5
6
7
8
9
10
11
if ( hasUsedBefore && !a2 )
  return;
/* 省略 */
notRegistered = !sub_47ED80() && (trialDays > 40 || trialDays < 0);
/* 省略 */
if ( notRegistered )
{
  hasUsedBefore = 1;
  v26 = GetFocus();
  DialogBoxParamW(hInst, L"REMINDER", v26, sub_511C20, 0);
}

接下来点入 sub_47ED80 函数,发现它是全局变量 byte_5CC130 的 get 函数。
按 X 键查询引用找到另一个函数 sub_47EFD0,是这个全局变量的 set 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
char sub_47ED80()
{
  return byte_5CC130;
}
 
char __stdcall sub_47EFD0(char a1)
{
  char result; // al
 
  result = a1;
  byte_5CC130 = a1;
  return result;
}

这时候逻辑就呼之欲出了:如果试用天数已经超过了 40 天并且还没有注册,那么就弹出窗口提示购买。
其中注册的信息 isRegistered 最终是储存在 byte_5CC130 中,sub_47ED80 是 getRegisterStatus,sub_47EFD0 是 setRegisterStatus。

猜想 setRegisterStatus 是注册验证的最终归宿,如果让这个函数始终将 isRegistered 设置为 1,就可以绕过验证算法。

1
2
3
4
5
setRegisterStatus proc near
    mov     al, [esp+4]
    mov     isRegistered, al
    retn    4
setRegisterStatus endp

mov al, [esp+4] 占 4 字节,可以改成 xor al, alinc al 各 2 字节,完美。
应用 patch,打开看一下,标题的 evaluation copy 没有了,原本的 40 天提示也变成了字串 "registered to"。

注册成功

修改注册信息

由于我们强行绕过了验证算法,"registered to" 后面并没有用户名。
秉着一贯到底的原则,接下来破解 About 对话框并署名。

由于已经知道弹窗调用的 API 是 DialogBoxParamW,故用 OllyDBG 附加 WinRAR,在 DialogBoxParamW 处下断点。
选择 枚举目标导入表函数 > USER32.dll > DialogBoxParamW ,然后打开 About 对话框,可以看到断点命中。

查看当前的栈

  • 00F05E9B CALL 到 DialogBoxParamW 来自 WinRAR.00F05E95
  • 00DF0000 hInst = 00DF0000
  • 00F5B448 pTemplate = "ABOUTRARDLG"
  • 011A06CC h0wner = 011A06CC
  • 00EF7280 DlgProc = WinRAR.00EF7280
  • 00000000 lParam = NULL

在 IDA 中定位 DlgProc,浏览一遍之后,找到下面这一段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v21 = (const WCHAR *)sub_47ED70();
if ( getRegisterStatus() )
{
  v22 = (const WCHAR *)sub_4B6AC0(0x3C0u);
  SetDlgItemTextW(v4, 102, v22);
  SetDlgItemTextW(v4, 103, v21);
  SetDlgItemTextW(v4, 104, v21 + 256);
}
else
{
  v24 = v21 + 512;
  if ( *v24 )
    SetDlgItemTextW(v4, 104, v24);
}

可以发现这些控件信息都是动态生成的 UTF-16LE 字串。
在没有注册的情况下,104 号控件显示 "40 days trial copy",因此猜测 sub_47ED70 显示和注册用户有关的信息,可能包含用户名;而 sub_4B6AC0 应该是直接输出 "registered to"。
在这些地方下断点,可以验证之。

接下来就是想办法输出自己的署名:该字串可以放在栈上,也可以放在数据节区。这里我选择后者。
.rdata 节后有大概 1024 字节的空间,正好给我们操作,注意要用 UTF-16LE。

1
2
005820A0  xx xx 68 00 61 00 63 00  6B 00 65 00 64 00 20 00
005820B0  62 00 79 00 20 00 D7 72  66 65 50 5B 00 00 xx xx

重命名这段空间为 wAbout,选择 Options > String literals > Currently > UTF-16LE ,字串就插入成功。

1
2
.rdata:005820A2 wAbout:
.rdata:005820A2                 text "UTF-16LE", 'hacked by 狗敦子',0

接下来要想办法将字串地址传入 SetDlgItemTextW。
由于重定位的存在,想要传入位于 .rdata 节的字串地址,得要修改重定位表才行,显然这过于麻烦。
观察到 call ds:SetDlgItemTextW 使用了导入表里的条目,因此这里一定有一条 4 字节的重定位要求,那么只要将字串的地址偷偷替换上去,就可以骗过加载器让其帮我们完成重定位。
这个 call 为 FF 15,占 6 字节,将其替换为 noppush wAbout(68 A2 20 58 00,共 5 字节)。
这时,push 的 4 字节立即数正好对上了 call 的 4 字节立即数。
这样,我们消耗了一条 call ds:SetDlgItemTextW 指令,并将字串传入后面的 SetDlgItemTextW。

1
2
3
4
5
6
7
8
.text:0050763F                 nop
.text:00507640                 push    wAbout          ; "hacked by 狗敦子"
.text:00507640                                         ; 这里本来是上一个 call ds:SetDlgItemTextW
.text:00507645                 nop                     ; lpString
.text:00507645                                         ; 这里本来是 push esi,我们直接 nop 掉
.text:00507646                 push    102             ; nIDDlgItem
.text:00507648                 push    ebx             ; hDlg
.text:00507649                 call    ds:SetDlgItemTextW

应用 patch,大功告成!

署名成功

总结

超级简单啊有没有!


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-1-29 19:22 被狗敦子编辑 ,原因: 修改了一些内容
收藏
免费 4
支持
分享
最新回复 (4)
雪    币: 3573
活跃值: (31026)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-8-9 14:16
1
雪    币: 267
活跃值: (625)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
其实我之前有写过winrar的注册算法
https://notabug.org/doublesine/winrar-keygen/src/master/doc/how-does-it-work.zh-CN.md

没必要给原版程序打patch的
2023-8-9 15:01
1
雪    币: 429
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习了,谢谢分享
2023-8-14 16:29
0
雪    币: 36264
活跃值: (7170)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
补丁的好,就喜欢这种直接干脆有无注册码皆是注册版。
2023-10-6 10:01
0
游客
登录 | 注册 方可回帖
返回
//