在Android上使用qemu-user运行可执行文件
2016-06-05 00:00
429 查看
摘要: 在Android上使用qemu-user运行可执行文件
[TOC]
不歪博客:http://my.oschina.net/ibuwai/blog
本文公开首发于阿里聚安全博客:https://jaq.alibaba.com/community/index.htm?spm=0.0.0.0.ycEUXK
QEMU可以解释执行可执行程序。既然QEMU可以解释执行可执行程序,那么QEMU就能够知道执行了哪些指令,从而可以跟踪指令的执行。QEMU编译出来的结果分为系统模式和用户模式,QEMU用户模式编译出来的可执行文件名为:qemu-user。关于QEMU更多的介绍请浏览官方网站:QEMU。
关于如何编译QEMU用户模式可执行文件,请参考这篇文章:编译可在Android上运行的qemu user mode
qemu-user的main函数源码在文件"linux-user/main.c"中。
解决办法一
(解决办法来源:https://gist.github.com/jserv/5019475)
找到"bionic/linker/linker_environ.cpp"文件,按照下面的代码修改该文件:
如果读者用过git再看上面的代码就可以很清楚的知道,上面的代码是运行"git diff"后所显示内容,代码做了哪些修改清楚的显示了出来。
如果读者没有见过"git diff"命令所显示的内容,那么这么做:找到"-static void __init_AT_SECURE(KernelArgumentBlock& args) {"这一行,从这行开始(包括这一行)每一行以减号开头的表示删除该行。
文件修改完成后在Android源码根目录运行下面的命令:
mmm命令用于编译"<Android源码根目录>/bionic/linker/"目录下的源码,这个目录下的源码编译完成后会生成一个名为"linker"的可执行文件,这个可执行文件的生成目录会在终端上显示出来,将这个linker覆盖设备上的"/system/bin/linker"文件。
覆盖设备上的"/system/bin/linker"文件的实际操作中,覆盖需要需要ROOT权限。"/system/bin/linker"文件覆盖完成以后,它的文件权限是这样的:
即linker属于root用户,并属于root用户组。但是linker原本的权限是下面这样的:
即linker属于root用户,并属于shell用户组。
所以linker覆盖完成后需要执行"chgrp shell /system/bin/linker"命令设置linker的用户组。
__init_AT_SECURE函数的被调路径:__linker_init -> __linker_init_post_relocation -> linker_env_init -> __init_AT_SECURE
解决办法二
在qemu源码目录下找到"linux-user/elfload.c"文件,文件中有create_elf_tables函数,在该函数中找到这一行代码:
在这行代码下添加一行代码:
在该文件中找到这一行:
将这一行改为:
解决办法思路说明:
[翻译] getauxval() and the auxiliary vector这篇文章中说了,fs/binfmt_elf.c文件中有内核的ELF二进制加载器源码,在该文件中也有一个create_elf_tables函数,fs/binfmt_elf.c文件create_elf_tables函数有下面一行代码:
即在标准的create_elf_tables函数中会添加AT_SECURE这一项。通过阅读“解决办法一”可以推断出,产生"FATAL: kernel did not supply AT_SECURE"错误是因为找不到AT_SECURE这一项,那么“解决办法二”的思路就是在qemu的create_elf_tables函数中添加这一项。
为什么在qemu的create_elf_tables函数中添加的AT_SECURE项对应的值是0呢?这是因为我图方便,标准代码中AT_SECURE项对应的值是函数security_bprm_secureexec(bprm)的返回值,我发现理解security_bprm_secureexec函数比较麻烦。那么为什么是0而不是其他常量值?这是因为在linux系统的终端上输入下面的命令:
最后显示AT_SECURE对应的值是0。上面命令中的"ps"可以是其他命令,如:ls。关于LD_SHOW_AUXV这个环境变量在《[翻译] getauxval() and the auxiliary vector》一文中有介绍。
create_elf_tables函数的被调路径:linux-user/main.c - main -> linux-user/linuxload.c - loader_exec -> linux-user/elfload.c - load_elf_binary -> linux-user/elfload.c - create_elf_tables
在Android上使用qemu-user运行可执行文件
@(QEMU)[QEMU][TOC]
不歪博客:http://my.oschina.net/ibuwai/blog
本文公开首发于阿里聚安全博客:https://jaq.alibaba.com/community/index.htm?spm=0.0.0.0.ycEUXK
前言
QEMU简要介绍:QEMU可以解释执行可执行程序。既然QEMU可以解释执行可执行程序,那么QEMU就能够知道执行了哪些指令,从而可以跟踪指令的执行。QEMU编译出来的结果分为系统模式和用户模式,QEMU用户模式编译出来的可执行文件名为:qemu-user。关于QEMU更多的介绍请浏览官方网站:QEMU。
关于如何编译QEMU用户模式可执行文件,请参考这篇文章:编译可在Android上运行的qemu user mode
qemu-user的main函数源码在文件"linux-user/main.c"中。
设置前提
本文研究的QEMU用户模式的可执行文件运行在(Android & arm cpu)上,下文中说的“设备”一词指的就是Android arm设备,设备的系统是CyanogenMod12.1 ROM,该ROM基于Android5.1.1。运行qemu-user
将qemu-user拷贝到设备中,运行该可执行程序时会提示无法找到libglib-2.0.so.0和libgthread-2.0.so.0这两个库,如果读者按照上文中引用的文章《编译可在Android上运行的qemu user mode》编译成功qemu-user,那么这两个库就会存在于Android NDK目录下,将这两个目录拷贝到设备的"/system/lib/"目录下,然后就可以成功运行qemu-user程序。运行错误解决:FATAL: kernel did not supply AT_SECURE
我在设备上运行qemu-user的时候出现了标题上显示的错误"FATAL: kernel did not supply AT_SECURE"。解决办法一
(解决办法来源:https://gist.github.com/jserv/5019475)
找到"bionic/linker/linker_environ.cpp"文件,按照下面的代码修改该文件:
diff --git a/linker/linker_environ.cpp b/linker/linker_environ.cpp index edc659a..4a6e4a0 100644 --- a/linker/linker_environ.cpp +++ b/linker/linker_environ.cpp @@ -42,20 +42,6 @@ bool get_AT_SECURE() { return _AT_SECURE_value; } -static void __init_AT_SECURE(KernelArgumentBlock& args) { - // Check auxv for AT_SECURE first to see if program is setuid, setgid, - // has file caps, or caused a SELinux/AppArmor domain transition. - bool kernel_supplied_AT_SECURE; - _AT_SECURE_value = args.getauxval(AT_SECURE, &kernel_supplied_AT_SECURE); - - // We don't support ancient kernels. - if (!kernel_supplied_AT_SECURE) { - const char* msg = "FATAL: kernel did not supply AT_SECURE\n"; - write(2, msg, strlen(msg)); - exit(EXIT_FAILURE); - } -} - // Check if the environment variable definition at 'envstr' // starts with '<name>=', and if so return the address of the // first character after the equal sign. Otherwise return NULL. @@ -108,44 +94,6 @@ static bool __is_valid_environment_variable(const char* name) { return true; } -static bool __is_unsafe_environment_variable(const char* name) { - // None of these should be allowed in setuid programs. - static const char* const UNSAFE_VARIABLE_NAMES[] = { - "GCONV_PATH", - "GETCONF_DIR", - "HOSTALIASES", - "LD_AOUT_LIBRARY_PATH", - "LD_AOUT_PRELOAD", - "LD_AUDIT", - "LD_DEBUG", - "LD_DEBUG_OUTPUT", - "LD_DYNAMIC_WEAK", - "LD_LIBRARY_PATH", - "LD_ORIGIN_PATH", - "LD_PRELOAD", - "LD_PROFILE", - "LD_SHOW_AUXV", - "LD_USE_LOAD_BIAS", - "LOCALDOMAIN", - "LOCPATH", - "MALLOC_CHECK_", - "MALLOC_TRACE", - "NIS_PATH", - "NLSPATH", - "RESOLV_HOST_CONF", - "RES_OPTIONS", - "TMPDIR", - "TZDIR", - NULL - }; - for (size_t i = 0; UNSAFE_VARIABLE_NAMES[i] != NULL; ++i) { - if (env_match(name, UNSAFE_VARIABLE_NAMES[i]) != NULL) { - return true; - } - } - return false; -} - static void __sanitize_environment_variables() { char** src = _envp; char** dst = _envp; @@ -153,10 +101,6 @@ static void __sanitize_environment_variables() { if (!__is_valid_environment_variable(src[0])) { continue; } - // Remove various unsafe environment variables if we're loading a setuid program. - if (get_AT_SECURE() && __is_unsafe_environment_variable(src[0])) { - continue; - } dst[0] = src[0]; ++dst; } @@ -167,7 +111,6 @@ void linker_env_init(KernelArgumentBlock& args) { // Store environment pointer - can't be NULL. _envp = args.envp; - __init_AT_SECURE(args); __sanitize_environment_variables(); }
如果读者用过git再看上面的代码就可以很清楚的知道,上面的代码是运行"git diff"后所显示内容,代码做了哪些修改清楚的显示了出来。
如果读者没有见过"git diff"命令所显示的内容,那么这么做:找到"-static void __init_AT_SECURE(KernelArgumentBlock& args) {"这一行,从这行开始(包括这一行)每一行以减号开头的表示删除该行。
文件修改完成后在Android源码根目录运行下面的命令:
. build/envsetup.sh breakfast hammerhead mmm <Android源码根目录>/bionic/linker/
mmm命令用于编译"<Android源码根目录>/bionic/linker/"目录下的源码,这个目录下的源码编译完成后会生成一个名为"linker"的可执行文件,这个可执行文件的生成目录会在终端上显示出来,将这个linker覆盖设备上的"/system/bin/linker"文件。
覆盖设备上的"/system/bin/linker"文件的实际操作中,覆盖需要需要ROOT权限。"/system/bin/linker"文件覆盖完成以后,它的文件权限是这样的:
-rwxr-xr-x root root 91902 2016-05-01 21:50 linker
即linker属于root用户,并属于root用户组。但是linker原本的权限是下面这样的:
-rwxr-xr-x root shell 91902 2016-05-01 21:50 linker
即linker属于root用户,并属于shell用户组。
所以linker覆盖完成后需要执行"chgrp shell /system/bin/linker"命令设置linker的用户组。
__init_AT_SECURE函数的被调路径:__linker_init -> __linker_init_post_relocation -> linker_env_init -> __init_AT_SECURE
解决办法二
在qemu源码目录下找到"linux-user/elfload.c"文件,文件中有create_elf_tables函数,在该函数中找到这一行代码:
NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes);
在这行代码下添加一行代码:
NEW_AUX_ENT(AT_SECURE, 0);
在该文件中找到这一行:
#define DLINFO_ITEMS 14
将这一行改为:
#define DLINFO_ITEMS 15
解决办法思路说明:
[翻译] getauxval() and the auxiliary vector这篇文章中说了,fs/binfmt_elf.c文件中有内核的ELF二进制加载器源码,在该文件中也有一个create_elf_tables函数,fs/binfmt_elf.c文件create_elf_tables函数有下面一行代码:
NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
即在标准的create_elf_tables函数中会添加AT_SECURE这一项。通过阅读“解决办法一”可以推断出,产生"FATAL: kernel did not supply AT_SECURE"错误是因为找不到AT_SECURE这一项,那么“解决办法二”的思路就是在qemu的create_elf_tables函数中添加这一项。
为什么在qemu的create_elf_tables函数中添加的AT_SECURE项对应的值是0呢?这是因为我图方便,标准代码中AT_SECURE项对应的值是函数security_bprm_secureexec(bprm)的返回值,我发现理解security_bprm_secureexec函数比较麻烦。那么为什么是0而不是其他常量值?这是因为在linux系统的终端上输入下面的命令:
LD_SHOW_AUXV=1 ps
最后显示AT_SECURE对应的值是0。上面命令中的"ps"可以是其他命令,如:ls。关于LD_SHOW_AUXV这个环境变量在《[翻译] getauxval() and the auxiliary vector》一文中有介绍。
create_elf_tables函数的被调路径:linux-user/main.c - main -> linux-user/linuxload.c - loader_exec -> linux-user/elfload.c - load_elf_binary -> linux-user/elfload.c - create_elf_tables
相关文章推荐
- QEMU 1: 使用QEMU创建虚拟机
- KVM网络性能测试(1)
- KVM网络性能测试(2)
- KVM网络性能测试(3)
- 使用 QEMU 进行系统仿真(QEMU介绍)
- QEMU编译及使用方法
- QEMU内在:整体架构和线程模型
- qemu/kvm/qemu-kvm/virsh的区别
- 一个简单的时间片轮转多道程序内核代码 的实现
- Linux最小根文件系统的建立,内核模块的编译,Qemu模拟测试最小系统
- 深入理解Qemu+KVM之基础篇
- Build DroidScope with Android-4.1.1_r6 source code
- kvm安装以及用kvm装虚拟机
- QEMU 使用的镜像文件:qcow2 与 raw
- (转载)qemu/kvm/qemu-kvm/virsh的区别
- qemu-img命令
- [转]Ivshmem实现分析与性能测试
- 用QEMU+GDB 调试LINUX KERNEL
- KVM虚拟化环境安装