最终代码移步第七章节!!!
shield是小红书请求header中的一个加密参数,具体算法是在so中计算的,so文件是libshield.so。我这边主要就讲一下如何利用unidbg调用so生成shield参数。因篇幅可能较长,这篇文章就讲下如何找到Native函数和函数所对应的偏移位置。
一、寻找Native函数
老规矩,首先反编译apk后用JD-GUI工具打开(也可使用jadx等工具),直接搜索shield,结果意料之中,在java层没搜到什么有用的信息,算法大概率放在了so层。
那我们只能在so层中寻找突破口,这里推荐一位大佬开发的工具frida_hook_libart(项目地址:https://github.com/lasting-yang/frida_hook_libart),它是把在libart.so中的常用函数都进行了hook。
总共有三个hook文件hook_RegisterNatives.js、hook_art.js、hook_artmethod.js,我们先使用这个hook_art。
首先在手机上打开frida-server服务,电脑终端运行命令:“frida -U –no-pause -f com.xingin.xhs -l hook_art.js”。
然后找啊找,会发现在NewStringUTF这个函数中,出现了shield结果。这里所对应的so文件是libshield.so 。
然后运行命令:“frida -U –no-pause -f com.xingin.xhs -l hook_RegisterNatives.js”,看一下在libshield里动态注册了哪些Native函数。
可以看到,在so中,注册了initializeNative、intercept、initialize这几个函数。
然后在java层找下,可以找到在“com.xingin.shield.http.XhsHttpInterceptor”这个类下,如下图:
二、寻找Native函数的偏移位置
方法一:
上文有使用Frida hook了so的RegisterNatives函数,hook到的同时,也打印出了函数的offset偏移位置。
initializeNative函数的偏移位为:0x6c11d
intercept函数的偏移位为:0x6b9e9
initialize函数的偏移位为:0x6b801
方法二:
首先使用ida打开libshield.so文件,使用shift+F12打开String window窗口。
在窗口中直接搜索刚刚找到的Native函数,如intercept:
然后点进去,就能找到这里
intercept函数的偏移位就是0x6b9e9。
方法三:
同样使用ida打开libshield.so文件,在Exports窗口中找到JNI_OnLoad(动态注册的函数都会在JNI_OnLoad中进行注册),然后进去F5反汇编下。
然后可以看到sub_9FA0这个函数,点进去。
这边有很多函数,逐个进行查看,最终找到sub_6B360这个地方,点进去。
可以看到java层的“com.xingin.shield.http.XhsHttpInterceptor”这个类,在点进去,就到了刚刚方法二中的这个位置,偏移位直接复制下就好。
三、搭建unidbg环境
首先把unidbg项目拉下来(项目地址:https://github.com/zhkl0228/unidbg)。
在test中新建一个类,然后把小红书apk和对应的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() {
// 创建模拟器实例
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.xhs").build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
// 创建Android虚拟机
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函数
我们分析下这块代码:
1 首先需要调initializeNative这个初始化函数;
2 第二步调initialize函数,获取cPtr;
3 最终生成shield是调用intercept函数,需传入一个“okhttp3/Interceptor$Chain”,paramLong就是第二步获取的cPtr值。
五、补缺失环境
先把初始化函数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();
}
直接运行下,会发现报错了。
这个报错一般就说明要补环境了。
这里先解释下这个:“java/nio/charset/Charset->defaultCharset()Ljava/nio/charset/Charset;”
这句话的意思是调用java/nio/charset/Charset这个类下的defaultCharset函数,返回值是Ljava/nio/charset/Charset,unidbg缺少这一块的处理逻辑,我们需要手动补上。
我们可以直接点“com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:388)”这个位置,进去后会看到一个方法,我们需要重写callStaticObjectMethodV方法,然后把Charset对象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);
}
继续运行,发现又报错了
这次是缺少一个versionCode参数,这个参数可以在AndroidManifest.xml文件中找到,我这个版本是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);
}
然后继续运行,报错了就补环境,我就不把所有的都写出来了,比较多,挑几个讲一下。
sDeviceId:
这里是缺少一个sDeviceId参数,我这里是通过IDA分析这块代码,然后打印GetStringUTFChars函数结果找到的。
ps:
这里分析IDA代码有个技巧,像这块代码,v1大概率就是env,可以在v1上右键“set lvar type”,把类型改为JNIEnv*,然后确定,会发现很多函数能显示出来了,看起来会直观很多。
main_hmac:
这个参数可以直接adb连上去,在s.xml文件下找到。
然后依次把三个函数的所有环境补完,就可以运行生成shield了。
下面是三个Native函数的代码逻辑:
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);
}
六、最终效果
七、最终还原源码
"""
生成小红书app接口参数中的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 "关键代码找我拿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 "关键代码找我拿,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=''):
"""
生成签名 根据main_hmac device_id解密出一个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)