反编译APK分析APP的加密算法

本文详细解析了微爱APP中sig签名的生成算法,通过反编译APK文件,找到了加密算法HmacSHA1及密钥,并介绍了如何模拟HTTP请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在研究微爱这款应用的请求时,发现每一条请求都携带了sig这个参数,并且sig随着每一次登录都会变化,不同的行为触发的HTTP请求所携带的sig也都不相同。

sig

图片中的sig是经过URLEecode的,我把他Decode一下得到gO5EnwNaGxqEWk/uyGWQn6+sktk=

这显然就是一个经过了Base64编码的字符串,但在对该字符串Base64解码的时候发现结果是乱码,这说明该串是被加密的。

这里我注意到sig是28位的base64,所以我猜测是不是对某些字符串做了MD5的16位编码之后再base64编码,测试了几个字符串结果都与sig不同,只能作罢。

要弄明白加密算法是什么,只剩下了一条路,反编译APK并分析了。

把手机上的APK com.welove520.welove.apk 拷贝到电脑中,使用dex2jar:

d2j-dex2jar.bat com.welove520.welove.apk

得到com.welove520.welove-dex2jar.jar,接着使用JD-GUI就可以查看jar的代码了,因为被混淆过,所以阅读非常困难。

要想从茫茫的被混淆代码中找到自己需要的,是不可能的事情,所以只能另辟蹊径。

打开Android Studio,打开logcat,把手机连上电脑,选择监视welovelog信息,随意在APP中做一些操作,观察logcat窗口,果然出现了有效的信息。

logcat

这里包含了SigUtils,相比这是生成sig的类吧,在代码中搜索SigUtilssig,发现sig注入的代码块和SigUtils的逻辑:
sig
具体算法则在com.welove520.welove.l.e.a(String, String, Map)方法中,查看之:

public static String a(String paramString1, String paramString2, Map<String, String> paramMap)
{
    return encode(paramString1, paramString2, sloveMapData(paramString1, paramString2, paramMap).getBytes());
}

public static String encode(String paramString1, String paramString2, byte[] paramArrayOfByte)
{
    try
    {
        paramString1 = Mac.getInstance("HmacSHA1");
        paramString1.init(new SecretKeySpec("8b5b6eca8a9d1d1f".getBytes(), "HmacSHA1"));
        paramString1 = Base64.encodeToString(paramString1.doFinal(paramArrayOfByte), 0).replaceAll("\r", "").replaceAll("\n", "").trim();
        return paramString1;
    }
    catch (NoSuchAlgorithmException paramString1)
    {
        Log.e("SigUtils:", String.valueOf(paramString1.toString()));
        return "";
    }
    catch (InvalidKeyException paramString1)
    {
        for (;;)
        {
            Log.e("SigUtils:", String.valueOf(paramString1.toString()));
        }
    }
}

找到加密的算法了!
HmacSHA1,且密钥是写死在代码中的8b5b6eca8a9d1d1f,接下来找到被加密的字段就可以了,还是在这个被混淆的类中:

private static String sloveMapData(String paramString1, String paramString2, Map<String, String> paramMap)
{
    Object localObject = new String[paramMap.size()];
    paramString2 = new StringBuilder(paramString2);
    StringBuilder localStringBuilder = new StringBuilder();
    LinkedHashMap localLinkedHashMap = new LinkedHashMap();
    Iterator localIterator = paramMap.entrySet().iterator();
    int i = 0;
    while (localIterator.hasNext())
    {
        localObject[i] = ((String)((Map.Entry)localIterator.next()).getKey());
        i += 1;
    }
    Arrays.sort((Object[])localObject, String.CASE_INSENSITIVE_ORDER);
    int j = localObject.length;
    i = 0;
    while (i < j)
    {
        localIterator = localObject[i];
        localLinkedHashMap.put(localIterator, a((String)paramMap.get(localIterator)));
        i += 1;
    }
    paramMap = localLinkedHashMap.entrySet().iterator();
    while (paramMap.hasNext())
    {
        localObject = (Map.Entry)paramMap.next();
        localStringBuilder.append((String)((Map.Entry)localObject).getKey()).append("=").append((String)((Map.Entry)localObject).getValue()).append("&");
    }
    paramString2.append("&").append(a(paramString1)).append("&").append(a(localStringBuilder));
    Log.d("SigUtils", "SigUtils#" + paramString2.toString());
    return paramString2.toString();
}

配合Logcat信息了解到加密的字段是{GET|POST}&{url}&{content}并且urlcontent都是经过Base64编码的,POSTurl 都是固定的,content则是请求的信息出除去sig字段,对这一构造得到的字符串进行HmacSHA1加密之后再进行Base64编码就是请求中需要的sig了!!

这里有一个坑就是Java自带的Base64编码的的结果是小写的,比如=编码之后是%3d,而微爱请求的则是%3D,这里需要转换大小写。

测试一下算法是否正确(我在这里使用了Golang):

package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/url"
)

func main() {
	key := []byte("8b5b6eca8a9d1d1f")
	mac := hmac.New(sha1.New, key)
	method := "POST"
	u := "http://api.welove520.com/v1/game/house/home"
	content := "access_token=562949961343086-*****9974dd0&love_space_id=8444*****15867"
	mac.Write([]byte(method + "&" + url.QueryEscape(u) + "&" + url.QueryEscape(content)))
	result := mac.Sum(nil)
	s := base64.StdEncoding.EncodeToString(result)
	fmt.Println(s)
}

执行得到的siggO5EnwNaGxqEWk/uyGWQn6+sktk=

与Fiddler截包获得的sig一致:

access_token=562949961343086-*****9974dd0&love_space_id=8444*****15867&sig=gO5EnwNaGxqEWk%2FuyGWQn6%2Bsktk%3D

只不过这里的sig也经过了URLEncode。

有了sig就方便各种模拟HTTP请求了。

### APK反编译概述 APK文件是Android应用程序包(Application Package File),用于分发和安装Android操作系统下的移动应用。APK反编译是指将已打包成二进制形式的应用程序还原为其源代码或者接近于源代码的形式的过程[^1]。 通过反编译可以查看应用程序内部结构,包括但不限于资源文件、布局设计、字符串定义等非机密数据;还可以获取到字节码文件——通常是`.class`转换而来的Dalvik可执行文件(即`.dex`)。需要注意的是,在某些情况下,这种行为可能违反版权法或软件许可协议的规定[^2]。 ### 反编译工具和技术手段 对于想要研究合法开源项目或是出于安全审计目的而言,存在多种技术和工具可以帮助完成这一过程: - **apktool**: 主要用来解压并解析APK中的静态资源文件,如XML配置文件、图片和其他多媒体素材。该工具能够提取未经混淆处理过的原始资源,并允许修改后再重新打包回新的APK版本。 - **Jadx**: 是一款强大的Java逆向工程平台,支持直接打开APK文件并将其中包含的Dex指令集翻译为更易于理解的Java语法表示。它提供了图形界面版GUI以及命令行CLI两种操作方式供用户选择使用[^3]。 - **Frida/Objection**: 这些动态分析框架可以在不改变目标APP本身的情况下注入脚本,从而实时监控其运行状态甚至操控内存变量值的变化情况。这类技术特别适用于探索加密算法实现细节或者是绕过一些简单的防篡改机制[^4]。 ```bash # 使用ApkTool反编译APK $ apktool d example.apk -o output_folder/ ``` ```java // Jadx GUI可以直接拖拽APK文件进行反编译 jadx-gui example.apk ```
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值