Flutter逆向
Flutter逆向
恢复符号:
Blutter的使用
使用命令 python blutter.py /inp /out
接下来就是恢复符号,在ida_script/addNames.py中
查看函数流程:
在下面这个文件中查看对应的函数

根据对应的查看就行
分析流程:
准备阶段:
flutter的某些函数在ida里面分析的不好,你得找到文件开始的地方与ret的地方重新进行U-C-P定义,有时候这样还是看不太好,就需要配合trace了
我手写了一个基于stalker的trace代码,唯一的缺点可能就是慢了,步过处理小函数还是可以的
https://github.com/XiaoWaaay/frida_trace
在ida中查看的时候会发现有许多我们不需要关注的函数,很多对内存进行操作的函数,需要先检查内存什么的,就大大增加了我们的工作量,比如下面这样:


就很多这样的跳转,感觉这些作用都不大(针对我们还原算法而言),我们就可以根据trace的东西来寻找跳转回来的地址,这样比较精确,下面就是我的分析流程:
generate_token:
sub_2D9630 加密函数,时间戳+hash+base62
这个就是在那个文件中看到的

然后就是trace,其实就是找到关键位置,静态看也可以
代码在下面:


trace只是便于我们找到这些位置和参数,其实也是便于后续的hook
var soname = "libapp.so"
function generate_token() {
const bases = Module.findBaseAddress('libapp.so');
var addr = bases.add(0x3D467C)
console.log('[*] base', bases);
Interceptor.attach(addr, {
onEnter: function (args) {
console.log("第一个参数", args[0])
console.log("第一个参数", args[1])
console.log("第一个参数", args[2])
var buffer = Memory.readByteArray(ptr(args[2]), 0x30)
console.log(buffer)
}
})
}
function base62() {
const bases = Module.findBaseAddress('libapp.so');
var addr = bases.add(0x2D4204)
console.log('[*] base', bases);
Interceptor.attach(addr, {
onEnter: function (args) {
var buffer = Memory.readByteArray(ptr(this.context.x3), 0x30)
console.log('----x3------',buffer)
var baseaa = this.context.x3;
var address = baseaa.add(0x28);
var data = [
0x24,0xc,0x15,0x33,0x2f,0x5a,0x68,0x28
];
//写死这个值
Memory.writeByteArray(address, new Uint8Array(data));
var buffer = Memory.readByteArray(ptr(this.context.x5), 0x30)
console.log(buffer)
},
onLeave: function (ret) {
console.log("=======", ret)
var buffer = Memory.readByteArray(ptr(ret), 0x30)
console.log(buffer)
}
})
}
上面就是token生成的关键地方,用python集成一下:
import time
import hashlib
s='gctf25_'
for i in range(len(s)):
print(hex(ord(s[i])),end=" ")
print()
BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
def base62_encode(data: bytes) -> str:
num = int.from_bytes(data, byteorder='big')
if num == 0:
return BASE62_ALPHABET[0]
encoded = ""
base = len(BASE62_ALPHABET)
while num > 0:
num, rem = divmod(num, base)
encoded = BASE62_ALPHABET[rem] + encoded
return encoded
def base62_decode(s: str) -> bytes:
base = len(BASE62_ALPHABET)
num = 0
for char in s:
num = num * base + BASE62_ALPHABET.index(char)
byte_length = (num.bit_length() + 7) // 8
return num.to_bytes(byte_length, byteorder='big')
def generate_token():
timestmp=str(int(time.time()))
s=b"gctf25_"+timestmp.encode()
hashs=hashlib.sha1(s).digest()
print(hashs)
print(base62_encode(hashs[:8]))
generate_token()
encode分析:
这个encode还是比较复杂的,挺恶心人的,我最开始的静态没反编译好,就一直没找到key变换的地方,直到我搜索输入的这个PIN码的值的时候才发现(也是基于trace)
下面是hook代码:
function encode() {
const bases = Module.findBaseAddress('libapp.so');
var addr = bases.add(0x2d3cd0)
console.log('[*] base', bases);
Interceptor.attach(addr, {
onEnter: function (args) {
console.log(this.context.x0);
console.log(this.context.x1);
console.log(this.context.x2);
console.log(this.context.x3);
console.log(this.context.x4);
console.log(this.context.x5);
console.log(this.context.x6);
console.log(this.context.x7);
console.log(this.context.x8);
this.context.l
//x0是秘密
var buffer = Memory.readByteArray(ptr(this.context.x0), 0x60)
console.log('-----x0--------',buffer)
//猜测x1是token
var buffer = Memory.readByteArray(ptr(this.context.x1), 0x50)
console.log('-----x1--------',buffer)
//x4是pin码
var buffer = Memory.readByteArray(ptr(this.context.x4), 0x30)
console.log('-----x4--------',buffer)
}
})
}
先查看的参数,发现都出现了,下面就是根据trace猜测了
ida代码如下:

主要的加密就这些,看着很简单,我当时分析也是觉得简单
下面是我的分析过程:


后面发现对不上了,不知道怎么找后续的key了,开始猜哪里函数没反编译好,而且name和PIN码没用到,不应该,我就在race中搜索,果真搜到了

跳转看一下:

看着确实是我们想要的操作
而且他的循环次数就是我们的PIN码的大小,可以用0005试一下,我觉得还是比较好的,不会trace这么多了

可以看到第8次和第九次之间差了很多,其中就是有来进行key变换的
def rol8(v,shift):
return ((v<<shift)|(v >>(8- shift)))&0xff
def ror8(v,shift):
return ((v>>shift)|(v <<(8- shift)))&0xff
def encode_part1(v57: int, shift: int) -> int:
shift = shift & 0x7 # 保留低3位,相当于 % 8
if shift == 0:
return v57 & 0xFF
left = (v57 << shift) & 0xFF
right = (v57 >> (8 - shift)) & 0xFF
return (left | right) & 0xFF
def expend_key(data,pin,i):
i&=3
i+=1
shift=pin^i
shift=shift & 0x7
data=data[1:]+[data[0]]
for i in range(len(data)):
data[i]=ror8(data[i],shift)
return data
def encrypt(secret,pin,token):
token_data=base62_decode(token)
keys=token_data
for i in range(pin):
for j in range(len(secret)):
secret[j]+=keys[i%len(keys)]
secret[j]&=0xff
encode_part1(secret[j],j)
secret=[secret[-1]]+secret[:-1]
keys=expend_key(keys,pin,i)
return secret
这样就得到这个加密的key了,直接对着这个写一个解密代码就行,不过我们就是得自己手动爆破这个pin码了,四位数
小结:
根据这次比较基本了解了Flutter的基本流程,知道了怎么去做这种架构的题目,而且也因为这次的比赛,写了一个安卓端得trace代码,虽然很臃肿,但是也是能跑,后续等学得更多更深得时候再不断完善,也希望有大佬看到了可以给点帮助和建议

浙公网安备 33010602011771号