赞
踩
比赛是周五,上班时间没怎么打,队伍没进线下,太惨了
APK里调用原生so
核心函数是sub_8a2c和sub_a740,分别是AES加密和base64encode,密钥是a76572Rrrrs变量,解密后得到key是wonderfulday!!!
然后得到flag:
注意提交时需要替换成中文逗号
上网找了很久的flutter逆向的工具,都不管用,然后看做出来的人还不少,想着是不是有捷径?
果然发现了出题人留的捷径:my_plugin.dll里有核心算法
这里的输入已经是30位flag打乱顺序后的base64编码后的字符串了
先把输入逆序,再扩展成HEX双字节,变成80字节的数据,然后进行变种的TEA加密,最后和固定串比较。
采用动态调试得到比较值4C 75 15 5C E7 81 D7 D1 73 F1 1B 50 22 B2 4D CB F5 61 5D 21 E7 9E CA 3F C7 B5 76 7C B9 8C DD C7 FA 23 0D 99 D3 1A AB 0B 32 C9 12 8E F2 BA 07 D3 23 D1 2D E5 2C 8F B6 FB E3 53 D8 BD 4E 1E 2E 89 FA 66 DD 39 65 EC FE 87 60 5E 7C 30 00 6C 0C 34
写出exp:
import struct
import base64
def decrypt(v, k):
v0 = v[0]
v1 = v[1]
delta = 0x79B99E37
x = delta*32&0xffffffff
k0 = k[0]
k1 = k[1]
k2 = k[2]
k3 = k[3]
for i in range(32):
v1 += ((v0 << 4) + k2) ^ (v0 + x) ^ ((v0 >> 5) + k3)
v1 = v1 & 0xFFFFFFFF
v0 += ((v1 << 4) + k0) ^ (v1 + x) ^ ((v1 >> 5) + k1)
v0 = v0 & 0xFFFFFFFF
x -= delta
x = x & 0xFFFFFFFF
v[0] = v0
v[1] = v1
return v
if __name__ == '__main__':
enc=bytes.fromhex('4c75155ce781d7d173f11b5022b24dcbf5615d21e79eca3fc7b5767cb98cddc7fa230d99d31aab0b32c9128ef2ba07d323d12de52c8fb6fbe353d8bd4e1e2e89fa66dd3965ecfe87605e7c30006c0c34')
encrypted=[]
for i in range(len(enc)//4):
encrypted.append(struct.unpack('<I',enc[i*4:i*4+4])[0])
key = [0xBABEC0FE, 0xDEADBEEF, 0xFACEB00C, 0xDEADC0DE]
hexflag=''
for i in range(len(encrypted)//2):
decrypted = decrypt(encrypted[i*2:i*2+2], key)
x=struct.pack('<2I',decrypted[0],decrypted[1]).hex()
hexflag+=x[0]+x[3]+x[4]+x[7]+x[8]+x[11]+x[12]+x[15]
x=base64.b64decode(bytes.fromhex(hexflag).decode()[::-1]).decode()
pad='abcdefghijklmnopqrstuvwxyz1234'
corr='a4b3c2d1ezfygxhwivjuktlsmrnqop'
for i in range(len(pad)):
print(x[corr.index(pad[i])],end='')
flag为:
flag{Emp0wer_F1utter_w1th_C!!}
0解题,可想而知比较难。
这题比赛的时候没时间做,不过幸亏没看,否则多给1倍的时间也不够用。
程序看起来没有判断flag的逻辑,其实程序利用了gcc_except_table做了异常处理,在gcc异常处理的过程中会调用.eh_frame的DWARF调试信息进行寄存器计算,输入输出寄存器均是R12-R15可以动态调试,可以在gcc库里设置断点,打印寄存器的计算过程。我的操作就是在输入字符串以后设置断点,到达以后再设置一个断点:libgcc_s.so.1:_Unwind_GetTextRelBase+1123,这里就是DWARF虚拟操作码的处理部分,如下图:
多跑几遍就可以了解大致的逻辑,可以根据需要继续在每种操作上设置其他的断点进行记录
然后经过大量的IDA调试和信息输出,通过人肉得到大致逻辑(过程很痛苦,浪费了一个周末的时间):
R12的入参是形如050703FB04020100070603的8字节数值,而返回R12是偏移量,比如1,如果上次R12输入的是FB04020100070603,则下次R12的值就是03FB040201000706,就是右移的字节数
R13是一个计数器,相当于for 循环的i,每次增加0x5477d63e117fac13,从0开始
R14、R15初始的时候分别是输入flag前半部分和后半部分,再输出加密后的R14和R15,实际加密处理是根据R13数值得到的一个值进行异或。
由于是简单的异或,所以key可以根据输入和输出计算出来。
最后是EXP:
import sys
k3=[0x63be9781a14ac750,0xb8366dbda1abfa4b,0xcae43fee60afd26,0x61261a3a6269c000,0xb59df07bafc8f32d,0xa15c6b4a32fe9d6,0x5e8d9cf46488c6d3,0xb3057331e5e993ef,0x77d49729c4920fa,0x5bf51faebcabd5e1,0xb06cf5ef1b0a8acd,0x4e4cc28676a2fda,0x595ca268eac63417,0xadd478a5ae27810c,0x24c4ee629873e3e,0x56c42522eae53b41,0xab3bfb60674488a4,0xffb3d1a3dba44d9f,0x542ba7df381c2293,0xa8a37e1e587d9674,0xfd1b5459f9dd5d79,0x51932a99693f1861,0xa60b00d42a9e9704]
index=0x050703FB04020100070603
x,y=0xfcbf957dff20a9ca,0xc9867e171b17b6bd
#x,y=0x7cd65ad3bc8e919a,0xdc5caf27c9896237
k1,k2=0xc34c7a039b8fd712,0x25abe38011b4b716
x=x^k1
y=y^k2
for j in range(22,-1,-1):
#print(hex(x),hex(y))
x,y=y,x
x=x^k3[j]
print(hex(x^k1),hex(y^k2))
验证结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。