[网安实践III] 实验3.逆向分析

1 流量分析

借助Wireshark抓取Android模拟器中“QQ同步助手”登录和同步数据时的流量,回答以下问题:  
(1)筛选流量中,对应域名 "mpssync.3g.qq.com" 的IP地址;
(2)同步报文的TCP流量源IP:端口,目的IP:端口;
(3)分析同步流量的数据特征,并根据这些特征,能否获取报文的一些信息,例如密文长度信息;
(4)保存并上传流量分组文件。
  1. mpssync.3g.qq.com 的IP地址: 183.3.225.36113.96.237.110
  2. 同步报文 TCP 流量, 源 IP 为 10.12.181.187, 源端口为 3550, 目的 IP 为 113.96.237.110, 目的端口为 14000.
  3. 同步流量的数据特征: 传输数据的前4个字节表示后面加密数据的长度(单位字节), 数据的加密类型为分组加密.
  4. 流量分组文件: lab3.pcapng

分析过程

  1. 通过 Wireshark 截包, 过滤条件为 dns, 容易找到对域名 mpssync.3g.qq.com 的 DNS 查询和响应包, 如下图所示, 可以得到该域名对应的 IP 地址有 183.3.225.36116.96.237.110.
  • 注: 在使用同步助手时, 要先下载 QQ, 使用 QQ 账号进行快捷登录才能成功登录 QQ 同步助手. 此外, 需要在模拟器中添加联系人, 这样才能进行数据同步.
  1. 使用过滤条件来过滤与域名 mpssync.3g.qq.com 相关的数据包. 过滤条件如下, 即数据包的源或目的 IP 为域名的IP.
ip.src==113.96.237.110 or ip.dst == 113.96.237.110 or ip.dst == 183.3.225.36 or ip.src==183.3.225.36

如下图为过滤后得到的数据包.

网络安全 逆向 apk 网络安全逆向分析_IP


由于本机的 IP 地址是 10.12.181.187, 因此可以得出, 同步报文的 TCP 流量源 IP 为本机的 IP 10.12.181.187, 源端口为 3550, 目的 IP 为域名 mpssync.3g.qq.com 的其中一个 IP 地址 113.96.237.110, 目的端口为 14000.

3. 选择其中一个报文, 右键"追踪流"并使用"Hex 转储"模式查看上述报文的传输数据.

网络安全 逆向 apk 网络安全逆向分析_数据_02


通过观察上述数据可以发现, 传输的数据是经过加密的, 而且前四个字节表示了加密的数据长度(即传输的数据长度-4), 如上图报文长度为 158h, 加密的数据长度为 154h, 而前 4 个字节的数据即为 154h. 而提供了数据长度的加密类型一般是分组加密.

2 函数调用栈动态跟踪

借助AndroidKiller分析和DDMS的“Method Profiling”功能,分析“QQ手机助手”登录和同步时的函数调用栈,回答以下问题:
(1)程序的包名是什么?
(2)在DDMS中对“QQ手机助手”进行动态跟踪时,PC端与手机(或模拟器)端连接的端口是多少,并参考实验手册中6.2节中的示例,附上截图。
(3)trace文件中,列举若干涉及加解密函数的调用栈(附上截图)。
(4)上传trace文件。
  1. 程序包名: com.tencent.qqpim
  2. PC端与手机模拟器连接的端口为 8600
  3. 网络安全 逆向 apk 网络安全逆向分析_数据_03


  4. trace 文件中涉及加解密函数的调用栈
  • com.tencent.tccsync.TccTeaEncryptDecrypt.tccXXTeaDecrypt
  • com.tencent.tccsync.TccTeaEncryptDecrypt.encrypt
  • com.tencent.tccsync.TccTeaEncryptDecrypt.decrypt
  • com.tencent.tccsync.TccTeaEncryptDecrypt.tccXXTeaEncrypt
  • com.tencent.tccsync.TccTeaEncryptDecrypt.getXXTccTeaEncryptDecryptKey
  • 网络安全 逆向 apk 网络安全逆向分析_web安全_04


  • 网络安全 逆向 apk 网络安全逆向分析_IP_05


  • 网络安全 逆向 apk 网络安全逆向分析_web安全_06


  • 网络安全 逆向 apk 网络安全逆向分析_IP_07


  • 网络安全 逆向 apk 网络安全逆向分析_数据_08


  1. trace 文件: 略

分析过程

  1. 使用 AndroidKiller 打开 QQ手机助手的 APK, 即可得到程序包名: com.tencent.qqpim
  2. 网络安全 逆向 apk 网络安全逆向分析_网络安全 逆向 apk_09


  3. 在手机模拟器(此处使用的雷电模拟器)正在运行, 且其中运行有 QQ手机助手时, 启动 DDMS. 可以看到跟踪到的设备"雷电模拟器"对应的 emulator-5554, 其中可以看到进程列表中有运行的 QQ手机助手的包名, 选中后点击上面的 “Start Method Profiling” 按钮, 并选择 “Trace based profiling” 选项开始跟踪.
  • 注: DDMS 工具通过 Android SDK 的 tools/monitor.bat 文件进行启动, 要求 JDK 版本不能超过 1.8.
  1. 然后在手机模拟器上使用 QQ手机助手进行同步, 之后再点击按钮 “Stop Method Profiling”, 关闭跟踪. 如图在 Temp 路径下得到了跟踪的 trace 文件.
  2. 使用 Android SDK 工具 Traceview 查看上述得到的 trace 文件. 该工具由 tools/tracview.bat 文件启动, 使用如下命令打开 trace 文件.
PS> .\traceview.bat E:\xxx\ddms_lab3.trace    # 需要trace文件的绝对路径

网络安全 逆向 apk 网络安全逆向分析_web安全_10


在 Find 文本框中输入"encrypt"和"decrypt"来搜索和加密相关的函数, 具体的搜索结果和截图见上文的答案.

3 函数参数动态跟踪

安装Frida工具,编写Python脚本,基于动态跟踪结果,回答以下问题:
(1)基于DDMS动态跟踪结果,列举同步过程中,调用了哪些加解密函数,并分析函数参数列表中的参数含义,比如,明文、密文和密钥。
(2)针对每一加解密函数,编写Python脚本,跟踪函数的输入和输出,分析可能采用的密钥、明文或密文数组。
(3)在跟踪函数参数时,同时利用Wireshark抓取同步时的报文,将报文与跟踪的函数参数(密文)进行比对。
(4)上传相关脚本和跟踪结果文件。
  1. 同步过程中使用的加解密函数及其参数含义:
  • byte[] com.tencent.tccsync.TccTeaEncryptDecrypt.tccXXTeaDecrypt(byte[] arg1, byte[] arg2): 参数1为密文, 参数2为密钥, 返回值为明文.
  • byte[] com.tencent.tccsync.TccTeaEncryptDecrypt.tccXXTeaEncrypt(byte[] arg1, byte[] arg2): 参数1为明文, 参数2为密钥, 返回值为密文.
  • byte[] com.tencent.tccsync.TccTeaEncryptDecrypt.encrypt(byte[] arg1): 参数1为明文, 返回值为密文.
  • byte[] com.tencent.tccsync.TccTeaEncryptDecrypt.decrypt(byte[] arg1): 参数1为密文, 返回值为明文.
  • byte[] com.tencent.tccsync.TccTeaEncryptDecrypt.getXXTccTeaEncryptDecryptKsyy(): 返回值为密钥.
  1. 通过分析:
  • tccXXTeaEncrypttccXXTeaDecrypt 函数可能采用的密钥有: [8,6,3,8,1,8,0,2,6,9,6,3,3,3,1,h,^,J,9,o,`] , [1,7,7,2,7,0,1,4,9,7,&,C,O,M,N,:,8,6,3,8,1,8,0,2,6,9,6,3,3,3,1,&,1,2,3,8,5,9,0,5,6,9,&,1,6,2,1,6,6,9,5,1,0] , [D,F,G,#,$,%,^,#,%,$,R,G,H,R,(,&,*,M,<,>,<].
  • tccXXTeaDecrypt解密的明文有一些类似网址的数据.
  • tccXXTeaEncyptencrypt 加密的明文数据难以识别, 猜测应该是编码格式不正确. 不过在其中找到了个人的QQ号等信息
  • decrypt 函数中能在解密数据中看到一些类似网址的信息.
  • getXXTccTeaEncryptDecryptKsyy() 函数并没有截获到.
  1. 如下图可以看到, 使用 tccXXTeaEncypt 加密的数据与实际 Wireshark 截获的发送的数据是一致的. 同样的, decrypt 待解密的数据与 Wireshark 截获的收到的数据是一致的. 不同之处在于实际在发送时会在加密数据前添加了 4 个字节表示数据长度.
  2. 脚本代码
import frida
import sys

# 获取设备
redev = frida.get_remote_device()
print("redev:", redev)
# 获取应用进程
front_app = redev.get_frontmost_application()
print("front_app:", front_app)
qqtb = "com.tencent.qqpim"
session = redev.attach(qqtb)

# Js脚本对应字符串
jscode = """
//转换为数组
var toStr = function(title, obj){
    return title + ":["+JSON.parse(JSON.stringify(obj))+"] ";
} 
//函数标题
var funcTitle = function(funcName) {
    return "Hooked Function: "+funcName+" "
}
//转换为ASCII码输出
var toAscii = function(arr) {
    var s=[];
    for(var i=0;i<arr.length;i++){
        s.push(String.fromCharCode(arr[i]));
    }
    return s;
}
//转换为16进制字符串输出
var toHex = function(arr) {
    var s=[];
    for(var i=0;i<arr.length;i++){
        s.push((arr[i]&0xff).toString(16));
    }
    return s;
}
Java.perform(function () {
    //选择捕获包
    var tted = Java.use("com.tencent.tccsync.TccTeaEncryptDecrypt");
    //设置捕获函数 tccXXTeaDecrypt
    tted.tccXXTeaDecrypt.implementation = function (arg1, arg2) {
        var msg = funcTitle("tccXXTeaDecrypt");
        msg += toStr("arg1(Hex)", toHex(arg1));
        msg += toStr("arg2(ASCII)", toAscii(arg2));
        //输出返回值
        var rtn = this.tccXXTeaDecrypt(arg1, arg2);
        msg += toStr("rtn(ASCII)", toAscii(rtn));
        send(msg);
        return rtn;
    };
    //设置捕获函数 tccXXTeaEncrypt
    tted.tccXXTeaEncrypt.implementation = function (arg1, arg2) {
        var msg = funcTitle("tccXXTeaEncrypt");
        msg += toStr("arg1(ASCII)",toAscii(arg1));
        msg += toStr("arg2(ASCII)", toAscii(arg2));
        //输出返回值
        var rtn = this.tccXXTeaEncrypt(arg1, arg2);
        msg += toStr("rtn(Hex)", toHex(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 encrypt
    tted.encrypt.implementation = function (arg1) {
        var msg = funcTitle("encrypt");
        msg += toStr("arg1(ASCII)", toAscii(arg1));
        var rtn = this.encrypt(arg1);
        msg += toStr("rtn(Hex)",toHex(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 decrypt
    tted.decrypt.implementation = function (arg1) {
        var msg = funcTitle("decrypt");
        msg += toStr("arg1(Hex)", toHex(arg1));
        var rtn = this.decrypt(arg1);
        msg += toStr("rtn(ASCII)",toAscii(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 getXXTccTeaEncryptDecryptKey
    tted.getXXTccTeaEncryptDecryptKey = function () {
        var msg = funcTitle("getXXTccTeaEncryptDecryptKey");
        var rtn = this.getXXTccTeaEncryptDecryptKey();
        msg += toStr("rtn(Hex)",toHex(rtn));
        send(msg);
        return rtn;
    }
});
"""
script = session.create_script(jscode)

def on_message(message,data):
    print(message)


script.on('message', on_message)
script.load()
sys.stdin.read()

分析过程

  1. 安装 Frida 及其相关工具:
    在安装有 python 的基础上, 使用 pip 工具下载 Frida 及其工具
PS> pip install frida    # 安装Frida
PS> pip install frida-tools    # 安装frida-tools
PS> frida --version    # 查看Frida版本

如图所示, 当前下载的 Frida 版本为 14.2.18

网络安全 逆向 apk 网络安全逆向分析_安全_11

  1. 安装 frida-server 并启动:
    在 GitHub 上下载对应的服务端程序, 对应雷电模拟器, 下载的为 frida-server-14.2.18-android-x86.
    雷电模拟器的根目录下提供了 adb 工具, 使用该工具将 frida-server 传至手机模拟器中.
    使用 adb devices 指令可以查看当前连接的设备列表, 在启动雷电模拟器的情况下, 是自动连接的, 如下图所示, "-"后的数字即为连接的端口号.

    使用 push 命令将 frida-server 传至手机模拟器.
PS>  .\adb.exe push .\frida-server-14.2.18-android-x86 /data/local/tmp

使用 chmod 命令更高 frida-server 的权限, 然后启动.

PS> ./adb.exe shell
# cd /data/local/tmp
# chmod 777 frida-server-14.2.18-android-x86    # 更改权限
# ./frida-server-14.2.18-android-x86     # 启动

在一个新的控制台窗口使用命令 frida-ps -U 查看是否有正常输出.

网络安全 逆向 apk 网络安全逆向分析_安全_12

  1. 采用 frida 跟踪 Java 函数参数:
    编写如下 Python 代码:
import frida
import sys

# 获取设备
redev = frida.get_remote_device()
print("redev:", redev)
# 获取应用进程
front_app = redev.get_frontmost_application()
print("front_app:", front_app)
qqtb = "com.tencent.qqpim"
session = redev.attach(qqtb)
# Js脚本对应字符串
jscode = """
Java.perform(function () {
    //选择捕获函数TccTeaEncryptDecrypt
    var tted = Java.use("com.tencent.tccsync.TccTeaEncryptDecrypt");
    //设置捕获函数
    tted.tccXXTeaDecrypt.implementation = function (arg1, arg2) {
        //捕获到了函数
        send("Hook start ...");
        //输出参数1
        send("arg1:");
        send(arg1);
        //输出参数2
        send("arg2:");
        var ss = [];
        for (var i = 0; i < arg2.length; i++) {
            //将参数有ASCII码转为字符
            ss.push(String.fromCharCode(arg2[i]));
        }
        send(ss.toString());
        //输出返回值
        var rtn = this.tccXXTeaDecrypt(arg1, arg2);
        send("rtn:");
        send(rtn);
        return rtn;
    };
});
"""
script = session.create_script(jscode)

def on_message(message,data):
    print(message)

script.on('message', on_message)
script.load()
sys.stdin.read()

该脚本实际上就是选择了 QQ 手机助手中的解密函数 com.tencent.tccsync.TccTeaEncryptDecrypt 进行了捕获操作, 将其函数参数和返回结果进行了输出.

  1. 在手机模拟器中开启 QQ 手机助手后运行脚本. 并在手机模拟器中使用 QQ 手机助手进行同步操作, 即可捕获到数据.

    如图为脚本捕获的数据, 经分析可以看出, 对于该函数 TccTeaEncryptDecrypt, 其第一个参数值有正有负, 应该是密文; 第二个参数长度固定, 且在后续的捕获中重复出现, 应该是解密用的密钥; 返回值为 128 以内的数字, 应该是明文.
  • 注: 运行脚本时出现 frida.ServerNotRunningError: unable to connect to remote frida-server 的错误, 则需要开启端口转发, 使用如下命令:
PS> .\adb.exe forward tcp:27042 tcp:27042
  1. 同理, 使用上述方法, 通过修改脚本中的捕获函数, 可以对其它几个加解密函数进行捕获分析.
    最终脚本如下:
import frida
import sys

# 获取设备
redev = frida.get_remote_device()
print("redev:", redev)
# 获取应用进程
front_app = redev.get_frontmost_application()
print("front_app:", front_app)
qqtb = "com.tencent.qqpim"
session = redev.attach(qqtb)

# Js脚本对应字符串
jscode = """
//转换为数组
var toStr = function(title, obj){
    return title + ":["+JSON.parse(JSON.stringify(obj))+"] ";
} 
//函数标题
var funcTitle = function(funcName) {
    return "Hooked Function: "+funcName+" "
}
//转换为ASCII码输出
var toAscii = function(arr) {
    var s=[];
    for(var i=0;i<arr.length;i++){
        s.push(String.fromCharCode(arr[i]));
    }
    return s;
}
//转换为16进制字符串输出
var toHex = function(arr) {
    var s=[];
    for(var i=0;i<arr.length;i++){
        s.push((arr[i]&0xff).toString(16));
    }
    return s;
}
Java.perform(function () {
    //选择捕获包
    var tted = Java.use("com.tencent.tccsync.TccTeaEncryptDecrypt");
    //设置捕获函数 tccXXTeaDecrypt
    tted.tccXXTeaDecrypt.implementation = function (arg1, arg2) {
        var msg = funcTitle("tccXXTeaDecrypt");
        msg += toStr("arg1(Hex)", toHex(arg1));
        msg += toStr("arg2(ASCII)", toAscii(arg2));
        //输出返回值
        var rtn = this.tccXXTeaDecrypt(arg1, arg2);
        msg += toStr("rtn(ASCII)", toAscii(rtn));
        send(msg);
        return rtn;
    };
    //设置捕获函数 tccXXTeaEncrypt
    tted.tccXXTeaEncrypt.implementation = function (arg1, arg2) {
        var msg = funcTitle("tccXXTeaEncrypt");
        msg += toStr("arg1(ASCII)",toAscii(arg1));
        msg += toStr("arg2(ASCII)", toAscii(arg2));
        //输出返回值
        var rtn = this.tccXXTeaEncrypt(arg1, arg2);
        msg += toStr("rtn(Hex)", toHex(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 encrypt
    tted.encrypt.implementation = function (arg1) {
        var msg = funcTitle("encrypt");
        msg += toStr("arg1(ASCII)", toAscii(arg1));
        var rtn = this.encrypt(arg1);
        msg += toStr("rtn(Hex)",toHex(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 decrypt
    tted.decrypt.implementation = function (arg1) {
        var msg = funcTitle("decrypt");
        msg += toStr("arg1(Hex)", toHex(arg1));
        var rtn = this.decrypt(arg1);
        msg += toStr("rtn(ASCII)",toAscii(rtn));
        send(msg);
        return rtn;
    }
    //设置捕获函数 getXXTccTeaEncryptDecryptKey
    tted.getXXTccTeaEncryptDecryptKey = function () {
        var msg = funcTitle("getXXTccTeaEncryptDecryptKey");
        var rtn = this.getXXTccTeaEncryptDecryptKey();
        msg += toStr("rtn(Hex)",toHex(rtn));
        send(msg);
        return rtn;
    }
});
"""
script = session.create_script(jscode)

def on_message(message,data):
    print(message)


script.on('message', on_message)
script.load()
sys.stdin.read()

截获到的相关函数截图:

网络安全 逆向 apk 网络安全逆向分析_web安全_13


网络安全 逆向 apk 网络安全逆向分析_IP_14


网络安全 逆向 apk 网络安全逆向分析_web安全_15


网络安全 逆向 apk 网络安全逆向分析_安全_16

  1. 在跟踪的同时打开 Wireshark 进行截包, 按上述的过滤条件找到与域名 mpssync.3g.qq.com 的 IP 相关的数据包, 跟踪流后进行数据包和使用脚本捕获的数据进行对比, 可以看到是一致的, 具体见上述结果.

4 二进制代码分析

借助IDA Pro分析“QQ手机助手”中与同步相关的so文件,回答以下问题:
(1)与同步操作有关的文件名称;
(2)比较Java代码(借助AndroidKiller分析)与二进制代码中对应函数参数列表的差异;
(3)还原与加密密钥相关的二进制代码,以C代码形式呈现;
(4)还原二进制代码中核心的加解密代码,以C代码形式呈现;
(5)上传还原的C代码(说明其主要功能)。
  1. 与同步操作有关的文件名称: lib/armeabi/libSync.so
  2. 二进制代码中, 只有 getXXTccTeaEncryptDecryptKey 函数同 Java 代码一样, 函数参数均为 0; 其余 4 个加解密函数, 二进制代码比 Java 代码多 2 个参数: 第一个参数的类型为 JNIEnv*, 第二个参数的类型为 jobject.
  3. 还原的 C 代码: encdec.c
  4. 相关函数的主要功能:
  • getXXTccTeaEncryptDecryptKey: 获取固定密钥 DFG#$%^#%$RGHR(&*M<><
  • 调用函数: 调用函数: sub_16F4
  • decrypt: 给定密文使用固定密钥进行解密.
  • 调用函数: sub_16F4, sub_183C
  • encrypt: 给定明文使用固定密钥进行加密.
  • 调用函数: sub_16F4, sub_183C
  • tccXXTeaDecrypt: 给定明文和密钥进行解密.
  • 调用函数: sub_183C
  • tccXXTeaEncrypt: 给定密文和密钥进行解密.
  • 调用函数: sub_183C
  • sub_16F4: 获取固定密钥 DFG#$%^#%$RGHR(&*M<><
  • sub_183C: 给定待处理的数据, 密钥和模式(加密或者解密)进行加解密.
  • 调用函数: sub_C62C, sub_C49C
  • sub_C62C: 加密函数. 六个参数分别为明文数组指针, 明文长度, 密钥数组指针, 密钥长度, 用于存放密文的缓冲区指针及其长度. 函数的返回值为密文的长度.
  • 调用函数: sub_C4D8
  • sub_C49C: 解密函数. 六个参数分别为密文数组指针, 密文长度, 密钥数组指针, 密钥长度, 用于存放明文的缓冲区指针及其长度. 函数的返回值为明文的长度.
  • 调用函数: sub_C340
  • sub_C4D8: 具体的加密函数, 参数和返回值同 sub_C62C, 对密钥进行 MD5 哈希后使用类似 XXTEA 的加密算法进行数据加密.
  • 调用函数: sub_C2C4, sub_144C0
  • sub_C340: 具体的解密函数, 参数和返回值同 sub_C49C, 对密钥进行 MD5 哈希后使用类似 XXTEA 的解密算法进行数据解密.
  • 调用函数: sub_C2C4, sub_144C0
  • sub_C2C4: MD5 哈希算法
  • sub_144C0: 除法运算.

分析过程

  1. 首先借助 AndroidKiller 工具通过加解密函数所在的包名 com.tencent.tccsync.TccTeaEncryptDecrypt找到对应的 smali 文件, 并进行反编译, 得到其 Java 代码.

    可以看到, 上述提到的加解密函数此处只有定义, 因此是原生函数, 而该类中有一个静态初始化块, 其中有函数 getLibName(), 因此可以得知这些函数是通过外部加载的, 点击该函数跳转到其所在的类, 可以得到加载的库文件名 “Sync”, 因此最终可以确定加解密的函数应该在库文件 libSync.so 中实现.
  2. 使用 IDA Pro 打开 lib/armeabi/libSync.so 文件, 可以在函数列表中找到这些加解密函数.

    如图为 tccTeaEncrypt 函数反编译后的 C 代码, 可以看到其有 4 个参数, 比 Java 代码中的参数多了 2 个, 其他几个函数也是如此. 只有没有参数的 getXXTccTeaEncryptDecryptKey, 其反汇编后的 C 代码也没有参数. 根据 .so 库中有关 Java 导出函数的参数的约定,第一个参数的类型是结构体指针 JNIEnv*,第二个参数的类型一定是 jobject
  3. 经过对反汇编的函数进行分析得到. 和加解密相关的一共有 5 个函数.
    其中 tccXXTeaDecrypttccXXTeaEncryptencryptdecrypt 四个函数中都调用了 sub_183C 函数. 进过分析, 该 sub_183C 函数是进行数据加解密的核心函数, 其完整的函数签名为 jbyteArray __fastcall sub_183C(JNIEnv *env, jbyteArray srcArray, jbyteArray key, int doEncrypt). 其中第二个参数是待处理的数据的 byte[] 类型数组, 加密时是明文, 解密时是密文; 第三个参数是密钥的 byte[] 数组; 第四个参数是一个标记参数, 为 1 时表示加密, 为 0 时表示解密. 此外, 对于 tccXXTeaDecrypttccXXTeaEncrypt两个函数, 密钥数组是直接由参数传递给 sub_183C 函数; 而对于 encryptdecrypt 函数, 密钥是由 sub_16F4函数得到的, 而实际上其返回的是一个固定的密钥 DFG#$%^#%$RGHR(&*M<><. 而函数 getXXTccTeaEncryptDecryptKey 就是直接调用的 sub_16F4 获取的这一固定密钥.
    对于 sub_183C 函数即核心的加解密函数, 其主要分为三部分, 第一部分是由 GetByteArrayElementsGetArrayLength 分别获取待处理数组 SRCArray 和密钥 key 所对应的原始 byte 数组的指针以及数组的长度. 接下来第二部分通过判断第四个参数 doEncrypt 来对数据进行加密或者解密, 加密调用函数 subC62C, 解密调用函数 sub_C49C. 最后第三部分是通过 NewByteArray 函数为加密或解密后的数据创建 Java 的 byte数组, 然后通过 SetByteArrayRegion 将由第二部分的函数得到的加密或解密的数据存储到 Java 的数组中.
    因此, 对于加密数据, 主要是通过调用函数 sub_C62C. 其函数签名为 jsize __fastcall sub_C62C(jbyte *plainBuf, jsize plainLen, jbyte *key, jsize keyLen, void *cipherBuf, int bufLen). 六个参数分别为明文数组指针, 明文长度, 密钥数组指针, 密钥长度, 用于存放密文的缓冲区指针及其长度. 函数的返回值为密文的长度. 在该函数中主要进行了 cipherBuf 的特殊情况处理, 然后主要是通过调用 sub_C4D8 函数完成的数据加密.
    同样的, 对于解密数据, 主要是通过调用函数 sub_C49C. 其函数签名为 jsize __fastcall sub_C49C(jbyte *cipherBuf, jsize cipherLen, jbyte *keyBuf, jsize keyLen, char *plainBuf, int bufLen). 六个参数分别为密文数组指针, 密文长度, 密钥数组指针, 密钥长度, 用于存放明文的缓冲区指针及其长度. 函数的返回值为明文的长度. 在该函数中, 主要的解密操作是通过调用函数 sub_C340 得到的.
    对于 sub_C4D8sub_C340函数, 它们的函数签名分别和调用它们的函数 sub_C62Csub_C49C 是相同的. 通过分析和查阅有关资料, 通过一些比较有特点的数值, 如 520x9e3779b9可以得到, 这两个函数的核心算法与 XXTEA 加解密算法是相同的. 不过在此之前, 均使用了 sub_C2C4 函数进行了处理. 在 sub_C2C4 函数中调用的 sub_C160 中有 1732584193, -271733879 等一组数字, 而这些数字是在 MD5 算法中用到的, 因此可以推测 sub_C2C4 函数实际上为 MD5 哈希函数, 也就是说密钥在使用之前都会进行 MD5 的哈希操作.
  • PS: 逆向的 libSync.so 文件及相应的 IDA 文件: libSync.so libSync.idb

5 报文还原测试(选做)

根据抓取的报文,跟踪的密钥和还原代码,测试对跟踪和分析结果的正确性。上传分析过程和结果。