总览:JS逆向技巧总结

总的来说,JavaScript 逆向可以分为三大部分:寻找入口、调试分析和模拟执行。下面分别进行介绍。

一,寻找入口

一个网站加载了很多 JavaScript 文件,那么怎么从这么多 JavaScript 里面找到关键的位置,那就是一个关键问题。这就是寻找入口。

但开始寻找前,建议先做以下工作:

  • 判断参数是否需要逆向,可不可以直接复制参数

  • 加密参数是否由之前的请求返回

  • 按加密字符串的特征进行尝试,也许会有意外收获

下面介绍寻找入口时常用的手段。

1.1 巧用搜索

搜索是有一定技巧的,比如加密关键词是 signature,可以搜索 signature、“signature”、signature=、signature: 、sign
还可以搜索URL中的某些关键字、headers[ 等。
若遇到请求参数或者响应加密的情况,但通过跟栈找不到明文,有可能加密是在拦截器中处理的(请求拦截器Vt.interceptors.request、响应拦截器Vt.interceptors.response),因此可以搜索interceptors关键字。

PS:
在已有的页面链接中寻找其他页面的连接(从银行的详情页中搜索列表页链接):

  • 搜索关键字寻找
  • 搜索现有的url的某一部分关键字,有很大可能这些关键字会放在一起
  • 在这里插入图片描述

1.2 Ajax断点+调用栈

它可以在发生 Ajax 请求的时候触发断点。要设置断点,就要先观察 Ajax 请求,假设请求的 URL 为:https://spa2.scrape.center/api/movie/?limit=xxx&page=99

可以看到,URL 中包含 /api/movie ,因此可以填写 /api/movie ,当 Ajax 请求的 URL 包含填写的内容时,就会进入断点停止。

在这里插入图片描述

1.3 Initiator call stack

Initiator 主要是为了监听请求是怎样发起的,通过它可以快速定位到调用栈中。栈的调用顺序为从下到上。

在这里插入图片描述

1.4 事件监听+调用栈

有的时候找不到参数位置,但是知道它的触发条件,此时可以使用事件监听器进行断点。然后结合调用栈进行定位。

1.5 Hook 关键方法

Hook 技术又叫钩子技术,指在程序运行的过程中,对其中的某个方法进行重写,在原先的方法前后加入我们自定义的代码。相当于在系统没有调用该函数之前,钩子程序就先捕获该消息,得到控制权,这时钩子函数既可以加工处理该函数的执行行为,也可以强制结束消息的传递。

要对 JavaScript 代码进行 Hook 操作,就需要额外在页面中执行一些有关 Hook 逻辑的自定义代码,这里推荐一个浏览器插件,叫做 Tampermonkey,中文叫 油猴,利用它,可以在浏览器加载页面时自动执行某些 JavaScript 脚本。由于执行的是 JavaScript ,所以我们几乎可以在网页中完成任何我们想实现的效果,如自动爬虫、自动修改页面、自动响应事件等。

下面以 https://login1.scrape.center/ 网址为例,进行 base64 方法的 hook,以下为油猴中的脚本:

// ==UserScript==
// @name         HookBase64
// @namespace    https://login1.scrape.center/
// @version      0.1
// @description  Hook Base64 encode function
// @author       Vahan
// @match        https://login1.scrape.center/
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    function hook(object, attr){
        var func = object[attr]
        object[attr] = function(){
            console.log('hooked',object,attr)
            var ret = func.apply(object,arguments)
            debugger
            return ret
        }
    }
    hook(window,'btoa')
})();

如果观察出一些门道,可以多使用这种方法来尝试,如 Hook encode 方法,decode 方法,stringify 方法,log 方法,alert 方法等。

还可以利用浏览器中的snippet来进行hook, 当创建好一个 Snippet 片段后,可以从任何页面访问和运行它们(浏览器会一直保存创建的snippet)。当你运行一个片段,它从当前打开页面的上下文中执行(即使这个页面中的链接变化也依然生效)。
如何创建一个 Snippet 片段?
先打开开发者工具,进入到 Sources (源代码)面板,在导航栏里面选中 Snippets(代码段),点击 “New snippet(新建一个代码块)” ,输入你的代码,按Ctrl+S保存,run运行。

在这里插入图片描述

这两种方式都可以进行hook,油猴在应用方面的扩展性更强,但如果只是Hook调试用,snippet会更方便。

下面介绍利用snippet定位时的常用Hook脚本:
1,定位header关键字:

(function (){
    var org = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key,value){
        //在请求中发现某个关键字,断点
        if(key=='value'){
            debugger
        }
        return org.apply(this,arguments)
    }
})()

这段代码的目的是在设置包含特定键的HTTP请求头时,自动触发一个断点。这可以用于调试或监视特定的网络请求头设置。

2,定位url关键字:

(function () {
    var open = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function (method, url, async) {
        if (url.indexOf("key") != -1) {
            debugger;
        }
        return open.apply(this, arguments);
    };
})();

这段代码的目的是在发送包含特定字符串(在这个例子中是"key")的HTTP请求的URL时,自动触发一个断点。这可以用于调试或监视特定的网络请求。

3,定位cookie关键字:

(function() {
    "use strict";
    var cookieTemp = "";
    Object.defineProperty(document, "cookie", {
        set: function(val) {
            if (val.indexOf("cookie的参数名称") != -1) {
                debugger ;
            }
            cookieTemp = val;
            return val;
        },
        get: function() {
            return cookieTemp;
        }
    })
}
)();

Object.defineProperty(document, “cookie”, { … }) 这行代码使用Object.defineProperty方法修改了document对象上cookie属性的行为。现在,每当尝试获取或设置document.cookie的值时,都会分别调用定义的getter和setter函数。
这段代码演示了如何使用JavaScript的Object.defineProperty方法来拦截和自定义对document.cookie属性的访问和修改行为。

4,定位JSON.stringify、JSON.parse:

'''请求是密文'''
(function() {
    var _stringify = JSON.stringify;
    JSON.stringify = function(ps) {
        console.log("Hook JSON.stringify ——> ", ps);
        debugger;
        return _stringify(ps);  // 不改变原有的执行逻辑 
    }
})();

'''响应是密文'''
(function() {
    var _parse = JSON.parse;
    JSON.parse = function(ps) {
        console.log("Hook JSON.parse ——> ", ps);
        debugger;
        return _parse(ps);  // 不改变原有的执行逻辑 
    }
})(); 

一般用于请求和响应是密文。当请求加密时,会先将对象进行JSON.stringify处理,使对象变成json字符串格式,然后进行加密处理;当响应解密时,会对解密后的字符串进行JSON.parse处理,使json字符串变成对象,方便后续调用。

若想从案例中练习这些hook技巧,这里推荐大家阅读:https://blog.csdn.net/randy521520/article/details/134091541

二,调试分析

找到入口之后,我们需要搞清楚里面的逻辑是怎样的、里面调用了多少加密算法,经过了多少变量赋值和转换等,以便后面进行模拟调用或者逻辑改写。在这个过程中,我们主要借助浏览器的调试工具进行断点调试分析,或者借助于一些反混淆工具进行代码的反混淆等。

以下总结了一些调试分析时有用的技巧:

扣代码:扣代码时,同级目录的代码要扣全;

调试按钮:用的最多是逐行调试:

在这里插入图片描述

调用栈:可以看到 $_CCBk 的上一步是 $_CGII ,再上一步是 (anonymous)

在这里插入图片描述
在浏览器断点调试时,当你看到“call stack”中包含promise.then,这通常意味着当前的执行上下文是在处理一个Promise对象的.then()方法中的回调函数。
Promise是JavaScript中用于异步操作的对象,可能是一个网络请求、文件读取、定时器等。
.then()方法是Promise对象上的一个方法,用于注册当Promise状态变为fulfilled时要调用的回调函数,以及可选的当Promise状态变为rejected时要调用的回调函数。
这个方法返回一个新的Promise实例,这个新的Promise实例的状态由回调函数的执行结果决定。
在这里插入图片描述

改写 JavaScript 文件:

我们知道,一个网页里面的 JavaScript 是从对应服务器上下载下来并在浏览器执行的。有时,可能想要在调试的过程中对 JavaScript 做一些更改,比如说有以下需求:

  • 发现 JavaScript 文件中包含很多阻扰调试的代码或者无效代码、干扰代码,想要将其删除
  • 调试到某处,想要加一行 console.log 输出一些内容,以便观察某个变量或方法在页面加载过程中的调用情况。在某些情况下,这种方法比打断点调试更方便
  • 调试过程遇到某个局部变量或方法,想要把它赋值给 window 对象以便全局可以访问或调用
  • 在调试的时候,得到的某个变量中可能包含一些关键的结果,想要加一些逻辑将这些结果转发到对应的目标服务器

浏览器中的 Sources 面板的 Overrides 功能可以支持实现。在 Overrides 上选定一个本地的文件夹,用于保存需要更改的 JavaScript 文件。更改后保存,可以发现在选定的文件夹下生成了新的 JavaScript 文件,它用于在浏览器加载该 JavaScript 文件时进行替换。

在这里插入图片描述

注意:若使用其他网站调试时生成的本地文件夹,则对于这个网页来说可能不能成功保存 JavaScript 文件,需要新创建一个文件夹。

三,模拟执行

搞清楚整个逻辑后,就需要对整个加密过程进行逻辑复写或者模拟执行,以把整个加密流程模拟出来。

3.1 Python 改写或模拟执行

如果整体逻辑不复杂的话,可以尝试用 python 把整个加密流程实现一遍。

如果整体逻辑较复杂,可以使用 python 来模拟调用 JavaScript 的执行。这种方法需要抠出加密代码及需要的运行环境(可能需要补浏览器环境)。

# pip install PyExecJS
# 前提是已经安装nodejs,相当于通过python来调用nodejs执行js文件
import execjs

#1.实例化一个node对象
node = execjs.get()

#2.js源文件编译
ctx = node.compile(open('./wechat.js',encoding='utf-8').read())

#3.执行js函数,wechat.js 文件中有 getpwd() 函数
funcName = 'getpwd("{}")'.format('123456')
pwd = ctx.eval(funcName)
print(pwd)

3.2 Nodejs 模拟执行 + API

与 python 调用几乎一样,都是将算法代码与调用代码抠出来放在一个 js 文件中,但 nodejs 调用的兼容性更好,需要修改调整的地方较 python 调用少,只是为了让 python 代码能使用,还需要通过 express 暴露成 HTTP 服务,从而实现跨语言的调用。

有了方式一,这种方式作用不大。

3.3 浏览器模拟执行

在算法难以实现时,可以使用 playwright 改写 js 文件,然后进行调用。这种方法只需定位到算法的位置,然后将加密函数挂到全局 window ,当作黑盒调用即可。

安装 playwright :

pip3 install playwright 
playwright install

在这里插入图片描述

案例:https://spa2.scrape.center/

想要获取电影数据,就需要分析出 token 的加密逻辑:
在这里插入图片描述

使用 XHR 断点,定位到核心加密算法的位置如下:
在这里插入图片描述

将修改后的整个 js 文件保存到本地,命名为 chunk.js:
在这里插入图片描述
在这里插入图片描述

python 调用代码如下:

from playwright.sync_api import sync_playwright
import time
import requests

BASE_URL = 'https://spa2.scrape.center/'
INDEX_URL = BASE_URL + '/api/movie?limit={limit}&offset={offset}&token={token}'
MAX_PAGE = 10
LIMIT = 10

context = sync_playwright().start()
browser = context.chromium.launch(headless=True)
page = browser.new_page()
page.route( # 指定路由
    "**/js/chunk-10192a00.243cb8b7.js",
    lambda route: route.fulfill(path="./chunk.js")
)
page.goto(BASE_URL)

def get_token(offset):
    result = page.evaluate('''() => {
    return window.encrypt("%s","%s")
    }''' % ('/api/movie', offset))
    return result

for i in range(MAX_PAGE):
    offset = i * LIMIT
    token = get_token(offset)
    index_url = INDEX_URL.format(limit=LIMIT,offset=offset,token=token)
    response = requests.get(index_url)
    print('response',response.json())

在这里插入图片描述

BASE_URL的值来自下图位置:

在这里插入图片描述

这种方法似乎是一种神技,以后实操用起来。

emm,在对今日头条案例的_signature使用这种方法时,未成功,返回None。暂时没有找到是哪里出了问题。

下面这种形式更加通用:

from playwright.sync_api import sync_playwright


BASE_URL = 'https://static.geetest.com/static/js/slide.7.9.2.js'

context = sync_playwright().start()
browser = context.chromium.launch(headless=True)
page = browser.new_page()
page.route(
    "**/static/js/slide.7.9.2.js",
    lambda route: route.fulfill(path="chunk.js")
)
page.goto(BASE_URL)


result = page.evaluate('''() => {
return window.vahan;
}''')
print(result)
### 回答1: 我没有具体的指导,但是可以给你一些建议:首先,可以研究一些已经实现的逆向JS的解决方案,然后根据你的需求来实现你所需要的功能;其次,可以在网上搜索一些关于Python和JS逆向的教程,并且结合实践来掌握这些技术;最后,可以在社区中寻求帮助,可以得到其他更有经验的人的建议。 ### 回答2: Python和JavaScript是两种常见的编程语言逆向工程指的是通过分析和理解已编译的代码来了解其内部机制和工作原理。下面将简要介绍关于Python和JavaScript逆向教程。 对于Python逆向来说,首先需要了解Python的字节码和内部数据结构。可以通过使用dis库来反编译Python代码并查看生成的字节码指令,进而分析其执行过程。另外,还可以使用反汇编工具来反编译Python的.pyc文件,以便更深入地研究其运行机制。 在JavaScript逆向教程中,首先需要学习JavaScript的语法和基本概念。然后,可以使用开发者工具来查看JavaScript代码的执行过程和调试信息。此外,还可以使用JavaScript反编译工具来将经过压缩或混淆的JavaScript代码还原为可读性较高的形式,以方便分析和理解。 不管是Python还是JavaScript逆向,重要的是要有良好的编程基础和对底层机制的理解。通过阅读相关文档、学习逆向工程技巧,并进行实践和实际项目中的应用,可以逐渐掌握逆向工程的技能。 总结起来,Python和JavaScript逆向教程涉及到反编译字节码、反汇编.pyc文件、使用开发者工具和反编译工具等技术。通过学习相关知识、实践和实际项目应用,可以逐步掌握逆向工程的技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值