首页
论坛
课程
招聘
[原创]分析ollvm混淆非标准算法
2021-1-6 23:52 4071

[原创]分析ollvm混淆非标准算法

2021-1-6 23:52
4071

前言

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

思路

对于经ollvm混淆的算法,动态一步步调试,或者静态分析,都是一个比较大的工程。
通用的思路就是trace汇编指令及寄存器变化,追踪核心的计算式,从而还原算法。尤其对于非标准算法,非常有效。
我这里直接用ida trace关键函数起始地址,然后分析log日志,从而还原出算法。

解题

静态分析apk

jadx静态分析apk

java层代码比较简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
进程名: com.kanxue.ollvm_ndk_9
package 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);
            }
        });
    }
}

从上面获取实用信息:
1>libnative-lib.so和native方法。
2>adb logcat -s "kanxue",查看输入输出结果。

ida静态分析so

解压apk, ida64打开libnative-lib.so,这里在exports中搜索java,看看是否有UUIDCheckSum方法,定位方法的start和end的偏移地址:

1
2
3
4
5
.text:000000000000FF30 Java_com_kanxue_ollvm_1ndk_MainActivity_UUIDCheckSum
 
..略
 
.text:00000000000101CC                 RET

IDA trace log

修改ida的trace脚本,将start_ea和end_ea修改上面分析so的UUIDCheckSum的偏移起始地址:

1
2
start_ea = (module.base + 0xFF30)
end_ea = [((module.base + 0x101CC))]

ida attach com.kanxue.ollvm_ndk_9,trace保存日志记录,具体步骤略。

分析log

日志的样例输入输出:

1
2
input: OBjr9WRO8BUNXhcB0hsGn6sgqa8y63XDvjIr
output: hybFqNvkiOSHePfdkgOOeN5D_inJbR9K_k0Ts3qMkijRoQ9v

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,
观测结果,得出如下:

 

<1> input[0x17]=input[0x18]

 

<2>
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]都是如上的计算方式。

 

<3> 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

 

<4>0x22 和0x23分析
根据:
000000789DD8FF10 STRB W8, [X0,#0x23]
000000789DD8FF18 STRB W8, [X0,#0x22]
反推出[X0,#0x22]和[X0, #0x23]的数据来源。
以上处理过程的c代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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); 处理后的样例结果:

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

算法分析2

继续分析X0=00000078A9094380
000043B5 libc.so:memcpy PRFM #0, [X1]
000043B5 X0=00000078A9094530 X1=00000078A9094380 X2=0000000000000024
经过memcpy, X0=00000078A9094530

 

--->继续追踪:00000078A9094530地址
在:3543行发现libc.so memcpy
000043B5 libc.so:memcpy PRFM #0, [X1]
000043B5 X0=00000078A9094560 X1=00000078A9094530
X0= 00000078A9094560

 

--->继续追踪:00000078A9094560
3567: 000000789DD8FAA0 LDR X9, [SP,#0x20] X9=00000078A9094560
...
3664:
MOV X21, X0 X21=00000078A9094560
这里发现X21一直存入的是:00000078A9094560
定位处理数据地方:

 

这里发现X21一直存入的是:00000078A9094560
定位处理数据地方:

1
2
3
4
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
分析出下标计算式
<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=0000000000000035
libnative_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=0000000000000064

发现相关参数input_new[i]的规律,后续都是根据这4个计算式循环。
注意:
1>UBFIZ X8, X8, #2, #4 这种汇编指令,不知道咋整,后面直接抄袭ida反汇编的代码计算式的。
2>X23 :stru_37010 后面直接用frida dump的内存数据。
这里直接X2入参追踪的,其实可以方便一点,从input_new入手或者result结果入手反推,也可以快速定位算法位置。

完整算法代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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;
}

验证代码:

1
2
3
4
5
6
7
kanxue  : input: YJTRQNVwIJYZnQgNGhkGOweU4qogKhd2dfeR
output: k4HjiP1djRmHgPvppMOOhOnD_incrAeP_l1InyDDnhbznQeM
result= k4HjiP1djRmHgPvppMOOhOnD_incrAeP_l1InyDDnhbznQeM
 
kanxue  : input: KHuwjWk1vYiB7c9COhIx9d6n2qiIKCdXkot2
output: gyzOrAHkox0Hk6v3bwOOeyTD_lySnhqN_l1Cg4D2njzEpxiS
result= gyzOrAHkox0Hk6v3bwOOeyTD_lySnhqN_l1Cg4D2njzEpxiS

[2022夏季班]《安卓高级研修班(网课)》月薪两万班招生中~

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (2)
雪    币: 4937
活跃值: 活跃值 (2590)
能力值: ( LV9,RANK:195 )
在线值:
发帖
回帖
粉丝
天水姜伯约 活跃值 4 2021-1-8 01:12
2
0
又是一波代码混淆小高潮
雪    币: 33
活跃值: 活跃值 (727)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小爱逆天 活跃值 2021-1-11 15:32
3
0
观摩
游客
登录 | 注册 方可回帖
返回