首页
社区
课程
招聘
[原创]某谷米APP 请求接口sign值生成流程分析
发表于: 3天前 636

[原创]某谷米APP 请求接口sign值生成流程分析

3天前
636

地址:4oCLaHR0cHM6Ly9xaWd1bWkuY29t

对我这种新人很友好的一个逆向,顺便练练so逆向,算是新手村级别了

抓包检测

首先,抓不到包。

发现检测方式是NetworkSecurityTrustManager.checkPins,结合okhttp的一些检测:

一段frida脚本献上,spawn模式注入,直接干废

Java.perform(function () {
    console.log("[*] Starting SSL Pinning Bypass for OkHttp 4.x");

    // ============================================================
    // OkHttp CertificatePinner bypass
    // ============================================================
    try {
        var CertificatePinner = Java.use("okhttp3.CertificatePinner");

        CertificatePinner.check$okhttp.overload(
            "java.lang.String",
            "java.util.List"
        ).implementation = function (hostname, peerCertificates) {
            console.log("[+] OkHttp CertificatePinner.check bypassed for: " + hostname);
            return;
        };
    } catch (e) {
        console.log("[-] CertificatePinner.check$okhttp not found: " + e);
    }

    try {
        var CertificatePinner2 = Java.use("okhttp3.CertificatePinner");
        CertificatePinner2.check.overload(
            "java.lang.String",
            "java.util.List"
        ).implementation = function (hostname, peerCertificates) {
            console.log("[+] OkHttp CertificatePinner.check bypassed for: " + hostname);
            return;
        };
    } catch (e) {
        console.log("[-] CertificatePinner.check overload not found: " + e);
    }

    // ============================================================
    // TrustManager bypass
    // ============================================================
    try {
        var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
        var SSLContext = Java.use("javax.net.ssl.SSLContext");

        var TrustManager = Java.registerClass({
            name: "com.custom.TrustManager",
            implements: [X509TrustManager],
            methods: {
                checkClientTrusted: function (chain, authType) {},
                checkServerTrusted: function (chain, authType) {},
                getAcceptedIssuers: function () {
                    return [];
                }
            }
        });

        var TrustManagers = [TrustManager.$new()];
        var sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, TrustManagers, null);

        var sslSocketFactory = sslContext.getSocketFactory();
        console.log("[+] Custom TrustManager registered");
    } catch (e) {
        console.log("[-] TrustManager bypass failed: " + e);
    }

    // ============================================================
    // OkHttpClient.Builder SSLSocketFactory bypass
    // ============================================================
    try {
        var Builder = Java.use("okhttp3.OkHttpClient$Builder");

        Builder.sslSocketFactory.overload(
            "javax.net.ssl.SSLSocketFactory",
            "javax.net.ssl.X509TrustManager"
        ).implementation = function (sslSocketFactory, trustManager) {
            console.log("[+] OkHttpClient.Builder.sslSocketFactory hooked");
            var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
            var TrustManager = Java.registerClass({
                name: "com.custom.TrustManager2",
                implements: [X509TrustManager],
                methods: {
                    checkClientTrusted: function (chain, authType) {},
                    checkServerTrusted: function (chain, authType) {},
                    getAcceptedIssuers: function () {
                        return [];
                    }
                }
            });
            var SSLContext = Java.use("javax.net.ssl.SSLContext");
            var ctx = SSLContext.getInstance("TLS");
            ctx.init(null, [TrustManager.$new()], null);
            return this.sslSocketFactory(ctx.getSocketFactory(), TrustManager.$new());
        };
    } catch (e) {
        console.log("[-] OkHttpClient Builder hook failed: " + e);
    }

    // ============================================================
    // HostnameVerifier bypass
    // ============================================================
    try {
        var HostnameVerifier = Java.use("javax.net.ssl.HostnameVerifier");
        var OkHostnameVerifier = Java.use("okhttp3.internal.tls.OkHostnameVerifier");

        OkHostnameVerifier.verify.overload(
            "java.lang.String",
            "javax.net.ssl.SSLSession"
        ).implementation = function (hostname, session) {
            console.log("[+] OkHostnameVerifier bypassed for: " + hostname);
            return true;
        };
    } catch (e) {
        console.log("[-] OkHostnameVerifier bypass failed: " + e);
    }

    // ============================================================
    // WebViewClient SSL Error bypass
    // ============================================================
    try {
        var WebViewClient = Java.use("android.webkit.WebViewClient");
        WebViewClient.onReceivedSslError.implementation = function (
            webView,
            sslErrorHandler,
            sslError
        ) {
            console.log("[+] WebViewClient.onReceivedSslError bypassed");
            sslErrorHandler.proceed();
        };
    } catch (e) {
        console.log("[-] WebViewClient hook failed: " + e);
    }

    // ============================================================
    // NetworkSecurityConfig bypass
    // ============================================================
    try {
        var NetworkSecurityTrustManager = Java.use(
            "android.security.net.config.NetworkSecurityTrustManager"
        );
        NetworkSecurityTrustManager.checkPins.implementation = function (chain) {
            console.log("[+] NetworkSecurityTrustManager.checkPins bypassed");
            return;
        };
    } catch (e) {
        console.log("[-] NetworkSecurityTrustManager not found: " + e);
    }

    console.log("[*] SSL Pinning Bypass script loaded successfully");
});

sign算法在哪里?

然后就可以开始寻找sign算法了。首先我们会想到直接hook messagedigest算法,但是很遗憾,这么一hook下来什么都找不到。静态分析搜索sign,找到一个请求拼接的函数。混淆的很气人,全部都是o,O,0三个字符,难以分辨。找到一个okhttp拦截器。smali还原后大概如下。这个包com.uxin看起来像个第三方sdk,但是实际上是他们自家的东西。

package com.uxin.collect.certpin;

import android.content.Context;
import android.text.TextUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class OkHttpInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        // 1. 获取原始请求
        Request originalRequest = chain.request();
        Request.Builder requestBuilder = originalRequest.newBuilder();

        // 2. 读取全局配置类
        OooOOO0 config = OooOOO.OooO0OO();
        String header_c = config.OooOOo0();
        String header_identify = config.OooOOoo();
        String header_appId = config.OooOOO();
        String header_visitorId = config.OooOo0o();
        String authToken = OooOOO.OooO0O0();

        // 3. 读取设备信息
        String requestId = com.uxin.base.utils.device.OooO00o.Oooo00O();
        Context context = o00O0o0O.o00OOO00.OooO0o0().OooO0Oo();
        String ua = com.uxin.base.utils.device.OooO00o.OoooOO0(context, requestId);
        String deviceName = com.uxin.base.utils.device.OooO00o.OooOO0();
        String isV8aAbi = com.uxin.base.utils.device.OooO00o.OooooOO();
        String deviceId = com.uxin.base.utils.device.OooO00o.OoooO00();
        boolean isDarkMode = false;
        if (o00O0oOo.o0000.OooO0Oo() != null) {
            isDarkMode = o00O0oOo.o0000.OooO0Oo().OooOO0();
        }

        // 4. 统一添加公共请求头
        requestBuilder.addHeader("ua", ua);
        requestBuilder.addHeader("_c", header_c);
        requestBuilder.addHeader("Connection", "keep-alive");
        requestBuilder.addHeader("Accept", "*/*");
        requestBuilder.addHeader("device_name", deviceName);
        requestBuilder.addHeader("requestId", requestId);
        requestBuilder.addHeader("isV8aAbi", isV8aAbi);

        // 5. 条件添加请求头
        if (!TextUtils.isEmpty(header_identify)) {
            requestBuilder.addHeader("identify", header_identify);
        }
        if (!TextUtils.isEmpty(authToken)) {
            requestBuilder.addHeader("x-auth-token", authToken);
        }
        if (!TextUtils.isEmpty(header_appId)) {
            requestBuilder.addHeader("appId", header_appId);
        }
        if (!TextUtils.isEmpty(header_visitorId)) {
            requestBuilder.addHeader("visitor_id", header_visitorId);
        }

        // 6. expand 头拼接(设备信息 + did)
        String expandHeader = "";
        if (!TextUtils.isEmpty(config.OooOOo()) && !"unknown".equals(config.OooOOo())) {
            expandHeader = deviceId + "&did=" + config.OooOOo();
        }
        requestBuilder.addHeader("expand", expandHeader);

        // 7. ie 头(环境开关)
        boolean ieFlag = o00O0o0O.o00OOO0O.OooO0oO().OooOOO0();
        requestBuilder.addHeader("ie", ieFlag ? "1" : "0");

        // 8. dark_mode 头
        requestBuilder.addHeader("dark_mode", String.valueOf(isDarkMode));

        // 9. GET 请求:参数签名,自动加 sign 参数
        Map<String, String> paramMap = new HashMap<>();
        if ("GET".equals(originalRequest.method())) {
            HttpUrl url = originalRequest.url();
            if (url.querySize() > 0) {
                Set<String> names = url.queryParameterNames();
                Iterator<String> iterator = names.iterator();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    String value = url.queryParameter(key);
                    paramMap.put(key, value);
                }
                // 生成签名
                String sign = o00OO0.OooO0O0.OooO00o(paramMap);
                // 追加 sign 参数
                HttpUrl newUrl = url.newBuilder().addQueryParameter("sign", sign).build();
                requestBuilder.url(newUrl);
            }
        }

        // 10. POST Form 请求:参数签名,自动加 sign 字段
        if ("POST".equals(originalRequest.method())) {
            RequestBody body = originalRequest.body();
            if (body instanceof FormBody) {
                FormBody formBody = (FormBody) body;
                FormBody.Builder newFormBuilder = new FormBody.Builder();

                // 遍历原有表单参数
                for (int i = 0; i < formBody.size(); i++) {
                    String name = formBody.encodedName(i);
                    String value = formBody.encodedValue(i);
                    newFormBuilder.addEncoded(name, value);
                    paramMap.put(name, value);
                }

                // 生成签名并添加 sign
                String sign = o00OO0.OooO0O0.OooO00o(paramMap);
                newFormBuilder.addEncoded("sign", sign);
                requestBuilder.post(newFormBuilder.build());
            }
        }

        // 11. 执行最终请求
        Request newRequest = requestBuilder.build();
        return chain.proceed(newRequest);
    }
}

发现了sign字段的添加处。顺藤摸瓜,sign的函数找出来!

method static constructor <clinit>()V
    .registers 1

    .line 1
    const-string v0, "anonymous"

    .line 3
    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

    .line 6
    return-void
.end method
(省略)
method private native nativeEncode(JLjava/util/Map;)Ljava/lang/String;
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(J",
            "Ljava/util/Map<",
            "Ljava/lang/String;",
            "Ljava/lang/String;",
            ">;)",
            "Ljava/lang/String;"
        }
    .end annotation

由construct可知这里进入了native层,其实我对so层逆向不太懂。先hook拿个sign的示例出来。

Java.perform(function () {
    var Tool = Java.use("com.uxin.anonymous.AnonymousTool");
    var HashMap = Java.use("java.util.HashMap");
    var Iterator = Java.use("java.util.Iterator");

    Tool.OooO00o.overload('java.util.Map').implementation = function (map) {
        console.log(" AnonymousTool.OooO00o called");

        try {
            // 把 map 转型为 HashMap(或 Map 接口也行)
            var castedMap = Java.cast(map, Java.use("java.util.Map"));
            var entrySet = castedMap.entrySet();
            var iter = entrySet.iterator();
            while (iter.hasNext()) {
                var entry = Java.cast(iter.next(), Java.use("java.util.Map$Entry"));
                console.log("", entry.getKey(), "=", entry.getValue());
            }
        } catch (e) {
            console.log("map dump error:", e);
        }

        var result = this.OooO00o(map);
        console.log(" SIGN =", result);
        return result;
    };
});

这个hook,卡拦截器上,直接拿到原始输入map和输出的sign。我给的示例,这应该是一个统计接口,也可以得知通向服务器的所有需要sign的请求都在此被签名。

 clientVersion = 32
 deviceInfo = totalMem=6&cpuCount=4
 SIGN = c7857b30b57c36f25c497e1a8f3584f0

so层分析

本来打算直接unidbg了,但看sign出来应该是个md5,整个签名流程应该也没多复杂,ida,启动!

std::string *__usercall CAnonymous::Encrypt@<X0>(
        std::string *__return_ptr retstr@<X8>,
        CAnonymous *this@<X0>,
        std::map<std::string,std::string> *data@<X1>,
        int version@<W2>)
{
  size_t v4; // x0
  std::string *result; // x0
  unsigned __int8 *buffer; // [xsp+18h] [xbp-198h]
  const std::string *v7; // [xsp+30h] [xbp-180h]
  std::string v12; // [xsp+80h] [xbp-130h] BYREF
  std::string __str; // [xsp+98h] [xbp-118h] BYREF
  std::string v14; // [xsp+B0h] [xbp-100h] BYREF
  std::string v15; // [xsp+C8h] [xbp-E8h] BYREF
  std::string inputStr; // [xsp+E0h] [xbp-D0h] BYREF
  std::string __rhs; // [xsp+F8h] [xbp-B8h] BYREF
  std::string __lhs; // [xsp+110h] [xbp-A0h] BYREF
  std::string v19; // [xsp+128h] [xbp-88h] BYREF
  std::string p_msg; // [xsp+140h] [xbp-70h] BYREF
  std::string v21; // [xsp+158h] [xbp-58h] BYREF
  std::string salt; // [xsp+170h] [xbp-40h] BYREF
  EncodeTool1 encodetool1; // [xsp+188h] [xbp-28h] BYREF
  __int64 v24; // [xsp+1A8h] [xbp-8h]
  v24 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  if ( version )
    v7 = &_encodedsalt;
  else
    v7 = &_encodedsalt_old;
  std::string::basic_string();
  EncryptSalt(&salt, v7, &v21); //这里记住,下文要考
  std::string::~string(&v21);
  std::to_string(&__rhs, version);
  std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&__lhs, "version:", &__rhs);
  std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&v19, &__lhs, ",actual salt was:");
  std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&p_msg, &v19, &salt);
  CAnonymous::PrintLog(this, &p_msg);
  std::string::~string(&p_msg);
  std::string::~string(&v19);
  std::string::~string(&__lhs);
  std::string::~string(&__rhs);
  mapToQuery(&v15, data);
  std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&inputStr, &salt, &v15);
  std::string::~string(&v15);
  std::operator+<char>(&v14, "input string was:", &inputStr);
  CAnonymous::PrintLog(this, &v14);
  std::string::~string(&v14);
  EncodeTool1::EncodeTool1(&encodetool1);
  std::string::basic_string[abi:ne180000](retstr);
  if ( this->_useExtEncrypt )
  {
    std::string::operator=(retstr, &inputStr);
  }
  else
  {
    buffer = (unsigned __int8 *)std::string::data[abi:ne180000](&inputStr);
    v4 = std::string::length[abi:ne180000](&inputStr);
    EncodeTool1::GenerateEncodeTool1(&encodetool1, buffer, v4);
    EncodeTool1::ToString(&__str, &encodetool1);
    std::string::operator=[abi:ne180000](retstr, &__str);
    std::string::~string(&__str);
  }
  std::operator+<char>(&v12, "output string was:", retstr);
  CAnonymous::PrintLog(this, &v12);
  std::string::~string(&v12);
  std::string::~string(&inputStr);
  result = (std::string *)std::string::~string(&salt);
  _ReadStatusReg(TPIDR_EL0);
  return result;
}
加密流程之encodetool1

前面的加密流程,应该是对这个函数进行调用。首先我们要知道:EncodeTool1 是什么算法,名字像是 MD5/SHA/HMAC 的封装,看起来sign出来很md5,但万一是魔改算法呢?要看看情况。

void __fastcall EncodeTool1::encode_process(
        EncodeTool1 *this,
        EncodeTool1::encodetool1_context *ctx,
        unsigned __int8 *data)
{
  unsigned __int64 v3; // [xsp+0h] [xbp-C0h]
  __int64 v4; // [xsp+0h] [xbp-C0h]
  unsigned __int64 v5; // [xsp+0h] [xbp-C0h]
  __int64 v6; // [xsp+0h] [xbp-C0h]
  unsigned __int64 v7; // [xsp+0h] [xbp-C0h]
  __int64 v8; // [xsp+0h] [xbp-C0h]
  __int64 v9; // [xsp+0h] [xbp-C0h]
  __int64 v10; // [xsp+0h] [xbp-C0h]
  __int64 v11; // [xsp+0h] [xbp-C0h]
  __int64 v12; // [xsp+0h] [xbp-C0h]
  __int64 v13; // [xsp+0h] [xbp-C0h]
  __int64 v14; // [xsp+0h] [xbp-C0h]
  __int64 v15; // [xsp+0h] [xbp-C0h]
  __int64 v16; // [xsp+0h] [xbp-C0h]
  __int64 v17; // [xsp+0h] [xbp-C0h]
  __int64 v18; // [xsp+0h] [xbp-C0h]
  __int64 v19; // [xsp+0h] [xbp-C0h]
  __int64 v20; // [xsp+0h] [xbp-C0h]
  __int64 v21; // [xsp+0h] [xbp-C0h]
  __int64 v22; // [xsp+0h] [xbp-C0h]
  __int64 v23; // [xsp+0h] [xbp-C0h]
  __int64 v24; // [xsp+0h] [xbp-C0h]
  __int64 v25; // [xsp+0h] [xbp-C0h]
  __int64 v26; // [xsp+0h] [xbp-C0h]
  __int64 v27; // [xsp+0h] [xbp-C0h]
  __int64 v28; // [xsp+0h] [xbp-C0h]
  __int64 v29; // [xsp+0h] [xbp-C0h]
  __int64 v30; // [xsp+0h] [xbp-C0h]
  __int64 v31; // [xsp+0h] [xbp-C0h]
  __int64 v32; // [xsp+0h] [xbp-C0h]
  __int64 v33; // [xsp+0h] [xbp-C0h]
  __int64 v34; // [xsp+0h] [xbp-C0h]
  __int64 v35; // [xsp+0h] [xbp-C0h]
  unsigned __int64 v36; // [xsp+8h] [xbp-B8h]
  __int64 v37; // [xsp+8h] [xbp-B8h]
  unsigned __int64 v38; // [xsp+8h] [xbp-B8h]
  __int64 v39; // [xsp+8h] [xbp-B8h]
  unsigned __int64 v40; // [xsp+8h] [xbp-B8h]
  __int64 v41; // [xsp+8h] [xbp-B8h]
  __int64 v42; // [xsp+8h] [xbp-B8h]
  __int64 v43; // [xsp+8h] [xbp-B8h]
  __int64 v44; // [xsp+8h] [xbp-B8h]
  __int64 v45; // [xsp+8h] [xbp-B8h]
  __int64 v46; // [xsp+8h] [xbp-B8h]
  __int64 v47; // [xsp+8h] [xbp-B8h]
  __int64 v48; // [xsp+8h] [xbp-B8h]
  __int64 v49; // [xsp+8h] [xbp-B8h]
  __int64 v50; // [xsp+8h] [xbp-B8h]
  __int64 v51; // [xsp+8h] [xbp-B8h]
  __int64 v52; // [xsp+8h] [xbp-B8h]
  __int64 v53; // [xsp+8h] [xbp-B8h]
  __int64 v54; // [xsp+8h] [xbp-B8h]
  __int64 v55; // [xsp+8h] [xbp-B8h]
  __int64 v56; // [xsp+8h] [xbp-B8h]
  __int64 v57; // [xsp+8h] [xbp-B8h]
  __int64 v58; // [xsp+8h] [xbp-B8h]
  __int64 v59; // [xsp+8h] [xbp-B8h]
  __int64 v60; // [xsp+8h] [xbp-B8h]
  __int64 v61; // [xsp+8h] [xbp-B8h]
  __int64 v62; // [xsp+8h] [xbp-B8h]
  __int64 v63; // [xsp+8h] [xbp-B8h]
  __int64 v64; // [xsp+8h] [xbp-B8h]
  __int64 v65; // [xsp+8h] [xbp-B8h]
  __int64 v66; // [xsp+8h] [xbp-B8h]
  __int64 v67; // [xsp+8h] [xbp-B8h]
  __int64 v68; // [xsp+8h] [xbp-B8h]
  unsigned __int64 v69; // [xsp+10h] [xbp-B0h]
  __int64 v70; // [xsp+10h] [xbp-B0h]
  unsigned __int64 v71; // [xsp+10h] [xbp-B0h]
  __int64 v72; // [xsp+10h] [xbp-B0h]
  unsigned __int64 v73; // [xsp+10h] [xbp-B0h]
  __int64 v74; // [xsp+10h] [xbp-B0h]
  __int64 v75; // [xsp+10h] [xbp-B0h]
  __int64 v76; // [xsp+10h] [xbp-B0h]
  __int64 v77; // [xsp+10h] [xbp-B0h]
  __int64 v78; // [xsp+10h] [xbp-B0h]
  __int64 v79; // [xsp+10h] [xbp-B0h]
  __int64 v80; // [xsp+10h] [xbp-B0h]
  __int64 v81; // [xsp+10h] [xbp-B0h]
  __int64 v82; // [xsp+10h] [xbp-B0h]
  __int64 v83; // [xsp+10h] [xbp-B0h]
  __int64 v84; // [xsp+10h] [xbp-B0h]
  __int64 v85; // [xsp+10h] [xbp-B0h]
  __int64 v86; // [xsp+10h] [xbp-B0h]
  __int64 v87; // [xsp+10h] [xbp-B0h]
  __int64 v88; // [xsp+10h] [xbp-B0h]
  __int64 v89; // [xsp+10h] [xbp-B0h]
  __int64 v90; // [xsp+10h] [xbp-B0h]
  __int64 v91; // [xsp+10h] [xbp-B0h]
  __int64 v92; // [xsp+10h] [xbp-B0h]
  __int64 v93; // [xsp+10h] [xbp-B0h]
  __int64 v94; // [xsp+10h] [xbp-B0h]
  __int64 v95; // [xsp+10h] [xbp-B0h]
  __int64 v96; // [xsp+10h] [xbp-B0h]
  __int64 v97; // [xsp+10h] [xbp-B0h]
  __int64 v98; // [xsp+10h] [xbp-B0h]
  __int64 v99; // [xsp+10h] [xbp-B0h]
  __int64 v100; // [xsp+10h] [xbp-B0h]
  unsigned __int64 v101; // [xsp+18h] [xbp-A8h]
  unsigned __int64 v102; // [xsp+18h] [xbp-A8h]
  __int64 v103; // [xsp+18h] [xbp-A8h]
  unsigned __int64 v104; // [xsp+18h] [xbp-A8h]
  __int64 v105; // [xsp+18h] [xbp-A8h]
  unsigned __int64 v106; // [xsp+18h] [xbp-A8h]
  __int64 v107; // [xsp+18h] [xbp-A8h]
  __int64 v108; // [xsp+18h] [xbp-A8h]
  __int64 v109; // [xsp+18h] [xbp-A8h]
  __int64 v110; // [xsp+18h] [xbp-A8h]
  __int64 v111; // [xsp+18h] [xbp-A8h]
  __int64 v112; // [xsp+18h] [xbp-A8h]
  __int64 v113; // [xsp+18h] [xbp-A8h]
  __int64 v114; // [xsp+18h] [xbp-A8h]
  __int64 v115; // [xsp+18h] [xbp-A8h]
  __int64 v116; // [xsp+18h] [xbp-A8h]
  __int64 v117; // [xsp+18h] [xbp-A8h]
  __int64 v118; // [xsp+18h] [xbp-A8h]
  __int64 v119; // [xsp+18h] [xbp-A8h]
  __int64 v120; // [xsp+18h] [xbp-A8h]
  __int64 v121; // [xsp+18h] [xbp-A8h]
  __int64 v122; // [xsp+18h] [xbp-A8h]
  __int64 v123; // [xsp+18h] [xbp-A8h]
  __int64 v124; // [xsp+18h] [xbp-A8h]
  __int64 v125; // [xsp+18h] [xbp-A8h]
  __int64 v126; // [xsp+18h] [xbp-A8h]
  __int64 v127; // [xsp+18h] [xbp-A8h]
  __int64 v128; // [xsp+18h] [xbp-A8h]
  __int64 v129; // [xsp+18h] [xbp-A8h]
  __int64 v130; // [xsp+18h] [xbp-A8h]
  __int64 v131; // [xsp+18h] [xbp-A8h]
  __int64 v132; // [xsp+18h] [xbp-A8h]
  __int64 v133; // [xsp+38h] [xbp-88h]
  __int64 v134; // [xsp+40h] [xbp-80h]
  __int64 v135; // [xsp+48h] [xbp-78h]
  __int64 v136; // [xsp+50h] [xbp-70h]
  __int64 v137; // [xsp+58h] [xbp-68h]
  __int64 v138; // [xsp+60h] [xbp-60h]
  __int64 v139; // [xsp+68h] [xbp-58h]
  __int64 v140; // [xsp+70h] [xbp-50h]
  __int64 v141; // [xsp+78h] [xbp-48h]
  __int64 v142; // [xsp+80h] [xbp-40h]
  __int64 v143; // [xsp+88h] [xbp-38h]
  __int64 v144; // [xsp+90h] [xbp-30h]
  __int64 v145; // [xsp+98h] [xbp-28h]
  __int64 v146; // [xsp+A0h] [xbp-20h]
  __int64 v147; // [xsp+A8h] [xbp-18h]
  __int64 v148; // [xsp+B0h] [xbp-10h]

  _ReadStatusReg(TPIDR_EL0);
  v133 = *(unsigned int *)data;
  v134 = *((unsigned int *)data + 1);
  v135 = *((unsigned int *)data + 2);
  v136 = *((unsigned int *)data + 3);
  v137 = *((unsigned int *)data + 4);
  v138 = *((unsigned int *)data + 5);
  v139 = *((unsigned int *)data + 6);
  v140 = *((unsigned int *)data + 7);
  v141 = *((unsigned int *)data + 8);
  v142 = *((unsigned int *)data + 9);
  v143 = *((unsigned int *)data + 10);
  v144 = *((unsigned int *)data + 11);
  v145 = *((unsigned int *)data + 12);
  v146 = *((unsigned int *)data + 13);
  v147 = *((unsigned int *)data + 14);
  v148 = *((unsigned int *)data + 15);
  v69 = ctx->state[1];
  v36 = ctx->state[2];
  v3 = ctx->state[3];
  v101 = (v36 & v69 | v3 & ~v69) + v133 + ctx->state[0] + 3614090360u;//md5round1的常数1
  v102 = (((unsigned int)v101 >> 25) | (v101 << 7)) + v69;
  v4 = (v69 & v102 | v36 & ~v102) + v134 + v3 + 3905402710u; //md5round1的常数2
  ……
  ctx->state[1] += (((unsigned int)v100 >> 11) | (unsigned __int64)(v100 << 21)) + v68;
  ctx->state[2] += v68;
  ctx->state[3] += v35;
}

注意到:

3614090360 = 0xD76AA478 ✅ MD5 Round1 第1个常数

3905402710 = 0xE8C7B756 ✅ MD5 Round1 第2个常数

好,不用往下看了,应该是md5,没啥问题。

加密流程之maptoquery
bool __usercall mapToQuery@<W0>(
        std::string *__return_ptr retstr@<X8>,
        const std::map<std::string,std::string> *cppMap@<X0>)
{
  _BOOL8 result; // x0
  std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long> >::pointer v3; // [xsp+20h] [xbp-B0h]
  std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long> >::pointer __lhs; // [xsp+28h] [xbp-A8h]
  std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long> > v7; // [xsp+70h] [xbp-60h] BYREF
  std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long>::__iter_pointer ptr; // [xsp+78h] [xbp-58h]
  std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long> > __x; // [xsp+80h] [xbp-50h] BYREF
  std::string v10; // [xsp+88h] [xbp-48h] BYREF
  std::string __str; // [xsp+A0h] [xbp-30h] BYREF
  std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string >,std::__tree_node<std::__value_type<std::string,std::string >,void *> *,long> > __y; // [xsp+B8h] [xbp-18h] BYREF
  std::map<std::string,std::string>::const_iterator it; // [xsp+C0h] [xbp-10h] BYREF
  __int64 v14; // [xsp+C8h] [xbp-8h]
  v14 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  std::string::basic_string[abi:ne180000](retstr);
  for ( it.__i_.__ptr_ = std::map<std::string,std::string>::begin[abi:ne180000](cppMap).__i_.__ptr_;
        ;
        std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string>,std::__tree_node<std::__value_type<std::string,std::string>,void *> *,long>>::operator++[abi:ne180000](&it) )
  {
    __y.__i_.__ptr_ = std::map<std::string,std::string>::end[abi:ne180000](cppMap).__i_.__ptr_;
    result = std::operator!=[abi:ne180000](&it, &__y);
    if ( !result )
      break;
    __lhs = std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string>,std::__tree_node<std::__value_type<std::string,std::string>,void *> *,long>>::operator->[abi:ne180000](&it);
    std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&v10, &__lhs->first, "=");
    v3 = std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string,std::string>,std::__tree_node<std::__value_type<std::string,std::string>,void *> *,long>>::operator->[abi:ne180000](&it);
    std::operator+[abi:ne180000]<char,std::char_traits<char>,std::allocator<char>>(&__str, &v10, &v3->second);
    std::string::operator+=[abi:ne180000](retstr, &__str);
    std::string::~string(&__str);
    std::string::~string(&v10);
    ptr = it.__i_.__ptr_;
    __x.__i_.__ptr_ = ZNSt6__ndk14nextB8ne180000INS_20__map_const_iteratorINS_21__tree_const_iteratorINS_12__value_typeINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES9_EEPNS_11__tree_nodeISA_PvEElEEEETnNS_9enable_ifIXsr29__has_input_iterator_categoryIT_EE5valueEiE4typeELi0EEESI_SI_NS_15iterator_traitsISI_E15difference_typeE(
                        it,
                        1).__i_.__ptr_;
    v7.__i_.__ptr_ = std::map<std::string,std::string>::end[abi:ne180000](cppMap).__i_.__ptr_;
    if ( std::operator!=[abi:ne180000](&__x, &v7) )
      std::string::operator+=[abi:ne180000](retstr, "&");
  }
  _ReadStatusReg(TPIDR_EL0);
  return result;
}


得到这个maptoquery后,map拼接的方式也就出来了。

saltkey1=val1&key2=val2&key3=val3

salt在哪里?

加密流程之salt

可恶!这一段卡我好久!!!

_encodedsalt点开可以获得一个字符串。本来想ida神力,这次出问题啦!

bss:0000000000104D08.0 _ZL12_encodedsalt % 1                     ; __r_..__value_..__s..__is_long_
.bss:0000000000104D08.0                                         ; DATA XREF: __cxx_global_var_init.1+10↑o
.bss:0000000000104D08.0                                         ; CAnonymous::Encrypt(std::map<std::string,std::string> &,int)+58↑o
.bss:0000000000104D08.1               % 1                     ; __r_..__value_..__s..__size_
.bss:0000000000104D09                 % 0x17                  ; __r_..__value_..__s.__data_
.bss:0000000000104D20 ; const std::string _saltKey

这里面哪有啊?一看ref是签名的globalvarinit,需要去找那个函数

; Attributes: static bp-based frame
; void __cdecl _cxx_global_var_init_1()
__cxx_global_var_init.1
obj= -8
var_s0=  0
; __unwind {
SUB             SP, SP, #0x20
STP             X29, X30, [SP,#0x10+var_s0]
ADD             X29, SP, #0x10
NOP
ADR             X0, _ZL12_encodedsalt ; this
STR             X0, [SP,#0x10+obj]
ADRL            X1, a6zf0r8 ; "6zF0R8;/"
BL              _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2B8ne180000ILi0EEEPKc ; std::string::basic_string<0>(char const*)
LDR             X1, [SP,#0x10+obj] ; obj
ADRP            X0, #_ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev_ptr@PAGE
LDR             X0, [X0,#_ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev_ptr@PAGEOFF] ; lpfunc
NOP
ADR             X2, __dso_handle ; lpdso_handle
BL              .__cxa_atexit
LDP             X29, X30, [SP,#0x10+var_s0]
ADD             SP, SP, #0x20 ; ' '
RET
; } // starts at 737FC
; End of function __cxx_global_var_init.1

看中间的adrl,有个salt!直接拼接美滋滋!

不对!生成的sign有问题!

加密流程之encryptsalt
  EncryptSalt(&salt, v7, &v21);

第一次做的时候,我并没有注意这个小小的玩意。

直到sign生产出来用不了,我才发现,我去还有个这函数!不多说了,ida双击!

std::string *__cdecl EncryptSalt(std::string *__return_ptr retstr, const std::string *text, const std::string *p_key)
{
  std::string *result; // x0
  unsigned __int8 __ch; // [xsp+3Fh] [xbp-41h]
  std::string::const_iterator __end1; // [xsp+68h] [xbp-18h] BYREF
  std::string::const_iterator __begin1; // [xsp+70h] [xbp-10h] BYREF
  __int64 v9; // [xsp+78h] [xbp-8h]
  v9 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  std::string::basic_string[abi:ne180000](retstr);
  __begin1.__i_ = std::string::begin[abi:ne180000](text).__i_;
  __end1.__i_ = std::string::end[abi:ne180000](text).__i_;
  while ( 1 )
  {
    result = (std::string *)std::operator!=[abi:ne180000]<char const*>(&__begin1, &__end1);
    if ( ((unsigned __int8)result & 1) == 0 )
      break;
    __ch = *std::__wrap_iter<char const*>::operator*[abi:ne180000](&__begin1);
    if ( isalpha(__ch) )
      isupper(__ch);
    else
      isdigit(__ch);
    std::string::push_back(retstr);
    std::__wrap_iter<char const*>::operator++[abi:ne180000](&__begin1);
  }
  _ReadStatusReg(TPIDR_EL0);
  return result;
}

这段代码的逻辑看似简单:遍历输入字符串 text 的每一个字符:

如果是字母 → 忽略大小写判断

如果是数字 → 也判断一下

然后把这个字符 【处理一下】 塞进结果 retstr 里 最后返回结果

但是这个“处理”怎么处理呢?在这里我们看不到,因为反编译会“吃掉”优化后的一些东西。怀疑encryptsalt这段代码原本是一个字符替换 / 异或 / 凯撒类加密,但反编译后丢失了真正的加密运算,只保留了遍历 + 判定字符类型。

看汇编。

=============== S U B R O U T I N E =======================================

; Attributes: bp-based frame

; std::string *__cdecl EncryptSalt(std::string *__return_ptr __struct_ptr retstr, const std::string *text, const std::string *p_key)
                EXPORT _Z11EncryptSaltRKNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES5_
_Z11EncryptSaltRKNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEES5_
                                        ; CODE XREF: EncryptSalt(std::string const&,std::string)+C↓j
                                        ; DATA XREF: LOAD:00000000000027E8↑o ...

var_80          = -0x80
var_74          = -0x74
var_70          = -0x70
var_6C          = -0x6C
var_68          = -0x68
var_60          = -0x60
var_54          = -0x54
var_50          = -0x50
__ch            = -0x41
var_31          = -0x31
p_key           = -0x30
text            = -0x28
p_encrypted     = -0x20
__end1          = -0x18
__begin1        = -0x10
var_8           = -8
var_s0          =  0

; __unwind { // __gxx_personality_v0
                SUB             SP, SP, #0x90
                STP             X29, X30, [SP,#0x80+var_s0]
                ADD             X29, SP, #0x80
                STR             X0, [SP,#0x80+var_68]
                MOV             X0, X8  ; this
                LDR             X8, [SP,#0x80+var_68]
                STR             X0, [SP,#0x80+var_60]
                MRS             X9, TPIDR_EL0
                LDR             X9, [X9,#0x28]
                STUR            X9, [X29,#var_8]
                STUR            X0, [X29,#p_encrypted]
                STUR            X8, [X29,#text]
                STUR            X1, [X29,#p_key]
                MOV             W8, WZR
                STURB           W8, [X29,#var_31]
                BL              _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2B8ne180000Ev ; std::string::basic_string(void)
                LDUR            X8, [X29,#text]
                STR             X8, [SP,#0x80+__ch+1]
                LDR             X0, [SP,#0x80+__ch+1] ; this
                BL              _ZNKSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5beginB8ne180000Ev ; std::string::begin(void)
                STUR            X0, [X29,#__begin1]
                LDR             X0, [SP,#0x80+__ch+1] ; this
                BL              _ZNKSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE3endB8ne180000Ev ; std::string::end(void)
                STUR            X0, [X29,#__end1]
                B               loc_7390C
; ---------------------------------------------------------------------------

loc_7390C                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+60↑j
                                        ; EncryptSalt(std::string const&,std::string)+174↓j
                SUB             X0, X29, #-__begin1 ; __x
                SUB             X1, X29, #-__end1 ; __y
                BL              _ZNSt6__ndk1neB8ne180000IPKcEEbRKNS_11__wrap_iterIT_EES7_ ; std::operator!=<char const*>(std::__wrap_iter<char const*> const&,std::__wrap_iter<char const*> const&)
                TBZ             W0, #0, loc_73A20
                B               loc_73920
; ---------------------------------------------------------------------------

loc_73920                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+74↑j
                SUB             X0, X29, #-__begin1 ; this
                BL              _ZNKSt6__ndk111__wrap_iterIPKcEdeB8ne180000Ev ; std::__wrap_iter<char const*>::operator*(void)
                LDRB            W8, [X0]
                STRB            W8, [SP,#0x80+__ch]
                LDRB            W0, [SP,#0x80+__ch] ; __ch
;   try {
                BL              _ZL7isalphai ; isalpha(int)
                STR             W0, [SP,#0x80+var_6C]
                B               loc_73940
; ---------------------------------------------------------------------------

loc_73940                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+94↑j
                LDR             W8, [SP,#0x80+var_6C]
                CBZ             W8, loc_739C0
                B               loc_7394C
; ---------------------------------------------------------------------------

loc_7394C                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+A0↑j
                LDRB            W0, [SP,#0x80+__ch] ; __ch
                BL              _ZL7isupperi ; isupper(int)
                STR             W0, [SP,#0x80+var_70]
                B               loc_7395C
; ---------------------------------------------------------------------------

loc_7395C                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+B0↑j
                LDR             W8, [SP,#0x80+var_70]
                CBZ             W8, loc_739A0
                B               loc_73968
; ---------------------------------------------------------------------------

loc_73968                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+BC↑j
                LDR             X0, [SP,#0x80+var_60] ; this
                LDRB            W9, [SP,#0x80+__ch]
                MOV             W8, #0x9B
                SUBS            W1, W8, W9
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc ; std::string::push_back(char)
                B               loc_73980
; ---------------------------------------------------------------------------

loc_73980                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+D4↑j
                B               loc_739BC
; ---------------------------------------------------------------------------
;   cleanup() // owned by 73934
                MOV             X8, X0
                LDR             X0, [SP,#0x80+var_60]
                STR             X8, [SP,#0x80+var_50]
                MOV             W8, W1
                STR             W8, [SP,#0x80+var_54]
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev ; std::string::~string()
                B               loc_73A64
; ---------------------------------------------------------------------------

loc_739A0                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+B8↑j
                LDR             X0, [SP,#0x80+var_60] ; this
                LDRB            W9, [SP,#0x80+__ch]
                MOV             W8, #0xDB
                SUBS            W1, W8, W9
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc ; std::string::push_back(char)
                B               loc_739B8
; ---------------------------------------------------------------------------

loc_739B8                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+10C↑j
                B               loc_739BC
; ---------------------------------------------------------------------------

loc_739BC                               ; CODE XREF: EncryptSalt(std::string const&,std::string):loc_73980↑j
                                        ; EncryptSalt(std::string const&,std::string):loc_739B8↑j
                B               loc_73A10
; ---------------------------------------------------------------------------

loc_739C0                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+9C↑j
                LDRB            W0, [SP,#0x80+__ch] ; __ch
                BL              _ZL7isdigiti ; isdigit(int)
                STR             W0, [SP,#0x80+var_74]
                B               loc_739D0
; ---------------------------------------------------------------------------

loc_739D0                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+124↑j
                LDR             W8, [SP,#0x80+var_74]
                CBZ             W8, loc_739F8
                B               loc_739DC
; ---------------------------------------------------------------------------

loc_739DC                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+130↑j
                LDR             X0, [SP,#0x80+var_60] ; this
                LDRB            W9, [SP,#0x80+__ch]
                MOV             W8, #0x69 ; 'i'
                SUBS            W1, W8, W9
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc ; std::string::push_back(char)
                B               loc_739F4
; ---------------------------------------------------------------------------

loc_739F4                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+148↑j
                B               loc_73A0C
; ---------------------------------------------------------------------------

loc_739F8                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+12C↑j
                LDR             X0, [SP,#0x80+var_60] ; this
                LDRB            W1, [SP,#0x80+__ch]
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc ; std::string::push_back(char)
;   } // starts at 73934
                B               loc_73A08
; ---------------------------------------------------------------------------

loc_73A08                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+15C↑j
                B               loc_73A0C
; ---------------------------------------------------------------------------

loc_73A0C                               ; CODE XREF: EncryptSalt(std::string const&,std::string):loc_739F4↑j
                                        ; EncryptSalt(std::string const&,std::string):loc_73A08↑j
                B               loc_73A10
; ---------------------------------------------------------------------------

loc_73A10                               ; CODE XREF: EncryptSalt(std::string const&,std::string):loc_739BC↑j
                                        ; EncryptSalt(std::string const&,std::string):loc_73A0C↑j
                B               loc_73A14
; ---------------------------------------------------------------------------

loc_73A14                               ; CODE XREF: EncryptSalt(std::string const&,std::string):loc_73A10↑j
                SUB             X0, X29, #-__begin1 ; this
                BL              _ZNSt6__ndk111__wrap_iterIPKcEppB8ne180000Ev ; std::__wrap_iter<char const*>::operator++(void)
                B               loc_7390C
; ---------------------------------------------------------------------------

loc_73A20                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+70↑j
                MOV             W8, #1
                STURB           W8, [X29,#var_31]
                LDURB           W8, [X29,#var_31]
                TBNZ            W8, #0, loc_73A40
                B               loc_73A34
; ---------------------------------------------------------------------------

loc_73A34                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+188↑j
                LDR             X0, [SP,#0x80+var_60]
                BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev ; std::string::~string()
                B               loc_73A40
; ---------------------------------------------------------------------------

loc_73A40                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+184↑j
                                        ; EncryptSalt(std::string const&,std::string)+194↑j
                MRS             X8, TPIDR_EL0
                LDR             X8, [X8,#0x28]
                LDUR            X9, [X29,#var_8]
                SUBS            X8, X8, X9
                B.NE            loc_73A8C
                B               loc_73A58
; ---------------------------------------------------------------------------

loc_73A58                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+1AC↑j
                LDP             X29, X30, [SP,#0x80+var_s0]
                ADD             SP, SP, #0x90
                RET
; ---------------------------------------------------------------------------

loc_73A64                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+F4↑j
                LDR             X0, [SP,#0x80+var_50]
                STR             X0, [SP,#0x80+var_80]
                MRS             X8, TPIDR_EL0
                LDR             X8, [X8,#0x28]
                LDUR            X9, [X29,#var_8]
                SUBS            X8, X8, X9
                B.NE            loc_73A8C
                B               loc_73A84
; ---------------------------------------------------------------------------

loc_73A84                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+1D8↑j
                LDR             X0, [SP,#0x80+var_80]
                BL              _Unwind_Resume
; ---------------------------------------------------------------------------

loc_73A8C                               ; CODE XREF: EncryptSalt(std::string const&,std::string)+1A8↑j
                                        ; EncryptSalt(std::string const&,std::string)+1D4↑j
                BL              .__stack_chk_fail
; } // starts at 738A8
; End of function EncryptSalt(std::string const&,std::string)

其实最关键的几行:

; 大写字母 (isupper = true)
MOV W8, #0x9B
SUBS W1, W8, W9    ; push_back(0x9B - ch)

; 小写字母 (isalpha but not isupper)
MOV W8, #0xDB
SUBS W1, W8, W9    ; push_back(0xDB - ch)

; 数字 (isdigit)
MOV W8, #0x69      ; 'i' = 105
SUBS W1, W8, W9    ; push_back(0x69 - ch)

; 其他字符
push_back(ch)      ; 原样保留

所以算法也就出来了。拿我们hook出来的东西试试看:

import hashlib

def encrypt_salt(raw: str) -> str:
    result = []
    for ch in raw:
        c = ord(ch)
        if ch.isalpha():
            if ch.isupper():
                result.append(chr(0x9B - c)) 
            else:
                result.append(chr(0xDB - c))
        elif ch.isdigit():
            result.append(chr(0x69 - c))
        else:
            result.append(ch)
    return result

def encrypt_salt_filtered(raw: str) -> str:
    chars = encrypt_salt(raw)
    return "".join(chars)

def map_to_query(params: dict) -> str:
    return "&".join(f"{k}={v}" for k, v in sorted(params.items()))

def sign(params: dict) -> str:
    salt = encrypt_salt_filtered("6zF0R8;/")
    print("salt:", salt)
    input_str = salt + map_to_query(params)
    print("input:", input_str)
    return hashlib.md5(input_str.encode()).hexdigest()

params = {
    "clientVersion": "32",
    "deviceInfo": "totalMem=6&cpuCount=4",
}
print("sign:", sign(params))
print("expected: c7857b30b57c36f25c497e1a8f3584f0")

可以看到是符合的。完结撒花。



传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 104
活跃值: (8377)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
2天前
0
游客
登录 | 注册 方可回帖
返回