Android下的注入的效果是类似于Windows下的dll注入,关于Windows下面的注入可以参考这篇文章Windows注入术。而Android一般处理器是arm架构,内核是基于linux,因此进程间是弱相互作用,不存在Windows下类似于CreateRemoteThread 作用的函数,可以在其他进程空间内创建线程来加载我们的.so文件,所以我们所采用的方法就是依赖于linux下的ptrace()函数,将目标进程作为我们进程的子进程操作目标进程的寄存器和内存来运行我们加载.so文件的代码将.so链接到目标进程。

源代码来自于看雪论坛Android 注入

关于arm汇编指令ARM汇编指令

ptrace使用部分可以参考ptrace分析

接下来看第一个部分,通过dlopen函数将我们的.so文件链接到目标进程,也就是我们要直接”注入”的代码,

.global _dlopen_addr_s             @dlopen函数在目标进程中的地址
.global _dlopen_param1_s @dlopen参数1<.so>在目标进程中的地址
.global _dlopen_param2_s @dlopen参数2在目标进程中的地址 .global _dlsym_addr_s @dlsym函数在目标进程中的地址
.global _dlsym_param2_s @dlsym参数2<函数名>在目标进程中的地址 .global _dlclose_addr_s @dlclose在目标进程中的地址 .global _inject_start_s @shellcode的起始地址
.global _inject_end_s @shellcode的结束地址 .global _inject_function_param_s .global _saved_cpsr_s @保存CPSR,以便执行完"hook_entry"之后恢复环境
.global _saved_r0_pc_s @保存r0-r15,以便恢复环境 .data _inject_start_s:
@ debug loop
:
@sub r1, r1, #
@B 3b @ dlopen
ldr r1, _dlopen_param2_s @设置dlopen第二个参数, flag
ldr r0, _dlopen_param1_s @设置dlopen第一个参数 .so
ldr r3, _dlopen_addr_s @dlopen函数
blx r3 @执行dlopen函数
subs r4, r0, # @检测并保存dlopen的返回值
beq 2f @dlsym
ldr r1, _dlsym_param2_s @设置dlsym第二个参数,第一个参数已经在r0中了
ldr r3, _dlsym_addr_s @dlsym函数地址
blx r3 @执行dlsym函数
subs r3, r0, # @检测并保存dlsym的返回值 "hook_entry"的地址
beq 1f @call our function
ldr r0, _inject_function_param_s @设置"hook_entry"第一个参数
blx r3 @执行"hook_entry"函数,
subs r0, r0, #
beq 2f :
@dlclose
mov r0, r4 @保存的dlopen返回值设为dlcose的第一个参数
ldr r3, _dlclose_addr_s
blx r3 @执行dlclose函数 :
@restore context
ldr r1, _saved_cpsr_s @恢复CPSR
msr cpsr_cf, r1
ldr sp, _saved_r0_pc_s @恢复寄存器r0-r15
ldmfd sp, {r0-pc} _dlopen_addr_s:
.word 0x11111111 _dlopen_param1_s:
.word 0x11111111 _dlopen_param2_s:
.word 0x2 _dlsym_addr_s:
.word 0x11111111 _dlsym_param2_s:
.word 0x11111111 _dlclose_addr_s:
.word 0x11111111 _inject_function_param_s:
.word 0x11111111 _saved_cpsr_s:
.word 0x11111111 _saved_r0_pc_s:
.word 0x11111111 _inject_end_s: .space 0x400, @代码段空间大小 0x4000-0x3c00

.end

那怎么把我们的这段汇编代码写入目标进程空间并执行呢?

  1. 在目标进程中找到内存来存放我们代码,这里是通过调用mmap实现
  2. 把汇编代码写入到目标进程的内存中
  3. 执行我们的汇编代码

首先,我们要通过mmap在目标进程中找到存放我们汇编代码的内存:

  1. 获取目标进程中的mmap地址。这里和Windows不一样,在Windows中基本的模块的基地址是一样的,而且可以通过VirtualAllocEx()函数来目标进程中申请一块内存。而在linux下,我们是通过mmap函数来建立以个映射区,作用相当于申请一块内存,但是每个进程中的mmap函数地址不一样,所以我们要比较”libc.so”在我们进程中和目标进程中基地址来获得目标进程中mmap函数地址。
  2. 将mmap函数的参数写入目标进程中。mmap有6个参数,前4个参数通过r0~r3参数,剩下两个通过堆栈传参,写入SP。
  3. PC 寄存器设置为mmap的地址,lr设置为0 返回时,我们进程的waitpid( pid, NULL, WUNTRACED )会继续向下执行。
  4. 把我们准备好的寄存器写入目标进程的(PTRACE_SETREGS),并让目标进程继续执行,此时就直接去执行mmap函数了。
  5. mmap执行完之后,我们的waitpid继续执行,申请的内存首地址在r0寄存器中(PTREACE_GETREGS)。

之后我们要为我们汇编代码中的全局变量赋值:

  1. 获取目标进程中的dlopen函数地址赋值给_dlopen_addr_s
  2. 获取目标进程中的dlsym函数地址赋值给_dlsym_addr_s
  3. 获取目标进程中的dlclose函数地址赋值为_dlclose_addr_s
  4. 将要注入的.so的路径拷贝到汇编代码中,然后计算路径在目标进程中的绝对地址传递给_dlopen_param1_s
  5. 将要注入的.so文件中的”hook_entry”拷贝到汇编代码中,然后计算路径在目标进程中的绝对地址传递给_dlsym_param2_s
  6. 保存目标进程的CPSR寄存器的值 _saved_cpsr_s
  7. 将目标进程的r0~r15的值存入汇编代码变量中,计算此变量在目标进程的地址保存在_saved_r0_pc_s中
  8. 将整个汇编代码块写入目标进程(PTRACE_POKETEXE,……),内存为mmap所分配
  9. 将目标进程的pc寄存器设置为汇编代码在目标进程中的地址,然后PTRACE_DETACH,让目标进程执行,此时便会执行我们的汇编代码,链接到我们的.so文件
/*attach到目标进程ptrace_attach*/
int ptrace_attach( pid_t pid )
{
if ( ptrace( PTRACE_ATTACH, pid, NULL, ) < )
{
perror( "ptrace_attach" );
return -;
}
//暂停目标进程
waitpid( pid, NULL, WUNTRACED );
//DEBUG_PRINT("attached\n");
//做出系统调用或者准备退出的时候暂停
if ( ptrace( PTRACE_SYSCALL, pid, NULL, ) < )
{
perror( "ptrace_syscall" );
return -;
}
//子进程暂停之后立即返回
waitpid( pid, NULL, WUNTRACED );
return ;
}
/*获取目标进程寄存器的值*/
int ptrace_getregs( pid_t pid, struct pt_regs* regs )
{
if ( ptrace( PTRACE_GETREGS, pid, NULL, regs ) < )
{
perror( "ptrace_getregs: Can not get register values" );
return -;
}
return ;
}
/*设置目标进程的寄存器*/
int ptrace_setregs( pid_t pid, struct pt_regs* regs )
{
if ( ptrace( PTRACE_SETREGS, pid, NULL, regs ) < )
{
perror( "ptrace_setregs: Can not set register values" );
return -;
}
return ;
}
/*获取指定模块在目标进程中的基地址*/
void* get_module_base( pid_t pid, const char* module_name )
{
FILE *fp;
long addr = ;
char *pch;
char filename[];
char line[];
if ( pid < )
{
/* self process */
snprintf( filename, sizeof(filename), "/proc/self/maps", pid );
}
else
{
snprintf( filename, sizeof(filename), "/proc/%d/maps", pid );
}
fp = fopen( filename, "r" );
if ( fp != NULL )
{
while ( fgets( line, sizeof(line), fp ) )
{
if ( strstr( line, module_name ) )
{
pch = strtok( line, "-" );
addr = strtoul( pch, NULL, );
if ( addr == 0x8000 )
addr = ;
break;
}
}
fclose( fp ) ;
}
return (void *)addr;
}
/*获取函数在目标进程中的地址*/
void* get_remote_addr( pid_t target_pid, const char* module_name, void* local_addr )
{
void* local_handle, *remote_handle;
/* 指定模块在我们自己进程的基地址*/
local_handle = get_module_base( -, module_name );
/* 指定模块在目标进程的基地址*/
remote_handle = get_module_base( target_pid, module_name );
DEBUG_PRINT( "[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle );
/*mmap函数在目标进程的绝对地址*/
return (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle );
}
/*在目标进程中执行指定函数*/
int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs )
{
uint32_t i;
for ( i = ; i < num_params && i < ; i ++ )
{
regs->uregs[i] = params[i];
}
//
// push remained params onto stack
//
if ( i < num_params )
{
/*sp-4 ,参数入栈*/
regs->ARM_sp -= (num_params - i) * sizeof(long) ;
ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long) );
}
/*pc寄存器指向要call的地址*/
regs->ARM_pc = addr;
/*
* 判断最后一位,如果是1就是thumb指令集
* 0是arm指令集
*/
if ( regs->ARM_pc & )
{
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
}
else
{
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
}
regs->ARM_lr = ; /*目标进程执行完mmap之后暂停*/
if ( ptrace_setregs( pid, regs ) == -
|| ptrace_continue( pid ) == - )
{
return -;
}
/*等待目标进程中mmap执行完成*/
waitpid( pid, NULL, WUNTRACED );
return ;
}
/*把代码写入目标进程*/
int ptrace_writedata( pid_t pid, uint8_t *dest, uint8_t *data, size_t size )
{
uint32_t i, j, remain;
uint8_t *laddr; union u {
long val;
char chars[sizeof(long)];
} d; /*
* arm汇编都是以4字节对齐
*/
j = size / ;
remain = size % ; laddr = data; for ( i = ; i < j; i ++ )
{
memcpy( d.chars, laddr, );
/*往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数*/
/*ptrace(PTRACE_POKETEXT, pid, addr, data)*/
ptrace( PTRACE_POKETEXT, pid, dest, d.val );
dest += ;
laddr += ;
} if ( remain > )
{
/*
* ptrace(PTRACE_PEEKTEXT, pid, addr, data)
* 从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据
*/
d.val = ptrace( PTRACE_PEEKTEXT, pid, dest, );
for ( i = ; i < remain; i ++ )
{
d.chars[i] = *laddr ++;
}
/*
* ptrace(PTRACE_POKETEXT, pid, addr, data)
* 往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据
*/
ptrace( PTRACE_POKETEXT, pid, dest, d.val );
} return ;
}
int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size )
{
int ret = -;
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr;
void *local_handle, *remote_handle, *dlhandle;
uint8_t *map_base;
uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs, original_regs;
/*
* shellcode.s 中的全局变量
*/
extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
_dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
_saved_cpsr_s, _saved_r0_pc_s; uint32_t code_length; long parameters[]; DEBUG_PRINT( "[+] Injecting process: %d\n", target_pid ); /*作为目标进程的父进程,暂停目标进程*/
if ( ptrace_attach( target_pid ) == - )
return EXIT_SUCCESS; /*读取目标进程寄存器*/
if ( ptrace_getregs( target_pid, &regs ) == - )
goto exit; /* save original registers */
memcpy( &original_regs, &regs, sizeof(regs) );
/*mmap函数在目标进程的绝对地址*/
mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap ); DEBUG_PRINT( "[+] Remote mmap address: %x\n", mmap_addr ); /* call mmap */
parameters[] = ; // addr
parameters[] = 0x4000; // size
parameters[] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
parameters[] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
/*匿名映射,映射区不与任何文件关联*/
parameters[] = ; //fd
parameters[] = ; //offset DEBUG_PRINT( "[+] Calling mmap in target process.\n" ); if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, , &regs ) == - )
goto exit; if ( ptrace_getregs( target_pid, &regs ) == - )
goto exit; DEBUG_PRINT( "[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc ); /*mmap的返回值,映射区的基地址*/
map_base = (uint8_t *)regs.ARM_r0; dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen );
dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym );
dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose ); DEBUG_PRINT( "[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x\n", dlopen_addr, dlsym_addr, dlclose_addr );
/*???*/
remote_code_ptr = map_base + 0x3C00; //0x400内存 shellcode space . 0x400
local_code_ptr = (uint8_t *)&_inject_start_s; //shellcode的开始的基地址 /*
* dlopen dlsym dlclose在目标进程中的地址
*/
_dlopen_addr_s = (uint32_t)dlopen_addr;
_dlsym_addr_s = (uint32_t)dlsym_addr;
_dlclose_addr_s = (uint32_t)dlclose_addr; DEBUG_PRINT( "[+] Inject code start: %x, end: %x\n", local_code_ptr, &_inject_end_s ); //shellcode 的长度
code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s; dlopen_param1_ptr = local_code_ptr + code_length + 0x20; dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH;
saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH;
inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; /* dlopen parameter 1: library name */
strcpy( dlopen_param1_ptr, library_path );
_dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr );
DEBUG_PRINT( "[+] _dlopen_param1_s: %x\n", _dlopen_param1_s ); /* dlsym parameter 2: function name */
strcpy( dlsym_param2_ptr, function_name ); /*hook_entry*/
_dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr );
DEBUG_PRINT( "[+] _dlsym_param2_s: %x\n", _dlsym_param2_s ); /* saved cpsr */
_saved_cpsr_s = original_regs.ARM_cpsr; /* saved r0-pc */
memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), * ); // r0 ~ r15
_saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr );
DEBUG_PRINT( "[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s ); /* Inject function parameter */
memcpy( inject_param_ptr, param, param_size );
_inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr );
DEBUG_PRINT( "[+] _inject_function_param_s: %x\n", _inject_function_param_s ); DEBUG_PRINT( "[+] Remote shellcode address: %x\n", remote_code_ptr );
/*向目标进程写入shellcode*/
ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 ); memcpy( &regs, &original_regs, sizeof(regs) );
regs.ARM_sp = (long)remote_code_ptr; /*执行链接.so文件的代码*/
regs.ARM_pc = (long)remote_code_ptr;
ptrace_setregs( target_pid, &regs );
ptrace_detach( target_pid ); // inject succeeded
ret = ; exit:
return ret;
}
int main(int argc, char** argv) {
pid_t target_pid;
target_pid = find_pid_of("/system/bin/servicemanager");
inject_remote_process( target_pid, "/dev/yuki/payload.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!") );
}

Android 注入详解的更多相关文章

  1. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  2. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  3. Android ActionBar详解

    Android ActionBar详解 分类: Android2014-04-30 15:23 1094人阅读 评论(0) 收藏 举报 androidActionBar   目录(?)[+]   第4 ...

  4. Android 签名详解

    Android 签名详解 AndroidOPhoneAnt设计模式Eclipse  在Android 系统中,所有安装 到 系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程 ...

  5. Android编译系统详解(一)

    ++++++++++++++++++++++++++++++++++++++++++ 本文系本站原创,欢迎转载! 转载请注明出处: http://blog.csdn.net/mr_raptor/art ...

  6. Android布局详解之一:FrameLayout

      原创文章,如有转载,请注明出处:http://blog.csdn.net/yihui823/article/details/6702273 FrameLayout是最简单的布局了.所有放在布局里的 ...

  7. 【整理修订】Android.mk详解

    Android.mk详解 1. Android.mk 的应用范围 Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译. 一个Android.mk文件可以编 ...

  8. Android菜单详解(四)——使用上下文菜单ContextMenu

    之前在<Android菜单详解(二)——创建并响应选项菜单>和<Android菜单详解(三)——SubMenu和IconMenu>中详细讲解了选项菜单,子菜单和图标菜单.今天接 ...

  9. Android签名详解(debug和release)

    Android签名详解(debug和release)   1. 为什么要签名 1) 发送者的身份认证 由于开发商可能通过使用相同的Package Name来混淆替换已经安装的程序,以此保证签名不同的包 ...

随机推荐

  1. LD_PRELOAD

    下面的helloworld会在屏幕上打印出什么内容? 1 2 3 4 5 6 #include <stdio.h> int main(int argc, char* argv[], cha ...

  2. App_Store - IOS应用审核的时隐私政策模板

    隐私政策  Poposoft尊重并保护所有使用服务用户的个人隐私权.为了给您提供更准确.更有个性化的服务,Poposoft会按照本隐私权政策的规定使用和披露您的个人信息.但Poposoft将以高度的勤 ...

  3. file_get_contents函数和curl函数不能用

    某天file_get_contents()突然不能用了,检查了下php配置文件 allow_url_fopen=on 没问题   各种重启也没用 最后在ssh下执行 chmod 755 /etc/re ...

  4. c++ 中static关键字

    static可以用于修饰普通的变量和函数,也可以用于修饰类的成员 普通应用 1.修饰普通变量 修饰全局变量:将变量的作用域限制在所属文件 修饰局部变量:将变量的生存周期延迟到程序结束 2.修饰普通函数 ...

  5. SVN补充

    为什么使用SVN? 1.需求1:备份,以防电脑死机断电等 2.需求2:代码还原,代码不管你改成什么样都可以找到某一段的版本 3.需求3:协同修改,下载修改同一个文件,防止被相互覆盖 4.需求4:多版本 ...

  6. VIM技巧:翻页

    整页翻页 ctrl-f ctrl-bf=forword b=backward 翻半页ctrl-d ctlr-ud=down u=up 滚一行ctrl-e ctrl-y zz 让光标所在的行居屏幕中央z ...

  7. Liferay 7 portlet中所有能在@Component中修改的属性

    "com.liferay.portlet.action-timeout", "com.liferay.portlet.active", "com.li ...

  8. 2015最新百度搜索引擎(seo优化)排名算法

    多少年来,对于弄清百度排名算法成为了一代又一代站长的最高目标.随着百度推出了搜索引擎网页质量**,直接揭开了神秘的百度排名算法,这是作为站长福音啊.现在小编就来为大家介绍一下. 首先想要得到直接需要的 ...

  9. 博文&零散信息阅读

    关于培养方案: 全国一线高校.网易云课堂和我院培养计划的区别主要体现在: 基础课上,我院删去了物理课程的必修要求. 专业课上,删去了汇编语言.编译原理.信息安全技术等学科的必修要求. 专业选修课上,我 ...

  10. 【转载】MyBatis之传入参数

    原文地址:http://blog.csdn.net/liaoxiaohua1981/article/details/6862764 在MyBatis的select.insert.update.dele ...