在移动安全领域,ShellCode技术已经成为一个不可或缺的重要组成部分。无论是应用加固、安全防护,还是漏洞利用研究,都离不开对ShellCode的深入理解和灵活运用。本文将以简单的shellCode为例,详细介绍在ARM平台下ShellCode的实现原理和关键技术。
ShellCode本质上是一段位置无关的代码片段。根据应用场景的不同,它的用途大体上有两种:
选择哪种方案取决于具体的使用场景和安全限制。本文将重点介绍第一种方案的技术细节。
在ARM64架构下,ShellCode必须保证位置无关性。这是因为:
由于ShellCode的独立性要求,大量标准库函数无法直接使用。这要求我们:
在没有链接器支持的情况下,需要:
在ARM64架构中,系统调用使用特定的寄存器约定:
通过这种方式,我们实现了一套完整的系统调用封装框架,支持0-6个参数的系统调用。
关键属性说明:
这段代码实现了标准的ARM64函数调用约定,确保ShellCode能够正确地接收参数并返回结果。
通过读取/proc/self/maps文件获取已加载库的基址。这里参考了Dobby框架的实现,解析运行时模块信息:
解析目标SO文件的符号表以获取函数地址:
通过这种方式,我们就在ShellCode中实现了动态符号解析的功能,可以在运行时获取目标函数的地址,无需依赖系统链接器。
这些编译选项确保生成的ShellCode具有最小的体积和最大的兼容性。
本项目已在GitHub开源,欢迎大家参与讨论和改进。
本文作者:Imy
项目开源地址:[ef4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6u0d9f1W2u0L8h3#2E0P5i4W2&6i4K6u0r3b7i4u0E0f1$3S2W2L8r3I4o6L8$3c8W2i4K6g2p5
声明:本文仅供安全研究和学习交流使用
static inline long syscall3(long number, long arg1, long arg2, long arg3) {
register long x8 __asm__("x8") = number;
register long x0 __asm__("x0") = arg1;
register long x1 __asm__("x1") = arg2;
register long x2 __asm__("x2") = arg3;
__asm__ volatile("svc #0" : "+r"(x0) : "r"(x8), "r"(x1), "r"(x2) : "memory");
return x0;
}
ssize_t sys_write(int fd, const void *buf, size_t count) {
return syscall3(SYS_write, fd, (long)buf, count);
}
static inline long syscall3(long number, long arg1, long arg2, long arg3) {
register long x8 __asm__("x8") = number;
register long x0 __asm__("x0") = arg1;
register long x1 __asm__("x1") = arg2;
register long x2 __asm__("x2") = arg3;
__asm__ volatile("svc #0" : "+r"(x0) : "r"(x8), "r"(x1), "r"(x2) : "memory");
return x0;
}
ssize_t sys_write(int fd, const void *buf, size_t count) {
return syscall3(SYS_write, fd, (long)buf, count);
}
void __attribute__((naked, noreturn, section(".text._start"))) _start(void) {
void __attribute__((naked, noreturn, section(".text._start"))) _start(void) {
#ifdef ARCH_ARM64
__asm__ volatile (
"stp x29, x30, [sp, #-16]!\n"
"mov x29, sp\n"
"bl shellcode_main_refactored\n"
"mov sp, x29\n"
"ldp x29, x30, [sp], #16\n"
"ret\n"
:
:
: "memory", "x0", "x29", "x30"
);
#endif
#ifdef ARCH_ARM64
__asm__ volatile (
"stp x29, x30, [sp, #-16]!\n"
"mov x29, sp\n"
"bl shellcode_main_refactored\n"
"mov sp, x29\n"
"ldp x29, x30, [sp], #16\n"
"ret\n"
:
:
: "memory", "x0", "x29", "x30"
);
#endif
typedef struct {
char path[MAX_PATH_LEN];
uintptr_t load_address;
} dobby_runtime_module_t;
int dobby_get_runtime_module(const char* name, dobby_runtime_module_t* module) {
}
typedef struct {
char path[MAX_PATH_LEN];
uintptr_t load_address;
} dobby_runtime_module_t;
int dobby_get_runtime_module(const char* name, dobby_runtime_module_t* module) {
}
int dobby_resolve_symbol(const char* lib_name, const char* symbol_name, uintptr_t* symbol_address) {
dobby_runtime_module_t module;
if (dobby_get_runtime_module(lib_name, &module) != MAPS_PARSER_SUCCESS) {
return RESOLVER_ERR_NOT_FOUND;
}
return RESOLVER_SUCCESS;
}
int dobby_resolve_symbol(const char* lib_name, const char* symbol_name, uintptr_t* symbol_address) {
dobby_runtime_module_t module;
if (dobby_get_runtime_module(lib_name, &module) != MAPS_PARSER_SUCCESS) {
return RESOLVER_ERR_NOT_FOUND;
}
return RESOLVER_SUCCESS;
}
uintptr_t shellcode_main_refactored() {
static const char* TARGET_LIBRARY = "libxxx.so";
static const char* TARGET_SYMBOL = "xxx";
uintptr_t symbol_addr;
int result = dobby_resolve_symbol(TARGET_LIBRARY, TARGET_SYMBOL, &symbol_addr);
if (result != RESOLVER_SUCCESS) {
return (uintptr_t)result;
}
typedef void (*ioctl_func)();
ioctl_func target_func = (ioctl_func)symbol_addr;
target_func();
return symbol_addr;
}
uintptr_t shellcode_main_refactored() {
static const char* TARGET_LIBRARY = "libxxx.so";
static const char* TARGET_SYMBOL = "xxx";
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-7-13 16:38
被IIImmmyyy编辑
,原因: