吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 975|回复: 15
上一主题 下一主题
收起左侧

[Web逆向] 猿人学第三题逆向分析

  [复制链接]
跳转到指定楼层
楼主
heihei123 发表于 2026-3-31 17:27 回帖奖励
本帖最后由 heihei123 于 2026-3-31 17:40 编辑

猿人学第三题逆向分析

目标网址:

aHR0cHM6Ly9tYXRjaDIwMjUueXVhbnJlbnh1ZS5jbi9tYXRjaDIwMjUvdG9waWMvMw==

1、代码还原

1.1、if转switch 1


主要讲一下上面的if是如何还原的,以下面的代码举个最简例子。


if (++loc_up1 > 125) {
    if (loc_up1 < 127) {
        code = 1
    }
}

想要运行到code这个位置。

1、从上往下的条件是[++loc_up1 > 125, loc_up1 < 127]。
2、++loc_up1 > 125 就是 loc_up1 > 124。
3、由于上面loc_up1自增了,所以这个分支下面所有的loc_up1实际都加1 就相当于 (loc_up1+1) < 127 也就是 loc_up1 < 126。
由此可以得到下面代码

let pPath = if_path, old_path;
// 运行到代码块时loc_up1的值、case loc_up1的值
let values = 0, case_value;
// test数组
let s_arr = [];
while(true){
  let right_v = test.right.value, s = {}, value = 0;
  if(tmp_if){
      s.operator =`${test.operator}`
  }else{
      s.operator = `${dict_oper[test.operator]}`
  }
  // ++a
  if(test.left.prefix){
      if(test.left.operator == '++'){
          s.value = right_v - 1
          value += 1
      }else if(test.left.operator == '--'){
          s.value = right_v + 1
          value -= 1
      }
  }else if(test.left.prefix === false){// a--
      s.value = right_v
      value += test.left.operator == '++' ? 1 : -1
  }else{// a
      s.value = right_v
  }
  // 自增会影响到下面的值
  if(value != 0){
      if(value > 0){
          s_arr.map(n => n.value--)
      }else{
          s_arr.map(n => n.value++)
      }
  }
  s_arr.push(s)
  values += value

  old_path = pPath;
  pPath = pPath.parentPath.parentPath
  test = pPath.node.test
  tmp_if = pPath.node.consequent == old_path.parentPath.node
  if(pPath.type != 'IfStatement' || pPath.parentPath.type != 'BlockStatement'){
      break
  }
}

1.2、if转switch 2

不想麻烦的话我们也可以模拟运行,说下大致思路,当code=1时,其对代码就是case loc_up1_start: loc_up1_end=loc_up1;code;

loc_up1 = loc_up1_start
if (++loc_up1 > 125) {
    if (loc_up1 < 127) {
        code = 1
    }
}
code,loc_up1_end = loc_up1

1.3、VMP虚假分支


if转switch处理完后的代码如上图。
这里的虚假分支类型还挺多,比如最开始mJ(258)中指令Exp_arg14[258]-Exp_arg14[262] 执行的是 (!!null) !== (!false) 明显这个是true在case 2中就不会走else,所以碰到UnaryExpression之类的节点自己判断一下是否可以转成BooleanLiteral节点。
还有像下面这个,经常用这个自执行函数传的Exp_arg15里面的数字做一些运算,也有虚假分支。

部分还原结果如下。

2、逆向分析

2.1、首先打开f12抓包



很明显就是/topic/3_captcha_jpg接口就是我们需要分析的,由于动态调试下面的字符可能会不一样,自己复现的时候可以固定Date.now 和 Math.random=()=>0.5。

另外在函数bZ是生成字符串的,也需要输出一下结果。

2.2、插桩分析

直接搜索 mJ(,发现有3处,2处是自执行函数,直接在mJ(Exp_arg1, loc_array1, [], loc_call1)处添加日志断点Exp_arg1, JSON.stringify(loc_array1)。


1、可以看到mJ(7394) 传入的数组经过btoa(String.fromCharCode(...arr_7394)) 恰好就是FYvF9MS4vf3JxuioegzO3wHeceH0vV2IrZOtWPp876/w/uCBoCgL+Oyz/b2fOds6

2、再向上看7172 [-251731839,-1607988232,-323748419,-1623598278]
其实就是arr_7394后16位就是new Uint8Array(n
ew Uint32Array(arr_7172.reverse()).buffer).reverse()

3、其实也没什么好说的,直接看结果就打印 Exp_arg1,loc_array1和mJ(Exp_arg1, loc_array1, [], loc_call1)的结果,如果不理解结果怎么来的就在switch开始的位置添加日志断点 JSON.stringify(Exp_arg11), loc_up1,loc_mem1, JSON.stringify(Exp_arg10) 主要看Exp_arg11的变化就行了,日志太长可以自己看怎么过滤合适 JSON.stringify(Exp_arg11, (key,value) => {return (key && typeof value == 'object' && value && !Array.isArray(value)) ? 'obj' : value})

4、
最后比较关键的地方,就是mJ(5126,) 这个位置,明显是Date.now() + location.pathname + ''.padEnd(16,'f') 16个f很明显是由6537生成的 *str.charAt(Math.floor(Math.random() 62))


5、后面的/3_captcha_check接口也是一样的道理,hook XMLHttpRequest.prototype.send 或者在jquery.js的 r.send(i.hasContent && i.data || null)断点,不然会刷新网页。
可以看到起始节点就是mJ(5755,), val(验证码结果)+3_captcha_jpg的fff和Date.now()。

3、JS代码

3.1、3_captcha_jpg接口

function encrypt_1(str){
    function get_value(buf, key){
        // 大小端转换
        buf = new Uint32Array(new Uint8Array(buf.reverse()).buffer).reverse()
        // key的前16位
        buf = buf.map((n,i) => n ^ key[i])
        res = []
        for(var i = 0; i < buf.length; i++){
            t = buf[i]
            tt = 0;
            switch(i%2){
                case 0:// 2949
                    // 1111 -> 15 共移动了4次
                    tt = ((t >>> 4) | ((t & 15) << 28)) >>> 0
                    break
                case 1:// 2897
                    tt = ((t << 4) | ((t >>> 28))) >>> 0
                    break
            }
            res[(i+2)%4] = tt
        }
        // key的后16位
        res = res.map((n,i) => n ^ key[i+4]).reverse()
        res = new Uint8Array(new Uint32Array(res).buffer)
        res = [...res].reverse()
        // debugger
        return res
    }
    key = [-427576346,-1801525873,-2098817616,-442851354,-1632178244,-1612212864,-457334555,-1330518619]
    pad = str.length % 16 ? 16 - str.length % 16 : 0;
    str += String.fromCharCode(pad).repeat(pad)
    buf = Array.from(str, n => n.charCodeAt())

    res_arr = []
    for(var index = 0; index < buf.length; index+=16){

        res_arr.push(...get_value(buf.slice(index, index+16), key))
    }
    res = btoa(String.fromCharCode(...res_arr))
    return res
}

上面的key是固定的,其实是来源于下图

3.2、3_captcha_check接口

这个接口是将类似"1ffffffffffffffff1774880015396"的字符进行魔改md5加密,没什么好说的,太长就不贴了,对着标准的md5改就行了。
就是要注意下面,这两个其实是一个东西。

最后返回这个 {"success": true, "msg": "success"} 就是成功了。
3.7z (18.05 KB, 下载次数: 7)

免费评分

参与人数 7威望 +1 吾爱币 +25 热心值 +6 收起 理由
anranleixia + 1 + 1 感谢楼主分享
windliberty + 1 + 1 感谢分享!
anotherNEw + 1 + 1 用心讨论,共获提升!
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lkb00077 + 1 我很赞同!
Bluenightmoon + 1 我很赞同!
xlln + 1 + 1 我很赞同!

查看全部评分

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

推荐
anotherNEw 发表于 2026-4-2 09:39
等等,对我好像挺有用的吗,有一个收藏网站喜欢加密链接进行跳转,还喜欢隐藏网址,虽然能逆向除用的是AES-26加密,但还是以防万一其他网站也有这个问题......
沙发
skycg 发表于 2026-3-31 21:16
3#
iugopgpo11124 发表于 2026-3-31 22:42
4#
Kotoamatsukami 发表于 2026-3-31 22:55

浏览增加见识
5#
beixll 发表于 2026-4-1 02:32
感谢分享。。。。。。。。。
6#
1285144534 发表于 2026-4-1 08:55
感谢分享
7#
Bluenightmoon 发表于 2026-4-1 10:24
感谢分享,很有用
8#
xyj201314 发表于 2026-4-1 10:34
感谢分享
9#
mi360 发表于 2026-4-1 15:10

感谢分享
10#
return2002 发表于 2026-4-1 15:28
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-4-12 03:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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