学了这么一段时间的Android
,难得见到的一道比较对口的逆向题,
不过比较遗憾的是,题目用的函数抽取的方法的文章我刚好看过,还研究了蛮久,,不过当时就一直没有怎么看懂,只死磕出了在抽取指令时的操作,但是却不明白他是通过什么以及如何将指令给填充回去的
不过经过实打实分析了这个题目后,对指令填充有了一定的理解,虽然这个题目填充的方式比较简易,但是至少知道了核心思想。果然知识表面上看懂了都还不是自己的,只有真正上手实践了才能理解的更加深刻
首先通过AndroidManifest.xml
找到入口Activity
:com.mrctf.android2022.StubApp
StubApp
类里先看attachBaseContext
判断了e
类的a
函数返回值,如果True
则取e.d()
的ClassLoader
设置了两个so
文件的路径、API
的检查以及
e.f()
=> e.e()
=> e.b()
e.c()
e.b()
函数通过在su
常见路径下创建su
文件来检查是否被root
e.c()
通过执行which su
命令后读取输出来检查是否有su
文件
如果有su
则会输出
如果没有su
则没有输出
所以总的来说 attachBaseContext
的e.a
函数加载了两个数据文件夹下so
文件的路径以及运行环境和root
的检测
e.d()
中分别读取并解密了assets
文件夹下的三个文件res.dat
、libc++_shared.so
和build.json
其中res.dat
和libc++_shared.so
作为两个so
文件
build.json
作为Dex
文件通过InMemoryDexClassLoader
加载
InMemoryDexClassLoader
能够通过从一个包含着DEX
文件的缓冲区中加载类。可以用来执行没有被写进本地文件系统的代码
尔后调用了e.g()
来将当前的Activity
的classLoader
替换成我们加载了自定义类的classLoader
因为当前的Activity
的mClassLoader
已经是Activity
默认的PathClassLoader
,而该PathClassLoader
并没有加载我们的自定义类,而且我们接下来要启动的MainActivity
也是由PathClassLoader
加载的,而且我们的InMemoryDexClassLoader
和PathClassLoader
都是继承于BaseDexClassLoader
,所以后续如果要调用自定义类的话就会找不到
接下来分析解密几个so
首先是由res.dat
解密得到libnative.so
读取res.dat
文件后调用了decodeSo
函数进行解密存放在应用的数据目录下的libnative.so
,而decodeSo
是libstub.so
里的native
函数
但是在libstub.so
里却没有直接找到decodeSo
函数,因此应该是JNI_OnLoad
里动态注册的
另外libstub.so
使用了Ollvm
的字符串加密和控制流平坦化
字符串在加载时调用了.init_array
里的以.datadiv_decode
开头的函数对字符串进行解密
因此对于字符串解密有两种方法
这里参考官方给的WP中使用了AndroidNativeEmu
框架
解密前:
解密后:
sub_3F44
里调用了sub_4D06
是RegisterNatives
动态注册函数
RegisterNatives的定义
所以可以看到sub_3A74
就是实际调用的decodeSo
函数
因为
实际上干的内容就是逐字节异或0x22
,然后保存到文件,即可得到libnative.so
但是后面JNI_OnLoad
还没有分析完
后面调用了sub_2A2C
和sub_2DEC
后面看了源码后才知道这两个函数分别是通过inLine Hook
来Hook
libc.so
的execve
和Hook
libart.so
的loadMethod
项目地址:ele7enxxh / Android-Inline-Hook
大致使用方法
MainActivity
存在的dex
由build.json
异或49即可得到
MainActivity
里检查了输入长度然后调用了Utils.nativeCheck
Utils
加载了libnative.so
我们直接搜索发现好像有nativeCheck
,但是查看了函数内容提示是个错误的flag
仔细看发现这里的函数在Utils
前有两个下划线
因此实际上的函数又是一个动态注册的
使用以下脚本去除一些混淆
我们可以看到是有注册nativeCheck
和check
函数
但是注册的函数处的参数的地址没有有用的东西
后来看了源码之后,在sub_3D74
又是一个Inline Hook
,
dword_1B37C
是在JNI_OnLoad
里赋了JNIEnv*
类型
而+860
偏移是RegisterNatives
函数(前面分析的是有看到过,所以还算比较熟悉,或者是查看Structures
里的JNINativeInterface
的定义)
因此sub_3D74
函数里Hook
了RegisterNatives
函数,实际调用的是sub_3834
由前面定义知道a3
是JNINativeMethod
所以干的事情就是把传进去的函数减了2022才是实际的函数地址
nativeCheck
函数的地址为0x29BC+3-2022=0x21D8
nativeCheck
内对输入的参数调用了Utils
的test
函数,但是我们看到的函数只有一个return true
这涉及到nativeCheck
里的其他操作
首先我们先回到libstub
里对loadMethod
的Hook
上分析
Hook
后的函数将dexfile
偏移0xB24
的地址给读取了出来并存到了shm
文件下
我们从dex
中可以看到该处地址刚好是存放test
函数地址的地址
然后在libnative.so
的JNI_OnLoad
函数读取了shm
文件里的内容,赋给了methodAddr
尔后在nativeCheck
里给该地址赋了值,即把指令填了回去
再调用test
函数
然后再把test
函数里的指令给清空
因此只需将指令填回去即可恢复test
函数的内容
对输入进行异或后,又调用了check
函数
check函数里对输入进行了填充,然后又调用了Encrypto函数后和签名进行异或,最后与目标数据进行比较
import
logging
import
sys
from
unicorn
import
*
import
struct
from
androidemu.emulator
import
Emulator
from
UnicornTraceDebugger
import
udbg
logging.basicConfig(
stream
=
sys.stdout,
level
=
logging.DEBUG,
format
=
'%(asctime)s %(levelname)7s %(name)34s | %(message)s'
)
logger
=
logging.getLogger(__name__)
emulator
=
Emulator(vfp_inst_set
=
True
, vfs_root
=
'vfs'
)
str_datas
=
{}
def
hook_mem_write(uc,
type
,address,size,value,userdata):
try
:
curdata
=
struct.pack(
"I"
, value)[:size]
str_datas[address]
=
curdata
except
:
print
(size)
emulator.mu.hook_add(UC_HOOK_MEM_WRITE,hook_mem_write)
emulator.load_library(
'lib/libc.so'
,do_init
=
False
)
lib_module
=
emulator.load_library(
'lib/libstub.so'
,do_init
=
True
)
base_addr
=
lib_module.base
sodata
=
open
(
'lib/libstub.so'
,
'rb'
).read()
for
address,value
in
str_datas.items():
if
base_addr < address < base_addr
+
lib_module.size:
offset
=
address
-
base_addr
-
0x1000
print
(
'address:0x%x data:%s offset:0x%x '
%
(address, value, offset))
sodata
=
sodata[:offset]
+
value
+
sodata[offset
+
len
(value):]
with
open
(
'lib/libstub_new.so'
,
'wb'
) as
file
:
file
.write(sodata)
import
logging
import
sys
from
unicorn
import
*
import
struct
from
androidemu.emulator
import
Emulator
from
UnicornTraceDebugger
import
udbg
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-7-10 00:42
被si1enceZ编辑
,原因: 标题出现错误