Android进程注入

21 篇文章 0 订阅
11 篇文章 1 订阅

Android平台的进程注入和linux平台的注入技术类似,采用ptrace机制来实现
ptrace
在执行系统调用之前,内核会先检查当前进程是否处于被“跟踪”(traced)的状态。如果是的话,内核暂停当前进程并将控制权交给跟踪进程,使跟踪进程得以察看或者修改被跟踪进程的寄存器。

ptrace()是一个系统调用,它允许一个进程控制另外一个进程的执行.不仅如此,我们还可以借助于ptrace修改某个进程的空间(内存或寄存器),任何传递给一个进程(即被跟踪进程)的信号(除了会直接杀死进程的SIGKILL信号)都会使得这个进程进入暂停状态,这时系统通过wait()通 知跟踪进程,这样,跟踪进程就可以修改被跟踪进程的行为了.
如果跟踪进程在被跟踪进程的内存中设置了相关的事件标志位,那么运行中被跟踪进程也可能因为特殊的事件而暂停.跟踪结束后,跟踪进程甚至可以通过设置被跟踪进程的退出码(exit code)来杀死它,当然也可以让它继续运行.

ptrace

#include <sys/types.h>
#include <sys/ptrace.h>
int ptrace(int request, pid_t pid, caddr_t addr, int data);
我们可以看到,ptrace有4个参数,其中,request决定ptrace做什么,pid是被跟踪进程的ID,data存储从进程空间偏移量为addr的地方开始将被读取/写入的数据.
request:
PT_ATTACH: 	attach 进程
PT_DETACH:	detach 进程
PTRACE_SINGLESTEP: 单步
PTRACE_SYSCALL, PTRACE_CONT: 继续
PTRACE_POKETEXT, PTRACE_POKEDATA, PTRACE_POKEUSER: 读数据
PTRACE_PEEKTEXT, PTRACE_PEEKDATA, PTRACE_PEEKUSER:写数据
PTRACE_GETREGS: 得到寄存器

注入流程

0.保存现场
1.查找目标进程的mmap函数
2.在目标进程中调用mmap分配内存
3.查找目标进程dlopen、dlsym函数地址
4.往目标进程中写入shellcode,填充data
5.执行shellcode,调用hook入口
6.恢复现场,Deattach进程,完成注入

代码片段
 

int main(int argc, char** argv) {
	pid_t target_pid;
	if(argc < 2){
		printf("Input target pid\n");
		return -1;
	}
	target_pid = atoi(argv[1]);
	inject_remote_process( target_pid, "/data/local/tmp/libcode.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!") );
	return 0;
}

然后inject_remote_process如下

int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size )
{
	int ret = -1;
	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;
	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[10];



	DEBUG_PRINT( "[+] Injecting process: %d\n", target_pid );

	if ( ptrace_attach( target_pid ) == -1 )//先attach进程,对ptrace的封装,代码在下段
		return EXIT_SUCCESS;


	if ( ptrace_getregs( target_pid, &regs ) == -1 )//保存当前状态,寄存器之类的
		goto exit;

	/* save original registers */
	memcpy( &original_regs, &regs, sizeof(regs) );

	mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap );//函数在下面,作用是找到目标进程那些dlopen之类的函数地址,可以拿到自己dlopen地址,与自己进程lib.c的base差值和在目标进程一样,所以vaddr就得到,然后加上目标进程lib.c的base,因为自己进程是root可读对方。

	DEBUG_PRINT( "[+] Remote mmap address: %x\n", mmap_addr );
//下面是控制目标进程给我们做事,比如分配空间,调用mmp函数
	/* call mmap */
	parameters[0] = 0;	// addr
	parameters[1] = 0x4000; // size,按页对齐
	parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // prot
	parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags,匿名,下面参数忽略
	parameters[4] = 0; //fd
	parameters[5] = 0; //offset

	DEBUG_PRINT( "[+] Calling mmap in target process.\n" );
//这个函数在下面,
	if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, &regs ) == -1 )
		goto exit;//目标进程异常,挂起



	if ( ptrace_getregs( target_pid, &regs ) == -1 )//读出这个值。
		goto exit;


	DEBUG_PRINT( "[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc );

	map_base = (uint8_t *)regs.ARM_r0;//r0就是分配在目标进程shellcode

	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 );
//除了在目标进程放shellcode还要控制stack,code使用的是pc,栈式sp,所以在内存一个点,pc向上,sp向下。

	remote_code_ptr = map_base + 0x3C00;
	local_code_ptr = (uint8_t *)&_inject_start_s;


	_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 );
//shell拷贝到目标进程。shellcode在下面片段,恢复寄存器在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 *///填写shellcode里用到的参数
	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 );
	_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), 16 * 4 ); // 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 );
	ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 );//把参数shellcode一次性写到目标进程。

	memcpy( &regs, &original_regs, sizeof(regs) );
	regs.ARM_sp = (long)remote_code_ptr;
	regs.ARM_pc = (long)remote_code_ptr;
	ptrace_setregs( target_pid, &regs );
	ptrace_detach( target_pid );

	// inject succeeded
	ret = 0;

exit:
	return ret;
}

 ptrace_attach

int ptrace_attach( pid_t pid )
{
	if ( ptrace( PTRACE_ATTACH, pid, NULL, 0  ) < 0 )
	{
		perror( "ptrace_attach" );
		return -1;
	}

	waitpid( pid, NULL, WUNTRACED );//让内核等一下目标进程

	//DEBUG_PRINT("attached\n");

	if ( ptrace( PTRACE_SYSCALL, pid, NULL, 0  ) < 0 )//让他执行完当前sysycall
	{
		perror( "ptrace_syscall" );
		return -1;
	}



	waitpid( pid, NULL, WUNTRACED );

	return 0;
}

int ptrace_detach( pid_t pid )
{
	if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 )
		{
			perror( "ptrace_detach" );
			return -1;
		}

		return 0;
}

 get_remote_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( -1, module_name );//拿到自己的base
	remote_handle = get_module_base( target_pid, module_name );//拿到远程的base

	DEBUG_PRINT( "[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle );

	return (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle );//自己函数地址-自己base+远程base
}

get_module_base

 

void* get_module_base( pid_t pid, const char* module_name )
{
	FILE *fp;
	long addr = 0;
	char *pch;
	char filename[32];
	char line[1024];

	if ( pid < 0 )
	{
		/* self process */
		snprintf( filename, sizeof(filename), "/proc/self/maps");
	}
	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, 16 );

				if ( addr == 0x8000 )
					addr = 0;

				break;
			}
		}

				fclose( fp ) ;
	}

	return (void *)addr;
}

 ptrace_call

int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs )
{
	uint32_t i;
//r0-r3
	for ( i = 0; i < num_params && i < 4; i ++ )
	{
		regs->uregs[i] = params[i];
	}

	//
	// push remained params onto stack
	//
	if ( i < num_params )
	{
		regs->ARM_sp -= (num_params - i) * sizeof(long) ;
		ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long) );
	}
//上面就是把另外两个参数通过栈,想做的是先把这两参数push到栈上,然后操作栈顶
	regs->ARM_pc = addr;
	if ( regs->ARM_pc & 1 )//thumb
	{
		/* thumb */
		regs->ARM_pc &= (~1u);
		regs->ARM_cpsr |= CPSR_T_MASK;
	}
	else
	{
		/* arm */
		regs->ARM_cpsr &= ~CPSR_T_MASK;//如果前面是thumb把1清掉
	}

//那么如何notify进程我们mmp执行完了。就是通过下面这句话。原因是当函数调用时候,当我们使用bl或者bx,链接寄存器指向的是下一条返回地址,如果把下条返回地址赋值成0,返回时候pc=0,就会产生异常。相当于一个notify,然后用下面那个waitpid得到异常模式,确定mmp执行完。所以其实下面不一定是0,只要是无效即可。
	regs->ARM_lr = 0;//把链接寄存器的值赋值0	

	if ( ptrace_setregs( pid, regs ) == -1 
		|| ptrace_continue( pid ) == -1 )//调用continue让进程继续执行
	{
		return -1;
	}


	waitpid( pid, NULL, WUNTRACED );

	return 0;
}

shellcode

.global _dlopen_addr_s
.global _dlopen_param1_s
.global _dlopen_param2_s

.global _dlsym_addr_s
.global _dlsym_param2_s

.global _dlclose_addr_s

.global _inject_start_s
.global _inject_end_s

.global _inject_function_param_s

.global _saved_cpsr_s
.global _saved_r0_pc_s

.data

_inject_start_s:
	@ debug loop
3:
	@sub r1, r1, #0
	@B 3b

	@ dlopen
	ldr r1, _dlopen_param2_s
	ldr r0, _dlopen_param1_s
	ldr r3, _dlopen_addr_s@dopen地址
	blx r3
	subs r4, r0, #0
	beq	2f

	@dlsym@要去找hook——entry
	ldr r1, _dlsym_param2_s
	ldr r3, _dlsym_addr_s
	blx r3
	subs r3, r0, #0
	beq 1f

	@call our function
	ldr r0, _inject_function_param_s
	blx r3
	subs r0, r0, #0
	beq 2f

1:
	@dlclose
	mov r0, r4
	ldr r3, _dlclose_addr_s
	blx r3

2:
	@restore context
	ldr r1, _saved_cpsr_s
	msr cpsr_cf, r1@把寄存器都回复了
	ldr sp, _saved_r0_pc_s
	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, 0

.end

也可以用SO的init(init_array)实现注入,不一定要用hook_entry,或者找到目标进程dlopen控制栈区调用?

http://blog.csdn.net/l173864930/article/details/38456313

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值