首页
社区
课程
招聘
[原创]UPX arm64-linux 源码分析 修复Unidbg以支持模拟未脱壳的UPX安卓so
发表于: 5天前 901

[原创]UPX arm64-linux 源码分析 修复Unidbg以支持模拟未脱壳的UPX安卓so

5天前
901

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

git版本提交SHA:611d67542ae0ae9f8128ff121ae2a21ea007261d

压缩流程

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

每个格式(如 PackLinuxElf64amdPackLinuxElf64arm)通过 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 ,处理符号表和重定位。
  • UPX 将原文件内容按块(Block)读取。
  • 调用 compressWithFilters() 进行压缩。现在版本一般是NRV算法,也见过LZMA的。

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 4天前 被_0xA1pha编辑 ,原因:
收藏
免费 32
支持
分享
最新回复 (15)
雪    币: 104
活跃值: (7737)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
5天前
0
雪    币: 3384
活跃值: (6670)
能力值: ( LV11,RANK:185 )
在线值:
发帖
回帖
粉丝
3
1
5天前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享
4天前
0
雪    币: 151
活跃值: (1294)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
666
4天前
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
向大佬学习
4天前
0
雪    币: 387
活跃值: (1696)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
666
4天前
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
4
4天前
0
雪    币: 3350
活跃值: (4319)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
过来瞧瞧
4天前
0
雪    币: 212
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
66
4天前
0
雪    币: 1105
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
学习一下upx的原理
4天前
0
雪    币: 176
活跃值: (264)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习一下upx的原理
3天前
0
雪    币: 118
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
点赞
2天前
0
雪    币: 0
活跃值: (360)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
66666666666666
2天前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
66666666
1小时前
0
雪    币: 260
活跃值: (450)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
11
57分钟前
0
游客
登录 | 注册 方可回帖
返回