Dalvik下 一代壳dump脱壳方案
核心总是在内存当中把dex文件dump下来
首先还是dalvik下 dexclassloader加载dex文件的流程 我们看源码
既然是dalvik就看4.4及以前版本的,
可以看到就一个父类的构造函数,我们进去看

可以看到这里面也还是把parent作为节点给父classloader构造函数

跟,这个时候可以看到俩模拟器 进入第二个

功能也简单,就是设置好parent

再回到上一级

这条 跟进去 可以看到
5 public DexPathList(ClassLoader definingContext, String dexPath,
86 String libraryPath, File optimizedDirectory) {
87 if (definingContext == null) {
88 throw new NullPointerException("definingContext == null");
89 }
90
91 if (dexPath == null) {
92 throw new NullPointerException("dexPath == null");
93 }
94
95 if (optimizedDirectory != null) {
96 if (!optimizedDirectory.exists()) {
97 throw new IllegalArgumentException(
98 "optimizedDirectory doesn't exist: "
99 + optimizedDirectory);
100 }
101
102 if (!(optimizedDirectory.canRead()
103 && optimizedDirectory.canWrite())) {
104 throw new IllegalArgumentException(
105 "optimizedDirectory not readable/writable: "
106 + optimizedDirectory);
107 }
108 }
109
110 this.definingContext = definingContext;
111 ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
112 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
113 suppressedExceptions);
114 if (suppressedExceptions.size() > 0) {
115 this.dexElementsSuppressedExceptions =
116 suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
117 } else {
118 dexElementsSuppressedExceptions = null;
119 }
120 this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
121 }
122
这条 跟进去

如果是dex文件 就loaddexfile 继续跟

可以看到调用了loaddex

继续 loaddex

应该改是这个 跟opendexfile 这里还给了一个cookie

继续跟 我们可以看到这是一个native函数

找到这个 来到Cpp了

以下代码
static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
152 JValue* pResult)
153{
154 StringObject* sourceNameObj = (StringObject*) args[0];
155 StringObject* outputNameObj = (StringObject*) args[1];
156 DexOrJar* pDexOrJar = NULL;
157 JarFile* pJarFile;
158 RawDexFile* pRawDexFile;
159 char* sourceName;
160 char* outputName;
161
162 if (sourceNameObj == NULL) {
163 dvmThrowNullPointerException("sourceName == null");
164 RETURN_VOID();
165 }
166
167 sourceName = dvmCreateCstrFromString(sourceNameObj);
168 if (outputNameObj != NULL)
169 outputName = dvmCreateCstrFromString(outputNameObj);
170 else
171 outputName = NULL;
172
173 /*
174 * We have to deal with the possibility that somebody might try to
175 * open one of our bootstrap class DEX files. The set of dependencies
176 * will be different, and hence the results of optimization might be
177 * different, which means we'd actually need to have two versions of
178 * the optimized DEX: one that only knows about part of the boot class
179 * path, and one that knows about everything in it. The latter might
180 * optimize field/method accesses based on a class that appeared later
181 * in the class path.
182 *
183 * We can't let the user-defined class loader open it and start using
184 * the classes, since the optimized form of the code skips some of
185 * the method and field resolution that we would ordinarily do, and
186 * we'd have the wrong semantics.
187 *
188 * We have to reject attempts to manually open a DEX file from the boot
189 * class path. The easiest way to do this is by filename, which works
190 * out because variations in name (e.g. "/system/framework/./ext.jar")
191 * result in us hitting a different dalvik-cache entry. It's also fine
192 * if the caller specifies their own output file.
193 */
194 if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
195 ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
196 dvmThrowIOException(
197 "Re-opening BOOTCLASSPATH DEX files is not allowed");
198 free(sourceName);
199 free(outputName);
200 RETURN_VOID();
201 }
202
203 /*
204 * Try to open it directly as a DEX if the name ends with ".dex".
205 * If that fails (or isn't tried in the first place), try it as a
206 * Zip with a "classes.dex" inside.
207 */
208 if (hasDexExtension(sourceName)
209 && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
210 ALOGV("Opening DEX file '%s' (DEX)", sourceName);
211
212 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
213 pDexOrJar->isDex = true;
214 pDexOrJar->pRawDexFile = pRawDexFile;
215 pDexOrJar->pDexMemory = NULL;
216 } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
217 ALOGV("Opening DEX file '%s' (Jar)", sourceName);
218
219 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
220 pDexOrJar->isDex = false;
221 pDexOrJar->pJarFile = pJarFile;
222 pDexOrJar->pDexMemory = NULL;
223 } else {
224 ALOGV("Unable to open DEX file '%s'", sourceName);
225 dvmThrowIOException("unable to open DEX file");
226 }
227
228 if (pDexOrJar != NULL) {
229 pDexOrJar->fileName = sourceName;
230 addToDexFileTable(pDexOrJar);
231 } else {
232 free(sourceName);
233 }
234
235 free(outputName);
236 RETURN_PTR(pDexOrJar);
237}
如果是dex的

跟这个dvmrawdexfileopen
这个里面我们可以看到先open了这个函数,对魔数,大小做了判断

我们要知道的是在dalvik虚拟机里面会变成这个dex会被优化成为odex文件
这个里面也可以找到这个优化过程

接着跟
可以看到里面这 先fork出一个子进程 使用dexopt 进行优化

我们看看这个所谓的opt是怎么写的呢 接着看
是一个有main的cpp

如果是dex就在这

接着跟
里面两个,一个做初始化,一个做优化

继续往里面看

这里把dex 映射到了一个地址

还可以进去下面的rewrite看看

这个里面的
网上公开的比较多的脱壳点就是这dvmdexfileopenpartial,和上面的这个dexfileparse进行hook 这俩都是优化过后的

都是有起始地址传入的,可以看到其实这些都可以作为hook点的
frida,xposed都可以进行hook,第一个地址,第二个文件大小,dump下来就好
我们就可以直接在这个源码当中修改,通过内存dump
注意这边没用fopen用open是因为有些加壳厂商可能对fopen这些函数做了hook

刷入之后 基本上都是正常操作

回过头看第一次进行映射的是在什么时候

我们进行也可以在这边,未优化的时候进行hook

这个也可以用

rewrite map dexswapandverify 还有上面俩都可以
总的来说 dalvik下脱壳点不少
实践中看到一个函数抽取壳 后面研究

ART下dex加载流程和通用脱壳点
android8.0后首次引入 inmemorydexclassloader
下面就是inmemorydexclassloader实现加载的一个流程
可以看到第一个是多个dexfile,第二个为单个

不管咋样都会调用basedexclassloader中的构造函数 我们看看
是这个

进入dexpathlist 里面的这个 可以看到上面对context做了一些判断,然后后面重点是调用makeinmemorydexelements

来到这里,里面会遍历这个dexfile,然后new一个dexfile出来

是这个

继续跟
可以看到有两种 格式

但是这俩玩意都是native的 可以看到

我们去搜一下
俩函数都在 dalvik_system_DexFile.cc
可以看到这里进行memcpy,有地址 也是可以hook进行dump的


我们可以看到最后都会调用下面这个个 可以看到进行了createdexfile

这里的参数也有映射的地址

里面还调用了这个 我们传入了loaction,信息

来到这里 可以看到有路径有映射

这个里面还调用了opencommon函数,里面就有地址,大小,等信息
里面调用了new dexfile 更有地址 大小等信息

所以看到加载逻辑当中有很多函数有这些地址啊大小啊啥的
inmemorydexclassloader并没有生成oat 优化的东东
课程帮忙总结了一下可供hook的函数
好这个就是inmemorydexclassloader里面的

还有一个dexclassloader

往上跟 四个参数

dexpathlist
是这个重载 关键是makedexelements

继续跟

继续跟

跟

跟

这里的opendexfile是关键,再往下是native函数了 其实和inmemory一样都有这个

这里就是jni层的一个实现了

可以看到这里面第一次出现oat这个东西,这个东西后面经常遇到

再往下翻下就可以看到这个,这个函数最终回调dex2oat 生成oat

跟这opendexfilesfromoat,进去呢 检验有没有oat 必然没有 所以执行

下面这个
继续跟 这里面会调用dex2oat完成oat的编译

这里是关键

进去 可以看到,通过pushback压入一些参数

最后使用exec


跟,这里首次进行了子进程的hook

在对dex2oat相关代码进行hook或者修改时,都会对dex2oat进行一个阻塞
要想art下抽取技术的实现,也要阻塞这个dex2oat
讲讲抽取
如果上面成功阻塞了这个dex2oat使得这个oat文件没有成功生成,在

里面会走到这,就会去调用一个dex文件

详细看看,和上面那个open还不一样
里面我们可以看到我们第一个openandreadmagic可以读到文件路径,不是最好的,但也可以用

defcon已有研究

仅需看那个open

下面调用了opencommon

同时还有mapfile

所以

我们尝试一些重合的
如opencommon和dexfile
还是一样的编译

还是一个整体壳的脱壳方法
上面就是两种
一种是inmemorydexclassloader
一种是禁用掉dex2oat之后用dexclassloader
也可以看看没有禁用掉dex2oat之后 的脱壳流程

有main啊,有点像那个opt、

解析参数 打开配置文件啊等等

里面这个调用要跟进去

来到这里

实际上里面的脱壳点还有不少
甚于说对class加载或者method列的时候,都可以,只要对内存中dex文件有操作,都可以尝试。