-
-
一个自定义classloader的函数抽取壳样本
-
发表于: 2020-11-9 19:18 7347
-
题目出自2W班7月第三题
题目要求:基于frida实现的fart的一个版本是通过对ClassLink类中的LoadMethod函数进行hook实现对函数粒度的脱壳的。请编写基于xposed实现的fart版本插件,能够实现对函数粒度的脱壳
根据题目要求,可以知道,我们通过hook LoadMethod即可得到DexFile进而得到base和size,即可dump出dex。如果有函数抽取,即遍历所有的类得到codeitem后还原即可。
本次实践分为两个部分,一个即是原始思路,照葫芦画瓢参照FART的脱壳思路去编写代码,遇到困难暂时没法解决的时候,转变思路二,改用FART配合xposed去实现。
下面是解题过程:
首先我们可以分析frida-fart是如何实现demp dex的。
粗略分析一下就是
那么我们在so层也可以画葫芦试试
测试安卓版本为8.1
部分代码如下
上述看起来一步一步的确是可以拿得到codeitem,然后进一步拿得到ins的。
但是有一个问题我一直没法解决,就是某些方法会报access violation 异常。我通过搜索发现这个是底层发出来的异常,软件层貌似没法catch。
通过使用frida-fart我发现frida版本的也有这样的问题,但是frida这边可以通过try catch捕捉,让程序继续行走。
因为暂时无法解决这个问题,所以我放弃这个方向。尝试用fart进行dump。
这里偷懒,就直接用寒冰大佬的xp+fart rom继续dump,就没有自己编译源码了。这里环境android6.0
那么这时候思路就转变了,通过hook loadMethod方法可以拿到artmethod,
而根据fart代码,dumpArtMethod这个方法,传入artmethod即可dump。
上述通过so层调用FART的dumpArtMethodFunction方法,可以dump出dex,以及classlist。这是FART自带的功能。
而在java层,我们需要遍历所有类。
首先hook掉DexClassLoader和PathClassLoader的构造函数,并将classloader存起来。
因为是自定义的Xposed,而所以名字为XcustomBridge。其实这里就是Xposed,仅供参考,不可照抄
紧接着加载我们的so,并遍历所有的classloader,执行loadClass操作。
将dump下来后bin文件批量恢复后,即可查看到,函数已经恢复了。
但是如果点多几个函数查看
会发现部分函数并没有复原
那么通过回想,我们可以得知,还原的代码其实都是app启动过程中调用过的方法,所以dump下来后,是包含代码的。因此这个壳也是函数抽取壳,而且恢复后不会复原。
而查看FART dump出来的ins文件,发现也缺了很多数据,那么这里可以猜测是某个环节出了问题导致没有dump出来。
然后通过回溯error log 可以发现是classloader的锅
那么这里我们知道了这个app有自定义的classloader。
根据提示,我们可以发现这是一个直接继承于classloader的类。
那么来看看通常我们是如何获取classlist的。
我们是通过获得BaseDexClassLoader的pathList字段从而进一步往下获取classList的。但是由于我们现在是直接继承于classloader,所以我们要想办法获取classlist。
那么这里就有两种方法可以实现了。
第一个,fart使用过程中是dump classlist出来。
因此这里我们直接遍历文件,然后loadClass即可。
接下来我们对dex方法体进行批量恢复后,可以看到,函数都恢复了。
少部分没有恢复的函数,是因为这个classloader没有找到类,报class not found。接下来我们可以将所有classloader都跑一遍这个list即可
方法二
其实fart也是解析DexFile从而拿到classlist的。
既然有了整个dexFile文件,那么有啥不能解析的呢。
在这里我们可以一层一层的不断获取不断遍历,就可以拿到我们想要的classname
拿到classname后我们就可以用这个去遍历loadClass后再dump dex即可。
可以看到这个题目最后想要说的就是自定义classloader如何进行函数抽取还原,那么这里我们需要熟悉Dexfile的文件结构以及了解classloader如何使用
void
*
pVoid
=
old_loadmethod3(thiz, thread, dex_file, it, klass, artmethod);
__android_log_print(
5
,
"hookso"
,
"pVoid ptr:%p"
, pVoid);
获取base和size
const DexHeader
*
base
=
dex_file.pHeader;
size_t size
=
dex_file.pHeader
-
>fileSize;
获取code item offset和method idx
uint32_t codeItemOffset
=
artmethod
-
>dex_code_item_offset_;
uint32_t idx
=
artmethod
-
>dex_method_index_;
hook prettymethod方法 主动调用获得方法名
const std::string &string
=
prettyMethodFunction(artmethod, artmethod,
true);
通过偏移可以拿到codeitem
long
codeItemAddr
=
(
long
) base
+
codeItemOffset;
CodeItem
*
codeItem
=
(CodeItem
*
) codeItemAddr;
这部分代码可以直接dump dex出来
int
pid
=
getpid();
char dexFilePath[
100
]
=
{
0
};
sprintf(dexFilePath,
"/sdcard/xxxxx/%p %d LoadMethod.dex"
, base, size);
mkdir(
"/sdcard/xxxxx"
,
0777
);
int
fd
=
open
(dexFilePath, O_CREAT | O_RDWR,
666
);
if
(fd >
0
) {
ssize_t i
=
write(fd, base, size);
if
(i >
0
) {
close(fd);
}
}
...
void
*
pVoid
=
old_loadmethod3(thiz, thread, dex_file, it, klass, artmethod);
__android_log_print(
5
,
"hookso"
,
"pVoid ptr:%p"
, pVoid);
获取base和size
const DexHeader
*
base
=
dex_file.pHeader;
size_t size
=
dex_file.pHeader
-
>fileSize;
获取code item offset和method idx
uint32_t codeItemOffset
=
artmethod
-
>dex_code_item_offset_;
uint32_t idx
=
artmethod
-
>dex_method_index_;
hook prettymethod方法 主动调用获得方法名
const std::string &string
=
prettyMethodFunction(artmethod, artmethod,
true);
通过偏移可以拿到codeitem
long
codeItemAddr
=
(
long
) base
+
codeItemOffset;
CodeItem
*
codeItem
=
(CodeItem
*
) codeItemAddr;
这部分代码可以直接dump dex出来
int
pid
=
getpid();
char dexFilePath[
100
]
=
{
0
};
sprintf(dexFilePath,
"/sdcard/xxxxx/%p %d LoadMethod.dex"
, base, size);
mkdir(
"/sdcard/xxxxx"
,
0777
);
int
fd
=
open
(dexFilePath, O_CREAT | O_RDWR,
666
);
if
(fd >
0
) {
ssize_t i
=
write(fd, base, size);
if
(i >
0
) {
close(fd);
}
}
...
先执行原来的loadMethod逻辑
void
*
pVoid
=
old_loadmethod3(thiz, thread, dex_file, it, klass, artmethod);
__android_log_print(
5
,
"hookso"
,
"pVoid ptr:%p"
, pVoid);
然后通过so层hook dumpArtmethod函数,并将参数传入
try
{
dumpArtMethodFunction(artmethod);
}catch (...){
}
先执行原来的loadMethod逻辑
void
*
pVoid
=
old_loadmethod3(thiz, thread, dex_file, it, klass, artmethod);
__android_log_print(
5
,
"hookso"
,
"pVoid ptr:%p"
, pVoid);
然后通过so层hook dumpArtmethod函数,并将参数传入
try
{
dumpArtMethodFunction(artmethod);
}catch (...){
}
XcustomBridge.hookAllConstructors(DexClassLoader.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
final ClassLoader classLoader
=
(ClassLoader) param.thisObject;
XcustomBridge.log(
"DexClassLoader:"
+
classLoader.toString());
mClassLoaders.add(classLoader);
}
});
XcustomBridge.hookAllConstructors(PathClassLoader.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
final ClassLoader classLoader
=
(ClassLoader) param.thisObject;
XcustomBridge.log(
"PathClassLoader:"
+
classLoader.toString());
mClassLoaders.add(classLoader);
}
});
XcustomBridge.hookAllConstructors(DexClassLoader.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
final ClassLoader classLoader
=
(ClassLoader) param.thisObject;
XcustomBridge.log(
"DexClassLoader:"
+
classLoader.toString());
mClassLoaders.add(classLoader);
}
});
XcustomBridge.hookAllConstructors(PathClassLoader.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
final ClassLoader classLoader
=
(ClassLoader) param.thisObject;
XcustomBridge.log(
"PathClassLoader:"
+
classLoader.toString());
mClassLoaders.add(classLoader);
}
});
XcustomHelpers.findAndHookMethod(Application.
class
,
"attach"
, Context.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
XcustomBridge.log(
"attach after"
);
mContext
=
(Context) param.args[
0
];
XcustomHelpers.callMethod(Runtime.getRuntime(),
"doLoad"
,
"/system/lib/libnative-lib.so"
, mContext.getClassLoader());
new Thread(new Runnable() {
@Override
public void run() {
try
{
Thread.sleep(
30
*
1000
);
} catch (InterruptedException e) {
e.printStackTrace();
Log.e(
"hook1"
, Log.getStackTraceString(e));
}
for
(
int
i
=
0
; i < mClassLoaders.size(); i
+
+
) {
ClassLoader classLoader
=
mClassLoaders.get(i);
TestClassloader(classLoader);
}
fart();
}
}).start();
}
});
XcustomHelpers.findAndHookMethod(Application.
class
,
"attach"
, Context.
class
, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super
.afterHookedMethod(param);
XcustomBridge.log(
"attach after"
);
mContext
=
(Context) param.args[
0
];
XcustomHelpers.callMethod(Runtime.getRuntime(),
"doLoad"
,
"/system/lib/libnative-lib.so"
, mContext.getClassLoader());
new Thread(new Runnable() {
@Override
public void run() {
try
{
Thread.sleep(
30
*
1000
);
} catch (InterruptedException e) {
e.printStackTrace();
Log.e(
"hook1"
, Log.getStackTraceString(e));
}
for
(
int
i
=
0
; i < mClassLoaders.size(); i
+
+
) {
ClassLoader classLoader
=
mClassLoaders.get(i);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课