首页
社区
课程
招聘
2
[原创]Tk滑块篇之Wasm扣代码
发表于: 2025-3-9 15:29 2517

[原创]Tk滑块篇之Wasm扣代码

2025-3-9 15:29
2517

前言

  • 免责声明: 本文内容仅供学习交流使用,请勿用于其他用途,如因使用本文内容而产生任何问题,与作者无关,如果本文侵害贵司权益,请联系 zhiyitracer@163.com 下架处理

  • 最后还剩下一个图片请求返回的内容是加密的,本章把这个图片加密解一下

  • 本篇做的是web的滑块,app跟web用的是同样的加密,用扣代码的方式把这个搞定

环境

  • 滑块版本: 3.0.22
  • 滑块地址: 点击

定位

  • 上图可以看到后端返回的内容是一个加密的值,那解密完成后会是一个字符串,然后要转成json供后面使用,肯定要调用到 JSON.parse这个函数

  • 所以这里直接Hook JSON.parse

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const originalJsonParse = JSON.parse;  // 保存原始的 JSON.parse 函数
     
    // 创建一个新的 JSON.parse 函数
    JSON.parse = function (text, reviver) {
        try {
            const result = originalJsonParse(text, reviver);        // 调用原始的 JSON.parse 函数
            debugger;
            console.log("Parsed result:", result);
            return result;
        } catch (error) {
            console.error("JSON parsing error:", error);
            throw error;
        }
    };

  • 从Console中执行后,刷新下图片,重新请求

  • 刷新后,就会hook到好几个请求,很多明显不是请求相关的,一直放过就行,直到看见下面这个明显就是解密后的结果,我们可以跟一下调用栈
    在这里插入图片描述

ts文件格式化

  • 有些朋友hook以后可能跟我的页面不一样,文件是.ts的代码都在一行 无法格式化(如下图)

    在这里插入图片描述

  • 这里需要设置一下

  • 设置 ==> 然后找到 JavaScript source maps 去掉前面的对号 就可以了

流程分析

  • 从hook处向上点一个堆栈 看到如下位置的代码

    在这里插入图片描述

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    case 10:
    return a = e.sent,
    e.next = 13,
    a.decryptData(atob(o.data));
    case 13:
        if (c = e.sent,
            s = {},
            e.prev = 15,
            s = JSON.parse(c.decData),
            !Qo) {
            e.next = 23;
            break
        }
        return Qo = !1,
        e.next = 21,
        _n("fetch_picture_start");
    1. c.decData 就是最后解密出来的值
    2. c = e.sent 这句话的意思是 c就是上一个 case 的返回值,所以我们要看谁跳到的 case13
    3. case10 中能看到有 e.next = 13 说明他的下一个 case 就是13 所以我们要看 case10 的返回值
    4. a.decryptData(atob(o.data)); 这是 case10 的最后一句话,我们断点打过来打一下
    5. 这里需要刷新一下图片,因为刚的断点位置已经过去了
    6. 还有我们前面的hook 也可以去掉了 JSON.parse = originalJsonParse; 执行一下 就可以

    在这里插入图片描述

  • 这里o.data就是服务端返回的加密的内容,然后做了一个atob,通过 a.decryptData(atob(o.data)) 变成了正常的内容

  • 跟进a.decryptData看一下解密的实现

    在这里插入图片描述

  • 这里把atob后的内容传进来,然后调用了 e 函数传进去

  • 再跟进e看一下

  • 这里是个异步直接单步跟,从e.apply进到下一个函数

    在这里插入图片描述

  • 这里我们分析了一下其他代码没啥核心的,直接把断点下到 switch 看一下执行流程

    在这里插入图片描述

  • 多点几次最后定位到case17 中的 d 就是我们需要的内容

    在这里插入图片描述

  • 上面看到有一个 ccall 的调用,那加密的核心逻辑很有可能是在这里,我们跟进去看一下

    在这里插入图片描述

  • 进入之后,有一个 a.apply 的调用,看起来比较像核心位置,点击进去

    在这里插入图片描述

  • 调用了 Oe.h 方法,点进 Oe.h 这里就是 wasm 代码了

    在这里插入图片描述

保存wasm文件

  • 本节我们用扣代码的方式来把这个wasm 搞定,下一节 会写 App的 wasm 纯算

  • 首先 我们先把当前打开的这个 wasm 文件保存下来

  • 新起一个项目,建一个js文件,用来扣代码,然后把上面我们存下来的wasm文件和js放到同级

还原控制流

  • 既然我们是从控制流进来的,我们先手动还原一下这个控制流看看都在干嘛

  • 这块代码我们按照 case 执行顺序 改完后就是这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    n = a(atob(r));
    u = t.allocate(n, t.ALLOC_NORMAL);
    s = i(4);
    l = t.ccall("cmd_clientDecrypt", "number", ["number", "number", "string", "number"], [u, n.length, null, s.offset]);
    f = t.getValue(s.offset, "i32");
    A = i(f);
    t.ccall("cmd_clientDecrypt", "number", ["number", "number", "number", "number"], [u, n.length, A.offset, s.offset]);
    f = t.getValue(s.offset, "i32");
    d = t.UTF8ArrayToString(A.data, 0, f);
    t._free(u);    
    c(s);
    c(A);
    console.log(d)
  • 最后拿到d也就能拿到解密后的结果了

找加载位置

  • wasm如果没有基础的同学,去看星球里这篇文章,讲的很清晰
    在这里插入图片描述

  • wasm 有同步和异步两种加载方式

    • 同步:

      1
      2
      const module = new WebAssembly.Module(wasmBuffer);
      const instance = new WebAssembly.Instance(module, importObject);
    • 异步:

      1
      2
      WebAssembly.instantiateStreaming    (需要比较新浏览器)
      WebAssembly.instantiate    (兼容性更好)
  • 所以,同步加载有一种方式,异步加载有两种方式,那我们直接搜索一下,把有加载的位置都下上断点,看看走了那一个

    • 异步加载一

      在这里插入图片描述

    • 同步加载一

  • 搜索完发现就这两处,直接都下上断点,重新运行

    在这里插入图片描述

  • 停在了这里 我们看一下 e 是一个和很长的数组,其实这里向上跟两个堆栈就能看到,这个e就是wasm的代码,也就是我们上面从网页上保存的那个.wasm的文件

  • 所以这里就是我们要找的加载位置

扣代码

  • 这里加载都是在一起的,我们可以直接把这一块代码都扣出来
  1. 首先把全部的代码复制到一个可以折叠的编辑器里,我这里是 notepad++

  2. 折叠完后,直接搜索 WebAssembly.instantiate(e, t) 这一块加载代码
    在这里插入图片描述

  3. 搜到之后 我们直接向上找他属于那一个代码块,把内容拿下来,不用函数包裹了

    • 开始:

      在这里插入图片描述

    • 结束:

      在这里插入图片描述

  4. 直接复制到我们的js文件

    在这里插入图片描述

    • 复制过来之后,586行会报个错,因为我们直接粘过来,他不是一个函数了,不需要返回,所以删掉这两行就可以了
  5. 处理一下,代码里的加载方式,让他读我们本地的文件,并且把调用方式用改成同步的,同步处理起来会简单些

    在这里插入图片描述

    • 这块代码我们直接改为同步加载
    1
    2
    3
    4
    5
    6
    function V(e, t, r) {
        const fs = require("fs");
        n = new WebAssembly.Module(fs.readFileSync("./00021e0a.wasm"));  // 传入前面保存的wasm文件路径
        b = new WebAssembly.Instance(n,t);    // 第一个参数是Model  第二个参数是 导入的对象
        return r(b);
    }
  6. 然后加载代码搞好之后,我们把前面控制流的代码封成一个函数,放在底部

    在这里插入图片描述

  7. 我们运行一下z1函数,然后会出现个报错

    在这里插入图片描述

    • 原因是因为Oe是一个空对象,那我们看一下Oe的生成

      在这里插入图片描述

    • 就是这个t函数 我们看一下t的返回值

      在这里插入图片描述

    • 问题就出在这,我们修改一下 让他返回t的执行结果

      1
      2
      3
      X(0, z, e, (function (e) {
      return t(e)
      }
    • 改成上面的就可以了

  8. 报错r没有定义,这里r 就是服务端返回的加密的内容,我们去复制一个

    在这里插入图片描述

  9. 直接去调用一下

    在这里插入图片描述

    • 复制结果,放到js里就可以 let r = 复制的结果
  10. 报错 a 没有定义

    在这里插入图片描述

    • 这里直接去把a扣一下

    在这里插入图片描述

    • 这里看到下一行有个i 函数,我们后面也会用到就一起拿下来
    • 还有一个 c 函数,也没有,我们直接点进去一起扣下来了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    a = function (e) {
        var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 0,
        r = 0;
        if (0 == (r = t <= 0 ? e.length : t))
            return null;
        for (var n = new Array(r), o = 0; o < r; o++)
            if (o > e.length)
                n[o] = 0;
            else {
                var i = e.charCodeAt(o);
                n[o] = i
            }
        return n
    },
    i = function (e) {
        if (e <= 0)
            return null;
        var r = t._malloc(e);
        return null == r ? null : (t.HEAPU8.set(new Uint8Array(e), r), {
            data: t.HEAPU8.subarray(r, r + e),
            offset: r
        })
    }
     
    c = function(e) {
        e && "number" == typeof e.offset ? t._free(e.offset) : console.log("free error ", e)
    }

    在这里插入图片描述

  11. 报错 t 未定义

    在这里插入图片描述

    • 我们在网站上看一下 t 是什么

    在这里插入图片描述

    • t 有这么多的方法,我们随便搜一个

      在这里插入图片描述

    • 看到一段熟悉的代码,前面我们处理return错误的时候就是这个函数,所以这里的c就是我们用到的t

      在这里插入图片描述

  12. 再次运行,就成功解密了

    在这里插入图片描述

交流群

  • 建立了一个爬虫交流群,如果有兴趣进群的话可以加我好友 拉你进群

图片描述


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-3-15 17:05 被kanxue编辑 ,原因:
收藏
免费 2
支持
分享
赞赏记录
参与人
雪币
留言
时间
mb_eodvrytd
谢谢你的细致分析,受益匪浅!
2025-4-7 05:08
Lazy.
+1
感谢你的贡献,论坛因你而更加精彩!
2025-3-9 15:32
最新回复 (2)
雪    币: 227
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
卧槽 大佬加你了
2025-3-9 15:31
0
雪    币: 142
活跃值: (1755)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
js的代码好看啊
2025-3-21 18:11
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册