小紅書shield算法破解分析參數(shù)

最終代碼移步第七章節(jié)?。?!
shield是小紅書請(qǐng)求header中的一個(gè)加密參數(shù),具體算法是在so中計(jì)算的,so文件是libshield.so。我這邊主要就講一下如何利用unidbg調(diào)用so生成shield參數(shù)。因篇幅可能較長(zhǎng),這篇文章就講下如何找到Native函數(shù)和函數(shù)所對(duì)應(yīng)的偏移位置。

一、尋找Native函數(shù)
老規(guī)矩,首先反編譯apk后用JD-GUI工具打開(也可使用jadx等工具),直接搜索shield,結(jié)果意料之中,在java層沒(méi)搜到什么有用的信息,算法大概率放在了so層。
那我們只能在so層中尋找突破口,這里推薦一位大佬開發(fā)的工具frida_hook_libart(項(xiàng)目地址:https://github.com/lasting-yang/frida_hook_libart),它是把在libart.so中的常用函數(shù)都進(jìn)行了hook。
總共有三個(gè)hook文件hook_RegisterNatives.js、hook_art.js、hook_artmethod.js,我們先使用這個(gè)hook_art。
首先在手機(jī)上打開frida-server服務(wù),電腦終端運(yùn)行命令:“frida -U –no-pause -f com.xingin.xhs -l hook_art.js”。

圖片.png

然后找啊找,會(huì)發(fā)現(xiàn)在NewStringUTF這個(gè)函數(shù)中,出現(xiàn)了shield結(jié)果。這里所對(duì)應(yīng)的so文件是libshield.so 。
然后運(yùn)行命令:“frida -U –no-pause -f com.xingin.xhs -l hook_RegisterNatives.js”,看一下在libshield里動(dòng)態(tài)注冊(cè)了哪些Native函數(shù)。

圖片.png

可以看到,在so中,注冊(cè)了initializeNative、intercept、initialize這幾個(gè)函數(shù)。
然后在java層找下,可以找到在“com.xingin.shield.http.XhsHttpInterceptor”這個(gè)類下,如下圖:

圖片.png

二、尋找Native函數(shù)的偏移位置
方法一:
上文有使用Frida hook了so的RegisterNatives函數(shù),hook到的同時(shí),也打印出了函數(shù)的offset偏移位置。

圖片.png

initializeNative函數(shù)的偏移位為:0x6c11d
intercept函數(shù)的偏移位為:0x6b9e9
initialize函數(shù)的偏移位為:0x6b801
方法二:
首先使用ida打開libshield.so文件,使用shift+F12打開String window窗口。
在窗口中直接搜索剛剛找到的Native函數(shù),如intercept:

圖片.png

然后點(diǎn)進(jìn)去,就能找到這里

圖片.png

intercept函數(shù)的偏移位就是0x6b9e9。
方法三:
同樣使用ida打開libshield.so文件,在Exports窗口中找到JNI_OnLoad(動(dòng)態(tài)注冊(cè)的函數(shù)都會(huì)在JNI_OnLoad中進(jìn)行注冊(cè)),然后進(jìn)去F5反匯編下。


圖片.png

然后可以看到sub_9FA0這個(gè)函數(shù),點(diǎn)進(jìn)去。

圖片.png

這邊有很多函數(shù),逐個(gè)進(jìn)行查看,最終找到sub_6B360這個(gè)地方,點(diǎn)進(jìn)去。

圖片.png

可以看到j(luò)ava層的“com.xingin.shield.http.XhsHttpInterceptor”這個(gè)類,在點(diǎn)進(jìn)去,就到了剛剛方法二中的這個(gè)位置,偏移位直接復(fù)制下就好。

圖片.png

三、搭建unidbg環(huán)境

首先把unidbg項(xiàng)目拉下來(lái)(項(xiàng)目地址:https://github.com/zhkl0228/unidbg)。

在test中新建一個(gè)類,然后把小紅書apk和對(duì)應(yīng)的libshiled.so文件放到resources資源目錄下。

然后把最基本的unidbg框架搭下,代碼如下:

public class ShieldTest extends AbstractJni {

    //ARM模擬器
    private final AndroidEmulator emulator;

    //vm
    private final VM vm;

    //載入的模塊
    private final Module module;

    public ShieldTest() {
        // 創(chuàng)建模擬器實(shí)例
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.xhs").build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));

        // 創(chuàng)建Android虛擬機(jī)
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/xhs/xhs_v6.97.0.apk"));
        vm.setJni(this);
        vm.setVerbose(true);

        //加載so
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/xhs/libshield_v6.97.0.so"), true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
    }

}

四、分析Native函數(shù)

圖片.png

我們分析下這塊代碼:

1 首先需要調(diào)initializeNative這個(gè)初始化函數(shù);

2 第二步調(diào)initialize函數(shù),獲取cPtr;

3 最終生成shield是調(diào)用intercept函數(shù),需傳入一個(gè)“okhttp3/Interceptor$Chain”,paramLong就是第二步獲取的cPtr值。

五、補(bǔ)缺失環(huán)境

先把初始化函數(shù)initializeNative的代碼邏輯寫上(偏移位置怎么找可以看我的上篇文章)。

public void initializeNative() {
    List<Object> params = new ArrayList<>();
    params.add(vm.getJNIEnv());
    params.add(0);
    module.callFunction(emulator, 0x6c11d, params.toArray());
}

public static void main(String[] args) {
    ShieldTest shieldTest = new ShieldTest();
    shieldTest.initializeNative();

}

直接運(yùn)行下,會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了。

圖片.png

這個(gè)報(bào)錯(cuò)一般就說(shuō)明要補(bǔ)環(huán)境了。

這里先解釋下這個(gè):“java/nio/charset/Charset->defaultCharset()Ljava/nio/charset/Charset;”

這句話的意思是調(diào)用java/nio/charset/Charset這個(gè)類下的defaultCharset函數(shù),返回值是Ljava/nio/charset/Charset,unidbg缺少這一塊的處理邏輯,我們需要手動(dòng)補(bǔ)上。

我們可以直接點(diǎn)“com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:388)”這個(gè)位置,進(jìn)去后會(huì)看到一個(gè)方法,我們需要重寫callStaticObjectMethodV方法,然后把Charset對(duì)象return回去就好。

@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
    switch (signature) {
        case "java/nio/charset/Charset->defaultCharset()Ljava/nio/charset/Charset;":
            return vm.resolveClass("java/nio/charset/Charset").newObject(Charset.defaultCharset());
    }
    throw new UnsupportedOperationException(signature);

}

繼續(xù)運(yùn)行,發(fā)現(xiàn)又報(bào)錯(cuò)了

圖片.png

這次是缺少一個(gè)versionCode參數(shù),這個(gè)參數(shù)可以在AndroidManifest.xml文件中找到,我這個(gè)版本是6970181,直接返回即可。

@Override
public int getIntField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
    switch (signature) {
        case "android/content/pm/PackageInfo->versionCode:I": {
            return 6970181;
        }
    }
    return super.getIntField(vm, dvmObject, signature);

}

然后繼續(xù)運(yùn)行,報(bào)錯(cuò)了就補(bǔ)環(huán)境,我就不把所有的都寫出來(lái)了,比較多,挑幾個(gè)講一下。

sDeviceId:

圖片.png

這里是缺少一個(gè)sDeviceId參數(shù),我這里是通過(guò)IDA分析這塊代碼,然后打印GetStringUTFChars函數(shù)結(jié)果找到的。


圖片.png
圖片.png

ps:

這里分析IDA代碼有個(gè)技巧,像這塊代碼,v1大概率就是env,可以在v1上右鍵“set lvar type”,把類型改為JNIEnv*,然后確定,會(huì)發(fā)現(xiàn)很多函數(shù)能顯示出來(lái)了,看起來(lái)會(huì)直觀很多。

圖片.png

main_hmac:

這個(gè)參數(shù)可以直接adb連上去,在s.xml文件下找到。

然后依次把三個(gè)函數(shù)的所有環(huán)境補(bǔ)完,就可以運(yùn)行生成shield了。

下面是三個(gè)Native函數(shù)的代碼邏輯:

public void initializeNative() {
    List<Object> params = new ArrayList<>();
    params.add(vm.getJNIEnv());
    params.add(0);
    module.callFunction(emulator, 0x6c11d, params.toArray());
}

public long initialize() {
    List<Object> params = new ArrayList<>();
    params.add(vm.getJNIEnv());
    params.add(0);

    params.add(vm.addLocalObject(new StringObject(vm, "main")));
    Number number = module.callFunction(emulator, 0x6b801, params.toArray());
    return number.longValue();
}

public void intercept(long cPtr) {
    List<Object> params = new ArrayList<>();
    params.add(vm.getJNIEnv());
    params.add(0);

    DvmObject<?> chain = vm.resolveClass("okhttp3/Interceptor$Chain").newObject(null);
    params.add(vm.addLocalObject(chain));
    params.add(cPtr);
    Number number = module.callFunction(emulator, 0x6b9e9, params.toArray());
    Object result = vm.getObject(number.intValue()).getValue();
}

public static void main(String[] args) {
    ShieldTest shieldTest = new ShieldTest();
    shieldTest.initializeNative();

    long cPtr = shieldTest.initialize();
    System.out.println(cPtr);

    shieldTest.intercept(cPtr);

}

六、最終效果

圖片.png

七、最終還原源碼

"""
生成小紅書app接口參數(shù)中的shield
"""

import base64
from functools import reduce
from operator import add, or_, xor
import base64

def bytes_to_int32(t):
    return [int.from_bytes(t[i * 4:(i + 1) * 4], byteorder='little') for i in range(len(t) // 4)]

def int32_to_bytes(t):
    return reduce(add, [bytearray((t % 2 ** 32).to_bytes(4, byteorder='little')) for t in t])

def byte_n(t, n):
    return bytearray(t.to_bytes(4, byteorder='little'))[n]

def read_int16(t):
    return int.from_bytes(t[:2], byteorder='little')

def read_int32(t):
    return int.from_bytes(t[:4], byteorder='little')

def b_swap32(t):
    return bytes_to_int32(int32_to_bytes([t])[::-1])[0]

data_a798 = bytearray('0123456789abcdef', encoding='utf-8')

AES_T3 = ["種子找我拿qq2891590578"]

AES_T4 = ["種子找我拿qq2891590578"]

AES_T1 = ["種子找我拿qq2891590578"]

AES_SBOX = ["種子找我拿qq2891590578"]

AES_RCON = ["種子找我拿qq2891590578"]

AES_T5 = ["種子找我拿qq2891590578"]

AES_T6 = ["種子找我拿qq2891590578"]

AES_T7 = ["種子找我拿qq2891590578"]

AES_T8 = bytes_to_int32( ["種子找我拿qq2891590578"])

AES_SIBOX = ["種子找我拿qq2891590578"]

def sub_2af14(a1, a3):
    a1 = bytes_to_int32(a1)
    a3 = bytes_to_int32(a3)

    a3[60] = 10

    a3[0] = b_swap32(a1[0]) ^ 0xF1892131
    a3[1] = b_swap32(a1[1]) ^ 0xFF001123
    a3[2] = b_swap32(a1[2]) ^ 0xF1001356
    a3[3] = b_swap32(a1[3]) ^ 0xF1234890

    v1 = 0
    v8 = 0

    while True:
        v3 = a3[v1 + 3]
        a3[v1 + 4] = reduce(xor, [
            a3[v1 + 0],
            AES_T3[(v3 >> 16) & 0xFF] & 0xFF000000,
            AES_T4[(v3 % 2 ** 16) >> 8] & 0xFF0000,
            AES_T1[v3 % 2 ** 8] & 0xFF00,
            AES_SBOX[4 * (v3 >> 24)],
            AES_RCON[v8]
        ])
        a3[v1 + 5] = a3[v1 + 1] ^ a3[v1 + 4]
        a3[v1 + 6] = a3[v1 + 2] ^ a3[v1 + 5]
        a3[v1 + 7] = a3[v1 + 3] ^ a3[v1 + 6]
        v8 += 1
        if v8 == 10:
            break
        v1 += 4

    return int32_to_bytes(a3)

def sub_2b2d0(a3):
    a3 = bytes_to_int32(a3)

    v13 = 0
    i = 4 * a3[60]
    while v13 < i:
        for j in range(4):
            v3 = a3[v13 + j]
            a3[v13 + j] = a3[i + j]
            a3[i + j] = v3
        v13 += 4
        i -= 4

    v15 = 0
    i = 1

    while i < a3[60]:
        v15 += 4
        a3[v15] = reduce(xor, [
            AES_T8[AES_SBOX[4 * (a3[v15] % 2 ** 8)]],
            AES_T5[AES_SBOX[4 * (a3[v15] >> 24)]],
            AES_T6[AES_SBOX[4 * ((a3[v15] >> 16) & 0xFF)]],
            AES_T7[AES_SBOX[4 * (a3[v15] % (2 ** 16) >> 8)]]
        ])
        a3[v15 + 1] = reduce(xor, [
            AES_T8[AES_SBOX[4 * (a3[v15 + 1] % 2 ** 8)]],
            AES_T5[AES_SBOX[4 * (a3[v15 + 1] >> 24)]],
            AES_T6[AES_SBOX[4 * ((a3[v15 + 1] >> 16) & 0xFF)]],
            AES_T7[AES_SBOX[4 * (a3[v15 + 1] % (2 ** 16) >> 8)]]
        ])
        a3[v15 + 2] = reduce(xor, [
            AES_T8[AES_SBOX[4 * (a3[v15 + 2] % 2 ** 8)]],
            AES_T5[AES_SBOX[4 * (a3[v15 + 2] >> 24)]],
            AES_T6[AES_SBOX[4 * ((a3[v15 + 2] >> 16) & 0xFF)]],
            AES_T7[AES_SBOX[4 * (a3[v15 + 2] % (2 ** 16) >> 8)]]
        ])
        a3[v15 + 3] = reduce(xor, [
            AES_T8[AES_SBOX[4 * (a3[v15 + 3] % 2 ** 8)]],
            AES_T5[AES_SBOX[4 * (a3[v15 + 3] >> 24)]],
            AES_T6[AES_SBOX[4 * ((a3[v15 + 3] >> 16) & 0xFF)]],
            AES_T7[AES_SBOX[4 * (a3[v15 + 3] % (2 ** 16) >> 8)]]
        ])
        i += 1

    return int32_to_bytes(a3)

def sub_2b954(a1, a2, a3):
    a1 = bytes_to_int32(a1)
    a3 = bytes_to_int32(a3)

    v17 = 0
    v16 = b_swap32(a1[0]) ^ a3[0]
    v15 = b_swap32(a1[1]) ^ a3[1]
    v14 = b_swap32(a1[2]) ^ a3[2]
    v13 = b_swap32(a1[3]) ^ a3[3]
    v8 = a3[60] >> 1

    while True:
        raise "關(guān)鍵代碼找我拿qq2891590578"

    v3 = reduce(or_, [
        AES_SIBOX[byte_n(v12, 3)] << 24,
        AES_SIBOX[byte_n(v9, 2)] << 16,
        AES_SIBOX[(v10 % 2 ** 16) >> 8] << 8,
        AES_SIBOX[(v11 % 2 ** 8)]
    ]) ^ a3[v17]

    a2[0] = byte_n(v3, 3)
    a2[1] = byte_n(v3, 2)
    a2[2] = byte_n(v3, 1)
    a2[3] = byte_n(v3, 0)

    v4 = reduce(or_, [
        AES_SIBOX[byte_n(v11, 3)] << 24,
        AES_SIBOX[byte_n(v12, 2)] << 16,
        AES_SIBOX[(v9 % 2 ** 16) >> 8] << 8,
        AES_SIBOX[(v10 % 2 ** 8)]
    ]) ^ a3[v17 + 1]

    a2[4] = byte_n(v4, 3)
    a2[5] = byte_n(v4, 2)
    a2[6] = byte_n(v4, 1)
    a2[7] = byte_n(v4, 0)

    v5 = reduce(or_, [
        AES_SIBOX[byte_n(v10, 3)] << 24,
        AES_SIBOX[byte_n(v11, 2)] << 16,
        AES_SIBOX[(v12 % 2 ** 16) >> 8] << 8,
        AES_SIBOX[(v9 % 2 ** 8)]
    ]) ^ a3[v17 + 2]

    a2[8] = byte_n(v5, 3)
    a2[9] = byte_n(v5, 2)
    a2[10] = byte_n(v5, 1)
    a2[11] = byte_n(v5, 0)

    v6 = reduce(or_, [
        AES_SIBOX[byte_n(v9, 3)] << 24,
        AES_SIBOX[byte_n(v10, 2)] << 16,
        AES_SIBOX[(v11 % 2 ** 16) >> 8] << 8,
        AES_SIBOX[(v12 % 2 ** 8)]
    ]) ^ a3[v17 + 3]

    a2[12] = byte_n(v6, 3)
    a2[13] = byte_n(v6, 2)
    a2[14] = byte_n(v6, 1)
    a2[15] = byte_n(v6, 0)

    return a2

def sub_2be2c(a1, a2, a3, a4, a7):
    # a1, v3, len(a1), v2, bytearray(256)
    a1 = bytes_to_int32(a1)
    a2 = bytes_to_int32(a2)
    a4 = bytes_to_int32(a4)

    v1 = 0
    v2 = 0
    v16 = bytearray(16)

    while a3:
        v16 = sub_2b954(int32_to_bytes(a1[v1:]), v16, int32_to_bytes(a4))

        i = 0
        while True:
            v10 = 0
            if i <= 0xF:
                v8 = 0
                if i < a3:
                    v8 = 1
                v10 = v8
            if not (v10 << 31):
                break

            a2[v2 + i] = v16[i] ^ a7[i]
            a7[i] = byte_n(bytes_to_int32(int32_to_bytes(a1)[i:])[v1], 0)
            i += 1

        a3 -= 16
        v1 += 4
        v2 += 16

        if a3 <= 0x10:
            while i <= 0xF:
                a7[i] = byte_n(bytes_to_int32(int32_to_bytes(a1)[i:])[v1], 0)
                i += 1
            break

    return a2

def sub_aaac(a1, a2):
    a1 = base64.b64decode(a1)

    v1 = [ord(t) for i, t in enumerate(a2) if i < 16]
    v2 = [0 for _ in range(256)]

    v2 = sub_2af14(v1, v2)  # aes

    v2 = sub_2b2d0(v2)  # 可能是aes解密 初始化密鑰

    v3 = bytearray(512)

    v3 = sub_2be2c(a1, v3, len(a1), v2, bytearray(256))  # aes解密

    return v3[16:80]

def make_ctx(a1, a2):
    v2 = sub_aaac(a1, a2)
    # print([hex(i) for i in v2])
    # v2 = [0x31, 0xa3, 0xf5, 0x5, 0x52, 0x8b, 0x45, 0xce, 0xf7, 0x96, 0xd1, 0x7d, 0x70, 0xa9, 0xa1, 0x3a, 0xde, 0xc, 0x37, 0xe1, 0x40, 0xe5, 0xe, 0x92, 0xfe, 0x9c, 0x38, 0xfa, 0x51, 0x5, 0x3d, 0x33, 0x35, 0x4a, 0x4, 0xbc, 0x96, 0x6f, 0x3c, 0xe9, 0xe8, 0x84, 0x27, 0xdb, 0x0, 0x4a, 0xd4, 0xe9, 0xcf, 0xfc, 0x95, 0x13, 0xc7, 0xa5, 0x1a, 0x64, 0xe1, 0x64, 0x93, 0x2a, 0xbe, 0x86, 0x11, 0x38]
    # 1, 35, 69, 103,137, 171, 205, 239,254, 220, 186, 152,118, 84, 50, 16
    # 118, 84, 50, 16, 254, 220, 186, 152, 137, 171, 205, 239, 1, 35, 69, 103
    v1 = [118, 84, 50, 16, 254, 220, 186, 152, 137, 171, 205, 239, 1, 35, 69, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 183, 201, 233, 121, 164,
          27, 215, 219, 129, 16, 36, 217, 136, 16, 104, 175, 247, 20, 155, 177, 91, 31, 255, 190, 215, 28, 136, 34, 97,
          102, 102, 147, 97, 102, 246, 158, 99, 25, 166, 33, 9, 20, 73, 238, 206, 29, 193, 175, 15, 28, 245, 42, 198,
          23, 71, 19, 70, 16, 169, 1, 149, 22, 253, 98, 37, 30, 246, 83, 20, 116, 2, 145, 230, 33, 210, 201, 251, 19,
          226, 230, 205, 97, 34, 151, 13, 213, 242, 237, 20, 90, 66, 5, 233, 119, 162, 249, 163, 119, 242, 217, 18, 111,
          98, 154, 76, 42, 146, 64, 179, 64, 192, 81, 90, 94, 38, 170, 199, 246, 233, 93, 16, 63, 214, 214, 7, 87, 195,
          66, 57, 252, 255, 145, 214, 124, 151, 68, 234, 188, 164, 169, 207, 220, 75, 112, 188, 188, 190, 198, 126, 140,
          40, 96, 75, 204, 246, 250, 39, 172, 234, 149, 16, 236, 212, 57, 208, 220, 217, 229, 136, 220, 230, 5, 29, 140,
          4, 249, 124, 162, 31, 34, 97, 157, 109, 101, 86, 172, 196, 28, 57, 229, 253, 68, 34, 41, 244, 167, 35, 148,
          171, 57, 160, 147, 245, 195, 89, 91, 101, 151, 255, 42, 69, 125, 36, 239, 245, 209, 93, 132, 133, 146, 204,
          12, 133, 224, 38, 153, 249, 79, 126, 153, 249, 20, 67, 153, 169, 130, 126, 83, 197, 161, 17, 8, 69, 166, 17,
          8, 69, 53, 242, 58, 189, 145, 211, 134, 235, 0, 0, 0, 0]

    backup = [86, 183, 201, 233, 121, 164, 27, 215, 219, 129, 16, 36, 217, 136, 16, 104, 175, 247, 20, 155, 177, 91, 31,
              255, 190, 215, 28, 136, 34, 97, 102, 102, 147, 97, 102, 246, 158, 99, 25, 166, 33, 9, 20, 73, 238, 206,
              29, 193, 175, 15, 28, 245, 42, 198, 23, 71, 19, 70, 16, 169, 1, 149, 22, 253, 98, 37, 30, 246, 83, 20,
              116, 2, 145, 230, 33, 210, 201, 251, 19, 226, 230, 205, 97, 34, 151, 13, 213, 242, 237, 20, 90, 66, 5,
              233, 119, 162, 249, 163, 119, 242, 217, 18, 111, 98, 154, 76, 42, 146, 64, 179, 64, 192, 81, 90, 94, 38,
              170, 199, 246, 233, 93, 16, 63, 214, 214, 7, 87, 195, 66, 57, 252, 255, 145, 214, 124, 151, 68, 234, 188,
              164, 169, 207, 220, 75, 112, 188, 188, 190, 198, 126, 140, 40, 96, 75, 204, 246, 250, 39, 172, 234, 149,
              16, 236, 212, 57, 208, 220, 217, 229, 136, 220, 230, 5, 29, 140, 4, 249, 124, 162, 31, 34, 97, 157, 109,
              101, 86, 172, 196, 28, 57, 229, 253, 68, 34, 41, 244, 167, 35, 148, 171, 57, 160, 147, 245, 195, 89, 91,
              101, 151, 255, 42, 69, 125, 36, 239, 245, 209, 93, 132, 133, 146, 204, 12, 133, 224, 38, 153, 249, 79,
              126, 153, 249, 20, 67, 153, 169, 130, 126, 83, 197, 161, 17, 8, 69, 166, 17, 8, 69, 53, 242, 58, 189, 145,
              211, 134, 235]

    ctx = [bytearray(v1), bytearray(v1), bytearray(v1)]

    ctx[0] = sub_md5(ctx[0], [t ^ 0x36 for t in v2], 0x40)
    ctx[1] = sub_md5(ctx[1], [t ^ 0x36 for t in v2], 0x40)
    ctx[2] = sub_md5(ctx[2], [t ^ 0x5C for t in v2], 0x40)

    return ctx

def sub_604db(a1):
    for i in range(4):
        a1[8 + i] = 0xFF

def sub_a6e8(a1, a2, a3):
    v3, v5, v6, v8 = a1, a2, 0, 0

    while True:
        v8 = v5[v6]
        if int.from_bytes(v3[8:12], 'little', signed=False) > 0:
            sub_604db(a1)

        v3[12 + 2 * v6] = data_a798[v8 >> 4]

        v10 = v5[v6]

        if int.from_bytes(v3[8:12], 'little', signed=False) > 0:
            sub_604db(a1)

        v3[12 + 2 * v6 + 1] = data_a798[v10 & 0xF]

        v6 += 1
        if v6 >= a3:
            break

def sub_abb8():
    v15 = bytearray(45)
    v17 = bytearray([187, 197, 152, 57, 144, 47, 56, 83, 99, 121, 252, 203, 124, 139, 35, 19])

    v15[0] = 0x20
    v15[4] = 0x20
    v15[8] = 0x00

    for i in range(32):
        v15[12 + i] = 0x20

    v15[44] = 0x00

    sub_a6e8(v15, v17, 0x10)

    return v15

def __ror4__(value, count):
    nbits = 32
    value %= 2 ** nbits
    low = value << (nbits - count)
    value >>= count
    value |= low
    return value

# 類似md5的update
def sub_2cda0(a1, a2, a3):
    a1 = bytes_to_int32(a1)
    v205 = a1
    v204 = a3
    v203 = 0
    v202 = a1[0]
    v201 = a1[1]
    v200 = a1[2]
    i = a1[3]
    while True:
        r = v204
        v204 -= 1
        if not r:
            break
        raise "關(guān)鍵代碼找我拿,base64解碼UVEyODkxNTkwNTc4"
    return int32_to_bytes(a1)

# 類似md5的init
def sub_md5(a1, a2, a3):
    a1 = bytes_to_int32(a1)

    v8 = a1[4] + 8 * a3
    if v8 < a1[4]:
        a1[5] += 1
    a1[5] += a3 >> 29
    a1[4] = v8
    v6 = a1[22]
    if v6:
        v9 = 6
        a1 = int32_to_bytes(a1)
        for i in range(64 - v6):
            a1[v9 * 4 + v6 + i] = a2[i]
        a1 = sub_2cda0(a1, a1[v9 * 4:], 1)  # md5
        a1 = bytes_to_int32(a1)
        v3 = 64 - v6
        a2 = a2[v3:]
        a3 -= v3
        a1[22] = 0
        for i in range(64):
            a1[v9 + i] = 0
    v7 = a3 >> 6
    if v7:
        a1 = int32_to_bytes(a1)
        a1 = sub_2cda0(a1, a2, v7)
        a1 = bytes_to_int32(a1)
        v4 = v7 << 6
        a2 = a2[v4:]
        a3 -= v4
    if a3:
        a1[22] = a3
        a1 = int32_to_bytes(a1)
        for i in range(a3):
            a1[24 + i] = a2[i]
        a1 = bytes_to_int32(a1)
    return int32_to_bytes(a1)

def sub_2dd88(a1, a2):
    v32 = 0
    v31 = 0
    v30 = 24
    a2 = bytes_to_int32(a2)
    v2 = a2[22]
    a2 = int32_to_bytes(a2)
    a2[v30 + v2] = 128
    v29 = v2 + 1
    if v29 >= 0x39:
        for i in range(64 - v29):
            a2[v30 + v29 + i] = 0
        v29 = 0
        a2 = sub_2cda0(a2, a2[24:], 1)
    for i in range(56 - v29):
        a2[v30 + v29 + i] = 0
    v3 = v30 + 56
    v4 = v3
    v3 += 1
    a2[v4] = read_int32(a2[v31 + 16:]) % 2 ** 8
    v5 = v3
    v3 += 1
    a2[v5] = (read_int16(a2[v31 + 16:]) >> 8) % 2 ** 8
    v6 = v3
    v3 += 1
    a2[v6] = read_int16(a2[v31 + 18:]) % 2 ** 8
    v7 = v3
    v3 += 1
    a2[v7] = a2[v31 + 19]
    v8 = v3
    v3 += 1
    a2[v8] = read_int32(a2[v31 + 20:]) % 2 ** 8
    v9 = v3
    v3 += 1
    a2[v9] = (read_int16(a2[v31 + 20:]) >> 8) % 2 ** 8
    v10 = v3
    v3 += 1
    a2[v10] = read_int16(a2[v31 + 22:]) % 2 ** 8
    a2[v3] = a2[v31 + 23]
    v3 -= 63
    a2 = sub_2cda0(a2[v31:], a2[v3:], 1)
    a2 = bytes_to_int32(a2)
    a2[(v31 + 88) // 4] = 0
    a2 = int32_to_bytes(a2)
    for i in range(64):
        a2[v3 + i] = 0
    v11 = read_int32(a2[v31:])
    v12 = v32
    v13 = v32 + 1
    a1[v12] = read_int32(a2[v31:]) % 2 ** 8
    v14 = v13
    v13 += 1
    a1[v14] = byte_n(v11, 1)
    v15 = v13
    v13 += 1
    a1[v15] = byte_n(v11, 2)
    v16 = v13
    v13 += 1
    a1[v16] = byte_n(v11, -1)
    v17 = read_int32(a2[v31 + 4:])
    v18 = v13
    v13 += 1
    a1[v18] = v17 % 2 ** 8
    v19 = v13
    v13 += 1
    a1[v19] = byte_n(v17, 1)
    v20 = v13
    v13 += 1
    a1[v20] = byte_n(v17, 2)
    v21 = v13
    v13 += 1
    a1[v21] = byte_n(v17, -1)
    v22 = read_int32(a2[v31 + 8:])
    v23 = v13
    v13 += 1
    a1[v23] = v22 % 2 ** 8
    v24 = v13
    v13 += 1
    a1[v24] = byte_n(v22, 1)
    v25 = v13
    v13 += 1
    a1[v25] = byte_n(v22, 2)
    a1[v13] = byte_n(v22, -1)
    v26 = read_int32(a2[v31 + 12:])
    a1[v13 + 1] = v26 % 2 ** 8
    a1[v13 + 2] = byte_n(v26, 1)
    a1[v13 + 3] = byte_n(v26, -2)
    a1[v13 + 4] = byte_n(v26, -1)

    return a1, a2

def get_oldsign(path='', params='', xy_common_params='', xy_platform_info='', data='', content='',
                main_hmac='', device_id=''):
    """
    生成簽名  根據(jù)main_hmac device_id解密出一個(gè)key。
    """
    ctx = make_ctx(main_hmac, device_id)
    content = bytearray(content, encoding='utf-8') or bytearray(
        ''.join([path, params, xy_common_params, xy_platform_info, data]), encoding='utf-8')
    # content = "/api/sns/v4/search/recommendkeyword=nnss&source=search_result_notes&word_request_id=&geo=eyJsYXRpdHVkZSI6MC4wMDAwMDAsImxvbmdpdHVkZSI6MC4wMDAwMDB9%0Afid=162925699210bf9c0d3447ec1a57edbfc9b9f44f9625&device_fingerprint=20210810140918508c2ccd6e986960ec8432e9c2edd16b01265a5749ac3489&device_fingerprint1=20210810140918508c2ccd6e986960ec8432e9c2edd16b01265a5749ac3489&launch_id=1629277029&tz=Asia%2FShanghai&channel=YingYongBao&versionName=7.1.0&deviceId=879246a0-b385-3400-b59d-76f63fa5baff&platform=android&sid=session.1629264087421090169948&identifier_flag=4&t=1629280763&project_id=ECFAAF&build=7010138&x_trace_page_current=search_entry&lang=zh-Hans&app_id=ECFAAF01&uis=lightplatform=android&build=7010138&deviceId=879246a0-b385-3400-b59d-76f63fa5baff".encode()
    # print(content)
    t1 = bytearray(16)
    ctx[0] = sub_md5(ctx[0], content, len(content))
    t1, ctx[0] = sub_2dd88(t1, ctx[0])
    ctx[0] = ctx[2].copy()
    ctx[0] = sub_md5(ctx[0], t1, 16)
    t1, ctx[0] = sub_2dd88(t1, ctx[0])
    # print(t1.hex())
    return t1.hex()

# oldsign = get_oldsign(
#     main_hmac="mXtwWNLkY+tzqBSiMdzc89grN5pkqeSWKe9mayVcsnTdvJBpgLv4CZpN9vGdOm9TITM8m1BwGVYdZAicMfrQ8gaQcY8JZ7Q2WK79Foi0093WoffnaDzet9I+rvtM8PDg",
#     device_id="98fd23e2-8b96-3fdd-92ca-a5d191250cd6", path="/api/sns/v1/system_service/check_code",
#     params="zone=86&phone=15270065469&code=123456",
#     xy_common_params="fid=161728778310debbddf17fc6716ba47ffbd57b170ffb&device_fingerprint=20210317164618c676e8334dd11e5066095451d7ac31a101a234c9b78b1cdb&device_fingerprint1=20210317164618c676e8334dd11e5066095451d7ac31a101a234c9b78b1cdb&launch_id=1617288292&tz=GMT&channel=Lite&versionName=6.86.0.1&deviceId=98fd23e2-8b96-3fdd-92ca-a5d191250cd6&platform=android&sid=session.1617259043556226191363&identifier_flag=0&t=1617272131&project_id=ECFAAF&build=6860179&x_trace_page_current=login_full_screen_sms_page&lang=zh-Hans&app_id=ECFAAF01&uis=light",
#     xy_platform_info="platform=android&build=6860179&deviceId=98fd23e2-8b96-3fdd-92ca-a5d191250cd6")
# print(oldsign)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容