轻JS逆向分析“攒经验”项目之某交易所Sign加密参数逆向分析

2,425 阅读5分钟

最近忙着在搞大数据相关的东西,没什么太多时间去研究复杂的JS,所以给大家来几个练手的网站“攒攒经验”吧!这次出的系列是《轻JS逆向分析“攒经验”项目》,之所以是“轻”,也表明这些案例并不复杂,也是为了多给大家练手,“攒经验”用的。

这篇文章是公众号《云爬虫技术研究笔记》的《JS逆向分析“攒经验”项目》的第一篇:《某交易所Sign加密参数逆向分析》

《JS逆向分析“攒经验”项目》的相关代码在代码库

github.com/lateautumn4…

本次案例+代码都已推送到下面的代码库

github.com/lateautumn4…

懂的老哥记得素质三连哈!ღ( ´・ᴗ・` )比心

在这里插入图片描述

1. 分析网站

1.1 分析网站网址

Base64加密:aHR0cHM6Ly93d3cubXhjLmlvLw==

1.2 分析网站缘由

这次分析是因为看到“XX群”里有单子关于解密一个交易所的请求中Header参数的某个加密字段,经过后来对该群主的询问,该单子的价格大概是400左右。

在这里插入图片描述

2. 分析步骤

下面开始我们正式来分析这个网站,因为聊天记录中没有提到具体的API请求,所以我们只是先找找哪个请求中包含了加密参数。

2.1 找准加密参数的请求

经过不断的寻找,最后发现在登录状态下点击主页的购买请求中就包含了该加密参数

在这里插入图片描述
在这里插入图片描述

2.2 找到加密块的位置

一般知道我们需要解决什么加密参数,第一步都是全局先搜搜该参数,也就是“x-mxc-sign”看看有没有相关的代码涉及到它

在这里插入图片描述
很遗憾!并没有,那么我们就给这个XHR请求下个XHR断点,看看请求的调用栈
在这里插入图片描述
添加XHR断点,重新修改首先的参数值,重新让该请求复现下
在这里插入图片描述
代码已经断在这个地方,看看这块代码的前后文
在这里插入图片描述
可以看到这里

k = [M[23], M[26], M[12], M[23], M[2], M[26], M[18], M[8], M[6], M[13]].join("")
k = "x-mxc-sign"
n[C][k] = b

从上面可以看出,加密参数名称x-mxc-sign是由字符串拼接出来的,难怪之前全局搜索不到,而且跟k值相关的就是n[C][k] = b,所以推测b的值可能就是加密后的值,我们再具体看看n[C]的值是什么

在这里插入图片描述
可以看出n[C]是个哈希表,那么n[C][k]就是给某个字典的参数x-mxc-sign赋值b,所以我们现在重点关注b的生成方式
在这里插入图片描述
整个生成方式还是比较清楚,根据图里面的方式,我们现在只需要了解v()和y.k以及u是什么就行了,这里我们现在console里面查看他们具体的值是什么,然后根据这个值去调试测试
在这里插入图片描述

  • 参数u是u_id,是个写死的值,我们不用去管它
  • v()是个md5函数,之后我们可以在调试的时候具体测试
  • Object(y.k)根据图上的Js代码可以看出,是跟Cookie有关的计算

有了上面的分析,我们开始调试,看看和我们的分析是否一样

在这里插入图片描述
在b、p、g这三个我们需要的点那里下断点,开始新的调试

首先先看看p的生成

在这里插入图片描述
上面的意思是查询Cookie中‘u_id=’这个字符的位置,然后如果有的话,记录索引为n,并赋值n为n+1+4(u_id的长度),再令t值为从n的位置开始算起第一个‘;’的位置,最后获取Cookie在长度区间为[n,t]的字符,其实大家会发现就是获取u_id的值(妈的,搞的这么复杂!)

翻译成Python大概就是

def get_p():
  index = _cookie.split(";")
  for i in index:
    if "u_id" in i:
      return i.split("u_id=")[-1]```

p的话没有什么特别大的问题只要知道v()是不是md5就行了,测试的过程中能够发现md5的计算过程,所以很明显,直接搞起

翻译成Python大概就是

def get_r() -> str:
    return str(int(round(time.time() * 1000)))


def get_g():
    p, r = get_p(), get_r()
    return get_md5(p+r)[7:],r

最后是获取我们目标的b值,公式是b = v()(r + s + g),其中s是formdata的序列化,然后其他同上

翻译成Python大概就是

def get_b(formdata) -> str:
    g, r = get_g()
    s = get_s(d=formdata)
    return get_md5(r+s+g)

到这里我们差不多就全部重写好了,接下来看看完整的代码

2.3 重写加密代码

根据上面的逻辑,整理的代码如下

import hashlib
import time
from typing import Dict
_cookie = "_ga=GA1.2.100938634.1582370964; _gid=GA1.2.927816681.1582370964; aliyungf_tc=AQAAABMm4j8LogsA5hre3Rf4QxA6bkWH; u_id=WEBabc27b4227ca5b27cb3e4fe61cf49d26d47eacdbefadefb28106cee6ef801cc4; account=l************%40163.com"


def get_md5(string: str) -> str:
    hl = hashlib.md5()
    hl.update(string.encode(encoding='utf-8'))
    return hl.hexdigest()


def get_p() -> str:
    index = _cookie.split(";")
    for i in index:
        if "u_id" in i:
            return i.split("u_id=")[-1]


def get_r() -> str:
    return str(int(round(time.time() * 1000)))


def get_g() -> str:
    p, r = get_p(), get_r()
    return get_md5(p+r)[7:], r


def get_s(d: Dict[str, str]) -> str:
    return "&".join(f"{k}={v}" for k, v in d.items())


def get_sign(formdata) -> str:
    g, r = get_g()
    s = get_s(d=formdata)
    return get_md5(r+s+g)

2.4 测试

代码整理好之后,简单的测试下,首先我们让t的值也就是r值为固定时间测试我们的程序和网站自己计算的x-mxc-sign值是否是一样的

在这里插入图片描述
在这里插入图片描述
可以看到,程序输出的值和原始值是一样的,现在我们再测试对方的网站也是可以直接访问到的,证明我们已经完成了整个加密参数的逆向分析。

3. 分析复盘

经过我们的分析和测试,现在已经可以正确的去请求该交易所的API了。我们简单的来复盘下这次的分析流程,我们通过几个关键点来回忆下:

  • 加密参数在请求中的位置(消息头Header/消息体Body等等)
  • 用什么方法下断点
  • 如何寻找进一步下断点的地方
  • 调试分析+重写JS

通过以上几点我们能够很明确的理清这次分析的整个思路,也为我们之后再遇到类似的Header请求头参数加密提供了一丝丝“经验”,这次算是个简单的“攒经验”项目,大家还有类似的“轻JS逆向分析”的案例可以给我提供,我也会再去发掘类似的案例给大家来分析分析。