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

[网安实践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.
    在这里插入图片描述
    [网安实践III] 实验3.逆向分析_第1张图片
  • 注: 在使用同步助手时, 要先下载 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

如下图为过滤后得到的数据包. [网安实践III] 实验3.逆向分析_第2张图片
由于本机的 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 转储"模式查看上述报文的传输数据.
[网安实践III] 实验3.逆向分析_第3张图片
通过观察上述数据可以发现, 传输的数据是经过加密的, 而且前四个字节表示了加密的数据长度(即传输的数据长度-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
    [网安实践III] 实验3.逆向分析_第4张图片
  3. 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
    [网安实践III] 实验3.逆向分析_第5张图片
    [网安实践III] 实验3.逆向分析_第6张图片
    [网安实践III] 实验3.逆向分析_第7张图片
    [网安实践III] 实验3.逆向分析_第8张图片
    在这里插入图片描述
  1. trace 文件: 略

分析过程

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

[网安实践III] 实验3.逆向分析_第14张图片
在 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(): 返回值为密钥.
  2. 通过分析:
    • 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解密的明文有一些类似网址的数据.
      [网安实践III] 实验3.逆向分析_第15张图片
    • tccXXTeaEncyptencrypt 加密的明文数据难以识别, 猜测应该是编码格式不正确. 不过在其中找到了个人的QQ号等信息
      [网安实践III] 实验3.逆向分析_第16张图片
      [网安实践III] 实验3.逆向分析_第17张图片
    • decrypt 函数中能在解密数据中看到一些类似网址的信息.
      [网安实践III] 实验3.逆向分析_第18张图片
    • getXXTccTeaEncryptDecryptKsyy() 函数并没有截获到.
  3. 如下图可以看到, 使用 tccXXTeaEncypt 加密的数据与实际 Wireshark 截获的发送的数据是一致的. 同样的, decrypt 待解密的数据与 Wireshark 截获的收到的数据是一致的. 不同之处在于实际在发送时会在加密数据前添加了 4 个字节表示数据长度.
    [网安实践III] 实验3.逆向分析_第19张图片
    [网安实践III] 实验3.逆向分析_第20张图片
  4. 脚本代码
    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
    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
    在这里插入图片描述

  2. 安装 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 查看是否有正常输出.
    [网安实践III] 实验3.逆向分析_第21张图片

  3. 采用 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 进行了捕获操作, 将其函数参数和返回结果进行了输出.

  4. 在手机模拟器中开启 QQ 手机助手后运行脚本. 并在手机模拟器中使用 QQ 手机助手进行同步操作, 即可捕获到数据.
    [网安实践III] 实验3.逆向分析_第22张图片
    如图为脚本捕获的数据, 经分析可以看出, 对于该函数 TccTeaEncryptDecrypt, 其第一个参数值有正有负, 应该是密文; 第二个参数长度固定, 且在后续的捕获中重复出现, 应该是解密用的密钥; 返回值为 128 以内的数字, 应该是明文.

    • 注: 运行脚本时出现 frida.ServerNotRunningError: unable to connect to remote frida-server 的错误, 则需要开启端口转发, 使用如下命令:
    PS> .\adb.exe forward tcp:27042 tcp:27042
    
  5. 同理, 使用上述方法, 通过修改脚本中的捕获函数, 可以对其它几个加解密函数进行捕获分析.
    最终脚本如下:

    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
    script = session.create_script(jscode)
    
    def on_message(message,data):
        print(message)
    
    
    script.on('message', on_message)
    script.load()
    sys.stdin.read()
    

    截获到的相关函数截图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    [网安实践III] 实验3.逆向分析_第23张图片

  6. 在跟踪的同时打开 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 代码.
    [网安实践III] 实验3.逆向分析_第24张图片
    可以看到, 上述提到的加解密函数此处只有定义, 因此是原生函数, 而该类中有一个静态初始化块, 其中有函数 getLibName(), 因此可以得知这些函数是通过外部加载的, 点击该函数跳转到其所在的类, 可以得到加载的库文件名 “Sync”, 因此最终可以确定加解密的函数应该在库文件 libSync.so 中实现.
    [网安实践III] 实验3.逆向分析_第25张图片
  2. 使用 IDA Pro 打开 lib/armeabi/libSync.so 文件, 可以在函数列表中找到这些加解密函数.
    [网安实践III] 实验3.逆向分析_第26张图片
    如图为 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 报文还原测试(选做)

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

你可能感兴趣的:(网安实践III,web安全,安全)