首页
社区
课程
招聘
[原创]MRCTF2022 stuuuuub 题解
发表于: 2022-7-9 23:12 13090

[原创]MRCTF2022 stuuuuub 题解

2022-7-9 23:12
13090

学了这么一段时间的Android,难得见到的一道比较对口的逆向题,

不过比较遗憾的是,题目用的函数抽取的方法的文章我刚好看过,还研究了蛮久,,不过当时就一直没有怎么看懂,只死磕出了在抽取指令时的操作,但是却不明白他是通过什么以及如何将指令给填充回去的

不过经过实打实分析了这个题目后,对指令填充有了一定的理解,虽然这个题目填充的方式比较简易,但是至少知道了核心思想。果然知识表面上看懂了都还不是自己的,只有真正上手实践了才能理解的更加深刻

首先通过AndroidManifest.xml找到入口Activitycom.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则没有输出

图片描述

所以总的来说 attachBaseContexte.a函数加载了两个数据文件夹下so文件的路径以及运行环境和root的检测

e.d()中分别读取并解密了assets文件夹下的三个文件res.datlibc++_shared.sobuild.json

其中res.datlibc++_shared.so作为两个so文件

build.json作为Dex文件通过InMemoryDexClassLoader加载

InMemoryDexClassLoader能够通过从一个包含着DEX文件的缓冲区中加载类。可以用来执行没有被写进本地文件系统的代码

尔后调用了e.g()来将当前的ActivityclassLoader替换成我们加载了自定义类的classLoader

图片描述

因为当前的ActivitymClassLoader已经是Activity默认的PathClassLoader,而该PathClassLoader并没有加载我们的自定义类,而且我们接下来要启动的MainActivity也是由PathClassLoader加载的,而且我们的InMemoryDexClassLoaderPathClassLoader都是继承于BaseDexClassLoader,所以后续如果要调用自定义类的话就会找不到

图片描述

图片描述

接下来分析解密几个so

首先是由res.dat解密得到libnative.so

图片描述

读取res.dat文件后调用了decodeSo函数进行解密存放在应用的数据目录下的libnative.so,而decodeSolibstub.so里的native函数

图片描述

但是在libstub.so里却没有直接找到decodeSo函数,因此应该是JNI_OnLoad里动态注册的

图片描述

图片描述

另外libstub.so使用了Ollvm的字符串加密和控制流平坦化

字符串在加载时调用了.init_array里的以.datadiv_decode开头的函数对字符串进行解密

图片描述

因此对于字符串解密有两种方法

这里参考官方给的WP中使用了AndroidNativeEmu框架

解密前:

图片描述

解密后:

图片描述

图片描述

sub_3F44里调用了sub_4D06RegisterNatives动态注册函数

RegisterNatives的定义

图片描述

所以可以看到sub_3A74就是实际调用的decodeSo函数

图片描述

因为

实际上干的内容就是逐字节异或0x22,然后保存到文件,即可得到libnative.so

但是后面JNI_OnLoad还没有分析完

后面调用了sub_2A2Csub_2DEC

后面看了源码后才知道这两个函数分别是通过inLine HookHook libc.soexecveHook libart.soloadMethod

项目地址:ele7enxxh / Android-Inline-Hook

大致使用方法

图片描述

图片描述

MainActivity存在的dexbuild.json异或49即可得到

图片描述

MainActivity里检查了输入长度然后调用了Utils.nativeCheck

图片描述

Utils加载了libnative.so

图片描述

我们直接搜索发现好像有nativeCheck,但是查看了函数内容提示是个错误的flag

仔细看发现这里的函数在Utils前有两个下划线

图片描述

因此实际上的函数又是一个动态注册的

使用以下脚本去除一些混淆

图片描述

我们可以看到是有注册nativeCheckcheck函数

但是注册的函数处的参数的地址没有有用的东西

后来看了源码之后,在sub_3D74又是一个Inline Hook

dword_1B37C是在JNI_OnLoad里赋了JNIEnv*类型

图片描述
+860偏移是RegisterNatives函数(前面分析的是有看到过,所以还算比较熟悉,或者是查看Structures里的JNINativeInterface的定义)

图片描述

因此sub_3D74函数里HookRegisterNatives函数,实际调用的是sub_3834

图片描述

由前面定义知道a3JNINativeMethod

所以干的事情就是把传进去的函数减了2022才是实际的函数地址

nativeCheck函数的地址为0x29BC+3-2022=0x21D8

nativeCheck内对输入的参数调用了Utilstest函数,但是我们看到的函数只有一个return true

这涉及到nativeCheck里的其他操作

首先我们先回到libstub里对loadMethodHook上分析

图片描述

Hook后的函数将dexfile偏移0xB24的地址给读取了出来并存到了shm文件下
图片描述

我们从dex中可以看到该处地址刚好是存放test函数地址的地址

然后在libnative.soJNI_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编辑 ,原因: 标题出现错误
收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 1776
活跃值: (1500)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
楼主求一个原件
2022-7-15 11:25
0
雪    币: 19
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
大佬您好,可以给一份UnicornTraceDebugger的代码吗?网上没搜到。感谢!hanjiefang2008@gmail.com
2024-8-13 14:21
0
游客
登录 | 注册 方可回帖
返回
//