首页
社区
课程
招聘
[原创]UPX arm64-linux 源码分析 修复Unidbg以支持模拟未脱壳的UPX安卓so
发表于: 2026-3-4 17:34 2145

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

2026-3-4 17:34
2145

通过阅读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的。

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2026-3-5 10:33 被_0xA1pha编辑 ,原因:
收藏
免费 37
支持
分享
最新回复 (23)
雪    币: 104
活跃值: (8062)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
2026-3-4 17:39
0
雪    币: 3422
活跃值: (6785)
能力值: ( LV11,RANK:185 )
在线值:
发帖
回帖
粉丝
3
1
2026-3-4 17:40
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2026-3-4 19:27
0
雪    币: 151
活跃值: (1354)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
666
2026-3-4 19:47
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
向大佬学习
2026-3-4 21:04
0
雪    币: 387
活跃值: (1751)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
666
2026-3-4 22:39
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
4
2026-3-4 23:58
0
雪    币: 3425
活跃值: (4434)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
过来瞧瞧
2026-3-5 01:54
0
雪    币: 211
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
66
2026-3-5 08:45
0
雪    币: 1217
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
学习一下upx的原理
2026-3-5 09:36
0
雪    币: 176
活跃值: (274)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习一下upx的原理
2026-3-6 11:29
0
雪    币: 118
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
点赞
2026-3-7 14:40
0
雪    币: 0
活跃值: (420)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
66666666666666
2026-3-7 14:45
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
66666666
2026-3-9 16:44
0
雪    币: 365
活跃值: (625)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
11
2026-3-9 17:40
0
雪    币: 79
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
666
2026-3-12 02:15
0
雪    币: 206
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
good
2026-3-12 03:27
0
雪    币: 265
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
666
2026-3-12 10:40
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
666
2026-3-13 09:55
0
雪    币: 235
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
666
2026-3-13 23:14
0
雪    币: 71
活跃值: (1903)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
666
2026-3-21 16:45
0
雪    币: 1550
活跃值: (4318)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
23
123
2026-3-22 20:39
0
雪    币: 96
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
666
2026-3-23 10:16
0
游客
登录 | 注册 方可回帖
返回