大神论坛

找回密码
快速注册
查看: 370 | 回复: 0

[macOS] 对 Mac端的 QCAD专业版 逆向破解教程 附注入库补丁源码

主题

帖子

0

积分

初入江湖

UID
599
积分
0
精华
威望
0 点
违规
大神币
68 枚
注册时间
2023-09-16 15:12
发表于 2023-10-28 16:55
本帖最后由 燃病软 于 2023-10-28 16:55 编辑

破解 QCAD for Mac

因为今天要修改一个图,不能有其他公司的名字,光改PDF没用,所以只能从dxf文件下手。搜来搜去又不想装Autodesk CAD,太麻烦了,所以随便找了个QCAD破一下,好像这软件还挺好用。

1. 分析

没激活前是这样的

充斥着Trial等字样,右下角还有一个审判按钮。
其次,使用大约30-40分钟左右,会有退出弹窗

并显示我试用到期。
并且最重要的是,导出图纸到pdf有一很大的水印,如果没有水印我可能就不会破解他了。

我打算以“This is a trial version of”为切入点,暴力搜索所有文件试试。

就让审判来得更猛烈些吧!

2. 审判

常规搜索我们是搜索不到的。所以我们需要用一些特别的手段来暴力搜索所有二进制里的文件。
因为我们知道,英文字符串大部分就是原文在二进制中,所以我们暴力搜索一定能搜索到相关的内容。

find /Applications/QCAD.app/Contents -type f -exec sh -c 'strings "$1" | grep "This is a trial version of" && echo "$1"' sh {} \;

差不多也就搜索到这了。
我们发现第一个文件和第二个文件都存在这个字符串,那么我们就着重搞这两个文件。
首先打开第一个:

因为只有几百K,分析速度很快,我们一搜,还真有。
那这下不得不跳过去看下了:

糟糕糟糕OMG,魔法怎么施灵辣!
却说这大魔导师落叶不愧是强者,事情发展出乎意料却也不急,冷笑着说道:“尔等不过区区Qt多语言,待我略施小技,必取你首级!”

那QCAD本就是小作坊出产,哪里见过这等阵仗?当下心中狂跳,嘴上却是硬道:“兀那乱臣贼子!休要在此胡言乱语妖言惑众,我等在此会你一二,看你还有什么手段?”

那大魔导师落叶出道以来哪里受到如此挫折?闻得QCAD猖狂挑衅,当下却是道心不稳,怒从心中起,恶言道:“区区蝼蚁也敢在本魔导师面前螳臂当车?死来!”

当下落叶长啸一声,法杖高举,大喝道:“天圆地方乾坤倒转,禁术·Hopper disassembler,启动!”
说时迟那时快,原本万里无云的蓝天诡异的传出一声巨响,那QCAD当即被一个邪异蓝色光罩团团围住,进出不得。QCAD惊怒交加,一时不察竟受制于人,怒不可遏:“贼子敢尔!”

那大魔导师落叶也不说话,匆忙的结印,操作着究极禁术HP疯狂攻击:

此时令所有人震惊的一幕出现了,那究极禁术竟然对QCAD毫发无伤!

QCAD原本还略有慌乱,此时发现自己没有受到任何伤害,眼珠一转便哈哈大笑:“黄毛小儿,想害我?再去修炼几年!”

”可恶!“落叶面如金纸,噗的吐出一口黑血,原来这禁术无法施展太久,否则会反噬施术者。一时之间,竟然僵持不下。

远处,迦南世家伯爵迦南二世皱眉道:”此子成长速度太快,区区魔导师境界的魔力就已经能越级施展禁术,之前还得罪了他,如此看来只好斩草除根了!告诉QCAD大师,如果能当场斩杀魔子落叶,我们迦南世家将不遗余力的帮助他成为璃月大陆的风神代言人!“ 说到最后,竟然已经是咬牙切齿。
两名骑士对视一眼,不敢怠慢,当即领命去设法通知QCAD大师。

那QCAD见这所谓的大魔导师兼魔子对自己毫发无伤,更是轻视三分,又收到迦南二世伯爵送来的密信,当即冷笑一声:”黄口小儿,你的死期到了!“

当即吟唱法术,欲要突破牢笼。

此时落叶本就被反噬遭到重创,哪里敢让QCAD突破?拼了命维护牢笼,一时之间却也想不到别的办法。

此时,落叶突然想到这里:

这可不就是苦苦寻觅么线索?查不到又如何?还不是漏出了破绽?
当即狂喜,迅速xref过去一看:
果然发现这arg0+0x28偏移处的地址值若为0,则跳转到7a73,也就是trial模式。

那么就让他不跳转吧!
落叶默默想着,迅速回到IDA:

找到checkLicense函数,进去发现若v4 = QVariant::toInt((QVariant )&v8, 0LL);的值大于9,则走一串神秘的js脚本,并传递了this的指针地址,盲猜返回上级函数会让if ( ((_BYTE *)this + 40) )判断成立,因为默认v4得到的值是NULL,也就是0,所以这将是我们的突破口!

v4 = QVariant::toInt((QVariant *)&v8, 0LL);
QVariant::~QVariant((QVariant *)&v8);
QVariant::~QVariant((QVariant *)&v9);
*((_BYTE *)this + 40) = 0;
if ( v4 > 3 )
{
if ( v4 > 9 )
QTimer::singleShot(
(QTimer *)"(_0x3c3852,_0x10a608){var _0x36519d=a0_0x3651();return a0_0x4121=function(_0x4121b1,_0x35735a){_0x4121"
"b1=_0x4121b1-0xd3;var _0x3e8b74=_0x36519d[_0x4121b1];return _0x3e8b74;}...............省略",
(int)this,
(const QObject *)"1uninitSlot()",
v5);

于是想办法将这v4 = QVariant::toInt((QVariant *)&v8, 0LL);改成v4 = 10:

光标放在这一行,切换到HexView:

自动定位到对应的十六进制地址,右键点击菜单Edit...修改为:

最后Apply即可。
返回反汇编即可看到

被修改成了赋值0xa,加一个NOP,nop是为了对齐指令。否则ida分析会出现断层。
最后保存到文件,重启app看效果。

却说那落叶狂喜道:“找到你了!”操作法器ida狠狠的向QCAD攻去!

QCAD惨叫一声,怒吼:“你敢伤我!我今日必将你碎尸万段!”

没想到此时又生事端,那签名没有通过,落叶情急之下确实连这等基础操作也忘了,连忙补上:

重新打开QCAD:

还是这样,但至少能打开了。
刚才修改的是一个插件,所以我们看下关于:

可以看到还是试用版本,落叶满脸自信最终被打脸,这让要脸的魔子如何忍受?当即两眼发红,恶声道:“老贼,受死!”
当即也不恋战,看到这里:

大大的Trial赫然在目。
这次,我看你怎么躲!落叶默默冷笑。

重新暴力搜索,发现仍然在这个文件内,当即冷笑道:“老贼,我已找到你的弱点,你乖乖引颈就戮吧!”

说完顾不得QCAD的狂骂,回到IDA迅速搜索:

果然搜索到了!
进一步看:

LABEL94来自LABLE89,89来自LABEL_79...一路溯源到:LABEL_56。

LABEL_56:
if ( !(unsigned __int8)RPluginBase::isTrial((RPluginBase *)a2) )
{
v50 = (volatile signed __int32 *)QString::fromAscii_helper((QString *)"NameOverride", dword_C, v25);
QVariant::QVariant((QVariant *)&v49, "QCAD Professional");
QMap<QString,QVariant>::insert(v2, &v50, &v49);
goto LABEL_107;
}
v50 = (volatile signed __int32 *)QString::fromAscii_helper((QString *)"TrialExpiredReason", &dword_10[2], v25);
QVariant::QVariant((QVariant *)&v49, (const QString *)(a2 + 48));
QMap<QString,QVariant>::insert(v2, &v50, &v49);
QVariant::~QVariant((QVariant *)&v49);
v27 = v50;
if ( *v50 != -1 )
{
if ( *v50 )
{
if ( _InterlockedDecrement(v50) )
goto LABEL_62;
v27 = v50;
}
QArrayData::deallocate(v27, 2LL, 8LL);
}
LABEL_62:
v50 = (volatile signed __int32 *)QString::fromAscii_helper((QString *)"TrialExpired", dword_C, v26);
QVariant::QVariant((QVariant *)&v49, a2[41]);
QMap<QString,QVariant>::insert(v2, &v50, &v49);
QVariant::~QVariant((QVariant *)&v49);
v29 = v50;
if ( *v50 == -1 )
goto LABEL_67;
。。。省略
LABEL_74:
v34 = v47;
if ( *v47 == -1 )
goto LABEL_79;
if ( *v47 )
{
if ( _InterlockedDecrement(v47) )
goto LABEL_79;
v34 = v47;
}
QArrayData::deallocate(v34, 2LL, 8LL);
LABEL_79:
v35 = v45;
if ( *v45 == -1 )
goto LABEL_84;
if ( *v45 )
{
if ( _InterlockedDecrement(v45) )
goto LABEL_84;
v35 = v45;
}
QArrayData::deallocate(v35, 2LL, 8LL);
LABEL_84:
v36 = v46;
if ( *v46 == -1 )
goto LABEL_89;
if ( *v46 )
{
if ( _InterlockedDecrement(v46) )
goto LABEL_89;
v36 = v46;
}
QArrayData::deallocate(v36, 2LL, 8LL);
LABEL_89:

终于找到!

if ( !(unsigned __int8)RPluginBase::isTrial((RPluginBase *)a2) )
{
v50 = (volatile signed __int32 *)QString::fromAscii_helper((QString *)"NameOverride", dword_C, v25);
QVariant::QVariant((QVariant *)&v49, "QCAD Professional");
QMap<QString,QVariant>::insert(v2, &v50, &v49);
goto LABEL_107;
}

如果RPluginBase::isTrial返回0就是专业版,否则就是试用版。
此番终于找到重点,落叶狂笑道:“老狗,你死期已至也!”
那QCAD被人发现要害,终于变了脸色,口气也软了三分:“放过老儿,我可以保你无恙走出这里!”

落叶闻言冷笑道:“三年...你知道这三年我怎么过的吗?走出?我就是来复仇的!”
大魔导师的气势此时终于完全散发出来,强大的威压让璃月大陆上的智慧生灵纷纷跪下祈祷,不知又是哪位法神震怒。

当下落叶也不啰嗦,进入isTrial函数,将下面的语句强制返回0:

bool __fastcall RPluginBase::isTrial(RPluginBase *this)
{
return *((_BYTE *)this + 40) == 0;
}

将这个函数头开始直接覆盖为6a 00 58 c3,保存修改到文件,重新运行试试:

神奇的一幕出现了,虽然还有激活弹窗,但是这里已经成功授权!

包括这里也变成了授权状态,和上面的试用版本截然不同!

于是,落叶记住了两个函数:checkLicense 和 isTrial,让其他的文件中的这两个函数也统统消失吧!
对照

有激活字样的文件名,一一修改这里不再赘述。

总共需要修改这9个文件,同样的checkLicense 强制
mov     ebx, 0Ah
nop

isTrial变成6a 00 58 c3.

却说那奄奄一息的QCAD看着缓缓走来的魔子落叶,惊恐道:“不...!不能杀我!我背后的迦南世家不会放过你的!”

落叶毫无反应,淡漠的看着挣扎的QCAD冷冷道:“三年前,曾经你们迦南家族从我手里抢走的,我要亲手一步步夺回来!”

目光如电,看向迦南二世隐匿的方向,迦南二世心头一惊,连忙低喝道:“发现我们了,快走!此子绝不是区区魔导师实力!暂且避其锋芒!”
临走前仇恨的看着魔子落叶:“早晚要除掉你这个孽障!”,一咬牙,化为一阵青烟遁走。

落叶看着求饶的QCAD,一脚踏下,只听得喀喳一声,世间一片寂静,再无声响。

重新打开QCAD看下:

成了激活所有功能,导出pdf也没有水印,弹窗也没有了,非常完美。

3. 结束

这篇教程不涉及到LLDB/补丁注入破解,尽管我是lldb+注入hook一路调试过来的,本篇教程仅用于给X1a0He同学以及其他同学用于入门逆向学习用,所以讲的非常精炼,省略了很多繁杂的步骤。
最后留了一个30分钟强制关闭的漏洞,请各位同学根据上面的思路自己处理掉吧!

4. LLDB 解决破解不完全依然提示弹框

我们lldb /Applications/QCAD.app/Contents/MacOS/QCAD启动后
可以看到:
23:46:18: Debug:    loading plugins...
23:46:19: Debug:    RDwgPlugin::init: trial
23:46:19: Debug:    RProScriptsPlugin::init: trial
23:46:20: Debug:    RTracePlugin::init: trial
23:46:20: Debug:    loading static plugins...
Warning:  Populating font family aliases took 632 ms. Replace uses of missing font family "Sans" with one that exists to avoid this cost.
Warning:  Cannot read file ':/scripts/Pro/Block/CreateLibraryItem/CreateLibraryItem-inverse.svg', because: Expected '?', '!', or '[a-zA-Z]', but got '<'. (line 1)
23:46:22: Debug:    loading plugins...
23:46:22: Debug:    loading static plugins...
23:46:23: Debug:    loading plugins...
23:46:23: Debug:    loading static plugins...
23:46:23: Debug:    openFiles:

这三个依然是trial阶段,所以我们要patch掉:
RDwgPlugin::init: trial
RProScriptsPlugin::init: trial
RTracePlugin::init: trial

可以看到结果是这样子,所以我们ida打开这个文件直接搜:

可以看到这里存在字符串

仔细一看,这里checkLicense里面的修改根本没起作用,那么我们只好暴力点,直接全部强制条转:
我们将其jz跳转改为nop即可:

改后:

把指令所在位置强制改为9090,即可nop。

保存到文件,启动:


成功解决第一个,那么第二个第三个我就不多说了,大家去试试吧!


注:若转载请注明大神论坛来源(本贴地址)与作者信息。


下方隐藏内容为本帖所有文件或源码下载链接:

游客你好,如果您要查看本帖隐藏链接需要登录才能查看, 请先登录

返回顶部