首页
论坛
课程
招聘
[原创] 看雪.京东 2018CTF 第五题 APK-ExecuteTable
2018-6-26 11:12 2034

[原创] 看雪.京东 2018CTF 第五题 APK-ExecuteTable

2018-6-26 11:12
2034

一开始打算静态逆,结果还是天真了,在错误的道路上奔跑了太久,不调试很难做出来吧。

 

这个题的难点主要有

  1. libexecute_table.so中有反调试,动态库被加载时会创建线程读取/proc/self/status中的TracerPid检测是否被调试。
  2. 动态库被加载时会读取/proc/self/maps获取自身的基地址,然后通过分析程序头和.dynamic段,修改符号表,改变JNI_OnLoad的地址。
  3. 动态库在加载时,使用mprotect改变内存权限,使用自修改代码来生成新的JNI_OnLoad
  4. 在程序里面搞了很多死分支,也就是根本不会被执行到的分支,影响分析。
  5. C++的string类真的很烦,函数重载导致一个功能的函数有好几个。

Java层

Java层比较简单,就是调用native函数lkdakjudajndn,如果lkdakjudajndn返回1,则成功。

libexecute_table.so

修复库函数名及识别关键函数

拖到IDA Pro里,首先是ELF文件.plt.got部分的函数没有正确命名,不知道为啥,写一个小脚本命名一下,然后就可以得到正确的库函数名。

import idaapi
import idc

rel_start_addr = 0x5D00
rel_end_addr = 0x5EE8

func_start_addr = 0x5EFC

rel_addr = rel_start_addr
func_addr = func_start_addr

print 'start renaming ...'
while rel_addr < rel_end_addr:
    cmt = idc.get_cmt(rel_addr, False)
    func_name = '_' + cmt.split(' ')[1]
    print func_name
    idc.set_name(func_addr, func_name)
    rel_addr += 8
    func_addr += 12

print 'done'

修复后的结果如下

 

然后还有4个直接用系统调用实现的函数,sys_openat, sys_read, sys_cloase, sys_mprotect

 

还有一个关键的函数初始化一个结构体,结构体中是函数指针

 

这些函数很关键。

调试

程序里有明文字符串,所以可以猜想有反调试,然后原JNI_OnLoad函数看起来不正常,应该有自修改代码。所以在sys_openatsys_mprotect处下断点。

 

sys_openat处的断点,可以发现sys_openat会打开两个文件,/proc/self/status/proc/self/maps

 

/proc/self/status

/proc/self/maps

其中,打开/proc/self/maps是用来获取libexecute_table.so的基地址,以便修改JNI_OnLoad的符号表。
打开/proc/self/status是为了读取TracerPid用于反调试,要patch一下这个函数,使其反调试失效。然后用patch过的so文件换掉原so文件。

 

sys_mprotect处的断点,可以找到修改符号表的地方。

真正的JNI_OnLoad函数是sub_D374B260(这个地址只有260是准确的)。

在真正的JNI_OnLoad中,可以看到函数lkdakjudajndn的注册过程

其地址为0xD374BC98,同样,调试的时候高位地址会随机化。

 

sys_mprotect处的断点,还可以发现,有自修改代码,但是我没有研究其过程,让它默默的改就好了。

lkdakjudajndn

lkdakjudajndn函数的验证过程本身没有那么烦,但是弄了一堆条件分支就很烦了,不过发现规律后可以很明确的判断程序会走哪个分支。

 

比方说下图中v14的取值,很明显只能是1。

 

可以看到其验证流程是把输入做一些变换,然后与3ww3U53wOAWG333wwPZ56GGw0PO02OUW对比,如果相同则正确。

 

其变换过程并不复杂,都是一些异或、置换、替换之类的操作,但是描述起来很麻烦,就不详细写了。直接把解题的IDAPython代码贴出来,其中有几个地址需要根据实际情况替换一下。代码很乱,不要吐槽我,不想整理了。

import idaapi

input = '1234567891234567'
print 'input %s' % input

input = input[1:] + input[0]

base = idaapi.get_dword(0xD3776CF8)

print 'base %x' % base
print 'base+33 %x' % (base+33)

out1 = ''
for c in input:
    out1 += chr(idaapi.get_byte(base + 33 + ord(c)))
print 'out1 %s' % out1.encode('hex')


out2 = ''

for c in out1:
    out2 += chr( ((ord(c)&0xf) << 4) | (ord(c) >> 4) )
print 'out2 %s' % out2.encode('hex')
out3 = ''

for i in range(16):
    out3 += chr( ord(out2[i]) ^ idaapi.get_byte(0xD3777098 + i%4) )
print 'out3 %s' % out3.encode('hex')

out4 = ''
for i in range(8):
    out4 += out3[2*i+1] + out3[2*i]
print 'out4 %s' % out4.encode('hex')

out5 = ''
for c in out4:
    out5 += chr(idaapi.get_byte(base + 33 + ord(c)))
print 'out5 %s' % out5.encode('hex')

out5 = out5[1:] + out5[0]

table = 'A3Cw6Gb0OZWPU52s'
out6_1 = ''
out6_2 = ''

for c in out5:
    out6_1 += table[ ord(c) >> 4]
    out6_2 = table[ ord(c) & 0xf] + out6_2
out6 = out6_1 + out6_2
print out6

# 1234567891234567 -> 

# target = 'Uww3C3sG6sCAb55sPAOPUA0O6OCA25AO'

print 'decoding .....'
target = '3ww3U53wOAWG333wwPZ56GGw0PO02OUW' # '3ww3U53wOAWG333'
print target

out5 = ''
out6_1 = target[:16]
out6_2 = target[16:]
for i in range(16):
    out5 += chr( (table.index(out6_1[i]) << 4) | (table.index(out6_2[15-i])) )
out5 = out5[-1] + out5[:-1]
print 'out5 %s' % out5.encode('hex')

base = 0xd3748fc2

table = []

print 'start'

for i in range(256):
    e = idaapi.get_byte(base + i)
    if e in table:
        print '*******************************dup'
    table.append(e)

out4 = ''
for c in out5:
    out4 += chr(table.index(ord(c)))
print 'out4 %s' % out4.encode('hex')

out3 = ''
for i in range(8):
    out3 += out4[2*i+1] + out4[2*i]
print 'out3 %s' % out3.encode('hex')

out2 = ''
for i in range(16):
    out2 += chr( ord(out3[i]) ^ idaapi.get_byte(0xD3777098 + i%4) )
print 'out2 %s' % out2.encode('hex')

out1 = ''
for c in out2:
    out1 += chr( ((ord(c)&0xf) << 4) | (ord(c) >> 4) )
print 'out1 %s' % out1.encode('hex')

input = ''
for c in out1:
    input += chr(table.index(ord(c)))
input = input[-1] + input[:-1]
print input

最后,flag为C0ngRa7U1AtIoN2U

 

我感觉我要弃赛了。


看雪学院推出的专业资质证书《看雪安卓应用安全能力认证 v1.0》(中级和高级)!

最后于 2018-6-26 14:23 被iweizime编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 85
活跃值: 活跃值 (22)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
RorschachL 活跃值 2018-6-26 14:15
2
0
分析的很透彻,部分函数没有正确命名可能是因为我改了点section导致的
雪    币: 923
活跃值: 活跃值 (184)
能力值: ( LV15,RANK:734 )
在线值:
发帖
回帖
粉丝
iweizime 活跃值 7 2018-6-26 14:22
3
0
RorschachL 分析的很透彻,部分函数没有正确命名可能是因为我改了点section导致的
惊现作者
游客
登录 | 注册 方可回帖
返回