动静态SO层算法还原和脱机(吾爱论坛2021春节安卓题目)
zsk Lv4

文章的案例来自吾爱论坛2021春节安卓题目

链接:案例链接

java用的是unicode编码,c语言用的是ASCII编码

明文限制的长度是30,检测函数在nativel-lib文件里

image

解压apk并用ida打开so文件
大概分析一下,传进来的参数判断是否为30位,然后进入sub_B90进行处理后,再进入sub_D90处理,结果为v9,最后v9和v19作比较,相等返回1,不等返回0;

image

用ida动态调试

调试前先把ida目录下的dbgsrv、android-server拷贝到手机上
并修改权限 777
这里需要开启frida去获取so的基址+sub_B90的偏移量=sub_B90的绝对地址
先运行frida,再运行android-server
frida代码

1
2
3
4
5
6
7
8
9
10
11
function hook_KeyStore_load() {
Java.perform(function () {
var addr_libnatuve = Module.findBaseAddress("libnative-lib.so")
if(addr_libnatuve) {
console.log("so基址:", addr_libnatuve)

}
});
}

setImmediate(hook_KeyStore_load)

打印结果
so基址: 0x7613313000
0x7613313000+8AC
所以需要在动调调试的时候在位置打上断点

image

没显示汇编按空格

image

以go方式打开ida

选择Debugger–》Attach–》Remote ARM Linux/Android debugger
输入手机IP,选择进程

image


image


image

image

加载好后跳到指定位置,或者快捷键G

image

打上断点后,点击绿色小箭头继续运行,app点击验证,会断到当前位置
可以转为汇编跟静态ida的作比较,Edit–》Code,或者快捷键C

image

image

按F8单步调试
打开寄存器,进入

image

image

用Frida

这里直接用fridahook 函数,sub_B90
sub_B90的偏移量为B90
这就是第一轮加密后的

image

hook 第二个函数
这是明文经两轮加密后的结果
image

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
function hook_KeyStore_load() {
Java.perform(function () {
var addr_libnatuve = Module.findBaseAddress("libnative-lib.so")
if(addr_libnatuve) {
console.log("so基址:", addr_libnatuve)
var sub_B90 = addr_libnatuve.add(0xB90)
Interceptor.attach(sub_B90, {
onEnter: function(args){
this.arg0 = args[0]
this.arg1 = args[1]
this.arg2 = args[2]

// console.log("
",hexdump(this.arg0))
// console.log("
",this.arg1);
// console.log("
",hexdump(this.arg2));
console.log("参数一:",Memory.readCString(this.arg0))
console.log("参数二:",this.arg1.toInt32());
console.log("参数三",Memory.readCString(this.arg2))
},
onLeave: function(retval){
console.log("sub_B90 onLeave");
console.log("运算结果");
console.log(Memory.readByteArray(this.arg0, 30)); // 这里把结果赋值给了第一个参数
}
})

var sub_D90 = addr_libnatuve.add(0xD90)
Interceptor.attach(sub_D90, {
onEnter: function(args){
console.log("sub_D90 onEnter");
this.arg0 = args[0]
this.arg1 = args[1]

console.log("参数一:",Memory.readByteArray(args[0], 30))
console.log("参数二:", this.arg1.toInt32())
},
onLeave: function(retval){
console.log("sub_D90 onLeave");
console.log("运算结果");
console.log(Memory.readCString(retval));
}
})
}
});
}

setImmediate(hook_KeyStore_load)

image

主动调用

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
68
# -*- coding: UTF-8 -*-

import frida, sys

jscode = '''

var destAddr = ''; //定位xsp地址

function inline_hook() {
var so_addr = Module.findBaseAddress("libnative-lib.so");


if (so_addr) {
console.log("so_addr:", so_addr);

var addr_b90 = so_addr.add(0xB90);
var sub_b90 = new NativeFunction(addr_b90 , 'int', ['pointer', 'int', 'pointer']);
Interceptor.attach(sub_b90, {
onEnter: function(args)
{
destAddr = args[0];
console.log('onEnter B90');
},
//在hook函数之后执行的语句
onLeave:function(retval)
{
console.log('onLeave B90');
}
});


var addr_b2c = so_addr.add(0xb2c);
console.log("The addr_b2c:", addr_b2c);
Java.perform(function() {
Interceptor.attach(addr_b2c, {
onEnter: function(args) {
console.log("addr_b2c OnEnter :", Memory.readByteArray(destAddr.sub(0x38),64) );
}
})
})
}
}
setImmediate(inline_hook)


'''
def on_message(message, data):
if message['type'] == 'send':
print(" {0}".format(message['payload']))
else:
print(message)
pass
#print(frida.enumerate_devices())
# 查找USB设备并附加到目标进程
device = frida.get_usb_device()
#pid = device.spawn(["com.live.xctv"])

#session = device.attach(pid)
session =device.attach('cn.pojie52.cm01') #这里是要注入的apk包名
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print(' Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()

image

异或

大概分析一下sub_b90,是根据传入的第三个参数s把v20进行了一个初始化,然后再把参数a1和v20进行了异或运算,主要看这个异或运算,先设想一下,如果是把a1进行了异或,那么得到的结果和a1之前的数据再异或就可以计算出异或的key,这里我们把它叫做xorkey,那么先看一下我们传入的参数,是30个1,也就是30个0x31 ,然后看结果,第一位是0xe0,0x31^0xe0 = 209,然后把参数改为30个2,即0x32,得出首位的结果是0xe3,0xe3^0x32结果也是209,证明我们的思路是正确的,然后依次求出所有的xorkey,

这里的值是输入,30个1加密后异或来的
明文 ^ 密钥 = 密文
# 拿到密钥
明文 ^ 密文 = 密钥

image

1
2
3
4
5
6
7
8
9
10
a = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]
b = [0xe0, 0x6b, 0x37, 0xa1, 0x75, 0xd7, 0xf6, 0xd4, 0xef, 0x19, 0xc6, 0xc3, 0x57, 0xa0, 0xf9, 0xb4, 0x73, 0xee, 0xc8,
0xd1, 0xb3, 0x30, 0x1a, 0x0a, 0x09, 0x52, 0x06, 0x8c, 0x1f, 0x7c]

c = []
for i in range(0, 30):
c.append(a[i] ^ b[i])

print(c)

结果:

[209, 90, 6, 144, 68, 230, 199, 229, 222, 40, 247, 242, 102, 145, 200, 133, 66, 223, 249, 224, 130, 1, 43, 59, 56, 99, 55, 189, 46, 77]


异或的key找到了,现在要找异或后的正确值,才能算出答案明文
1.调试
![image.png]image
调试到这里,要在寄存器钟拿到v19
这里就是判断的地方,偏移量为B2C

image

image

调试

没计算前
image
计算后

image

这就是要的密文了
“5Gh2/y6Poq2/WIeLJfmh6yesnK7ndnJeWREFjRx8”

另一种方式hook寄存器

image
基地址加B30就是结果寄存器的位置,同时我们需要的是x9寄存器的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function hook_KeyStore_load() {
Java.perform(function () {
var addr_libnatuve = Module.findBaseAddress("libnative-lib.so")
if(addr_libnatuve) {
console.log("so基址:", addr_libnatuve)
var LDRB = addr_libnatuve.add(0xB30)
console.log("LDRB", LDRB)
Interceptor.attach(LDRB, {
onEnter: function(args){
console.log(Memory.readCString(this.context.x9)) // 上下文获取x9寄存器
},
onLeave: function(retval){
console.log("运算结果");
console.log(Memory.readCString(retval));
}
})

}
});
}

setImmediate(hook_KeyStore_load)

image

异或出答案

密钥:[‘0xd1’, ‘0x5a’, ‘0x6’, ‘0x90’, ‘0x44’, ‘0xe6’, ‘0xc7’, ‘0xe5’, ‘0xde’, ‘0x28’, ‘0xf7’, ‘0xf2’, ‘0x66’, ‘0x91’, ‘0xc8’, ‘0x85’, ‘0x42’, ‘0xdf’, ‘0xf9’, ‘0xe0’, ‘0x82’, ‘0x1’, ‘0x2b’, ‘0x3b’, ‘0x38’, ‘0x63’, ‘0x37’, ‘0xbd’, ‘0x2e’, ‘0x4d’]
结果:“5Gh2/y6Poq2/WIeLJfmh6yesnK7ndnJeWREFjRx8”
结果^密钥=答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import base64

a = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]
b = [0xe0, 0x6b, 0x37, 0xa1, 0x75, 0xd7, 0xf6, 0xd4, 0xef, 0x19, 0xc6, 0xc3, 0x57, 0xa0, 0xf9, 0xb4, 0x73, 0xee, 0xc8,
0xd1, 0xb3, 0x30, 0x1a, 0x0a, 0x09, 0x52, 0x06, 0x8c, 0x1f, 0x7c]

c = []
for i in range(0, 30):
c.append(a[i] ^ b[i])

print(c) # [209, 90, 6, 144, 68, 230, 199, 229, 222, 40, 247, 242, 102, 145, 200, 133, 66, 223, 249, 224, 130, 1, 43, 59, 56, 99, 55, 189, 46, 77]

d = [0xd1, 0x5a, 0x6, 0x90, 0x44, 0xe6, 0xc7, 0xe5, 0xde, 0x28, 0xf7, 0xf2, 0x66, 0x91, 0xc8, 0x85, 0x42, 0xdf, 0xf9, 0xe0, 0x82, 0x1, 0x2b, 0x3b, 0x38, 0x63, 0x37, 0xbd, 0x2e, 0x4d]
data = base64.b64decode('5Gh2/y6Poq2/WIeLJfmh6yesnK7ndnJeWREFjRx8'.encode())
flag = bytes([d[i] ^ data[i] for i in range(len(d))]).decode()
print(flag)

运行结果为:

52pojieHappyChineseNewYear2021

 评论