分析ollvm混淆非标准算法
2021-04-12 18:58:00 Author: mp.weixin.qq.com(查看原文) 阅读量:173 收藏

本文为看雪论坛文章

看雪论坛作者ID:laifuling

前言

样例来源于3W班9月份第三题,考察还原ollvm混淆后的算法。

思路

对于经ollvm混淆的算法,动态一步步调试,或者静态分析,都是一个比较大的工程。

通用的思路就是trace汇编指令及寄存器变化,追踪核心的计算式,从而还原算法。尤其对于非标准算法,非常有效。

我这里直接用ida trace关键函数起始地址,然后分析log日志,从而还原出算法。

解题

1. 静态分析apk

(1) jadx静态分析apk

java层代码比较简单
进程名: com.kanxue.ollvm_ndk_9package com.kanxue.ollvm_ndk; import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.kanxue.ollvm_ndk_9.R;import org.apache.commons.lang3.RandomStringUtils; public class MainActivity extends AppCompatActivity {    public static native String UUIDCheckSum(String str);     static {        System.loadLibrary("native-lib");    }     /* access modifiers changed from: protected */    public void onCreate(Bundle bundle) {        super.onCreate(bundle);        setContentView((int) R.layout.activity_main);        final TextView textView = (TextView) findViewById(R.id.sample_text);        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {            public void onClick(View view) {                String randomAlphanumeric = RandomStringUtils.randomAlphanumeric(36);                String UUIDCheckSum = MainActivity.UUIDCheckSum(randomAlphanumeric);                textView.setText(UUIDCheckSum);                Log.e("kanxue", "input: " + randomAlphanumeric + " output: " + UUIDCheckSum);            }        });    }}

从上面获取实用信息:

a. libnative-lib.so和native方法。


b. adb logcat -s "kanxue",查看输入输出结果。

(2) ida静态分析so

解压apk,IDA64打开libnative-lib.so,这里在exports中搜索java,看看是否有UUIDCheckSum方法,定位方法的start和end的偏移地址:
.text:000000000000FF30 Java_com_kanxue_ollvm_1ndk_MainActivity_UUIDCheckSum ..略 .text:00000000000101CC                 RET

2. IDA trace log

修改ida的trace脚本,将start_ea和end_ea修改上面分析so的UUIDCheckSum的偏移起始地址:
start_ea = (module.base + 0xFF30)end_ea = [((module.base + 0x101CC))]
ida attach com.kanxue.ollvm_ndk_9,trace保存日志记录,具体步骤略。

3. 分析log

日志的样例输入输出:
input: OBjr9WRO8BUNXhcB0hsGn6sgqa8y63XDvjIroutput: hybFqNvkiOSHePfdkgOOeN5D_inJbR9K_k0Ts3qMkijRoQ9v

(a) input算法分析1

直接用010Editor打开日志文件,从UUIDCheckSum入参开始,逐步追踪输入的str使用过程。具体跟踪:

Java_com_kanxue_ollvm_1ndk_MainActivity_UUIDCheckSum输入参数:其中X2为string。

根据日志:X2=0000007FF45F9998

---> (*_env)->GetStringUTFChars(_env, _uuid, 0LL);
libart.so:_ZN3art8CheckJNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh

得到:jstring转换为chars的数据存入内存地址:X0=00000078A9094350
经过:libc.so:strdup

X0=00000078A9094380

--->继续分析input数据在哪里处理。

全局搜索:[X0,

观测结果,得出如下:
 

a. input[0x17]=input[0x18]

b.
000000789DD8FE14 LDRSW X22, [SP,#0x18]
000000789DD8FE18 LDRB W20, [X0,X22]
000000789DD8FE24. EOR W24, W20, #1
000000789DD8FE28 STRB W24, [X0,X22]

这里:input下标X22定位位置:000000789DD8FE14。

全局搜索:000000789DD8FE14

除了下标:0x8 0xD 0xE 0x12 0x18 0x22 0x23

其他的input[k]都是如上的计算方式。
 
c. 0x8 0xD等分析


全局搜索[X0, 结果的第823行,发现:

unk_789DD8FE4C LDRSW X20, [SP,#0x18] X20=0000000000000008
000000789DD8FE50 STRB W7, [X0,X20]

追溯W7=0x2D
最终:0x8 0xD 0x12 0x18被赋予固定0x2D


而0xE:

unk_789DD8FE40 LDRSW X20, [SP,#0x18] X20=000000000000000E
000000789DD8FE44 STRB W6, [X0,X20]
//MOV W6, #0x34 X6=0000000000000034
input[0xE]=0x34
 
d. 0x22 和0x23分析


根据:

000000789DD8FF10 STRB W8, [X0,#0x23]
000000789DD8FF18 STRB W8, [X0,#0x22]

反推出[X0,#0x22]和[X0, #0x23]的数据来源。

以上处理过程的c代码:
int len = input.length();int t_23 =0xFF;int t_22 = 0x0;for(int i=0; i<len-2; i++){    if(i == 0x8 || i==0xD || i==0x12 || i==0x18){        input[i] = 0x2D;        continue;    }    if( i==0xE){        input[i] = 0x34;        continue;    }    if(i== 0x17){        input[i] = input[i+1];    }    t_23 = t_23 ^ input[i];    t_22 = t_22 + input[i];    //计算式    input[i] = input[i] ^ 1;}t_22 = t_22 - (t_22 & 0xFFFFFFF0);t_23 = t_23 & 0xF;input[0x22]= key_xmmword_37060[t_22];input[0x23]=key_xmmword_37060[t_23];std::cout << input << std::endl;

上述分析出:

input经sub_FCB4(_uuid_cpy, len); 处理后的样例结果:

NCks8VSN-CTOY-4C1i-Fo7rp-`9x72YEwkba

(b) 算法分析2

3664:
MOV X21, X0 X21=00000078A9094560
这里发现X21一直存入的是:00000078A9094560
定位处理数据地方:
000043B5    libnative_lib.so:000000789DD8F11C    LDRB            W8, [X21,X24]       X8=000000000000004E         ;debug input_new[0x0]=0x4E   X21=00000078A9094560                000043B5    libnative_lib.so:000000789DD8F120    LSR             X8, X8, #2          X8=0000000000000013                       000043B5    libnative_lib.so:000000789DD8F124    LDRB            W1, [X23,X8]        X1=0000000000000068          ;debug X23[0x13]=0x68   result[0x0]=0x68// X23=X23=000000789DDB7010  对应的是:stru_37010
分析发现如下规律:
X23的下标共有4个计算方式:
分析出下标计算式<1>libnative_lib.so:unk_789DD8F11C    LDRB            W8, [X21,X24]       X8=0000000000000043                       libnative_lib.so:000000789DD8F120    LSR             X8, X8, #2          X8=0000000000000010 <2>libnative_lib.so:000000789DD8F130    LDRB            W8, [X21,X24]       X8=0000000000000043                       libnative_lib.so:000000789DD8F134    MOV             W24, W22            X24=000000000000000A                      libnative_lib.so:000000789DD8F138    CMP             X24, X20            C=0 Z=0 N=1                               libnative_lib.so:000000789DD8F13C    UBFIZ           X8, X8, #4, #2      X8=0000000000000030                       libnative_lib.so:000000789DD8F140    B.CS            unk_789DD8F198                                                libnative_lib.so:000000789DD8F144    LDRB            W9, [X21,X24]       X9=0000000000000054                       libnative_lib.so:000000789DD8F148    ORR             X8, X8, X9,LSR#4    X8=0000000000000035libnative_lib.so:000000789DD8F14C    LDRB            W1, [X23,X8]        X1=0000000000000050 <3>libnative_lib.so:000000789DD8F158    LDRB            W8, [X21,X24]       X8=0000000000000054                       libnative_lib.so:000000789DD8F15C    ADD             W24, W22, #1        X24=000000000000000B                      libnative_lib.so:000000789DD8F160    CMP             X24, X20            C=0 Z=0 N=1                               libnative_lib.so:000000789DD8F164    UBFIZ           X8, X8, #2, #4      X8=0000000000000010                       libnative_lib.so:000000789DD8F168    B.CS            unk_789DD8F1C0                                                libnative_lib.so:000000789DD8F16C    LDRB            W9, [X21,X24]       X9=000000000000004F                       libnative_lib.so:000000789DD8F170    ORR             X8, X8, X9,LSR#6    X8=0000000000000011                       libnative_lib.so:000000789DD8F174    LDRB            W1, [X23,X8]        X1=0000000000000066                    ;debug result[0xE]=0x66    <4>                                                       libnative_lib.so:000000789DD8F180    LDRB            W8, [X21,X24]       X8=000000000000004F                       libnative_lib.so:000000789DD8F184    AND             X8, X8, #0x3F       X8=000000000000000F                       libnative_lib.so:000000789DD8F188    LDRB            W1, [X23,X8]        X1=000000000000006
发现相关参数input_new[i]的规律,后续都是根据这4个计算式循环。


注意:

(1) UBFIZ X8, X8, #2, #4 这种汇编指令,不知道咋整,后面直接抄袭ida反汇编的代码计算式的。

(2) X23 :stru_37010 后面直接用frida dump的内存数据。

这里直接X2入参追踪的,其实可以方便一点,从input_new入手或者result结果入手反推,也可以快速定位算法位置。

4. 完整算法代码
#include <iostream> int main() {    std::string key_xmmword_37060 = "0123456789abcdef";    std::string key_stru_37010 = "0123456789-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";     std::string input = "5XH1S4C0qs1ofLGzvX7gTCwWOUqy2fxPUqc0";     int len = input.length();     int t_23 =0xFF;    int t_22 = 0x0;    for(int i=0; i<len-2; i++){        if(i == 0x8 || i==0xD || i==0x12 || i==0x18){            input[i] = 0x2D;            continue;        }        if( i==0xE){            input[i] = 0x34;            continue;        }        if(i== 0x17){            input[i] = input[i+1];        }        t_23 = t_23 ^ input[i];        t_22 = t_22 + input[i];        input[i] = input[i] ^ 1;    }    t_22 = t_22 - (t_22 & 0xFFFFFFF0);    t_23 = t_23 & 0xF;    input[0x22]= key_xmmword_37060[t_22];    input[0x23]=key_xmmword_37060[t_23];    std::cout << input << std::endl;    int k = 48;   int i = 0;   std::string result;   result.resize(k);   for(int j=0; j < k; j++){      int k_index;      int sk = j%4;      if(j!=0 && sk == 0)          i=i+3;      if(sk==0){          k_index =(input[i] >> 0x2) & 0xFF;      }else if(sk==1){        //    UBFIZ           X8, X8, #4, #2          int v11 = (input[i] & 0x3) *16;          //ORR             X8, X8, X9,LSR#4          k_index = v11 | (input[i+1] >> 0x4);      }else if(sk==2){          //UBFIZ           X8, X8, #2, #4          int v13 = (input[i+1] & 0xF) * 4;          k_index = v13 | (input[i+2] >> 0x6) ;      }else if(sk==3){          k_index = input[i+2] & 0x3F;      }      result[j] = key_stru_37010[k_index];   }    std::cout<<"result=" << result<<std::endl;    return 0;}
验证代码:
kanxue  : input: YJTRQNVwIJYZnQgNGhkGOweU4qogKhd2dfeRoutput: k4HjiP1djRmHgPvppMOOhOnD_incrAeP_l1InyDDnhbznQeMresult= k4HjiP1djRmHgPvppMOOhOnD_incrAeP_l1InyDDnhbznQeM kanxue  : input: KHuwjWk1vYiB7c9COhIx9d6n2qiIKCdXkot2output: gyzOrAHkox0Hk6v3bwOOeyTD_lySnhqN_l1Cg4D2njzEpxiSresult= gyzOrAHkox0Hk6v3bwOOeyTD_lySnhqN_l1Cg4D2njzEpxiS

本文附件可点击左下方阅读原文自行下载!

- End -

看雪ID:laifuling

https://bbs.pediy.com/user-home-814746.htm

  *本文由看雪论坛 laifuling 原创,转载请注明来自看雪社区。

# 往期推荐

公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458383583&idx=1&sn=596d100a3258f8c66a981f624d034d8d&chksm=b180c45586f74d439e48353f7b8848127b020645a99e07392f9661a34b6f2d1472ca597e46ce#rd
如有侵权请联系:admin#unsafe.sh