我是逆向练习生,羽墨。
目前最为流行的md文件编辑器,当属Typora,免费,简洁,让人爱不释手
但是去年突然开始收费,让人不知所措。。。
所以今天用简洁的手法,带大家体验破解第一视角
(其实刚收费的时候笔者就尝试过破解,那个时候刚学了几天汇编,打开调试器愣是看了一整天,毛都看不出来。。所以经过一段时间学习,又来了。。这就是不怕贼偷,就怕贼惦 记?。。。)
注:上篇主要为分析过程,下篇主要为实现,详情参见Typora破解之内存破解
用IDA打开 Typora.exe (不建议,可能是我电脑问题,IDA分析了三四个小时。。。),打开后发现程序中有 electron ,V8字样,由于前几天刚好分析了一个IE漏洞,由V8联想到 JavaScript引擎,此时猜测与JS语言有关
打开程序目录,查看一下有没有JS代码,一番查找,看到了asar文件以及node字样,感觉有些眼熟,哪里见过。好的,问一下度娘,百度搜索asar,electron,nodemodules
不搜不知道,一搜吓一跳,原来这是使用 NodeJs的electron框架开发的桌面应用,JS也能写桌面程序了。。。原谅我孤陋寡闻
继续搜索相关资料,得到以下信息:
1.electron 使用了谷歌的 V8引擎以及渲染引擎(意思是这种程序跟个浏览器差不多呗)
2.electron 有主进程与渲染进程 ,通过IPC交换信息,渲染进程只负责渲染(难怪我附加调试有好几个进程。。)
3.Typora.exe是electron框架,基本与开发者的代码无关(修改框架复杂度太高,一般人不会去动,所以别去逆向它了)
4.查看目录发现,在app.asar.unpacked中发现了一个 main.node ,看名字很奇怪,.node文件是啥东西
5.app.asar是打包的JS代码,并且只是简单的打包,没有任何加密措施,且electron也没有代码保护措施(圈起来要考)
根据第五条信息,笔者尝试使用工具对asar进行解包,解包失败不知道什么原因(或许必须使用NodeJS自带的解包工具?但是解包也是加密文件,我又懒得下载nodejs,需要的时候再解吧。。),放进010editor查看,得到文件名与一些密文,此时陷入了僵局
打开typora进程,x64dbg附加,查看有没有有用的信息(刚开始我不知道这个是NodeJS开发,想着通过注册窗口跟踪程序流程,好家伙差点就逆到引擎去了),发现了有好几个进程,后面命令行参数可以看到 渲染 gpu等关键字,并且目录参数指向了app.asar
根据前面得到的信息,这些进程都是由主进程创建的,程序逻辑是由主进程处理,那么附加主进程看一下(没参数的那个)
看到了主进程加载了main.node模块,什么.node也能当dll加载 ??, 或许它本来就是一个dll呢(也不排除加壳后,手动加载的情况)
那么使用PE工具查一下,VS2017编译的64位DLL。好家伙,以为换个名字我就不认识你了
好,到这一步,已知信息:
1.JS文件被加密
2.框架为electron
3.框架会加载main.node模块
4.解析JS脚本的是V8引擎
5.c++支持node api进行开发
根据我们的已知信息,来对程序的整体逻辑进行一个简单的分析
1.框架对代码无保护且修改框架难度过高,V8引擎不支持解析加密的JS代码,那么JS代码如何运行?
站在一个开发者的角度,我可能会想到由框架加载我的解密代码,把js代码解密后送到js引擎去执行就可以了
2.那么解密代码放在哪里合适呢?
既然框架被编译为二进制了,且根据之前的分析,妥妥的c++开发,想要最简单的方法实现解密,加载同样为c/c++编译的二进制代码即可。在Windows平台上,想要加载代码执行,也就剩动态链接库了。由此可以推测,之前找到的main.node,可能就是解密模块。
1.根据之前的推测,main.node负责解密,按照程序员开发习惯,Ctrl CV实现,也就是使用公开的一些算法
2.IDA加载main.node,按照逆向惯例,先搜索一波字符串
此时看到了buffer,base64,app.asar等关键字。猜测一下,app.asar加载到buffer然后进行base64解密(哈哈哈,我都笑了)
3.继续开展搜索工作,base64未免太过简单了吧,使用FindCrypt3插件 ,搜索一下算法常量吧
此时找到了AES的算法常量,前两个是重复的,可能是插件问题。
4.好的,现在面临一个问题,我不懂算法,怎么解密。。。只能去问度娘了,搜索一下AES加密解密原理与 C 实现代码
5.根据搜索得知:
6.对解密常量进行交叉引用跟踪,找到这个函数以后,继续对函数进行交叉引用跟踪
大概经过三四次的跟踪,发现了这个函数,在这个函数里F5查看反编译代码,发现了app.asar字符串的引用
7.此时进行推测,这个函数加载了app.asar的内容,并且调用 SUB_180003E40进行解密
8.跟进SUB_180003E40 进行查看 ,此时发现了base64字符串的引用,推测对buffer进行了base64解密
9.看到很多不认识的API,百度搜索得知,这是Node API,简单去看一下函数功能http://nodejs.cn/api/n-api.html#napi_call_function
10.根据文档得到的信息,参照这一部分Node API,得到了如下信息(猜测调用了此函数)笔者对这语法难以理解,程序的目的是把对象进行base64编码?但是看这代码,base64也没有被当作参数传递进去(不纠结了)
11.暂时先不管node api的语法与功能,继续往下看
看到了这部分的函数调用,进入查看,发现与 C实现AES算法结构相似,推测这部分为AES解密
根据目前的分析,得到如下信息
到了这一步,想要继续破解,首先要得到js代码,有两个办法以及面临的问题
1.分析算法,找到密钥,如果是CBC模式,还需要找到IV ,之后使用解密算法,解密app.asar的js代码
2.分析程序执行流程,找到解密后的缓冲区,直接拷走,得到彻底解密后的js代码
面临的问题:
1.根据之前的分析,我选择第二种获得js代码的办法 , 分析程序执行流程,得到解密后的 JS代码
2.分析解密前的 js函数调用, 由之前的分析得知, 参数有两个 , 在V27的位置, V27是由参数 a3 +8 得来的 。好,动态调式一波
3.x64dbg打开typora.exe,下一个dll断点,根据之前分析,他是动态加载的(多打了个a 懒得换图了)
4.断点设置完成后,断在loadlibrary ,进入模块入口后 ,去IDA计算偏移,定位到前面分析的函数位置
对了,记得使用x64dbg自带的 PEB隐藏功能,并忽略所有异常,这个模块有简单的反调试手段(查看导入表可知,太过简单不分析反调试手段了)
定位函数位置,直接看后4位即可,同样为模块偏移 674A的位置
5.下断后查看decrypt(命名一下方便)函数的参数,x64架构下,函数参数为 rcx rdx r8 r9 rsp+0x20
前四个从左到右,超过四个则入栈,rsp+0x20为起始地址,详情可参考微软x64调用约定
6.r8 = a3 查看 *(a3 + 8)的值 ,这个值为之前分析的 v27 地址, 也就是argv 继续查看指针指向内容
现在得到了buffer.from js函数的两个参数 ,第一个像是密文,第二个没看出来,不是预料中的base64,所以之前的推断貌似是错的?
7.关注一下这两个地址 0000079908482119 00000799083CFEA5 在调用完js函数会有什么改变,直接来到调用的位置,调试器同步来到这个位置
好,来到这个位置再次确认参数, 第五个参数为 rsp+20 , 也就是rax的值, rax为argv ,进入内存查看,得到前面同样的地址 ,也就是 (a3 + 8),步过这个函数,查看对参数的改变
8.执行完后查看刚才记录的位置,好的好的,耍我呢,啥都没变,并且后续也没调用相关数据(或许是最后一个参数返回了一个对象,忘记看了,如果返回的话应该是密文相关的东西,并且放到了某个数据结构中,所以在IDA中没看到直接使用的行为)
继续分析,到了AES解密代码部分,既然是解密,那肯定得把密文的缓冲区拿过来吧
首先看到一串16进制的赋值,v46开头的数组 刚好32个字节,也就是256bit 。 有点像是AES-256的样子了
然后看到申请了 32字节的内存,v32
之后调用了 sub_18000B060函数,对v46 与 v32进行操作, 目测参数为 (目标地址,源地址,大小)
进入 sub_18000B060函数查看 ,一大堆运算 ,根本不想看 ,根据之前的推测,可能是作者感觉直接把密钥放在程序中有些不妥,所以对密钥进行一个类似于解密或hash运算的工作(不展开分析了)
9.继续分析
可以看到sub_180007000 函数 ,参数v45 IDA提示我是一个 char[256]的数组,v32为32字节的地址,v10为一串神秘数据
可以得到一个结论,在经过 v46的一系列运算,得到了一个同样大小 32字节的数据,再把数据 与 v10神秘数据进行操作,放入v45的256字节的数组中(好家伙 这是密钥吗 搞这么复杂)
跟进sub_180007000 查看, 把v10放到了 v45数组的0xF0的位置
之后调用了sub_180007800函数对自己的PE文件有些操作,简单看了下前面的汇编,主要内容为,把v32 放到 v45中,大小为32字节
10.继续分析
接下来看一下sub_180005c00 函数, 使用了v27 ,之前分析出来的密文地址就在v27中, v30没看出来,应该是传出参数,后面用到了
猜测这个函数对密文进行一波操作, 看一下返回值用来做什么,这个伪代码看的头疼,汇编看一下
人工反编译一下 首先确定rax为一个指针 *(rax+8) - *rax
就是这个地址里面存储了两个值,拿第二个值减第一个值得到一个size
此时猜测,这两个值或许是 密文的开始地址与结束地址?
然后用这个size 申请了一块内存 , IDA 命名为 Block , sub_18000B060 之前分析过, 对*v12进行操作, 结果给到Block
把V12代入rax中 , *(v12+8) - *v12
, 结束地址减去开始地址得到 size
由此可以验证猜测, *v12 为密文开始地址 ,V13为密文大小
11.继续分析第三个方框的内容
以v13 + 1 的大小 申请了一块内存 v14 , sub_18000B060 对 Block 再次进行操作, 结果给到v14
v14的最后一个字节置为0 ,推测已经把密文转换为字符串了 , 需要一个 NULL 结尾
由上可得 v15 = v14[v13-1] , 也就是从v14中取了一个字节的值 ,位置在null字符的前一byte
12.继续分析sub_180006AC0
可以看到,sub_180006AC0 的参数 , v45(256字节数组),block ,v13
结合之前对 sub_180007000的分析,可以得知, 目前v45的状态 ,v45[0-31]为32字节的类似密钥的东西 v45[0xF0] 为 v10的神秘数据 ,v13为Block大小
跟进简单查看 ,查看后感觉可读性不好,笔者对照汇编代码,重新修改了一下反编译代码
根据目前的分析,可以推测 ,sub_180006AC0函数为 主要的解密算法函数,看着像是 AES CBC模式,因为对算法不熟悉,大胆猜测一下
key存放在v45中, 前32字节 ,也就是256位 , iv存放在 block+v5中 (不清楚对不对)
13.继续分析剩下的内容
好的好的,看着有点头疼,后面的代码大概意思就是,又对解密后的数据进行了一系列操作,最后返回了一个缓冲区
读者感兴趣可以自行分析,实在是写不动了
1.根据前面的分析,我们已经大致了解了程序流程,来到调用解密函数的函数,只需要在彻底解密后,送到JS引擎执行的时候,拿到解密的JS代码即可
2.根据上层调用代码,可以得到,解密后返回了一个值,作为调用JS函数的参数 ,定位到678F偏移处,x64dbg同步定位
3.断下后查看v28的内容 , RSP+20 的位置,然后继续查看这个指针的指针的内容,最后得到了解密后unicode形式的JS代码
4.把内容拷走,拿到010editor,把00去掉,变成ascii形式,检查一下得到的数据
看起来跟密钥有关的一串字符编码数据
搜索一下license相关的数据, 找到不少,看起来也像是代码,应该没问题
如果懂算法与NodeJS,可以通过分析,找到关键的key等数据,对app.asar进行解包解密操作得到JS代码进行修改后,打包回去即可
可能遇到的问题:对app.asar进行完整性校验
简单说几种思路,由于main.node是后加载的模块,所以内存破解有些难度
可能遇到的问题:对main.node或者框架进行完整性校验,更加强大的反调试手段
方法还有很多,不再一一列举,这里只能提出思路
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-5-17 17:36
被yumoqaq编辑
,原因: