首页
社区
课程
招聘
[原创]猿人学app 第二题算法还原
发表于: 2023-8-13 08:33 2265

[原创]猿人学app 第二题算法还原

2023-8-13 08:33
2265

使用unidbg辅助还原一个简单的算法

java层的定位太过简单这里就直接跳过了
ida打开样本so

发现是静态注册,这里给上JNIEnv
发现两个函数 sub_AE0,sub_C8C
先进 sub_AE0 看一眼

发现经过了一系列的操作然后调用了sub_A40
再进sub_C8C看一眼

发现多个右移操作,很像base64
看一眼byte_3000, 直接就看到码表了, base64八九不离十了

现在主要的任务是把 sub_AE0 还原,先进行一波简单的手工计算

1
2
3
4
5
6
//这段代码的逻辑就是将输入的值填充到16位,填充的数是16 - (v4 & 0xF)也就是0x4
if ( (v4 & 0xF) != 16 )
  {
    memset((char *)v6 + v4, 16 - (v4 & 0xF), 16 - (v4 & 0xF));
    v4 = v4 - (v4 & 0xF) + 16;
  }

输入的值为 2:1691846483
转换成16进制并填充后 0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
然后是一个循环

1
2
3
4
5
6
7
8
9
10
11
12
do
    {
      for ( i = 0LL; i != 16; ++i )
      {
//因为v27出还没有被赋值所以*((_BYTE *)&v27 + i)整体就是为0,也就可以得到v8 = byte_E73[(unsigned __int8)(*((_BYTE *)v9 + i) ^ v8)],而*((_BYTE *)v9 + i 刚好是填充后的第i个数
        v8 = *((_BYTE *)&v27 + i) ^ byte_E73[(unsigned __int8)(*((_BYTE *)v9 + i) ^ v8)];
        *((_BYTE *)&v27 + i) = v8;
      }
      v11 = v7++ == (unsigned __int64)(v4 - 1) >> 4; //v4 就是我们的len所以(v4 - 1) >> 4 的值就是 15 >> 4 = 0,所以while循环只进行一次
      ++v9;
    }
    while ( !v11 );

下面计算16轮循环
v8 = 0 0x23 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
v8 = 0xc7 0x23 0xd4 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
v8 = 0x54 0x23 0xd4 0xd9 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
........ 还有好几轮懒得算了,直接上unidbg跑一下

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
63
64
65
66
67
package com.yuanrenxue.match2022.fragment.challenge;
 
 
//创建包
 
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;
import unicorn.Arm64Const;
 
import java.io.File;
import java.util.ArrayList;
import java.util.List;
 
public class ChallengeTwoFragment {
    private final AndroidEmulator emulator;
    private final VM vm;
    public int sum = 0;
    private final Memory memory;
    private final Module module;
    public ChallengeTwoFragment(){
        //创建模拟器
        emulator = AndroidEmulatorBuilder
                //可以创建32,64
                .for64Bit()
                //添加动态后端
                .addBackendFactory(new Unicorn2Factory(true))
                .build();
        //获取内存接口
        memory = emulator.getMemory();
        //sdk版本
        memory.setLibraryResolver(new AndroidResolver(23));
        //创建虚拟机
        vm = emulator.createDalvikVM();
        //加载elf文件
        DalvikModule dalvikModule = vm.loadLibrary(new File("/home/cxq/unidbg/unidbg-android/src/test/java/com/yuanrenxue/match2022/fragment/challenge/libmatch02.so"), true);
        module = dalvikModule.getModule();
 
        vm.callJNI_OnLoad(emulator, module);
    }
    public void  sign(String string)
        DvmObject object = ProxyDvmObject.createObject(vm, this);
//调用
        DvmObject object1 = object.callJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", string);
//获取返回值
        String value = (String) object1.getValue();
        System.out.println("args is =>" + string + " result is =>" + value);
        System.out.println(sum);
    }
    public static void main(String[] args) {
        ChallengeTwoFragment mainActivity = new     ChallengeTwoFragment();
        mainActivity.sign("2:1691846483");
    }
}

使用emulator.attach().addBreakPoint(module.base + 0xbac); 打下断点,读一下x12寄存器的内容

可以看到前面几个值和我们算出来的一样
继续往下在走
到下面v28不知道是啥,在unidbg看一眼,在0xBC0处下断点 读sp+0x30处的位置得到

1
2
3
4
5
v28 = 23 F0 E8 B9 7A F3 68 50  //转换后的后八个字节
 v13 = v28;  0x23
v14 = BYTE1(v28); 0xf0
v15 = BYTE2(v28); 0xe8
v16 = BYTE3(v28); 0xb9

同理可得后面几位

1
2
3
4
v18 = BYTE4(v28); 0x7A
v19 = BYTE5(v28); 0xF3
v20 = BYTE6(v28); 0x68
v21 = HIBYTE(v28); 0x50

由于 _QWORD 是一个无符号 64 位整数类型,也就是八个字节
*(_QWORD *)v17 = v12; 将v12的前八字节赋值到v17的前八个位置
其实就是把转换后的数据添加到input数据后面

1
v22 = ((unsigned __int64)(len_ + 15) >> 4) + 1;// 31 >> 4  + 1 = 2 ,whiule循环循环两次

也就是说sub_A40会执行两次
进入sub_A40 sub_A40(&v26, &v25, &v26);
第一次进入时

1
2
3
v25 = 0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
 v6 = (int8x16_t *)malloc(48u);
 v6[1] = *a2;  0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
1
2
v6[2] = veorq_s8(v9, *a2);
//这个的意思就是将v9 与 a2中的内容按位 ^ 后给到v6[2],v9初始并没有赋值所以

v6[2] = 0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
v6就等于

1
2
3
0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0 0x0 0x0 0x0
 0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4
 0x32 0x3a 0x31 0x36 0x39 0x31 0x38 0x34 0x36 0x34 0x38 0x33 0x4 0x4 0x4 0x4

同样打开unidbg验证一下 在0xA94处下断点
dump出x0寄存器的内容

与推测内容一样

1
2
3
4
5
6
for ( i = 0LL; i != 48; ++i )
{
v8 = v6->n128_u8[i] ^ byte_E73[v8];      
// 化简一下得到 v8 = v6[i] ^ byte_E73[v8]
v6->n128_u8[i] = v8;               // v6[i] = v8;
}

这个轮数太多了不好算,直接看跑完循环后v6的内容
同样打开unidbg,在0xAC8 打下断点,读取x0寄存器的值

第二次进入sub_A40(&v26, &v25, &v26);
v26里面的内容就变成了上面x0寄存器的第一行内容了
v25 变成了之前计算到的 0x23 0xD4 0xD9 0xDF 0x8E 0x08 0x04 0x04 0x23 0xF0 0xE8 0xB9 0x7A 0xF3 0x68 0x50
v6[2] = veorq_s8(v9, *a2); 就变成了0x3C 0xAC 0x40 0xE4 0xAB 0x2B 0xB5 0x1D 0x10 0xBB 0xFB 0xF0 0x10 0x8A 0x26 0x5C
继续验证一下在0xa94 下断点 ,因为是要在第二次进入下断点所以需要输入c回车跳过第一个断点

打一个结束断点观察sub_A40第二次计算的返回值

取前16个字节放入sub_C8C中,用cyberchef快速验证一下是不是base64

frida hook也安排上

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
function hookJava() {
    Java.perform(function () {
        var Secure = Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeTwoFragment")
        Secure.sign.implementation = function (str1){
            console.log(str1)
            // str1 = "2:1691845341"
            var result = this.sign(str1)
            console.log(result)
            return result
        }
    })
}
function hookNative() {
    var baseAddr = Module.findBaseAddress("libmatch02.so");
    console.log("find libmatch02 addr is =>" + baseAddr)
    var func_addr = baseAddr.add(0xA40)
    Interceptor.attach(func_addr, {
        onEnter: function (args) {
            this.arg1 = args[0]
            this.arg2 = args[1]
            this.arg3 = args[2]
            console.log("arg1 is => \n")
            console.log(hexdump(this.arg1))
            console.log("arg2 is => \n")
            console.log(hexdump(this.arg2))
        },onLeave: function (retval) {
            console.log("result is => \n" + hexdump(retval))
        }
    })
}
function hookNative1() {
    var baseAddr = Module.findBaseAddress("libmatch02.so");
    console.log("find libmatch02 addr is =>" + baseAddr)
    var func_addr = baseAddr.add(0xAe0)
    Interceptor.attach(func_addr, {
        onEnter: function (args) {
            this.arg1 = args[0]
            this.arg2 = args[1]
            // this.arg3 = args[2]
            console.log("arg1 is => \n")
            console.log(hexdump(this.arg1))
            console.log("arg2 is => \n")
            console.log(hexdump(this.arg2))
        },onLeave: function (retval) {
            console.log("result is => \n" + hexdump(retval))
        }
    })
}
function main(){
    hookJava()
    hookNative1()
    hookNative()
 
}
setImmediate(main)

2:1691846483
KQoxyQVdDxnvn2oR0ohGyA==
结果正确
使用python进行还原

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
import string
import base64
arr = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 1, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
       0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
       0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
       4, 0xC7, 0x23, 0xC3, 0x18, 0x96, 5, 0x9A, 7, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 9, 0x83,
       0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1,
       0, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF,
       0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 2, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3,
       0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0xC,
       0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81,
       0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0xB, 0xDB, 0xE0, 0x32,
       0x3A, 0xA, 0x49, 6, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37,
       0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 8, 0xBA, 0x78, 0x25,
       0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5,
       0x66, 0x48, 3, 0xF6, 0xE, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
       0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0xD,
       0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0xF, 0xB0, 0x54, 0xBB, 0x16]
arr1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4, 0x4, 0x4, 0x4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4, 0x4, 0x4, 0x4]
arr2 = [0 for _ in range(48)]
# arr1 = [0 for _ in range(48)]
def getsign(sss):
    for i in range(len(sss)):
        arr1[i + 16] = ord(sss[i])  # Convert characters to their ASCII values
        arr1[i + 32] = ord(sss[i])  # and assign to arr1
    x8 = 0
    for i in range(16):
        a = hex(int(arr1[16 + i]))
        b = arr[int(a, 16) ^ x8]
        x8 = b
        arr2[i + 16] = x8
    v8 = 0
    v7 = 0
    while(v7 != 20):
        for i in  range(48):
            v8 = arr1[i] ^ arr[v8 % 256]
            arr1[i] = v8
        v8 += v7
        v7 = v7 +1
    for i in range(16):
        arr2[i] = arr1[i]
    for i in range(16):
        arr2[i + 32] = arr2[i] ^ arr2[i + 16]
    v8 = 0
    v7 = 0
    while (v7 != 20 ):
        for i in range(48):
            v8 = arr2[i] ^ arr[v8 % 256]
            arr2[i] = v8
        v8 += v7
        v7 += 1
    return arr2[:16]
# '2:1691845341'
# oFJCNh4R3fct9cahTWpfPw==
byte_data = bytes(getsign("2:1691845341"))
# print(getsign("2:1691845341"))
encoded_data = base64.b64encode(byte_data)
encoded_string = encoded_data.decode('utf-8')
print(encoded_string)

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//