关于javascript:爬虫系列0-无内鬼破解前端JS参数签名

74次阅读

共计 6850 个字符,预计需要花费 18 分钟才能阅读完成。

PS:这是一个系列,坐等我缓缓填坑。

PS:不太会间接能跑的代码,抛砖引玉。

PS:那些我也不太纯熟的就不搞了,包含(破滑块、验证码..)

PS: 反编译搞 Apk 会有很长的几个文章,稍后缓缓更。

最近,和某 XX 单位的网站 gang 上了。

他们家的网页只容许在微信客户端关上,抓包就跟蛋疼了。

不过,手上有 Root 后的 Google Nexus5X,也有 whistle 跨平台抓包工具,

这个倒没太折腾,抓包工具证书往手机零碎根证书一扔,完事。

安卓 7.0 及以上用户证书导入的问题 – entr0py – 博客园


抓到了包,上面蛋疼的事件开始了。


前言: body 加密
嗯?申请 Body 是这一串货色?

嗯?时隔三年,神奇海螺又呈现了?

// json
{

"encryKey": "14a625eb2ec957f9b53412b01de37044e7e2aa6b4b911111c75091cba2a0315b",
"data": "44bc0dab8db8017603586f40554742d14a0c23dd009e35cae5b5ac87dbf7962a311fae30070763d2b48b564d72191fd07a881ebcccfb7c0fdd33e4067bc5119cee5e2fa5eaac10da995c86c8a092dcc3",
"sign": "cc3f924bbb6d57a15bd3e130230f51e55a04fa9e459d177440fbd10bce4b02d0",
"timestamp": 1627224237000

}
很显著,每个单词咱们都晓得,每个字母和数值咱们也懂。

然而 ….

除了 timestamp 咱们能够生成,其余的显著是加密后数据和签名。

一点都不高能的预警
先说一下思路:

捞出外围 JS 文件
读懂加密过程,捞出要害参数
用其余语言实现波及到的加密函数
比照加密后果是否统一,尝试去伪造申请
捞 JS

首先这货的微信浏览器的,所以没方法应用浏览器开发者工具。

不过,抓包下面不是搞掂了么?间接从抓包后果看 HTML 就完事。

乖乖一个个申请看,找到第一个返回 HTML 的响应体。

于是,找到了这个 …

哦,看到了 ….

<script src=”/umi.a029b1fd.js”></script>

看到这货,本宝宝小心脏有点乱跳了。

拜访一看。

害,看起来没的那么简略啊,显著这货是被 webpack 打包后的 JS 文件。

先下载回来再说 …

umi.a029b1fd.js 下载到本地,一看 1.5M。

关上一看,毫无疑问没有格式化 …

得嘞,大 JS 文件格式化,先关上我的 Ubuntu 机器再说。

哦,VS Code format 崩;加大内存,持续崩。

搜了一波,找到了神器 Online JavaScript beautifier

文件扔上去,再下载下来 …

完事。

毫无疑问,这就是 webpack 打包后的货色了。

没得事,全局搜一波下面的参数。

完满,看到这个,是不是答案曾经进去了。

看看,每个参数怎么算的都通知我了,还能做撒?还须要做撒?

于是,我午睡去了。

……..

其实,最头疼的货色就在这里了。

这时候,很多人会说,上 AST 还原 JS 嘛。

AST 形象语法树 – 最根底的 javascript 重点常识,99% 的人基本不理解 – SegmentFault 思否

啧啧啧。

情理是这个情理,不过还有其余的思路吗?

间接写个 index.html 引入这个 JS 也成的啊。

<html>

<body>
    <h1>test</h1>
</body>

<script src="./app.js"></script>
</html>

开始解 JS

 var O = Date.parse(new Date),
 Y = Object(h["e"])(!1, 16),
 j = Object(h["a"])(JSON.stringify({
 data: b,
 timestamp: O
                        }), Y),
 P = Object(h["f"])(j, Y);
 T = {encryKey: Object(h["a"])(Y, h["b"]),
 data: j,
 sign: P,
 timestamp: O
}

在代码外面看到了一堆这种 h[“a”] h[“e”],而后跟着参数(j, Y)。

咱们显著晓得,这是 JavaScript 的一个函数调用,h 看起来是一个 map 或者是对象,

这里是在调用它的 a 办法,传入了(j, Y)

在这里,咱们最想晓得的就是 h[“a”]的定义是什么样的,

因为晓得定义实现,也就能还原残缺代码逻辑。

跟一点代码(VS Code 跳转定义性能),咱们能看到 h 是什么?

h = n(“jNxd”),

看到这里其实是很头疼的,n 是个什么玩意咱们齐全无从得悉。

不过这里也能失去点信息,各种各样的函数或者对象都是绑定在”n“上的,

咱们只有拿到 n,咱们须要的 h,h[a], h[b] 都晓得是什么了。

怎么拿到 n 呢?情谊提醒,善用 debugger。

开始寻找 n
刚刚咱们曾经残缺把 app.js(umi.a029b1fd.js 格式化之后的文档)导入咱们的 index.html

用浏览器关上看看页面。

页面没什么问题,咱们尝试在 app.js 下面加点 debugger 吧。

加在哪呢?(目标只有一个,能获取的到 n)

….h 左近后面能够吗?

浏览器控制台关上,刷新页面,切换到 Console 页面。

试试这里能不能有 n 对象。

咦,看起来有戏。

试试 h = n(“jNxd”)

很好,很好,看起来这里是 OK 的,

h[“a”]也是一个函数,合乎咱们下面看到的。

点击一下下面 h[“a”]输入的内容,能够跳转到函数定义。

于是,咱们来到了重头戏。

       s = (e, t) => {var n = i.a.enc.Utf8.parse(t),
                r = i.a.enc.Utf8.parse(t),
                o = i.a.enc.Utf8.parse(e),
                a = i.a.AES.encrypt(o, n, {
                    iv: r,
                    mode: i.a.mode.CBC,
                    padding: i.a.pad.Pkcs7
                });
            return i.a.enc.Hex.stringify(a.ciphertext)
        },
        u = (e, t) => {var n = i.a.enc.Utf8.parse(t),
                r = i.a.enc.Utf8.parse(t),
                o = i.a.enc.Hex.parse(e),
                a = i.a.enc.Base64.stringify(o),
                s = i.a.AES.decrypt(a, n, {
                    iv: r,
                    mode: i.a.mode.CBC,
                    padding: i.a.pad.Pkcs7
                });
            return s.toString(i.a.enc.Utf8).toString()},
        c = (e, t) => i.a.enc.Hex.stringify(i.a.HmacSHA256(e, t)),
        l = (e, t, n) => {
            var r = "",
                i = t,
                o = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "-", ".", "~", "!", "@", "#", "$", "%", "^", "*", "(", ")", "_", ":", "<", ">", "?"];
            e && (i = Math.round(Math.random() * (n - t)) + t);
            for (var a = 0; a < i; a += 1) {var s = Math.round(Math.random() * (o.length - 1));
                r += o[s]
            }
            return r
        }

看看,代码都进去了,还须要撒?

明天的教程完结,早点睡 ….


微微一笑,如同没那么简略。

宝哥微微一笑,发现事件没那么简略。

已知,下面这一堆货色,

要不是 i.a.AES,要不是 HmacSHA256

没什么花色。

那么最大的问题就是,

这个加密过程是怎么搞的。

加密向量是什么?秘钥在哪?

SHA256 用的是什么参数?参加加密的数据是怎么拼接的?

PS:还在写 ….

重头戏上场
回到下面的代码

if (p[“d”] && “get” !== u.toLowerCase() && !g) {

                var O = Date.parse(new Date),
                    Y = Object(h["e"])(!1, 16),
                    j = Object(h["a"])(JSON.stringify({
                        data: b,
                        timestamp: O
                    }), Y),
                    P = Object(h["f"])(j, Y);
                T = {encryKey: Object(h["a"])(Y, h["b"]),
                    data: j,
                    sign: P,
                    timestamp: O
                }

}
这里能够看出每个变量是怎么来的。

encryKey = Object(h[“a”])(Y, h[“b”]) // 调用了 a 办法

O= Date.parse(newDate) // 生成了工夫戳

Y=Object(h[“e”])(!1,16) // 调用了 e 办法

P=Object(h[“f”])(j,Y) 调用了 f 办法

于是咱们执行一下看看。

Y 看起来是个随机字符串,j,p 看起来都是字母 + 数字组合起来的字符串。

别离到定义出看看是撒。

h[“e”]

l = (e, t, n) => {

            var r = "",
                i = t,
                o = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "-", ".", "~", "!", "@", "#", "$", "%", "^", "*", "(", ")", "_", ":", "<", ">", "?"];
            e && (i = Math.round(Math.random() * (n - t)) + t);
            for (var a = 0; a < i; a += 1) {var s = Math.round(Math.random() * (o.length - 1));
                r += o[s]
            }
            return r
        }

哦,生成了随机字符串。

h[“a”]

        // n("jNxd")["a"] encryKey
        s = (e, t) => {var n = i.a.enc.Utf8.parse(t),
                r = i.a.enc.Utf8.parse(t),
                o = i.a.enc.Utf8.parse(e),
                a = i.a.AES.encrypt(o, n, {
                    iv: r,
                    mode: i.a.mode.CBC,
                    padding: i.a.pad.Pkcs7
                });
            return i.a.enc.Hex.stringify(a.ciphertext)
        },

哦,AES.encrypt 加密,应用的是 CBC/Pkcs7 对齐

h[“f”] HmacSHA256

(e, t) => i.a.enc.Hex.stringify(i.a.HmacSHA256(e, t))
h[“b”] 间接返回了一个固定的字符串。(毫无疑问是 IV 向量和加密 Key)

看看,没了啊。

外围的加密代码就这点。

                    var O = Date.parse(new Date),
                    Y = Object(h["e"])(!1, 16),
                    j = Object(h["a"])(JSON.stringify({
                        data: b,
                        timestamp: O
                    }), Y),
                    P = Object(h["f"])(j, Y);
                T = {encryKey: Object(h["a"])(Y, h["b"]),
                    data: j,
                    sign: P,
                    timestamp: O
                }

所以重点代码又回到这里了,看懂这里就是所有的逻辑了。

读一下,也就这样。

获取以后工夫戳 O
生成随机字符串 Y
把传入的 b(body)和工夫戳组合到一起,设定 IV 向量为 Y,应用 AES 加密
把密文 j 和 Y 进行 SHA256 签名
最用把 Y 也用 AES 加密,这个时候加密 IV 向量为 h[“b”]

换集体话

写死了一个 iv 向量,随机生成一个 16 位的 key,从 iv 向量对这个 Key 加密,

用这个 Key 作为另一个 iv 变量对申请体 Body 加密,

而后把下面一堆货色做一个 sha256 的签名。

哦,说好的前端参数签名加密。

到这里,其实破解过程曾经实现了。

这根本也是我睡醒之后,看了台风吃了晚饭回来之后,

开始抄 Python 把下面逻辑实现一波的前置思路了。

这个时候,咱们也要晓得一些货色。

JS 加密库 CryptoJS

Python 对应的加密库 pycrypto

最初用 Python 实现这个残缺逻辑还是折腾了好一会的,

也抄了不少别的代码,最初贴一下。

from Crypto.Cipher import AES
import base64
import time
import binascii


class AesEncrypt:
    def __init__(self, key, iv):
        self.key = key.encode('utf-8')
        self.iv = iv.encode('utf-8')

    # @staticmethod
    def pkcs7padding(self, text):
        """明文应用 PKCS7 填充"""
        bs = 16
        length = len(text)
        bytes_length = len(text.encode('utf-8'))
        padding_size = length if (bytes_length == length) else bytes_length
        padding = bs - padding_size % bs
        padding_text = chr(padding) * padding
        self.coding = chr(padding)
        return text + padding_text

    def aes_encrypt(self, content):
        """AES 加密"""
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        # 解决明文
        content_padding = self.pkcs7padding(content)
        # 加密
        encrypt_bytes = cipher.encrypt(content_padding.encode('utf-8'))
        # 从新编码
        result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
        print("加密 hex:", str(binascii.hexlify(encrypt_bytes),encoding='utf-8'))
        return result

    def aes_encrypt_to_hex(self, content):
        """AES 加密"""
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        # 解决明文
        content_padding = self.pkcs7padding(content)
        # 加密
        encrypt_bytes = cipher.encrypt(content_padding.encode('utf-8'))
        # 从新编码
        # result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
        return str(binascii.hexlify(encrypt_bytes),encoding='utf-8')

    def aes_decrypt(self, content):
        """AES 解密"""
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        content = base64.b64decode(content)
        text = cipher.decrypt(content).decode('utf-8')
        return text.rstrip(self.coding)


if __name__ == '__main__':
    key = '123'
    iv = '123'

    ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    p_json = {
        "CompanyName": "testmall",
              "UserId": "test",
              "Password": "grasp@101",
              "TimeStamp": "2019-05-05 10:59:26"
    }
    a = AesEncrypt(key=key, iv=iv)
    e = a.aes_encrypt("123")
    d = a.aes_decrypt(e)
    print("加密:", e)
    print("解密:", d)

好了,

真完了,

睡觉睡觉。

正文完
 0