Typora最新版逆向分析

查看 65|回复 10
作者:swiftR   
1.app.asar分析
通过大佬们的文章知道Typora是通过加载main.node文件执行的,直接分析app.asar文件就行,asar是electron用的归档文件通过asar解压出来即可
asar extract ./app.asar ./app.unpacked
解压出来后发现atom.js是加密后的文件,这个应该就是typora的js逻辑代码了。直接分析main.node文件看看有没有引用atom.js
2. main.node分析
通过查找main.node中的字符串找到0xE57D这里有对字符串./atom.js的引用

这里实际是调用napi_create_string_utf8创建了一个给js环境使用的字符串./atom.js,

通过静态分析知道v66是创建好的atom.js的字符串地址, 实际是被传入sub_4467中调用napi_call_function来调用js函数

通过动态分析可以知道这里执行的js函数是
(function makeRequireFunction(e) {
    const n = e.constructor;
    function t(e, n) {
        if ("string" != typeof e) throw new TypeError('The "' + n + '" argument must be of type string. Received type ' + typeof e)
    }
    const r = function(n) {
        return e.require(n)
    };
    function o(r, o) {
        return t(r, "request"),
        n._resolveFilename(r, e, !1, o)
    }
    return r.resolve = o,
    o.paths = function(r) {
        return t(r, "request"),
        n._resolveLookupPaths(r, e)
    },
    r.main = process.mainModule,
    r.extensions = n._extensions,
    r.cache = n._cache,
    r
})
参数是字符串atom.js,可以看到实际是require("./atom.js"),了解js的都知道直接require一个加密的js会直接报错,这里猜测应该是重写了require的底层

这里重新创建了一个_compile函数并调用了napi_define_properties来覆盖当前Module的_compile函数,js调用require函数时会调用到_compile函数解析js,应该是在_compile函数中完成对atom.js的解密,这里直接分析_compile函数。
通过动态分析最终会调用到sub_10BF0执行解密并执行atom.js中的代码

这里sub_4647会将atom.js进行解密并返回js的字符串指针。
3.解密流程分析
直接来到sub_B480,拿到atom.js的密文后首先通过base64解密,base64解密完成后去掉了解密后的最后一个字节并创建了新的字节数组。

下面要确定使用的是什么加密,sub_17070会使用到新创建出的密文去解密,每次解密后都会将密文的最后16个字节和解密后的数据异或操作基本可以确定key的最后16个字节是iv,加密方式是AES的CBC模式

直接在main.node偏移B784处断点拿到key和iv,rcx就是第一个参数前32字节是key最后16字节是iv

接下来去CyberChef验证下,解密时注意最后一个字节是用来计算iv的不参与到aes运算的需要删除

拿到js源码后找到验证相关的代码直接patch掉,再打包成app.asar替换原来的
4.破解加密
为了替换原来的atom.js需要通过aes加密后替换回去,这里的难点在于iv的计算是通过原字符串长度和最后一个字节共同计算的,可以看sub_1992的实现,这里为了省事直接调用dll的函数计算iv,把文件长度和最后一个字节改了就能直接成一个iv了
#include
#include
typedef int64_t(*FunctionPtr)(int64_t* array, int64_t p2, int64_t p3);
int main() {
    HMODULE hModule = LoadLibrary(L"./main.node");
    DWORD_PTR baseAddress = (DWORD_PTR)hModule;
    DWORD_PTR functionOffset = 0x1992;
    DWORD_PTR functionAddress = baseAddress + functionOffset;
    FunctionPtr func = (FunctionPtr)functionAddress;
    if (!func) {
        std::cerr
5.打包
万事俱备只欠东风,aes加密有了key和iv,这里最后一个计算iv的字节我用的'0xb7',按下边的步骤来就好
1.先将修改后的js文件进行aes加密
openssl enc -aes-256-cbc -in dump.js -out atom.byte  -iv 9B342BB23D876525D200266E2A2E46F7 -K aes_key
2.将计算iv用的字节写入到文件最后一个字节
printf '\xb7' >> atom.byte
3.base64加密
base64 --wrap=0 atom.byte > atom.js
4.asar打包覆盖原来的app.asar
asar.cmd pack .\appasar\  app.asar --unpack-dir "{main.node}"
最后启动激活完成

字节, 函数

Junm   

谢谢分享
无敌小儿   

太高级了,看不懂
swiftR
OP
  


zade233 发表于 2025-1-14 23:26
大佬大佬,图全挂啦~

不好意思,图补好了
chenxz2008   

对于我这种小白表示一点都看不懂,好好学习学习
mol7732   

感谢提供分享思路
hgd6367385   

太好了,正好在学
52pojie19   

大佬牛逼啊
jkl2024   

感谢分享,学习一下
cszdz999   

最新版?是哪一版?
您需要登录后才可以回帖 登录 | 立即注册

返回顶部