JavaScript逆向补环境

#VibeCoding·九月创作之星挑战赛#

什么是补环境

补环境是JS逆向中的一个重要手段。何谓补环境?浏览器的中的JS是在浏览器环境中执行的,逆向得出的JS是在本地Node.js环境中执行的,这里就有环境的差异。Node.js中是没有BOM和DOM的,为了让在浏览器中可以执行的代码在Node.js中也能正常执行,有时我们就要补上Node.js缺少的一些BOM和DOM对象等,这就是补环境。
在这里插入图片描述

补环境会遇到什么问题

有人说补环境是比较吃经验的,补多了很快就能搞定,新手则半天搞出来还是个有问题的环境。也有人补环境靠猜,运气好就能搞出来,运气不好就不行。还有人会直接使用开源网站上的补环境框架,但是这些框架往往不是万能的,出了问题新手往往又不知道如何把缺少的环境给补上。本文的目的就是利用一个案例,梳理出一套清晰的流程,从零开始补一个完整可用的环境,让大家在面对补环境问题时有一套解决问题的方案。

补环境需要的工具

工欲善其事,必先利其器。这里先介绍几个帮助我们完成补环境的工具。

Web文档

这里我们指的是MDN Web Docs。这里可以查看所有JS对象相关的文档,可以在补环境的时候让我们知道是补一个对象还是函数等等。比如
在这里插入图片描述
我们可以知道createElement()是Document对象的一个方法。

Proxy对象

Proxy可以理解为,在目标对象之前设一层"拦截",外界对该对象的访问,都必须通过这层拦截,可以对外界的访问进行过滤和改写(表示可以用它"代理"某些操作,可以翻为“代理器")。详细文档可以参考链接: Proxy - JavaScript | MDN。这里主要在一个函数中使用Proxy对象在本地代码中去监控代码中某些对象的属性获取和赋值,并打印出日志,从而我们就能得知本地代码缺少了哪些属性和对象。函数代码如下

function get_enviroment(proxy_array) {
    for (var i = 0; i < proxy_array.length; i++) {
        handler = '{\n' +
            '    get: function(target, property, receiver) {\n' +
            '        console.log("方法:", "get  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            // 'if (typeof target[property] == "undefined"){debugger}' +
            '        return target[property];\n' +
            '    },\n' +
            '    set: function(target, property, value, receiver) {\n' +
            '        console.log("方法:", "set  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            '        return Reflect.set(...arguments);\n' +
            '    }\n' +
            '}'
        eval('try{\n' + proxy_array[i] + ';\n'
            + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
            + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
    }
}
proxy_array = ['window', 'document', 'location', 'navigator', 'history', 'screen']
get_enviroment(proxy_array)

proxy_array里是默认需要监控的一些常见重要的浏览器对象,这个数组可以自己增加新的需要监控的对象。

Node.js调试工具node-inspect

node-inspect 是Node.js 的内置命令行调试工具,它是一个纯命令行的调试客户端,但是我们可以配合 Chrome DevTools 进行图形化调试。用法很简单,直接在终端输入node-inspect yourscript.js,然后我们打开chrome的开发者工具
在这里插入图片描述
点击这里产生的按钮,则打开一个新的浏览器调试工具窗口,可以在其中调试yourscript.js。

补环境的流程

这里我们以某冶这个网站为例,介绍下补环境的流程。这里涉及瑞数,我们不过多介绍。整个补环境有个总体思想,就是先保证js运行不报错补一些空方法空对象,然后再根据需要将这些空方法空对象更精细的补充完整。

补充基础环境

这里我们先把一些一定缺少的,或者大概率会被检测的环境补上。其中navigator和location需要去网页上的调试工具获取。

// 基础环境
window = global;
window.top = window;
window.addEventListener = function () { }
window.setInterval = function () { }
window.setTimeout = function () { }
window.clearInterval = function () { }
window.clearTimeout = function () { }
navigator = {
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
}
location = {
    "ancestorOrigins": {},
    "href": "https://www.ouyeel.com/steel/search?pageIndex=0&pageSize=50",
    "origin": "https://www.ouyeel.com",
    "protocol": "https:",
    "host": "www.ouyeel.com",
    "hostname": "www.ouyeel.com",
    "port": "",
    "pathname": "/steel/search",
    "search": "?pageIndex=0&pageSize=50",
    "hash": ""
}


// 核心必删项
try { delete require; } catch (e) { }
try { delete module; } catch (e) { }
try { delete exports; } catch (e) { }

// 推荐删除项
try { delete Buffer; } catch (e) { }
try { delete setImmediate; } catch (e) { }
try { delete clearImmediate; } catch (e) { }

// 原始目标变量
try { delete __dirname; } catch (e) { }
try { delete __filename; } catch (e) { }

加上代理函数

加上文介绍的利用Proxy跟踪对象的函数,默认跟踪window、document、navigator、history、screen这几个对象

// 基础环境
....
// 代理
function get_enviroment(proxy_array) {
    for (var i = 0; i < proxy_array.length; i++) {
        handler = '{\n' +
            '    get: function(target, property, receiver) {\n' +
            '        console.log("方法:", "get  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            // 'if (typeof target[property] == "undefined"){debugger}' +
            '        return target[property];\n' +
            '    },\n' +
            '    set: function(target, property, value, receiver) {\n' +
            '        console.log("方法:", "set  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            '        return Reflect.set(...arguments);\n' +
            '    }\n' +
            '}'
        eval('try{\n' + proxy_array[i] + ';\n'
            + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
            + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
    }
}
proxy_array = ['window', 'document', 'location', 'navigator', 'history', 'screen']
get_enviroment(proxy_array)

加上需要逆向的核心js代码

这里是瑞数相关的代码,这里就不贴上来了,感兴趣可以在这里下载,最终结果是要输出cookie

// 基础环境
......
// 代理
......
// 核心代码
......
// 逆向结果输出
console.log(document.cookie)
// 对外暴露函数
function RS_5() {
    const tm = document.cookie.toString().split(';')[0].split('=')

    return {name: tm[0], value: tm[1]}
}

本地执行js根据日志提示补充环境

使用node-inspect执行js后存在几种不同的情况,我们会采取不同的应对方案,然后再重新使用node-inspect执行js直到得到我们想要的逆向结果

没有任何报错

例如下图

在这里插入图片描述
日志没有任何报错,这时我们需要重点关注属性值类型为undefined的属性。这里我们需要用到上面介绍的两个工具Proxy和node-inspect。首先我们打开代理函数get_enviroment中的注释

'if (typeof target[property] == "undefined"){debugger}' +

这行代码可以在获取的属性值为undefined是断点停住,然后我们使用node-inspect运行我们的js,在浏览器调试工具中调试。在每个undefined的断点处,我们也给真实的网页上的js打上断点,然后刷新页面,对比我们的js和网页上的js相应的属性是否都是undefined。若不是,我们再利用我们介绍的另外一个工具Web文档,这里我们可以查询到document的createElement是一个我们需要补的方法。

// 基础环境
......

// 补充环境
document = {
    createElement(args) {
        console.log('document createElement:', args)
        debugger;
    }
}
// 代理
......

注意,我们在补充的方法里加上了日志和debugger,这些都是帮助我们后续如果需要更精细的补充这些方法时能够帮到我们。

进入到了补充方法的断点中

如果执行js进入到了我们补充的方法加入的debugger处,我们一般需要根据打印的日志,结合Web文档和和node-inspect调试工具,找到浏览器中真实js对应的方法的返回值,并在我们的js中补上同样类型的返回值,例如

在这里插入图片描述
刚刚补上的document的createElement方法,传入的参数是div,我们根据调用堆栈找到方法调用处,并在真实浏览器的同样位置打上断点,执行代码我们可以得知document的createElement方法传入参数为div时,返回值是一个对象
在这里插入图片描述
此时我们在自己的环境中的document的createElement方法增加如下代码

// 基础环境
......

// 补充环境
div = {
    ss: 'div'
}
document = {
    createElement(args) {
        console.log('document createElement:', args)
        if (args === 'div') {
            return div
        }
        debugger;
    }
}
// 代理
......
proxy_array = ['window', 'document', 'location', 'navigator', 'history', 'screen', 'div']
get_enviroment(proxy_array)

这里注意三个细节,第一createElement方法中debugger仍保留放在最后,因为可能createElement会接受不同的参数,我们仍需按照上述方法进入到方法内部并最终添加更多的if-else来返回不同的返回值。第二div对象我添加了一个自定义的属性ss(这里可以随意命名,注意不要有冲突),用来标记这个是什么对象方便调试。第三我们将div对象也加入了代理监控中。

遇到未捕获的异常

如果执行js直接报错,我们可以直接利用node-inspect进行调试,例如出现如下报错

在这里插入图片描述
直接使用node-inspect,在浏览器调试工具中打开“遇到未捕获的异常时暂停”,运行代码调试工具会在异常处暂停
在这里插入图片描述
在这里插入图片描述
这里我们之前添加的自定义属性ss派上用场了!可以知道是我们创建的div对象缺少了getElementsByTagName属性,通过查Web文档得出getElementsByTagName是一个方法,从而补上环境代码

// 基础环境
......

// 补充环境
div = {
    ss: 'div',
    getElementsByTagName(args){
        console.log('div getElementsByTagName:', args)
        debugger; 
    }
}

总结

综上,补环境基本思路就是使用node-inspect和Web文档,再加上代理监控对比本地js的属性和网站真实js的属性,两边保持一致。完整补环境代码参考JS逆向-补环境-欧冶金,因为涉及瑞数仅供参考。

03-26
### 逆向工程与反编译概述 逆向工程是一种通过对软件的目标代码进行分析,将其转化为更高级别的表示形式的过程。这一过程通常用于研究现有系统的内部结构、功能以及实现细节。在Java和Android领域,反编译工具被广泛应用于逆向工程中。 #### Java逆向工程中的Jad反编译工具 Jad是一款经典的Java反编译工具,能够将`.class`字节码文件转换为可读的`.java`源代码[^1]。虽然它可能无法完全恢复原始源代码,但它提供了足够的信息来帮助开发者理解已编译的Java程序逻辑。Jad支持多种反编译模式,并允许用户自定义规则以适应不同的需求。此外,其命令行接口和图形界面使得复杂代码的分析变得更加便捷。 #### Android逆向工程中的JEB反编译工具 针对Android应用的逆向工程,JEB是由PNF Software开发的一款专业级工具[^2]。相较于其他同类产品,JEB不仅具备强大的APK文件反编译能力,还能对Dalvik字节码执行高效而精准的操作。它的核心优势在于以下几个方面: - **广泛的平台兼容性**:除Android外,还支持ARM、MIPS等多种架构的二进制文件反汇编。 - **混淆代码解析**:内置模块能有效应对高度混淆的代码,提供分层重构机制以便于深入分析。 - **API集成支持**:允许通过编写Python或Java脚本来扩展功能并完成特定任务。 #### APK反编译流程及其意义 当涉及到具体的APK包时,可以通过一系列步骤提取其中的信息来进行全面的安全评估或者学习目的的研究工作[^3]。这些步骤一般包括但不限于获取资产目录(`assets`)内的资源数据;解密XML配置文档如`AndroidManifest.xml`定位应用程序启动点;最后利用上述提到的各种专用软件重现整个项目框架供进一步探讨。 ```bash # 使用apktool反编译APK示例 apktool d your_app.apk -o output_directory/ ``` 以上命令展示了如何借助开源工具ApkTool轻松拆卸目标安卓档案至易于探索的状态下。 ### 结论 无论是传统的桌面端还是现代移动端环境里头,恰当运用合适的反编译解决方案都是达成逆向工程项目成功不可或缺的一环。每种工具有各自专精之处,在实际应用场景当中应当依据具体需求做出明智的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SamsongSSS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值