文件格式分析
Typora采用的是electron框架构建跨平台桌面应用。从实现方式上来说,其本质还是基于chrome内核的html、js、css构成的应用。
electron打包的项目,最常见的就是 asar
格式的私有编码文件,里面包含文件名、大小、内容偏移量等数据,按文件头部的 json内容 解析即可提取出所有文件。
工具查看
在github上有一个项目讲到关于electron(该方案可以把启动文件编译为node二进制文件,作为启动入口,来保护薄弱的js代码。在项目启动时,将加密后的代码进行解密,交回electron流程进行执行,从而避免上面说的直接解包拿到源代码的可能。)
main
属性就是 electron 项目启动的主入口。 把 main.node
拖到ida中, 分析执行流程。
IDA分析主入口
啊这拖进去就是个dll啊
根据前面分析的,main.node
(.node 文件是 c/c++ 写的扩展文件)是负责解密的,先直接用插件看一下用了什么加密方法
采用的是AES对称加密算法,解密采用的是第三个常量数组。交叉引用看一下
注意到这里,推测这里就是先加载app.asar
的内容,然后调用sub_180003E40
进行解密
主要看一下sub_180003E40
函数,查阅一下资料可知,napi_create_string_utf8
函数目的是生成一个base64编码的字符串,结果存储在最后一个参数。napi_get_named_property
函数是从第二个参数中检索第三个参数的属性名称,获得的该属性的值存放在第四个参数。
主要看下面这部分AES解密,要么找到解密后的缓冲区,要么找到密钥,然后判断教秘方式进行解密。第一种方式要简单一点(因为加密算法不太会)。
分析流程获取解密后的缓冲区
napi_call_function
函数第三个参数就是要调用的函数,第五个参数是参数数组。
动态调试一下,下一个dll断点(note:注意选择调试-高级-隐藏PEB以及忽略所有异常,因为有反调试)
再通过IDA查看函数偏移
定位函数位置
我们知道64位程序调用约定是 rcx rdx r8 r9,超过四个通过栈传递,前面分析v27 = *(a3 + 8),a3也就是r8存储的内容
选择在转储中跟随右下角圈出来的地方,可以看到buffer.from的两个参数。最后没懂这里的操作,查看博客发现这个参数没有发生改变,后续也没有调用相关数据结构,继续往下看AES解密部分,解密首先肯定得获得密文缓冲区。
可以看到申请了32个字节的内存给v32,然后sub_18000B060
对v32和v46今次了操作,v32应该是目标地址,第三个参数是大小,v46就是源地址了。这个函数进去发现是一大堆运算,估计是对v46的一些解密或者hash之类的操作。
可以看到sub_180007000
函数对上一步通过v46得到的v32数据与v10数据进行操作,然后放到v45数组中(感觉应该是密钥,这么复杂应该是为了保护密钥吧),在这个函数中调用了sub_180007800
,这个函数对自己的PE文件进行了一些操作,感觉不是很重要,往下看看。
接下来分析一下sub_180005c00
函数,前面分析的密文地址应该就是在v27中,这个函数主要看它的返回值有什么用。
下面这段汇编的意思是,返回值存储在rax中,将*(rax+8)
的内容给r14,然后减去*rax
,得到一个size
。或许说这两个值是密文开始与结束地址?
可以看到用前面得到的size申请了一块Block
,函数sub_18000B060
对*v12进行操作,存储在Block
中。v12就是前面说的rax。所以分析到这里知道了,v12为密文开始地址,v13为密文大小。
以v13 + 1
的大小 申请了一块内存 v14 , sub_18000B060
对 Block 再次进行操作, 结果放到v14,v14的最后一个字节置为0 ,推测已经把密文转换为字符串了 , 需要一个 NULL 结尾。v15 = v14[v13-1]
, 也就是从v14中取了一个字节的值 ,位置在null字符的前一字节。
关注一下上面的sub_180006AC0
函数,这个函数是我们从解密数组一步步交叉引用找到的函数,v45是sub_180007000
函数对上一步通过v46得到的v32数据与v10数据进行操作得到的。(下面是这位大佬分析的)
1 | _int64 __fastcall sub_180006AC0(v45,block,block_size) |
得到解密后数据
来到调用解密函数的函数,只需要在彻底解密后,送到JS引擎执行的时候,拿到解密的JS代码即可。解密后返回了一个值,这个值作为了调用JS函数的参数
在x64dbg中调试,查看第五个参数也就是rsp+20
的位置。
一步步在转储中跟随,最后得到了解密后unicode形式的JS代码
破解思路
main.node
模块是运行起来后加载的模块。其实要破解的思路就是修改JS代码,修改判断逻辑。下面介绍的几种方法都是为了修改JS代码服务的。
1.调试器加载:在模块加载通知中断下,定位到解密函数下断,修改内存中的JS代码
2.导出表hook:参考进程替换(傀儡进程)技术,创建进程后挂起,由于main.node中的node api是使用框架中的导出api,所以可以替换导出函数为自己的函数,在调用时进行参数判断,如果为JS代码,则修改。
3.Dll劫持:替换main.node,由自己加载真正的main.node并调用,调用时,定位到解密函数并hook,等待JS代码并修改。
4.PE代码注入: 修改框架的PE文件,并加载自己的DLL,加载后进行导出表hook。
总结
总结一下分析思路:框架会加载解密模块main.node
,解密模块对app.asar
进行解密,然后将解密的代码送入JS引擎执行。