最新版xhs_9_20_0
补环境
网上教程挺多,我直接丢个成品
package com.ruhua.study.xhs;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.utils.Inspector;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.virtualmodule.android.JniGraphics;
import com.github.unidbg.virtualmodule.android.MediaNdkModule;
import com.github.unidbg.virtualmodule.android.SystemProperties;
import com.sun.jna.Pointer;
import okhttp3.*;
import okio.Buffer;
import okio.BufferedSink;
import org.apache.commons.codec.binary.Base64;
import unicorn.Arm64Const;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public class demo1 extends AbstractJni implements IOResolver {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public long num;
public DvmClass XhsHttpInterceptor;//静态
public DvmObject<?> xhshttpinterceptorObject; //实例
static Request request1;
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
System.out.println("open file:" + pathname);
return null;
}
demo1(){
// 创建模拟器实例
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("").addBackendFactory(new Unicorn2Factory(false)).build();
// 添加IO接口要加这句
emulator.getSyscallHandler().addIOResolver(this);
// 获取模拟器的内存操作接口
final Memory memory = emulator.getMemory();
// 设置系统类库解析
memory.setLibraryResolver(new AndroidResolver(23));
// 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
vm = emulator.createDalvikVM(new File("E:\\F\\ud\\unidbg-master\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\ruhua\\study\\xhs\\file\\小红书_9.20.0.apk"));
// 4个虚拟模块
// new AndroidModule(emulator,vm);
// new MediaNdkModule(emulator,vm);
// new JniGraphics(emulator,vm);
// new SystemProperties(emulator,null);
// 设置JNI
vm.setJni(this);
// 打印日志
vm.setVerbose(true);
// 加载目标SO
DalvikModule dm = vm.loadLibrary("xyass", true);
// DalvikModule dm = vm.loadLibrary(new File("unidbg-android/apks/xx/lib.so"), true);
//获取本SO模块的句柄,后续需要用它
module = dm.getModule();
// 调用JNI OnLoad
dm.callJNI_OnLoad(emulator);
XhsHttpInterceptor = vm.resolveClass("com/xingin/shield/http/Native");
// url = "https://edith.xiaohongshu.com/api/nike/v3/update/check/plugin?";
// String jsonBody = "{\"abi\":\"X64\",\"appVersion\":\"8.70.0\",\"appVersionCode\":8700313,\"application\":\"xhs\",\"baseType\":1,\"isTest\":1,\"plugins\":[{\"pluginName\":\"face_recognition\",\"pluginVersion\":\"8700.105.1\"},{\"pluginName\":\"davinci\",\"pluginVersion\":\"8700.108.1\"},{\"pluginName\":\"dev_tool_plugin\",\"pluginVersion\":\"8700.105.1\"},{\"pluginName\":\"animation_widgets\",\"pluginVersion\":\"8700.69.1\"},{\"pluginName\":\"document_preview\",\"pluginVersion\":\"8700.105.1\"},{\"pluginName\":\"sku_poi\",\"pluginVersion\":\"8700.107.1\"},{\"pluginName\":\"ai_engine\",\"pluginVersion\":\"8700.108.1\"},{\"pluginName\":\"xymap\",\"pluginVersion\":\"8700.108.1\"},{\"pluginName\":\"redscanner\",\"pluginVersion\":\"8700.105.1\"},{\"pluginName\":\"hey\",\"pluginVersion\":\"8700.107.1\"}]}";
// RequestBody body = RequestBody.create(
// MediaType.parse("application/json"), // 这里指定了请求体的类型为JSON
// jsonBody
// );
// request = new Request.Builder()
// .url(url)
// .addHeader("xy-direction","52")
// .addHeader("xy-scene","fs=1&point=-1")
// .addHeader("xy-common-params","fid=173889177110c91957adf78217727d0f5e60134f77f8&device_fingerprint1=20250217190846119f418d25f88d13283009703738ffa201c8be0b1852911d&gid=7c3e8c648188540b9d2809bd725d935cbd15932947359e6577830067&device_model=phone&tz=Asia%2FShanghai&channel=Vivo&versionName=8.70.0&deviceId=b2e8e75b-d18d-35dc-87c3-490cf0bb7f30&platform=android&sid=session.1739842095180283261761&identifier_flag=4&project_id=ECFAAF&x_trace_page_current=&lang=zh-Hans&app_id=ECFAAF01&uis=light&teenager=0&device_fingerprint=20250217190846119f418d25f88d13283009703738ffa201c8be0b1852911d&cpu_name=Qualcomm+Technologies%2C+Inc+SM8150&dlang=zh&launch_id=1738893890&overseas_channel=0&folder_type=none&t=1739844213&build=8700313")
// .post(body)
// .build();
};
public void callByAddress(){
// args list
List<Object> list = new ArrayList<>(10);
// jnienv
list.add(vm.getJNIEnv());
// jclazz
list.add(0);
// str1
list.add(vm.addLocalObject(new StringObject(vm, "str1")));
// strArr 假设字符串包含两个字符串
// str6_1
StringObject str6_1 = new StringObject(vm, "str6_1");
vm.addLocalObject(str6_1);
// str6_2
StringObject str6_2 = new StringObject(vm, "str6_2");
vm.addLocalObject(str6_2);
ArrayObject arrayObject = new ArrayObject(str6_1,str6_2);
list.add(vm.addLocalObject(arrayObject));
// 最后的int
list.add(1);
Number number = module.callFunction(emulator, 0x2301, list.toArray());
ArrayObject resultArr = vm.getObject(number.intValue());
System.out.println("result:"+resultArr);
};
public void callByAPI(){
DvmClass RequestCryptUtils = vm.resolveClass("com/meituan/android/payguard/RequestCryptUtils");
StringObject str6_1 = new StringObject(vm, "str6_1");
vm.addLocalObject(str6_1);
StringObject str6_2 = new StringObject(vm, "str6_2");
vm.addLocalObject(str6_2);
ArrayObject arrayObject = new ArrayObject(str6_1,str6_2);
ArrayObject result = RequestCryptUtils.callStaticJniMethodObject(emulator, "encryptRequestWithRandom()", "str1","str2", "str3","str4","str5",arrayObject,1);
System.out.println(result);
};
public Response callfun(){
DvmObject<?> chain = vm.resolveClass("okhttp3/Interceptor$Chain").newObject(null);
DvmObject<?> result = xhshttpinterceptorObject.callJniMethodObject(emulator, "intercept(Lokhttp3/Interceptor$Chain;J)Lokhttp3/Response;", chain, num);
return result != null ? (Response)result.getValue() : null;
}
public void callinitializeNative(){
XhsHttpInterceptor.callStaticJniMethod(emulator, "initializeNative()V");
//V要大写
}
public void callinitialize(){
xhshttpinterceptorObject = XhsHttpInterceptor.newObject("xhs");
num = xhshttpinterceptorObject.callJniMethodLong(emulator,"initialize(Ljava/lang/String;)J","main");
System.out.println("num:>>>>>>>>>>>>>>>>"+num);
}
public static void main(String[] args) {
demo1 demo = new demo1();
demo.callinitializeNative();
demo.callinitialize();
demo.callfun();
System.out.println("result:"+request1.headers());
}
@Override
public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "com/xingin/shield/http/ContextHolder->writeLog(I)V":
{
System.out.println("writeLog:"+vaList.getIntArg(0));
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()); //这个地方补成null出一个问题
// return DvmClass
}
case "com/xingin/shield/http/Base64Helper->decode(Ljava/lang/String;)[B":
{
String input = vaList.getObjectArg(0).toString();
System.out.println("----------------------- :" + input);
System.out.println("----------------------- :" + Arrays.toString(Base64.decodeBase64(input)));
return new ByteArray(vm, Base64.decodeBase64(input));
}
}
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
// case "com/xingin/shield/http/ContextHolder->sLogger:Lcom/xingin/shield/http/ShieldLogger;":{
// return vm.resolveClass("com/xingin/shield/http/ShieldLogger").newObject(null);
// }
case "com/xingin/shield/http/ContextHolder->sDeviceId:Ljava/lang/String;":{
// return vm.resolveClass("java/lang/String").newObject(null);
return new StringObject(vm, "057edeb4-300d-3c49-84e2-5b84e81dd105");
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
@Override
public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature){
case "com/xingin/shield/http/ContextHolder->sAppId:I":{
return -319115519;
}
}
return super.getStaticIntField(vm, dvmClass, signature);
}
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/content/Context->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;":
{
return vm.resolveClass("android/content/SharedPreferences").newObject(vaList.getObjectArg(0).getValue().toString());
}
case "android/content/SharedPreferences->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;":
{
String fileName = dvmObject.getValue().toString();
System.out.println("fileName:"+fileName);
switch (fileName) {
case "s": {
String key = vaList.getObjectArg(0).getValue().toString();
System.out.println("key:" + key);
switch (key) {
case "main": {
return new StringObject(vm, "");
}
case "main_hmac": {
return new StringObject(vm, "AsD3t9V+UHn+VjkBRkjPwCDCBpEP8AjGV/SBhMnn/teJhcLp8JN/YhIEeuUxZa/oLjb1V18PVw0cmbL98OXrHhM+//5OFY/sYGQY637GT4ZnmDCJxyNGR3aNpugeVXpA");
}
}
}
}
}
case "okhttp3/Interceptor$Chain->request()Lokhttp3/Request;":{
String url ="https://mediacloud.xiaohongshu.com/api/httpdns/prefetch";
Request request = new Request.Builder()
.url(url)
.addHeader("accept", "*/*")
.addHeader("host","mediacloud.xiaohongshu.com")
.build();
return vm.resolveClass("okhttp3/Request").newObject(request);
}
case "okhttp3/Request->url()Lokhttp3/HttpUrl;":{
return vm.resolveClass("okhttp3/HttpUrl").newObject(((Request)dvmObject.getValue()).url());
}
case "okhttp3/HttpUrl->encodedPath()Ljava/lang/String;":{
HttpUrl httpUrl = (HttpUrl) dvmObject.getValue();
return new StringObject(vm, httpUrl.encodedPath());
}
case "okhttp3/HttpUrl->encodedQuery()Ljava/lang/String;":{
HttpUrl httpUrl = (HttpUrl) dvmObject.getValue();
String query = httpUrl.encodedQuery();
return new StringObject(vm, query == null ? "" : query);
}
case "okhttp3/Request->body()Lokhttp3/RequestBody;":{
return vm.resolveClass("okhttp3/RequestBody").newObject(((Request)dvmObject.getValue()).body());
}
case "okhttp3/Request->headers()Lokhttp3/Headers;":{
return vm.resolveClass("okhttp3/Headers").newObject(((Request)dvmObject.getValue()).headers());
}
case "okio/Buffer->writeString(Ljava/lang/String;Ljava/nio/charset/Charset;)Lokio/Buffer;":{
Buffer buffer = (Buffer) dvmObject.getValue();
String arg0 = vaList.getObjectArg(0).toString();
Charset arg1 = (Charset) vaList.getObjectArg(1).getValue();
buffer.writeString(arg0,arg1);
return dvmObject;
}
case "okhttp3/Headers->name(I)Ljava/lang/String;":{
Headers buffer = (Headers) dvmObject.getValue();
int index = vaList.getIntArg(0);
System.out.println("index:"+index);
String name = buffer.name(index);
System.out.println("name:"+name);
return new StringObject(vm, name);
}
case "okio/Buffer->clone()Lokio/Buffer;":{
Buffer buffer = (Buffer) dvmObject.getValue();
return vm.resolveClass("okio/Buffer").newObject(buffer.clone());
}
case "okhttp3/Request->newBuilder()Lokhttp3/Request$Builder;":{
Request req = (Request) dvmObject.getValue();
return vm.resolveClass("okhttp3/Request$Builder").newObject(req.newBuilder());
}
case "okhttp3/Request$Builder->header(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;": {
Request.Builder builder = (Request.Builder) dvmObject.getValue();
String arg0 = vaList.getObjectArg(0).toString();
String arg1 = vaList.getObjectArg(1).toString();
builder.header(arg0,arg1);
return vm.resolveClass("okhttp3/Request$Builder").newObject(builder);
}
case "okhttp3/Request$Builder->build()Lokhttp3/Request;":{
Request.Builder builder = (Request.Builder) dvmObject.getValue();
request1 = builder.build();
return vm.resolveClass("okhttp3/Request").newObject(request1);
}
case "okhttp3/Interceptor$Chain->proceed(Lokhttp3/Request;)Lokhttp3/Response;":{
// try {
// Interceptor.Chain chain = (Interceptor.Chain) dvmObject.getValue();
// Request request = (Request) vaList.getObjectArg(0).getValue();
// Response response = chain.proceed(request);
// return vm.resolveClass("okhttp3/Response").newObject(response);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// }
return vm.resolveClass("okhttp3/Response").newObject(null);
}
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}
@Override
public void callVoidMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "okhttp3/RequestBody->writeTo(Lokio/BufferedSink;)V":{
RequestBody requestBody = (RequestBody) dvmObject.getValue();
BufferedSink bufferedSink = (BufferedSink) vaList.getObjectArg(0).getValue();
if(requestBody == null){
return;
}
try {
requestBody.writeTo(bufferedSink);
} catch (IOException e) {
throw new RuntimeException(e);
}
return;
}
}
}
@Override
public int callIntMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "okhttp3/Headers->size()I":{
Headers buffer = (Headers) dvmObject.getValue();
return buffer.size();
}
case "okio/Buffer->read([B)I":{
byte[] arg0 = (byte[]) vaList.getObjectArg(0).getValue();
Buffer buffer = (Buffer) dvmObject.getValue();
return buffer.read(arg0);
}
case "okhttp3/Response->code()I":{
return 200;
}
}
return super.callIntMethodV(vm, dvmObject, signature, vaList);
}
@Override
public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "okio/Buffer-><init>()V":{
return dvmClass.newObject(new Buffer());
}
}
return super.newObjectV(vm, dvmClass, signature, vaList);
}
}
探索纯算
首先要看见字符串的最后赋值出现位置,我们可以直接hook memcpy

去追踪地址123d8140
emulator.traceWrite(0x123d8140L, 0x123d8140L + 132);
去看看0x4bd60 这就是base64

无魔改base64

就是这两部分拼起来base64,先去追第一段,读写0x123e3070

继续去0x1c18c在libc,去0x1dbf8看看

几个memcpy的拼接

hook一下这个函数(),看看入参
在第三次断下后x0为前半部分,x7为后半部分

雀食来自a1+1


追下调用栈,是函数1dd34

继续看眼参数,第四次断下后,x0x1是两个参数

这里前16字节的栈为0xe4ffefb8,继续读写,这里发现0x20这个字节是单独的,所以我们先看下面的

发现是位于函数sub_4B3D0内的,这里已经初见端倪了,我们来分析一下



所以我们不能直接看出来的是第二三个字节以及最后四个字节,目前我们能确认单格式是这样的,前面两个比价困难,我们先看最后四个
20 ?? ?? 00 04 00 00 00 01 53 00 00 00 ?? ?? ?? ??
这里可以很明显的看出,v35来自v34,而此处为我们重命名的这个字符串拼接函数的返回值,在这个大函数中,一共调用了四次,这是第三次

分析这个函数可以得知返回值是一个指向std::string对象的指针,所以了解一下,这个对象的结构体

首先
第一个字节
0x51 = 0b01010001 最后一位是1,说明要使用__long(堆模式)
第一段:0x51
去掉标志位:81 & ~1 = 80
减去 \0 占位:80 - 1 = 79
堆缓冲区分配了 80 字节,可存 79 个字符
第二段:0x43
当前字符串长度为 67 个字符
第三段:0x12411000 地址
存放了当前字符串
所以这里实际上是总字符串长度,刚好为0x53,所以最后两部分实际上都是在获取字符串长度

所以现在只剩下第二三个字节了
交叉引用发现是跟入参相关

a3 = 4 雀食符合我们 2 3字节是00 04的事实,但是从哪里来的呢

继续往上追是sub_467DC,先别急,继续看看入参,这里a3应该还是4


然后继续找调用,向0xe4fff630下一个读写断点

这里追踪到地址是0x4ae64

w8是0x4,w8是上面x29-oxa8来的,继续在0xe4fff458下断点追

追到是0x4a5dc,这里x9就是0x4,看眼代码是从x22来的




继续在0x123e00c0追,看到是libc的地址,应该是memcpy写入,去看眼lr



应该追x22也就是0x123e0060


这俩xor,然后0x4往x27(0x123e0060)中写,x22和x19只差第一个字节,看看这俩16字节哪里来的,分别下断点0xe4fff130 0xe4fff088


这里看到是0x80a58,这里x8是目标信息,指向的是so中目标偏移0x15c50的地址,是一段硬编码




再去看看另一个,赋值地址每个都不一样,貌似涉及了某种算法,先看简单的

回顾下前面,这个字符串最后经过了base64,然后就是我们的16字节和后面的字节进行一个拼接,所以我们现在要对后面的字节是怎么来的进行探讨,也就是0x35及之后

这是第三次调用append函数(偏移1DD30)

第四次发现就被加密了

说明是在这中间,而很明显,在两个append之间,雀食有一坨看起来像加密的函数,不急慢慢看,先hook函数看眼入参

(x0)a1 = 1。(疑似固定值)

(x1)a2是个字符数组,代表build,如下。
0xE表示sso存储,占用了7个字节。



(x2)a3来自于AppId。


(x3)a4为0x4;——之前q1、q2异或的0x4。
(x4)a5是个string类型;

存放了device_id。

(x5)a6=0xe4fff521,似乎存放了16个字节。

(x6)a7 = 0x10,似乎代表a6的大小。

(x7)a8=0x38663439662d3038,存放了8个字符“80-f94f8”也就是deviceid的一部分

我这里已经提前给参数命名了()

改一下参数继续分析

这里清空数据开始正常加密

这里有一个循环不知道在干什么,hook看眼x10

这里123456...的很像rc4的sbox

这里看这个v49也很像rc4的初始化,像key

奇奇怪怪的

没绷住,字符串欺骗可还行

至此0x53个字节分析完了,现在还剩那个q0和最后16个字节,这16字节就是那个包含了四个append和rc4的大参数的sub_4B3D0的a6

然后去对0xe4fff501下读写断点

继续追0x46fb8

这里数据来源为x29-0x18,继续追


继续读写断点跟踪

很明显,追踪到这里的a2,a2由v2赋值

这里的w8就是v2

这里下断点,第二次才是目标数据

下断点

看起来像md5而且经对比,这个算法像是II算法,也就是md5最后一轮

发现了神秘md5常数,但是按理来说这都最后了,应该被变化了才对

搜索sub_84020,发现被调用了五次,从第一次开始追,x11是目标,x8只是魔数而已

追踪x29-0x30也就是x11

这里0xb0a457a3是由之前的魔数+0x7xxxx得到的,所以去追踪0x7xxxx,感觉签名就是md5的魔改

回到sub_8277C函数,看看入参和返回
1\
0000: 76 54 32 10 FE DC BA 98 89 AB CD EF 01 23 45 67 vT2..........#Eg
0000: 5E CB CD 0D 32 96 C8 95 16 B3 BD 04 D2 24 DE 96 ^...2........$..
0010: B6 91 50 F9 57 F8 95 1D 60 66 EA E8 EA 8D 5C 5E ..P.W...`f....\^
0020: CF A0 3C 52 4B B3 46 80 71 C6 64 71 0A 23 13 59 ..<RK.F.q.dq.#.Y
0030: 42 58 89 2F 90 5A C8 59 4A 54 F2 03 2A 23 6B 5E BX./.Z.YJT..*#k^
0000: 5C C2 05 46 7C 46 DE 18 4A 5B A7 5D 91 56 F9 F9 \..F|F..J[.].V..
-----------------------------------------------------------------------------------------
2\
0000: 76 54 32 10 FE DC BA 98 89 AB CD EF 01 23 45 67 vT2..........#Eg
0000: 34 A1 A7 67 58 FC A2 FF 7C D9 D7 6E B8 4E B4 FC 4..gX...|..n.N..
0010: DC FB 3A 93 3D 92 FF 77 0A 0C 80 82 80 E7 36 34 ..:.=..w......64
0020: A5 CA 56 38 21 D9 2C EA 1B AC 0E 1B 60 49 79 33 ..V8!.,.....`Iy3
0030: 28 32 E3 45 FA 30 A2 33 20 3E 98 69 40 49 01 34 (2.E.0.3 >.i@I.4
0000: EE 7E F0 3E 0E C3 7C 74 57 C4 47 58 4D A8 EC 20 .~.>..|tW.GXM..
-----------------------------------------------------------------------------------------
3\
0000: 5C C2 05 46 7C 46 DE 18 4A 5B A7 5D 91 56 F9 F9 \..F|F..J[.].V..
0000: 22 2F 61 70 69 2F 68 74 74 70 64 6E 73 2F 70 72 "/api/httpdns/pr
0010: 65 66 65 74 63 68 22 22 22 22 70 6C 61 74 66 6F efetch""""platfo
0020: 72 6D 3D 61 6E 64 72 6F 69 64 26 62 75 69 6C 64 rm=android&build
0030: 3D 39 32 30 30 38 30 35 26 64 65 76 69 63 65 49 =9200805&deviceI
0040: 64 3D 30 35 37 65 64 65 62 34 2D 33 30 30 64 2D d=057edeb4-300d-
0050: 33 63 34 39 2D 38 34 65 32 2D 35 62 38 34 65 38 3c49-84e2-5b84e8
0060: 31 64 64 31 30 35 22 00 00 00 00 00 00 00 00 00 1dd105".........
0000: D4 66 7B 62 C0 37 8B 29 E4 D6 CA 4F 8D DF FE E0 .f{b.7.)...O....
-----------------------------------------------------------------------------------------
4\
0000: D4 66 7B 62 C0 37 8B 29 E4 D6 CA 4F 8D DF FE E0 .f{b.7.)...O....
0000: 64 3D 30 35 37 65 64 65 62 34 2D 33 30 30 64 2D d=057edeb4-300d-
0010: 33 63 34 39 2D 38 34 65 32 2D 35 62 38 34 65 38 3c49-84e2-5b84e8
0020: 31 64 64 31 30 35 22 80 00 00 00 00 00 00 00 00 1dd105".........
0030: 00 00 00 00 00 00 00 00 38 05 00 00 00 00 00 00 ........8.......
0000: D9 5F A1 45 38 EA C7 98 3D 03 FF F9 8D 39 82 F5 ._.E8...=....9..
-----------------------------------------------------------------------------------------
5\
0000: EE 7E F0 3E 0E C3 7C 74 57 C4 47 58 4D A8 EC 20 .~.>..|tW.GXM..
0000: D9 5F A1 45 38 EA C7 98 3D 03 FF F9 8D 39 82 F5 ._.E8...=....9..
0010: 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 80 02 00 00 00 00 00 00 ................
0000: A3 57 A4 B0 8E CE 26 B8 F8 12 CE F0 41 01 0F 09 .W....&.....A...
第1次调用的返回值,是第3次加密的魔数值——0x36与key异或作为输入,魔数是标准md5魔数;
第2次调用的返回值,是第5次加密的魔数值——0x5C与key异或作为输入,魔数是标准md5魔数;
第3次调用的返回值,是第4次加密的魔数值——明文url + xy-common-params + xy-direction + platform + build + deviceId + xy-scene作为输入,魔数是第1次调用的返回值;
第4次调用的返回值,是第5次加密的输入——第4次调用的输入是第3次加密中,末尾不足64字节的部分;如果第3次调用刚好满足每组都是64字节,第4次调用的输入将是0x80开头,然后在最后的8个字节中,会描述前面的所有块的大小。
第5次加密就是将第4次的16字节进行了填充得到最终的16字节。