MRCTF2022 stuuuuub 题解
2023-2-5 18:0:17 Author: 看雪学苑(查看原文) 阅读量:13 收藏


本文为看雪论坛优秀文章

看雪论坛作者ID:si1enceZ

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

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


StubApp-一代壳分析

attachBaseContext

首先通过AndroidManifest.xml找到入口Activity:com.mrctf.android2022.StubApp

StubApp类里先看attachBaseContext。

判断了e类的a函数返回值,如果True则取e.d()的ClassLoader。

e.a()

设置了两个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()

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,所以后续如果要调用自定义类的话就会找不到。


libstub.so

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

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

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

decode String

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

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

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

① 手写idapython脚本执行这几个函数,对字符串进行解密。

② 使用unicorn引擎模拟执行,在进行写操作时对操作数的地址进行Hook,即可获取初始化后的so文件。

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

import loggingimport sys from unicorn import *import struct from androidemu.emulator import Emulatorfrom 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.basesodata = 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)

解密前:

解密后:

decodeSo

sub_3F44里调用了sub_4D06是RegisterNatives动态注册函数。

RegisterNatives的定义

jint RegisterNatives(JNIEnv *env, jclass clazz,const JNINativeMethod *methods, jint nMethods);

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

因为

a & ~b | ~a & b = a ^ b

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

JNI_OnLoad

但是后面JNI_OnLoad还没有分析完。后面调用了sub_2A2C和sub_2DEC。

后面看了源码后才知道这两个函数分别是通过inLine Hook来Hook libc.so的execve和Hook libart.so的loadMethod。

Inline hook

项目地址:https://github.com/ele7enxxh/Android-Inline-Hook

大致使用方法:

if(registerInlineHook(target_func,read_func,tmp_func) == ELE7EN_OK){    if(inlineHook(target_func != ELE7EN_OK)){        return -1;    }    else{        return 0;    }}


libnative.so

分析MainActivity

MainActivity存在的dex由build.json异或49即可得到。

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

Utils加载了libnative.so。

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

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

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

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

for addr,name in Names():    if name.startswith('x.') or name.startswith('y.'):        patch_dword(addr,0)

sub_3D74

我们可以看到是有注册nativeCheck和check函数,但是注册的函数处的参数的地址没有有用的东西。后来看了源码之后,在sub_3D74又是一个Inline Hook,dword_1B37C是在JNI_OnLoad里赋了JNIEnv*类型。


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

sub_3834

因此sub_3D74函数里Hook了RegisterNatives函数,实际调用的是sub_3834

由前面定义知道a3是JNINativeMethod。

typedef struct{    const char* name;    const char* signature;    void* fnPtr;}JNINativeMethod;

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

nativeCheck

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

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

这涉及到nativeCheck里的其他操作。


函数抽取壳-二代壳分析

myloadmethod

首先我们先回到libstub里对loadMethod的Hook上分析。

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

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

getPath

然后在libnative.so的JNI_OnLoad函数读取了shm文件里的内容,赋给了methodAddr。

nativeCheck

尔后在nativeCheck里给该地址赋了值,即把指令填了回去。

再调用test函数。

然后再把test函数里的指令给清空。

recovery

因此只需将指令填回去即可恢复test函数的内容。

对输入进行异或后,又调用了check函数。

解题

check函数里对输入进行了填充,然后又调用了Encrypto函数后和签名进行异或,最后与目标数据进行比较。

enc = [38,43,44,115,20,17,22,19,32,119,42,41,19,68,19,26,117,112,38,33,18,67,19,18,32,38,35,38,19,69,17,23,117,112,44,112,19,27,20,21,39,32,37,39,23,76,21,20,47,32,32,120,31,24,67,70,35,39,34,39,23,74,23,16,32,115,32,37,21,20,18,27,36,38,16,67,36,39,16,26]signByte = [0x33,0x31,0x3a,0x31,0x34,0x36,0x34,0x61,0x3b,0x39,0x32,0x39,0x3c,0x3f,0x3f,0x38,0x71,0x21,0x22,0x20,0x24,0x27,0x26,0x26,0x28,0x2b,0x2a,0x29,0x2c,0x29,0x29,0x2d,0x44,0x43,0x1b,0x47,0x16,0x15,0x15,0x17,0x18,0x4d,0x1a,0x36,0x31,0x3b,0x31,0x65,0x3d,0x30,0x33,0x30,0x31,0x3c,0x6d,0x3b,0x3d,0x6a,0x3f,0x21,0x21,0x23,0x23,0x76,0x25,0x23,0x27,0x28,0x2a,0x2a,0x2f,0x24,0x2e,0x2f,0x2f,0x41,0x12,0x12,0x13,0x1c,0x15,0x10,0x17,0x1b,0x1c,0x1f,0x30,0x35,0x32,0x35,0x35,0x36,0x36,0x36,0x3f,0x3a,0x39,0x3a,0x3c,0x6c,0x3d,0x3f,0x20,0x29,0x22,0x25,0x24,0x26,0x23,0x22,0x28,0x2d,0x2a,0x23,0x2d,0x2e,0x2e,0x2e,0x17,0x12,0x11,0x12,0x14,0x44,0x15,0x17,0x18,0x11,0x1a,0x36,0x31,0x31,0x36,0x31,0x35,0x32,0x37,0x3f,0x38,0x39,0x3b,0x3d,0x3a,0x3d,0x3c,0x21,0x21,0x73,0x20,0x24,0x25,0x2e,0x27,0x2e,0x29,0x29,0x2e,0x29,0x2d,0x2a,0x2f,0x41,0x10,0x11,0x13,0x15,0x12,0x15,0x14,0x19,0x19,0x4b,0x33,0x31,0x32,0x3b,0x34,0x33,0x36,0x34,0x3d,0x3c,0x3a,0x3f,0x3c,0x6f,0x3f,0x3c,0x20,0x20,0x25,0x20,0x27,0x24,0x26,0x76,0x2b,0x29,0x2a,0x23,0x2c,0x2b,0x2e,0x2c,0x15,0x14,0x12,0x17,0x14,0x16,0x17,0x14,0x18,0x18,0x1d,0x33,0x32,0x32,0x32,0x61,0x34,0x31,0x37,0x6c,0x3a,0x38,0x38,0x3d,0x3e,0x3e,0x3c,0x25,0x22,0x23,0x20,0x27,0x26,0x26,0x24,0x2a,0x2a,0x2b,0x28,0x25,0x2e,0x2d,0x2c,0x10,0x14,0x43,0x12,0x13,0x15,0x42,0x14,0x1c,0x1a,0x1c,0x33,0x31,0x31,0x36,0x37,0x35,0x35,0x30,0x3b,0x39,0x39,0x39,0x3f,0x3c,0x3d,0x36,0x23,0x22,0x21,0x23,0x21,0x74,0x25,0x27,0x2c,0x21,0x29,0x2a,0x2c,0x7c,0x2d,0x2f,0x10,0x19,0x12,0x15,0x14,0x16,0x13,0x12,0x18,0x1d,0x1a,0x36,0x30,0x31,0x33,0x35,0x32,0x35,0x34,0x39,0x39,0x6b,0x38,0x3c,0x3d,0x36,0x3f,0x26,0x21,0x21,0x26,0x21,0x25,0x22,0x27,0x20,0x28,0x29,0x2b,0x2d,0x2a,0x2d,0x2c,0x11,0x11,0x43,0x10,0x14,0x15,0x1e,0x17,0x1e,0x19,0x19,0x35,0x34,0x32,0x37,0x34,0x32,0x37,0x34,0x38,0x38,0x3d,0x38,0x3f,0x3c,0x3e,0x6e,0x23,0x21,0x22,0x2b,0x24,0x23,0x26,0x24,0x2d,0x2c,0x2a,0x2f,0x2c,0x7c,0x2f,0x2c,0x10,0x10,0x15,0x10,0x17,0x14,0x16,0x46,0x1b,0x19,0x1a,0x38,0x31,0x34,0x33,0x37,0x30,0x33,0x37,0x3c,0x39,0x68,0x3a,0x3f,0x3d,0x3f,0x38,0x23,0x22,0x23,0x23,0x75,0x26,0x26,0x27,0x20,0x29,0x2c,0x2b,0x2f,0x28,0x2b,0x2f,0x14,0x11,0x11,0x12,0x17,0x15,0x17,0x10,0x1b,0x1a,0x1a,0x38,0x33,0x32,0x32,0x36,0x37,0x35,0x37,0x38,0x6d,0x3a,0x3d,0x3c,0x34,0x3c,0x6e,0x28,0x27,0x26,0x2b,0x2c,0x23,0x70,0x20,0x28,0x7d,0x2a,0x2a,0x2c,0x2c,0x2e,0x2e,0x10,0x14,0x12,0x13,0x14,0x16,0x1e,0x15,0x18,0x18,0x1a,0x66,0x31,0x32,0x30,0x34,0x3d,0x34,0x37,0x39,0x39,0x6b,0x3b,0x3e,0x35,0x3c,0x3f,0x21,0x21,0x23,0x23,0x24,0x2d,0x27,0x73,0x7b,0x7c,0x2a,0x2e,0x2a,0x2d,0x27,0x7a,0x17,0x44,0x12,0x47,0x14,0x16,0x13,0x1f,0x4b,0x1a,0x18,0x38,0x33,0x31,0x65,0x60,0x67,0x67,0x35,0x38,0x3e,0x39,0x39,0x39,0x3d,0x6f,0x39,0x27,0x77,0x71,0x71,0x2c,0x71,0x74,0x2f,0x21,0x7f,0x2f,0x79,0x28,0x7b,0x2c,0x79,0x41,0x47,0x1b,0x13,0x42,0x16,0x14,0x13,0x19,0x10,0x48,0x37,0x31,0x3a,0x30,0x62,0x30,0x30,0x36,0x31,0x6c,0x6c,0x39,0x3e,0x39,0x3d,0x69,0x22,0x25,0x2a,0x2a,0x71,0x23,0x2f,0x26,0x21,0x2b,0x2b,0x2c,0x7e,0x25,0x2c,0x7d,0x46,0x10,0x15,0x15,0x10,0x47,0x42,0x1e,0x4c,0x11,0x18,0x39,0x63,0x36,0x36,0x34,0x34,0x31,0x66,0x6a,0x3e,0x32,0x68,0x6a,0x3e,0x6c,0x3b,0x29,0x23,0x24,0x70,0x23,0x70,0x72,0x75,0x7e,0x2c,0x7e,0x2f,0x2b,0x29,0x29,0x29,0x41,0x13,0x16,0x45,0x15,0x47,0x1e,0x17,0x4b,0x11,0x1c,0x65,0x30,0x37,0x34,0x32,0x37,0x32,0x64,0x6c,0x6f,0x6b,0x39,0x6f,0x3a,0x3b,0x3b,0x23,0x25,0x73,0x22,0x20,0x2d,0x26,0x75,0x2e,0x2d,0x28,0x29,0x2b,0x29,0x2e,0x2a,0x16,0x44,0x43,0x41,0x15,0x41,0x42,0x44,0x19,0x4f,0x13,0x61,0x31,0x31,0x36,0x33,0x34,0x32,0x64,0x6c,0x3d,0x33,0x6e,0x6e,0x3b,0x3e,0x3a,0x27,0x26,0x24,0x25,0x70,0x70,0x72,0x23,0x2b,0x2c,0x7e,0x2d,0x25,0x7f,0x2d,0x7c,0x44,0x16,0x10,0x17,0x47,0x12,0x1f,0x1f,0x1b,0x4c,0x18,0x38,0x38,0x63,0x32,0x31,0x64,0x35,0x33,0x3c,0x3b,0x39,0x6e,0x6d,0x3f,0x3b,0x69,0x76,0x20,0x2a,0x23,0x75,0x77,0x24,0x23,0x2d,0x7f,0x28,0x7a,0x2a,0x2b,0x2f,0x7d,0x18,0x18,0x41,0x47,0x10,0x11,0x16,0x1e,0x1f,0x4c,0x19,0x64,0x36,0x30,0x61,0x60,0x34,0x60,0x31,0x30,0x6a,0x3a,0x33,0x38,0x6c,0x3d,0x6e,0x26,0x28,0x71,0x21,0x2d,0x76,0x2f,0x73,0x29,0x2b,0x78,0x7d,0x7f,0x7e,0x27,0x79,0x11,0x17,0x12,0x17,0x15,0x43,0x13,0x13,0x18,0x4c,0x18,0x39,0x30,0x61,0x65,0x30,0x30,0x60,0x64,0x31,0x38,0x3d,0x6f,0x3c,0x6b,0x6b,0x3e,0x21,0x21,0x23,0x22,0x2d,0x73,0x20,0x20,0x7e,0x7d,0x2c,0x79,0x2d,0x2e,0x28,0x2d,0x41,0x19,0x40,0x13,0x45,0x12,0x15,0x17,0x11,0x4d,0x49,0x34,0x32,0x34,0x34,0x3d,0x35,0x63,0x33,0x3e,0x6c,0x3c,0x3c,0x6f,0x3f,0x3d,0x3d,0x27,0x70,0x77,0x20,0x26,0x23,0x72,0x74,0x2c,0x21,0x7b,0x23,0x29,0x2d,0x7f,0x2e,0x12,0x13,0x16,0x16,0x47,0x43,0x1f,0x1f,0x4a,0x1c,0x1b,0x36,0x32,0x34,0x36,0x34,0x60,0x30,0x64,0x6a,0x31,0x33,0x69,0x38,0x3e,0x3a,0x3e,0x27,0x73,0x25,0x20,0x70,0x23,0x26,0x27,0x2c,0x2d,0x2e,0x2d,0x7f,0x2f,0x26,0x7a,0x18,0x17,0x47,0x47,0x13,0x11,0x47,0x10,0x10,0x1a,0x48,0x64,0x62,0x67,0x66,0x3d,0x64,0x34,0x3e,0x3e,0x39,0x38,0x3f,0x34,0x3e,0x3a,0x39,0x73,0x26,0x20,0x76,0x71,0x70,0x73,0x74,0x7d,0x2a,0x23,0x2b,0x79,0x7b,0x29,0x26,0x13,0x19,0x46,0x46,0x41,0x1d,0x11,0x17,0x1e,0x4a,0x48,0x34,0x31,0x33,0x60,0x67,0x32,0x33,0x32,0x3a,0x3e,0x3d,0x3d,0x3c,0x39,0x6d,0x39,0x27,0x21,0x20,0x23,0x27,0x25,0x27,0x27,0x28,0x29,0x2b,0x7a,0x2f,0x2f,0x2f,0x2c,0x10,0x10]sign =[]for i in range(1023):    sign.append(signByte[i]^(i%43))data = []for i in range(len(enc)):    data.append(enc[i]^sign[i])key =0x20222022for j in range(0,80,8):    left = data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]    right = data[j + 4] << 24 | data[j + 5] << 16 | data[j + 6] << 8 | data[j + 7]     right = (right ^ left)&0xffffffff    left = (left ^ key)&0xffffffff      data[j] = (left >> 24) & 0xff    data[j + 1] = (left >> 16) & 0xff    data[j + 2] = (left >> 8) & 0xff    data[j + 3] = left & 0xff    data[j + 4] = (right >> 24) & 0xff    data[j + 5] = (right >> 16) & 0xff    data[j + 6] = (right >> 8) & 0xff    data[j + 7] = right & 0xffdata = [chr(i) for i in data if i !=0]flag = []for i in range(0,len(data),2):    if data[i]!=0:        flag.append(int(data[i]+data[i+1],16))for i in range(len(flag)-2,0,-1):    flag[i] = flag[i] ^ flag[i+1] ^iflag = [chr(i) for i in flag if i !=0]print(''.join(flag))

看雪ID:si1enceZ

https://bbs.kanxue.com/user-home-926331.htm

*本文由看雪论坛 si1enceZ 原创,转载请注明来自看雪社区

# 往期推荐

1.CVE-2022-21882提权漏洞学习笔记

2.wibu证书 - 初探

3.win10 1909逆向之APIC中断和实验

4.EMET下EAF机制分析以及模拟实现

5.sql注入学习分享

6.V8 Array.prototype.concat函数出现过的issues和他们的POC们

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458493900&idx=1&sn=26ad3adb676212fb11ac022fed7f6ada&chksm=b18e934686f91a50b44d19b12a282a1a71d8e676dd941c8b4fccee8a50e09e58741ef374aa7d#rd
如有侵权请联系:admin#unsafe.sh