吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1957|回复: 8
上一主题 下一主题
收起左侧

[Web逆向] 码上爬 8~13关js逆向代码

[复制链接]
跳转到指定楼层
楼主
buzaizb 发表于 2025-9-26 17:37 回帖奖励
本帖最后由 buzaizb 于 2025-9-26 18:02 编辑

各位好,这次给大家分享一下 码上爬 8~13 关的js逆向代码。  
  

在第11 关中 和前面不同的是使用了 WebAssembly(简称 Wasm)技术。
WebAssembly主要是借助Emscripten编译工具,使c/c++文件能转成wasm格式的文件,javaScript可以直接调用该文件执行其中的方法。从而可将一些api参数的加密逻辑隐藏其中。
对于逆向使用了这种技术的网站的情况下,我们可以借助 python 的 pywasm库来模拟执行 wasm文件。

通过对11关的请求的调用栈溯源可以得知,加密参数M 就是通过这个方法的来的,当前这个文件就是一个wasm文件。


因此我们将目标 .wasm 文件另存到本地,然后使用pywasm库来模拟执行加密方法即可:
wasm模拟执行,可以使用以下代码:
[Python] 纯文本查看 复制代码
import pywasm  
def generate_param_m(num, ts):  
runtime = pywasm.core.Runtime()  
m = runtime.instance_from_file('./encrypt.wasm')  
r = runtime.invocate(m, 'encrypt', [num, ts])  
return r[0]


在第12关中,可以看到整个js 文件,除了发送请求和方法名解析的方法。就只剩下了一个 interpreter。这个方法的内部就是一个自定义的解释器,请求参数中的加密逻辑也在其中。

对于这种情况,我们挨个去debug 溯源比较麻烦,因此直接copy 这个方法下来。整个12关的逆向过程就是缺啥补啥。
在补环境的过程中,下面的这段代码可以更好的帮助我们理清调用关系:

[JavaScript] 纯文本查看 复制代码
dtavm = {}
dtavm.log = console.log
 
function proxy(obj, objname, type) {
    function getMethodHandler(WatchName, target_obj) {
        let methodhandler = {
            apply(target, thisArg, argArray) {
                if (this.target_obj) {
                    thisArg = this.target_obj
                }
                let result = Reflect.apply(target, thisArg, argArray)
                if (target.name !== "toString") {
                    if (target.name === "addEventListener") {
                        dtavm.log(`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray[0]}], 结果 => [${result}].`)
                    } else if (WatchName === "window.console") {
                    } else {
                        dtavm.log(`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${result}].`)
                    }
                } else {
                    dtavm.log(`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${result}].`)
                }
                return result
            },
            construct(target, argArray, newTarget) {
                var result = Reflect.construct(target, argArray, newTarget)
                dtavm.log(`调用者 => [${WatchName}] 构造函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${(result)}].`)
                return result;
            }
        }
        methodhandler.target_obj = target_obj
        return methodhandler
    }
 
    function getObjhandler(WatchName) {
        let handler = {
            get(target, propKey, receiver) {
                let result = target[propKey]
                if (result instanceof Object) {
                    if (typeof result === "function") {
                        dtavm.log(`调用者 => [${WatchName}] 获取属性名 => [${propKey}] , 是个函数`)
                        return new Proxy(result, getMethodHandler(WatchName, target))
                    } else {
                        dtavm.log(`调用者 => [${WatchName}] 获取属性名 => [${propKey}], 结果 => [${(result)}]`);
                    }
                    return new Proxy(result, getObjhandler(`${WatchName}.${propKey}`))
                }
                if (typeof (propKey) !== "symbol") {
                    dtavm.log(`调用者 => [${WatchName}] 获取属性名 => [${propKey?.description ?? propKey}], 结果 => [${result}]`);
                }
                return result;
            },
            set(target, propKey, value, receiver) {
                if (value instanceof Object) {
                    dtavm.log(`调用者 => [${WatchName}] 设置属性名 => [${propKey}], 值为 => [${(value)}]`);
                } else {
                    dtavm.log(`调用者 => [${WatchName}] 设置属性名 => [${propKey}], 值为 => [${value}]`);
                }
                return Reflect.set(target, propKey, value, receiver);
            },
            has(target, propKey) {
                var result = Reflect.has(target, propKey);
                dtavm.log(`针对in操作符的代理has=> [${WatchName}] 有无属性名 => [${propKey}], 结果 => [${result}]`)
                return result;
            },
            deleteProperty(target, propKey) {
                var result = Reflect.deleteProperty(target, propKey);
                dtavm.log(`拦截属性delete => [${WatchName}] 删除属性名 => [${propKey}], 结果 => [${result}]`)
                return result;
            },
            defineProperty(target, propKey, attributes) {
                var result = Reflect.defineProperty(target, propKey, attributes);
                dtavm.log(`拦截对象define操作 => [${WatchName}] 待检索属性名 => [${propKey.toString()}] 属性描述 => [${(attributes)}], 结果 => [${result}]`)
                // debugger
                return result
            },
            getPrototypeOf(target) {
                var result = Reflect.getPrototypeOf(target)
                dtavm.log(`被代理的目标对象 => [${WatchName}] 代理结果 => [${(result)}]`)
                return result;
            },
            setPrototypeOf(target, proto) {
                dtavm.log(`被拦截的目标对象 => [${WatchName}] 对象新原型==> [${(proto)}]`)
                return Reflect.setPrototypeOf(target, proto);
            },
            preventExtensions(target) {
                dtavm.log(`方法用于设置preventExtensions => [${WatchName}] 防止扩展`)
                return Reflect.preventExtensions(target);
            },
            isExtensible(target) {
                var result = Reflect.isExtensible(target)
                dtavm.log(`拦截对对象的isExtensible() => [${WatchName}] isExtensible, 返回值==> [${result}]`)
                return result;
            },
        }
        return handler;
    }
 
    if (type === "method") {
        return new Proxy(obj, getMethodHandler(objname, obj));
    }
    return new Proxy(obj, getObjhandler(objname));
}
 
 
EventTarget = function EventTarget(){}
//这里放你要代理的对象 --->
window = globalThis
 
 
//document
document = {}
 
// navigator
navigator = {}
 
//location
location = {}
 
 
//history
history = {}
 
 
//screen
screen = {}
 
//localStorage
localStorage = {}
 
 
localStorage = proxy(localStorage, 'localStorage')
screen = proxy(screen, 'screen')
location = proxy(location, 'location')
history = proxy(history, 'history')
window = proxy(window, 'window')
document = proxy(document, 'document')
navigator = proxy(navigator, 'navigator')


最后第13关:
我们发送网络请求,看他的调用链,点进 loadPage 方法:

发现者整个文件中就只有一个方法:

但是,按照之前关卡的逻辑,他会使用一个请求发送前的拦截器。
所以我们全局搜索‘$’。最后在jqueryxhr.js文件中可以找到这个请求发送前的拦截器:


这里可以将整个文件的js 都拉下来。不过在复制到本地之前可以先反混淆一下,这是我用的工具网址 https://obf-io.deobfuscate.io/。


复制代码后,我们在本地加上打印方法运行,发现没有任何输出。这是因为他重写了log 方法,我们注掉就好了:


最后我们再次运行可以发现了。

到此这关的请求参数加密逆向完成了, 最后我们组装一下方法,让其方便python调用就行了。

最后的最后,我们再发送请求的时候还有个小坑,就是他的请求入参是一个字符串且一点不能改动。
[Python] 纯文本查看 复制代码
data = '{"page":"'+str(num)+'"}'


本文到此就结束了,非常感谢各位能看到这里。8~13关的代码我放在附件了。

码上爬.zip

322.24 KB, 下载次数: 7, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 6威望 +1 吾爱币 +24 热心值 +6 收起 理由
hanchao2021 + 1 谢谢@Thanks!
meet52 + 1 + 1 用心讨论,共获提升!
yuchen123 + 1 + 1 谢谢@Thanks!
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
liuxuming3303 + 1 + 1 谢谢@Thanks!
ZZ730605 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
ZZ730605 发表于 2025-9-26 21:38
感谢分享。
3#
millylee 发表于 2025-9-27 00:04
4#
walykyy 发表于 2025-9-27 08:47
5#
meet52 发表于 2025-9-28 10:48
感谢提供思路
6#
yhlw6b3a59n 发表于 2025-9-28 11:37
赞一个,感谢分享
7#
hanchao2021 发表于 2025-9-29 23:16
学习了,感谢分享!!!
8#
meet52 发表于 2025-10-9 15:29
第9题用什么工具解混淆?
9#
wiicando 发表于 2025-11-13 16:31

不错,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-2-17 10:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表