通过阅读UPX源码、Unidbg源码,找到了直接模拟未脱壳的UPX安卓so崩溃原因。目前此贡献已被合并至上游。
git版本提交SHA:611d67542ae0ae9f8128ff121ae2a21ea007261d

格式识别与分发 (src/packmast.cpp)

每个格式(如 PackLinuxElf64amd 或 PackLinuxElf64arm)通过 canPack() 检查输入文件是否可以被压缩。
对于 ARM64,它会识别 EM_AARCH64 机器码。
准备工作 (src/p_lx_elf.cpp, src/packer.cpp)
数据压缩 (src/p_unix.cpp 中的 pack2)
注入解压 Stub (src/p_lx_elf.cpp 中的 pack3)
收尾工作 (src/p_unix.cpp 中的 pack4)

入口启动 (Entry)
Stub 自解压
分析与内存规划 (do_xmap 函数)
循环解压块 (unpackExtent 函数)
权限恢复与清理
调用 mprotect 恢复内存段权限。
使用 ARM64 专用的 make_hatch_arm64 创建一段“逃生舱”代码,负责执行 munmap 后跳转回原始 OEP。这就是Unidbg无法直接带壳模拟的原因。
为什么要用“逃生舱”,因为 munmap 不能操作自己所在的内存,所以需要申请一块新的,在新内存上操作。
恢复现场
所有的系统调用均通过SVC 0执行,这块东西是直接汇编手搓的
根据运行时报错可以定位到如下位置,修改如下。
根据UPX源码得知:运行时mmap会一次申请所有段的内存(防止临近的段被分到别的程序去了),此时这些段的权限必然是各不相同的(参考正常的程序)。因此,在munmap时,就会产生跨不同权限段操作的情况,原来代码中removed.prot != remove.prot必然成立。这样就会throw错误中断。我们仅需把throw改为无害的log warn即可完美解决此问题。
经过测试,原先不能直接模拟的lib,经过这样修改代码可以直接模拟。这完美避免了分析魔改UPX和修复头等工作。
ddiff --git a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
index 756bca92e..a61afef46 100644
--- a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
+++ b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
@@ -195,8 +195,13 @@ public final int munmap(long start, int length) {
long size = aligned - removed.size;
while (size != 0) {
MemoryMap remove = memoryMap.remove(address);
+ if (remove == null) {
+ log.warn("munmap failed to find adjacent region at address=0x{}", Long.toHexString(address));
+ break;
+ }
if (removed.prot != remove.prot) {
- throw new IllegalStateException();
+ log.warn("munmap prot mismatch: removed.prot={}, remove.prot={}, address=0x{}", removed.prot, remove.prot, Long.toHexString(address));
}
address += remove.size;
size -= remove.size;
ddiff --git a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
index 756bca92e..a61afef46 100644
--- a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
+++ b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
@@ -195,8 +195,13 @@ public final int munmap(long start, int length) {
long size = aligned - removed.size;
while (size != 0) {
MemoryMap remove = memoryMap.remove(address);
+ if (remove == null) {
+ log.warn("munmap failed to find adjacent region at address=0x{}", Long.toHexString(address));
+ break;
+ }
if (removed.prot != remove.prot) {
- throw new IllegalStateException();
+ log.warn("munmap prot mismatch: removed.prot={}, remove.prot={}, address=0x{}", removed.prot, remove.prot, Long.toHexString(address));
}
address += remove.size;
size -= remove.size;
为什么要用“逃生舱”,因为 munmap 不能操作自己所在的内存,所以需要申请一块新的,在新内存上操作。
所有的系统调用均通过SVC 0执行,这块东西是直接汇编手搓的
经过测试,原先不能直接模拟的lib,经过这样修改代码可以直接模拟。这完美避免了分析魔改UPX和修复头等工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ddiff --git a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
index 756bca92e..a61afef46 100644
--- a/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
+++ b/unidbg-api/src/main/java/com/github/unidbg/spi/AbstractLoader.java
@@ -195,8 +195,13 @@ public final int munmap(long start, int length) {
long size = aligned - removed.size;
while (size != 0) {
MemoryMap remove = memoryMap.remove(address);
+ if (remove == null) {
+ log.warn("munmap failed to find adjacent region at address=0x{}", Long.toHexString(address));
+ break;
+ }
if (removed.prot != remove.prot) {
- throw new IllegalStateException();
+ log.warn("munmap prot mismatch: removed.prot={}, remove.prot={}, address=0x{}", removed.prot, remove.prot, Long.toHexString(address));
}
address += remove.size;
size -= remove.size;
|
- 对于 ARM64,会使用
ElfLinkerArm64LE。
- pack1():生成新的可执行文件头。对于安卓so,会特别处理
android_shlib ,处理符号表和重定位。
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 4天前
被_0xA1pha编辑
,原因: