zoukankan      html  css  js  c++  java
  • Android进程的so注入--Poison(稳定注入版)

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53869796

    Android进程的so注入已经是老技术了,网上能用的Android注入的工程也有很多,虽然分享代码的作者在测试的时候能注入成功,但是其他的同学使用这些代码的时候总是出现这样或者那样的问题。在Android逆向学习的这段时间里,我也陆续测试了几个作者给出的Android的注入的代码,但是总是效果不明显,今天就学习一下大牛boyliang分享的Android的so注入的代码框架Poison,作者boyliang的注入代码也是基于大牛古河分享的Andorid注入的代码修改过来的,做了一些改进和优化,后面的文章中就对注入代码进行学习一下。


    一、注入工程Poison-master代码的下载

    1)作者boyliang的注入代码的原下载地址已经失效了,但是从github上还是可以查找的,下载地址为:https://github.com/matrixhawk/Poison(缺少编译的Android.mk文件和Application.mk配置文件,需要自己编写)。

    windows环境下,NDK编译需要添加的include头文件(根据编译的版本需要进行修改)
    右击项目 --> Properties --> 左侧C/C++ General --> Paths and Symbols --> 右侧Includes --> GNU C++(.cpp) --> Add
    ${NDKROOT}platformsandroid-19arch-armusrinclude
    ${NDKROOT}sourcescxx-stlgnu-libstdc++4.8include 
    ${NDKROOT}sourcescxx-stlgnu-libstdc++4.8libsarmeabiinclude
    ${NDKROOT} oolchainsarm-Linux-androideabi-4.8prebuiltwindowslibgccarm-linux-androideabi4.8include


    2)为Poison-master工程添加编译需要的Android.mk文件和Application.mk文件如下:

    Android.mk文件:

    LOCAL_PATH := $(call my-dir)  
      
    include $(CLEAR_VARS)  
    
    # 编译生成的模块的名称
    LOCAL_MODULE := poison  
    
    # 需要被编译的源码文件 
    LOCAL_SRC_FILES := poison.c 
    	       elf_utils.c 
    	       ptrace_utils.c 
    	       tools.c 
    
    # 支持log日志打印android/log.h里函数调用的需要
    LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog  
    
    # 编译模块生成可执行文件 
    include $(BUILD_EXECUTABLE)  
    
    Application.mk文件:

    # 编译生成的模块运行支持的平台
    APP_ABI := armeabi-v7a  
    # 设置编译连接的工具的版本
    #NDK_TOOLCHAIN_VERSION = 4.9 


    3)即便是如此,使用NDK编译源码工程Poison-master,仍然会提示“jni/ptrace_utils.c:12:28: fatal error: cutils/sockets.h: No such file or directory”等的错误,这个问题怎么解决呢?作者boyliang已经为我们解决好了这个问题,需要对Android官方提供的NDK工具进行path,添加源码编译需要的一些系统的头文件,需要添加的Android系统头文件的ndk-patch可以从作者的github地址:https://github.com/boyliang/ndk-patch进行下载。使用的用法如下:



    4)哈哈,编译成功了,Android进程so注入的工具就有了。




    二、注入工程代码Poison的说明

    1)关于Android的so注入的详细的原理和细节,可以参考博文http://blog.csdn.net/qq1084283172/article/details/46859931,这篇博文里已经把Android的so注入的代码分析的很清楚了,基本把古河大牛的LibInject都说明白了。古河大牛的LibInject中涉及到了Android函数的Hook部分的模板的编写,作者boyliang的代码中没有涉及到Android函数的Hook部分的代码,这部分后面再研究,大牛boyliang和古河的so注入部分的思路是一样的,只不过boyliang的so注入代码中考虑到了"zygote"进程注入的特殊情况,在对进程目标pid进程ptrace时,有着特殊的处理。





    2)Andorid的so注入时,针对"zygote"进程注入的特殊处理的原因,可以参考《Android so注入》这篇博文给出的解释原因。

    A.针对"zygote"进程注入时,so库文件必须存放在“/system/lib/“路径下。



    B.针对"zygote"进程注入时,ptrace操作"zygote"进程时的特殊处理(直接搬过来)。


    可以看到ptrace_attach只是对ptrace(PTRACE_ATTACH,…)做了一个封装,但是在attach还做了一系列的waitpid和ptrace(PTRACE_SYSCALL,…)的操作,这是为什么呢。这里我们需要复习一下ptrace的执行过程,一旦对某个进程执行了ptrace操作,那么当目标进程执行系统调用,也就是把执行的控制权交给内核的时候,内核会检查当前进程是否被标记为”traced”,如果是,那么内核就会把控制权转交给跟踪进程。而此时跟踪进程正调用了wait函数在等待内核函数的信号,当接受到信号后跟踪进程就能继续执行。但是有时候会遇到被跟踪进程执行的系统调用是一个阻塞函数,比如recv,read,这样当目标进程系统调用开始的时候(PTRACE_ATTACH在系统调用开始暂停目标进程),它就会被暂停,而跟踪进程会被唤醒,一般这个时候跟踪进程会执行ptrace(PTRACE_GETREGS,…)等操作,这需要目标进程从系统调用返回,但是目标进程这个时候已经阻塞在系统调用里面了,无法返回,ptrace就会产生错误。知道这个情况,我们就很容易理解这段代码了。首先使用PTRACE_ATTACH标记目标进程,然后等待目标进程返回,这里的WUNTRACED表示目标进程暂停后就立即返回,而不是等待目标进程结束。当目标进程进入系统调用后,通知跟踪进程,跟踪进程再调用ptrace(PTRACE_SYSCALL,…)然后等待(PTRACE_SYSCALL在目标进程进入/退出系统调用的时候暂停目标进程),表示等待目标进程进入系统调用,然后再调用一次ptrace(PTRACE_SYSCALL,…)再等待,表示等待目标进程从系统调用返回,等第三次的wait返回后((可能会被阻塞),就可以进行系统调用了。
    这种做法还是有可能会被阻塞,就是第三次wait会等不到信号,也就是目标进程进入系统调用后一直不返回。什么时候会发生这种情况呢?其实zygote就是个很好的例子,这需要对zygote进程有一些了解,这里只简单的分析一下。zygote启动后会进入一个死循环,用来接收AMS的请求连接,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
     * Runs the zygote process's select loop. Accepts new connections as 
     * they happen, and reads commands from connections one spawn-request's 
     * worth at a time. 
     * 
     * @throws MethodAndArgsCaller in a child process when a main() should 
     * be executed. 
     */  
    private static void runSelectLoopMode() throws MethodAndArgsCaller {  
       	  ArrayList<FileDescriptor> fds = new ArrayList();
           ArrayList <ZygoteConnection> peers = new ArrayList();
           FileDescriptro[] fdArray = new FileDescriptor[4];
            ...... 
           while (true) {//死循环  
               ......  
                 
               if (index < 0) {  
                   throw new RuntimeException("Error in select()");  
               } else if (index == 0) {//index==0表示selcet接收到的是Zygote的socket的事件  
                   ZygoteConnection newPeer = acceptCommandPeer();  
                   peers.add(newPeer);  
                   fds.add(newPeer.getFileDesciptor());  
               } else {//调用ZygoteConnection对象的runOnce方法,ZygoteConnection是在index == 0时被添加到peers的  
                   boolean done;  
                   done = peers.get(index).runOnce();  
     
                   if (done) {  
                       peers.remove(index);  
                       fds.remove(index);  
                   }  
               }  
           }  
    	}
    

    index变量表示此时和zygote进程通信的个数,当index=0时也就是说没有socket连接,此时zygote调用acceptCommandPeer函数,该函数等待一个连接并返回一个ZygoteConnection对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    /*
     * Waits for and accepts a single command connection. Throws
     * RuntimeException on failure.
     */
    private static ZygoteConnection acceptCommandPeer() {
        try {
            return new ZygoteConnection(sServerSocket.accept());
        } catch (IOException ex) {
            throw new RuntimeException("IOException during accept()", ex);
        }
    }
    

    也就是说,当没有应用启动时,zygote进程一直处于阻塞状态。所以我们上面代码中的第三次wait会无法返回,解决办法也很简单,就是主动发起一个zygote的连接。我们看到第二个waitpid后面调用了一个connect_to_zygote函数,下面是它的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    static void* connect_to_zygote(void* arg){
    	int s, len;
    	struct sockaddr_un remote;
    //zygote进程接收socket连接的时间间隔是500ms,2s足以保证此socket连接能连接到zygote socket
    	LOGI("[+] wait 2s...");
    	sleep(2);
    	//sleep(0.5);
    	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
    		remote.sun_family = AF_UNIX;
    		strcpy(remote.sun_path, "/dev/socket/zygote");
    		len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    		LOGI("[+] start to connect zygote socket");
    		connect(s, (struct sockaddr *) &remote, len);
    		LOGI("[+] close socket");
    		close(s);
    	}
    
    	return NULL ;
    }
    

    这个函数的功能很简单,先发起socket连接,然后再关闭连接。看上去没有做什么有用的事情,但是它却非常重要,通过连接zygote,它使zygote进程解除了阻塞状态,我们才得以注入进zygote进程。

    说明:暂时对zygote这一块不是很熟悉,参考大牛http://zke1ev3n.me/2015/12/02/Android-so注入/的分析理由。


    C.针对调用目标pid进程的函数时,对于等待目标pid进程完成so注入需要注意的地方,大牛zke1ev3n也做了深入的说明和代码的补充。

    ptrace_dlopen函数构造dlopen函数的参数,然后调用ptrace_call开始加载so的过程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    
    int ptrace_call(pid_t pid, uint32_t addr, long *params, int num_params, struct pt_regs* regs) {
    	uint32_t i;
    
    	for (i = 0; i < num_params && i < 4; i++) {
    		regs->uregs[i] = params[i];
    	}
    
    	if (i < num_params) {
    		regs->ARM_sp-= (num_params - i) * sizeof(long);
    		ptrace_write(pid, (uint8_t *) regs->ARM_sp, (uint8_t *) &params[i], (num_params - i) * sizeof(long));
    	}
    
    	regs->ARM_pc= addr;
    	if (regs->ARM_pc& 1) {
    		/* thumb */
    		regs->ARM_pc &= (~1u);
    		regs->ARM_cpsr |= CPSR_T_MASK;
    	} else {
    		/* arm */
    		regs->ARM_cpsr &= ~CPSR_T_MASK;
    	}
    
    	regs->ARM_lr= 0;	//置子程序的返回地址为空,以便函数执行完后,返回到null地址,产生SIGSEGV错误
    
    	if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) {
    		return -1;
    	}
    
    //	waitpid(pid, NULL, WUNTRACED);	
    	
    	int status = 0;
    //	waitpid(pid,&stat,WUNTRACED);
      	pid_t res;
       	waitpid(pid, NULL, WUNTRACED);  
    	/*
    	 * Restarts  the stopped child as for PTRACE_CONT, but arranges for
    	 * the child to be stopped at the next entry to or exit from a sys‐
    	 * tem  call,  or  after execution of a single instruction, respec‐
    	 * tively.
    	 */
    	if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0) {
    		LOGE("ptrace_syscall");
    		return -1;
    	}
    
    	waitpid(pid, NULL, WUNTRACED);
    
    	if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL ) < 0) {
    		LOGE("ptrace_syscall");
    		return -1;
    	}
    
    	res = waitpid(pid, NULL, WUNTRACED);
    
    	LOGI("[+] status is %x",status);
        	if (res != pid || !WIFSTOPPED (status))//WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真
            	return 0;
    	LOGI("[+]done %d
    ",(WSTOPSIG (status) == SIGSEGV)?1:0);
    	//设置siginal 11信号处理函数
    /*	if(signal(SIGSEGV,handler) == SIG_ERR){
    		LOGE("[-]can not set handler for SIGSEGV");
    	}*/
    
    	return 0;
    }
    

    WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。对于使用PTRACE_CONT运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。这里的0xb7f就表示子进程进入了暂停状态,且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。那么什么时候会发生这种错误呢?显然,当子进程执行完注入的函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV
    这里还需要了解下arm架构的相关知识。首先是函数参数传递,在arm中,函数的前4个参数分别保存在r0-r3中,当参数大于4个,就依次压入栈中。此外,arm处理器实际上支持两套指令集,即arm和thumb。thumb为16位,arm为32位。这里通过判断pc的最后一位是否是1来确定指令集,这是因为编译器在用thmub指令集编译一个函数时会将函数的符号地址设置成真正的映射地址+1实现arm和thumb混编。此外,在切换arm和thumb指令时,还会修改CPSR处理器。在arm中,出了r0-r15这16个处理器,还有状态寄存器CPSR。关于CPSR的其他位这里先不讨论,我们只要知道CPSR寄存器的第低5位T标识了当前的指令集(T=0表示执行arm指令T=1表示执行Thumb指令),所以在切换指令集时需要修改这一位

    Arm与Thumb之间的状态切换是通过专用的转移交换指令BX来实现。BX指令以通用寄存器(R0~R15)为操作数,通过拷贝Rn到PC实现绝对跳转。BX利用Rn寄存器中目的地址值的最后一位判断跳转后的状态,如果为“1”表示跳转到Thumb指令集的函数中,如果为“0”表示跳转到Arm指令集的函数中。而Arm指令集的每条指令是32位,即4个字节,也就是说Arm指令的地址肯定是4的倍数,最后两位必定为“00”。所以,直接就可以将从符号表中获得的调用地址模4,看是否为0来判断要修改的函数是用Arm指令集还是Thumb指令集。

    三、注入工程Poison-master代码的注入测试

    用到的命令:

    cd xxxxxAndroidProject_Poison-masteruse_poison
    
    adb push poison /data/local/tmp
    adb push libmobisec.so /data/local/tmp
    adb shell chmod 0777 /data/local/tmp/poison
    adb shell chmod 0777 /data/local/tmp/libmobisec.so
    adb shell 
    su
    ps | grep com.example.androiddecod
    
    cat /proc/17569/maps
    
    /data/local/tmp/poison /data/local/tmp/libmobisec.so 17569
    
    cat /proc/17569/maps | grep libmobisec.so
    
    adb logcat -s TTT

    注入so库文件libmobisec.so到com.example.androiddecod进程中成功



    能编译成功的项目工程下载地址:http://download.csdn.net/detail/qq1084283172/9721443


    四、注入工程Poison-master代码的详细注释说明

    整个Poison-master工程的代码的结构图:



    整个Poison-master工程代码的详细分析注释:

    主文件poison.c

    #include <unistd.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <dlfcn.h>
    #include <sys/mman.h>
    #include <sys/ptrace.h>
    #include <sys/wait.h>
    
    #include "ptrace_utils.h"
    #include "elf_utils.h"
    #include "log.h"
    #include "tools.h"
    
    struct process_hook {
    	// 被注入的目标进程
    	pid_t 		pid;
    	// 注入到目标进程中so文件的路径
    	char 		*dso;
    //	void		*dlopen_addr;
    //	void 		*dlsym_addr;
    //	void		*mmap_addr;
    } process_hook = {0, "", NULL, NULL, NULL};
    
    
    // 主函数
    int main(int argc, char* argv[]) {
    
    	// 对传入的参数的个数进行判断(要求3个参数)
    	if(argc < 2)
    		exit(0);
    
    	// 保存寄存器的状态信息
    	struct pt_regs regs;
    
    	// 获取注入到目标进程中的so的文件的路径
    	process_hook.dso = strdup(argv[1]);
    	// 获取注入的目标进程的pid
    	process_hook.pid = atoi(argv[2]);
    
    //	process_hook.dlopen_addr = (void *)atol(argv[3]);
    //	process_hook.dlsym_addr = (void *)atol(argv[4]);
    //	process_hook.mmap_addr = (void *)atol(argv[5]);
    
    	// 判断注入到目标进程中so是否存在并且具有可读可执行权限
    	if (access(process_hook.dso, R_OK|X_OK) < 0) {
    
    		LOGE("[-] so file must chmod rx
    ");
    		return 1;
    	}
    
    	// 获取指定pid进程的名称
    	const char* process_name = get_process_name(process_hook.pid);
    
    	// 附加目标进程
    	ptrace_attach(process_hook.pid, strstr(process_name,"zygote"));
    
    	// 打印附加目标进程的信息
    	LOGI("[+] ptrace attach to [%d] %s
    ", process_hook.pid, get_process_name(process_hook.pid));
    
    	// 读取此时目标进程中所有的寄存器的状态信息
    	if (ptrace_getregs(process_hook.pid, &regs) < 0) {
    
    		LOGE("[-] Can't get regs %d
    ", errno);
    
    		// 读取失败跳转
    		goto DETACH;
    	}
    
    	// 打印目标进程的寄存器pc和R7的信息
    	LOGI("[+] pc: %x, r7: %d", regs.ARM_pc, regs.ARM_r7);
    
    	// dlsym参数为当前进程中的调用地址,获取目标pid进程中dlsy函数的调用地址
    	void* remote_dlsym_addr = get_remote_address(process_hook.pid, (void *)dlsym);
    	// 获取目标pid进程中dlopen函数的调用地址
    	void* remote_dlopen_addr =  get_remote_address(process_hook.pid, (void *)dlopen);
    
    //	if(remote_dlopen_addr == NULL && remote_dlsym_addr != NULL){
    //		remote_dlopen_addr = (void *)((uint32_t)remote_dlsym_addr - (uint32_t)process_hook.dlsym_addr + (uint32_t)process_hook.dlopen_addr);
    //	}else if(remote_dlopen_addr != NULL && remote_dlsym_addr == NULL){
    //		remote_dlsym_addr = (void *)((uint32_t)remote_dlopen_addr - (uint32_t)process_hook.dlopen_addr + (uint32_t)process_hook.dlsym_addr);
    //	}else if(remote_dlopen_addr == NULL && remote_dlsym_addr == NULL){
    //		LOGE("[-] Can not found dlopen_addr & dlsym_addr.
    ");
    //		goto DETACH;
    //	}
    //
    
    	// 打印目标进程的函数dlopen和dlsym的调用地址
    	LOGI("[+] remote_dlopen address %p
    ", remote_dlopen_addr);
    	LOGI("[+] remote_dlsym  address %p
    ", remote_dlsym_addr);
    
    	// 调用目标pid进程的dlopen函数加载指定的so库文件,获取返回的加载的模块的基址
    	if(ptrace_dlopen(process_hook.pid, remote_dlopen_addr, process_hook.dso) == NULL){
    
    		LOGE("[-] Ptrace dlopen fail. %s
    ", dlerror());
    	}
    
    	// 针对此时不同的模式,设置目标pid进程的CPSR寄存器的值
    	if (regs.ARM_pc & 1 ) {
    		// thumb
    		regs.ARM_pc &= (~1u);
    		regs.ARM_cpsr |= CPSR_T_MASK;
    	} else {
    		// arm
    		regs.ARM_cpsr &= ~CPSR_T_MASK;
    	}
    
    	// 恢复目标pid进程的寄存器的状态即恢复到注入前的运行状态
    	if (ptrace_setregs(process_hook.pid, &regs) == -1) {
    
    		LOGE("[-] Set regs fail. %s
    ", strerror(errno));
    		// 失败进行跳转
    		goto DETACH;
    	}
    
    	// 打印注入成功的消息
    	LOGI("[+] Inject success!
    ");
    
    DETACH:
        // 结束对目标pid进程的附加
    	ptrace_detach(process_hook.pid);
    
    	// 打印注入工作完成的消息
    	LOGI("[+] Inject done!
    ");
    
    	return 0;
    }
    
    ptrace_utils.h文件

    /*
     * ptrace_utils.h
     *
     *  Created on: 2013-6-19
     *      Author: boyliang
     */
    
    #ifndef PTRACE_UTILS_H_
    #define PTRACE_UTILS_H_
    
    #define CPSR_T_MASK		( 1u << 5 )
    
    int ptrace_getregs(pid_t pid, struct pt_regs* regs);
    
    int ptrace_setregs(pid_t pid, struct pt_regs* regs);
    
    int ptrace_attach( pid_t pid , int zygote);
    
    int ptrace_detach( pid_t pid );
    
    int ptrace_continue(pid_t pid);
    
    int ptrace_syscall(pid_t pid);
    
    int ptrace_write(pid_t pid, uint8_t *dest, uint8_t *data, size_t size);
    
    int ptrace_read( pid_t pid,  uint8_t *src, uint8_t *buf, size_t size );
    
    int ptrace_call(pid_t pid, uint32_t addr, long *params, int num_params, struct pt_regs* regs);
    
    void* ptrace_dlopen(pid_t target_pid, void* remote_dlopen_addr, const char*  filename);
    
    #endif /* PTRACE_UTILS_H_ */
    ptrace_utils.c文件

    /*
     * ptrace_utils.c
     *
     *  Created on: 2013-6-26
     *      Author: boyliang
     */
    
    #include <asm/ptrace.h>
    #include <sys/ptrace.h>
    #include <sys/wait.h>
    #include <dlfcn.h>
    #include <cutils/sockets.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #include "ptrace_utils.h"
    #include "log.h"
    
    /**
     * read registers' status
     */
    int ptrace_getregs(pid_t pid, struct pt_regs* regs) {
    
    	if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) {
    
    		perror("ptrace_getregs: Can not get register values");
    		return -1;
    	}
    
    	return 0;
    }
    
    /**
     * set registers' status
     */
    int ptrace_setregs(pid_t pid, struct pt_regs* regs) {
    
    	if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
    		perror("ptrace_setregs: Can not set register values");
    		return -1;
    	}
    
    	return 0;
    }
    
    // 解除zygote进程的阻塞状态
    static void* connect_to_zygote(void* arg){
    
    	int s, len;
    	struct sockaddr_un remote;
    
    	LOGI("[+] wait 2s...");
    	// 休眠一下
    	sleep(2);
    
    	/***
    	 * zygote启动后会进入一个死循环,用来接收AMS的请求连接.
    	 * 当没有应用启动时,zygote进程一直处于阻塞状态。
    	 * 所以我们后面代码中的第三次wait会无法返回,解决办法也很简单,就是主动发起一个zygote的连接。
    	 * 我们看到第二个waitpid后面调用了一个connect_to_zygote函数。
    	 */
    
    	// 创建socket套接字
    	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
    
    		// 设置连接的套接字的协议类型
    		remote.sun_family = AF_UNIX;
    		// 设置连接的套接字的目标
    		strcpy(remote.sun_path, "/dev/socket/zygote");
    		// 设置传递的参数的字节长度
    		len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    
    		LOGI("[+] start to connect zygote socket");
    		// 向"/dev/socket/zygote"目标套接字发起连接
    		connect(s, (struct sockaddr *) &remote, len);
    
    		LOGI("[+] close socket");
    		// 关闭socket套接字
    		close(s);
    	}
    
    	/***
    	 * 这个函数的功能很简单,先发起socket连接,然后再关闭连接。
    	 * 看上去没有做什么有用的事情,但是它却非常重要,
    	 * 通过连接zygote,它使zygote进程解除了阻塞状态,
    	 * 我们才得以注入进zygote进程。
    	 * 参考网址:http://zke1ev3n.me/2015/12/02/Android-so%E6%B3%A8%E5%85%A5/
    	 */
    
    	return NULL ;
    }
    
    /**
     * attach to target process 附加目标进程
     */
    int ptrace_attach(pid_t pid, int zygote) {
    
    	if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {
    
    		LOGE("ptrace_attach");
    		return -1;
    	}
    
    	waitpid(pid, NULL, WUNTRACED);
    
    	/*
    	 * Restarts  the stopped child as for PTRACE_CONT, but arranges for
    	 * the child to be stopped at the next entry to or exit from a sys‐
    	 * tem  call,  or  after execution of a single instruction, respec‐
    	 * tively.
    	 */
    	if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0) {
    
    		LOGE("ptrace_syscall");
    		return -1;
    	}
    
    	waitpid(pid, NULL, WUNTRACED);
    
    	// 针对zygote进程的特殊处理
    	if (zygote) {
    
    		// 当进程为zygote时,需要考虑为zygote进程解除阻塞状态,使进程注入得以进行
    		connect_to_zygote(NULL);
    	}
    
    	// 当目标进程在下次进/出系统调用时被附加调试
    	if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL ) < 0) {
    
    		LOGE("ptrace_syscall");
    		return -1;
    	}
    
    	// 等待进程附加操作返回
    	waitpid(pid, NULL, WUNTRACED);
    
    	return 0;
    }
    
    /**
     * detach from target process
     */
    int ptrace_detach( pid_t pid )
    {
        if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 )
        {
        	LOGE( "ptrace_detach" );
            return -1;
        }
    
        return 0;
    }
    
    
    int ptrace_continue(pid_t pid) {
    
    	if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
    
    		LOGE("ptrace_cont");
    		return -1;
    	}
    
    	return 0;
    }
    
    int ptrace_syscall(pid_t pid) {
    
    	return ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
    }
    
    /**
     * write data to dest 向目标pid进程中写入数据(4字节对齐)
     */
    int ptrace_write(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;
    
    	j = size / 4;
    	remain = size % 4;
    
    	laddr = data;
    
    	for (i = 0; i < j; i++) {
    
    		memcpy(d.chars, laddr, 4);
    		ptrace(PTRACE_POKETEXT, pid, (void *)dest, (void *)d.val);
    
    		dest += 4;
    		laddr += 4;
    	}
    
    	if (remain > 0) {
    
    		d.val = ptrace(PTRACE_PEEKTEXT, pid, (void *)dest, NULL);
    		for (i = 0; i < remain; i++) {
    
    			d.chars[i] = *laddr++;
    		}
    
    		ptrace(PTRACE_POKETEXT, pid, (void *)dest, (void *)d.val);
    
    	}
    
    	return 0;
    }
    
    // 从目标pid进程中读取数据(4字节对齐)
    int ptrace_read( pid_t pid,  uint8_t *src, uint8_t *buf, size_t size )
    {
        uint32_t i, j, remain;
        uint8_t *laddr;
    
        union u {
            long val;
            char chars[sizeof(long)];
        } d;
    
        j = size / 4;
        remain = size % 4;
    
        laddr = buf;
    
        for ( i = 0; i < j; i ++ )
        {
            d.val = ptrace( PTRACE_PEEKTEXT, pid, src, 0 );
            memcpy( laddr, d.chars, 4 );
            src += 4;
            laddr += 4;
        }
    
        if ( remain > 0 )
        {
            d.val = ptrace( PTRACE_PEEKTEXT, pid, src, 0 );
            memcpy( laddr, d.chars, remain );
        }
    
        return 0;
    }
    
    // 调用目标pid进程中的指定函数addr
    int ptrace_call(pid_t pid, uint32_t addr, long *params, int num_params, struct pt_regs* regs) {
    
    	uint32_t i;
    
    	// 在arm中,函数的前4个参数使用r0-r4的寄存器传递
    	for (i = 0; i < num_params && i < 4; i++) {
    
    		// 设置调用目标pid进程中的函数需要的参数
    		regs->uregs[i] = params[i];
    	}
    
    	// 当被调用的函数的参数个数超过4个时,其他的参数通过栈进行传递
    	if (i < num_params) {
    
    		// 抬高函数的栈顶
    		regs->ARM_sp-= (num_params - i) * sizeof(long);
    
    		// 向目标pid进程的内存中写入函数调用需要的超过4个的其他参数
    		ptrace_write(pid, (uint8_t *) regs->ARM_sp, (uint8_t *) ¶ms[i], (num_params - i) * sizeof(long));
    	}
    
    	// 设置目标pid进程的pc为将被调用的函数的地址
    	regs->ARM_pc= addr;
    
    	// 针对当前进程所处的不同模式,进行不同的处理
    	if (regs->ARM_pc& 1) {
    
    		/* thumb模式 */
    		regs->ARM_pc &= (~1u);
    		regs->ARM_cpsr |= CPSR_T_MASK;
    
    	} else {
    
    		/* arm模式 */
    		regs->ARM_cpsr &= ~CPSR_T_MASK;
    	}
    
    	// 设置函数调用的返回地址为0,调用的函数执行完,跳回到当前进程中
    	regs->ARM_lr= 0;
    
    	// 设置目标pid进程的寄存器的状态,并调用addr函数
    	if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) {
    
    		return -1;
    	}
    
    	// 等待函数的调用完成
    	waitpid(pid, NULL, WUNTRACED);
    
    	return 0;
    }
    
    //static void* thread_connect_to_zygote(void* arg){
    //	int s, len;
    //	struct sockaddr_un remote;
    //
    //	LOGI("[+] wait 2s...");
    //	sleep(2);
    //
    //	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
    //		remote.sun_family = AF_UNIX;
    //		strcpy(remote.sun_path, "/dev/socket/zygote");
    //		len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    //		LOGI("[+] start to connect zygote socket");
    //		connect(s, (struct sockaddr *) &remote, len);
    //		LOGI("[+] close socket");
    //		close(s);
    //	}
    //
    //	return NULL ;
    //}
    
    
    // 当目标pid进程为zygote时,加载so库文件之前,需要的测试处理
    static int zygote_special_process(pid_t target_pid){
    
    	LOGI("[+] zygote process should special take care. 
    ");
    
    	struct pt_regs regs;
    
    	// 获取目标pid进程的寄存器的状态值
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return -1;
    
    	// 获取目标pid进程的getpid函数的调用地址
    	void* remote_getpid_addr = get_remote_address(target_pid, getpid);
    	LOGI("[+] Remote getpid addr %p.
    ", remote_getpid_addr);
    
    	// 判断获取目标pid进程的getpid函数的调用地址是否成功
    	if(remote_getpid_addr == NULL){
    
    		return -1;
    	}
    
    	pthread_t tid = 0;
    	// 创建线程再次调用connect_to_zygote解除zygote进程的阻塞状态
    	pthread_create(&tid, NULL, connect_to_zygote, NULL);
    	// 释放线程
    	pthread_detach(tid);
    
    	// 调用目标pid进程中的getpid函数
    	if (ptrace_call(target_pid, remote_getpid_addr, NULL, 0, &regs) == -1) {
    
    		LOGE("[-] Call remote getpid fails");
    		return -1;
    	}
    
    	// 获取上面的函数调用完后目标pid进程的寄存器的状态,主要是为了获取getpid函数的返回值
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return -1;
    
    	// 打印调用getpid函数完后,目标pid进程的寄存器的状态
    	LOGI("[+] Call remote getpid result r0=%x, r7=%x, pc=%x, 
    ", regs.ARM_r0, regs.ARM_r7, regs.ARM_pc);
    
    	return 0;
    }
    
    // 调用目标pid进程的dlopen函数加载指定的so库文件,并返回加载的模块的基址
    void* ptrace_dlopen(pid_t target_pid, void* remote_dlopen_addr, const char*  filename){
    
    	struct pt_regs regs;
    
    	// 获取目标pid进程的寄存器的状态值
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return NULL ;
    
    	// 判断目标pid进程是否是zygote进程;如果是,加载so库文件之前,进行相应的测试处理
    	if (strcmp("zygote", get_process_name(target_pid)) == 0 && zygote_special_process(target_pid) != 0) {
    
    		return NULL ;
    	}
    
    	// 在目标pid进程中调用dlopen函数需要的参数
    	long mmap_params[2];
    
    	// filename为将要加载到目标pid进程中的so的路径字符串
    	// 要将filename字符串写入到目标pid进程中,filename_len即为需要分配的内存空间的大小
    	size_t filename_len = strlen(filename) + 1;
    
    	// 调用目标pid进程的mmap函数申请内存空间,用以保存filename字符串(即将要加载的so文件的路径)
    	void* filename_addr = find_space_by_mmap(target_pid, filename_len);
    	// 判断在目标pid进程是否调用mmap函数分配内存空间成功
    	if (filename_addr == NULL ) {
    
    		LOGE("[-] Call Remote mmap fails.
    ");
    		return NULL ;
    	}
    
    	// 将filename字符串(即将要加载的so文件的路径)写入到目标pid进程的内存地址filename_addr中
    	ptrace_write(target_pid, (uint8_t *)filename_addr, (uint8_t *)filename, filename_len);
    
    	// dlopen函数的参数--需要加载的so文件的路径字符串
    	mmap_params[0] = (long)filename_addr;
    	// dlopen函数的参数--flag,加载的要求
    	mmap_params[1] = RTLD_NOW | RTLD_GLOBAL;
    
    	// 获取目标pid进程中的dlopen函数的调用地址(调用参数已经准备好)
    	remote_dlopen_addr = (remote_dlopen_addr == NULL) ? get_remote_address(target_pid, (void *)dlopen) : remote_dlopen_addr;
    	if (remote_dlopen_addr == NULL) {
    
    		LOGE("[-] Get Remote dlopen address fails.
    ");
    		return NULL;
    	}
    
    	// 在目标pid进程调用dlopen函数,加载filename_addr指定的so库文件
    	if (ptrace_call(target_pid, (uint32_t) remote_dlopen_addr, mmap_params, 2, &regs) == -1)
    		return NULL;
    
    	// 获取目标pid进程的寄存器的状态值,主要是为了获取上面 dlopen函数调用的返回值
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return NULL;
    
    	LOGI("[+] Target process returned from dlopen, return r0=%x, r7=%x, pc=%x, 
    ", regs.ARM_r0, regs.ARM_r7, regs.ARM_pc);
    
    	// 返回目标pid进程中调用dlopen函数的返回的内存加载的模块基址
    	return regs.ARM_pc == 0 ? (void *) regs.ARM_r0 : NULL;
    }
    
    tool.h文件

    /*
     * tool.h
     *
     *  Created on: 2013-7-5
     *      Author: boyliang
     */
    
    #ifndef TOOL_H_
    #define TOOL_H_
    
    #include <stdio.h>
    #include <dlfcn.h>
    
    // 获取指定内存加载模块的导出函数的地址
    void *get_method_address(const char *soname, const char *methodname);
    
    // 获取目标pid进程的名称字符串
    const char* get_process_name(pid_t pid);
    
    #endif /* TOOL_H_ */
    tool.c文件

    /*
     * tool.c
     *
     *  Created on: 2013-7-5
     *      Author: boyliang
     */
    
    
    #include <stdio.h>
    #include <dlfcn.h>
    #include <stddef.h>
    
    // 获取指定内存加载模块的导出函数的地址
    void *get_method_address(const char *soname, const char *methodname) {
    
    	void *handler = dlopen(soname, RTLD_NOW | RTLD_GLOBAL);
    
    	return dlsym(handler, methodname);
    }
    
    
    // 获取目标pid进程的名称字符串
    const char* get_process_name(pid_t pid) {
    
    	static char buffer[255];
    	FILE* f;
    	char path[255];
    
    	// 格式化得到字符串"/proc/pid/cmdline"
    	snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
    
    	// 读取文件"/proc/pid/cmdline"的内容,获取进程的命令行参数
    	if ((f = fopen(path, "r")) == NULL) {
    
    		return NULL;
    	}
    
    	// 读取文件"/proc/pid/cmdline"的第1行字符串内容--进程的名称
    	if (fgets(buffer, sizeof(buffer), f) == NULL) {
    
    		return NULL;
    	}
    
    	// 关闭文件
    	fclose(f);
    
    	return buffer;
    }
    
    log.h文件

    /*
     * log.h
     *
     *  Created on: 2013-6-25
     *      Author: boyliang
     */
    
    #ifndef LOG_H_
    #define LOG_H_
    
    #include <android/log.h>
    
    // 主要用于消息的log打印
    
    #define LOG_TAG "TTT"
    
    #ifdef DEBUG
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    #else
    #define LOGI(...) while(0)
    #define LOGE(...) while(0)
    #endif
    
    #endif /* LOG_H_ */
    elf_utils.h文件

    /*
     * elf_utils.h
     *
     *  Created on: 2013-6-19
     *      Author: boyliang
     */
    
    #ifndef ELF_UTILS_H_
    #define ELF_UTILS_H_
    
    #include <stddef.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <string.h>
    #include <sys/mman.h>
    
    // 获取目标pid进程中指定so模块的加载基址
    void* get_module_base(pid_t pid, const char* module_name);
    
    // 在目标pid进程的内存空间中申请内存,申请成功返回的内存地址保存在r0中
    void* find_space_by_mmap(int target_pid, int size);
    
    // 在目标pid进程的"/system/lib/libc.so"的内存范围内(从内存结束地址往回的方向)查找内存空间
    void* find_space_in_maps(int pid, int size);
    
    // 通过系统函数的地址查找到该函数所在的模块的名称
    int find_module_info_by_address(pid_t pid, void* addr, char *module, void** start, void** end);
    
    // 通过指定的内存模块so的路径字符串,获取该内存模块的在目标进程pid中起始地址和结束地址
    int find_module_info_by_name(pid_t pid, const char *module, void** start, void** end);
    
    // 获取目标pid进程中指定函数的调用地址
    void* get_remote_address(pid_t pid, void *local_addr);
    
    #endif /* ELF_UTILS_H_ */
    elf_utils.c文件

    /*
     * elf_utils.c
     *
     *  Created on: 2013-6-25
     *      Author: boyliang
     */
    
    
    #include <stddef.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/ptrace.h>
    
    #include "tools.h"
    #include "elf_utils.h"
    #include "log.h"
    
    
    // 获取目标pid进程中指定so模块的加载基址
    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)) {
    
    			// 判断是否是在目标pid进程的内存中要查找到的so模块
    			if (strstr(line, module_name)) {
    
    				pch = strtok(line, "-");
    				// 获取目标pid进程中指定模块的基址
    				addr = strtoul(pch, NULL, 16);
    
    				if (addr == 0x8000)
    					addr = 0;
    
    				break;
    			}
    		}
    
    		fclose(fp);
    	}
    
    	return (void *) addr;
    }
    
    // 在目标pid进程的内存空间中申请内存,申请成功返回的内存地址保存在r0中
    void* find_space_by_mmap(int target_pid, int size) {
    
    	struct pt_regs regs;
    
    	// 获取目标pid进程的寄存器的状态
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return 0;
    
    	long parameters[10];
    
    	/* call mmap */
    	parameters[0] = 0;  // addr
    	parameters[1] = size; // 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
    
    	// 获取目标pid进程中的mmap函数的调用地址
    	void *remote_mmap_addr = get_remote_address(target_pid, get_method_address("/system/lib/libc.so", "mmap"));
    	LOGI("[+] Calling mmap in target process. mmap addr %p.
    ", remote_mmap_addr);
    
    	if (remote_mmap_addr == NULL) {
    
    		LOGE("[-] Get Remote mmap address fails.
    ");
    		return 0;
    	}
    
    	// 调用目标pid进程的mmap函数,在目标pid进程的内存中申请内存空间
    	if (ptrace_call(target_pid, (uint32_t) remote_mmap_addr, parameters, 6, &regs) == -1)
    		return 0;
    
    	// 获取目标pid进程的寄存器的状态
    	if (ptrace_getregs(target_pid, &regs) == -1)
    		return 0;
    
    	LOGI("[+] Target process returned from mmap, return r0=%x, r7=%x, pc=%x, 
    ", regs.ARM_r0, regs.ARM_r7, regs.ARM_pc);
    
    	// arm中,函数的返回值保存在寄存器r0中,返回在目标pid进程中申请的内存空间的地址
    	return regs.ARM_pc == 0 ? (void *) regs.ARM_r0 : 0;
    }
    
    // 分割字符串
    static char* nexttok(char **strp) {
    
    	// 以" "为基准分解字符串,将原字符串中第一个" "替换为''
    	// 第一个" "前面的字符串返回在p中,第一个" "后面的字符串在strp中
    	char *p = strsep(strp, " ");
    
    	// 返回分割的字符串
    	return p == NULL ? "" : p;
    }
    
    // 在目标pid进程的"/system/lib/libc.so"的内存范围内(从内存结束地址往回的方向)查找内存空间
    void* find_space_in_maps(int pid, int size) {
    
    	char statline[1024];
    	FILE * fp;
    	uint32_t* addr = (uint32_t*) 0x40008000;
    	char *address, *proms, *ptr;
    	const char* tname = "/system/lib/libc.so";
    	const char* tproms = "r-xp";
    
    	// 获取字符串"/system/lib/libc.so"的长度
    	int tnaem_size = strlen(tname);
    	// 获取字符串"r-xp"的长度
    	int tproms_size = strlen(tproms);
    
    	// 内存以4字节对齐
    	size = ((size / 4) + 1) * 4;
    
    	// 格式化得到字符串"/proc/pid/maps"
    	sprintf(statline, "/proc/%d/maps", pid);
    
    	// 打开文件"/proc/pid/maps"
    	fp = fopen(statline, "r");
    	if (fp == 0)
    		return 0;
    
    	// 读取文件"/proc/pid/maps"中内容(每次读一行)
    	while (fgets(statline, sizeof(statline), fp)) {
    
    		// 分割字符串
    		ptr = statline;
    		// 得到内存模块的起始和结束地址
    		address = nexttok(&ptr); // skip address
    		// 内存模块的属性
    		proms = nexttok(&ptr); // skip proms
    		nexttok(&ptr); // skip offset
    		nexttok(&ptr); // skip dev
    		nexttok(&ptr); // skip inode
    
    		// ptr中最终保存的是加载的内存模块的路径字符串
    		while (*ptr != '') {
    			if (*ptr == ' ')
    				ptr++;
    			else
    				break;
    		}
    
    		// 查找目标so模块
    		if (ptr && proms && address) {
    
    			// 判断是否是"r-xp"属性的模块
    			if (strncmp(tproms, proms, tproms_size) == 0) {
    
    				// 判断是否是"/system/lib/libc.so"模块
    				if (strncmp(tname, ptr, tnaem_size) == 0) {
    
    					// address like afe00000-afe3a000
    					if (strlen(address) == 17) {
    
    						// 获取内存加载模块/system/lib/libc.so的内存范围的结束地址(方便后面查找内存空间)
    						addr = (uint32_t*) strtoul(address + 9, NULL, 16);
    						// 在目标pid进程的/system/lib/libc.so的内存范围内查找到size大小内存空间
    						addr -= size;
    
    						printf("proms=%s address=%s name=%s", proms, address, ptr);
    						break;
    					}
    				}
    			}
    		}
    	}
    
    	// 关闭文件
    	fclose(fp);
    
    	// 返回在目标进程中查找到的内存空间的地址
    	return (void*) addr;
    }
    
    
    // 通过系统函数的地址查找到该函数所在的模块的名称
    int find_module_info_by_address(pid_t pid, void* addr, char *module, void** start, void** end) {
    
    	char statline[1024];
    	FILE *fp;
    	char *address, *proms, *ptr, *p;
    
    	// 格式化字符串得到"/proc/pid/maps"
    	if ( pid < 0 ) {
    
    		/* self process */
    		snprintf( statline, sizeof(statline), "/proc/self/maps");
    	} else {
    
    		snprintf( statline, sizeof(statline), "/proc/%d/maps", pid );
    	}
    
    	// 打开文件 /proc/pid/maps
    	fp = fopen( statline, "r" );
    	if ( fp != NULL ) {
    
    		// 每次一行,读取文件/proc/pid/maps中内容
    		while ( fgets( statline, sizeof(statline), fp ) ) {
    
    			// 解析读取为一行字符串信息
    			ptr = statline;
    			// 获取模块的起始和结束地址
    			address = nexttok(&ptr); // skip address
    			proms = nexttok(&ptr); // skip proms
    			nexttok(&ptr); // skip offset
    			nexttok(&ptr); // skip dev
    			nexttok(&ptr); // skip inode
    
    			while(*ptr != '') {
    				if(*ptr == ' ')
    					ptr++;
    				else
    					break;
    			}
    
    			p = ptr;
    			while(*p != '') {
    				if(*p == '
    ')
    					*p = '';
    				p++;
    			}
    
    			// 4016a000-4016b000
    			if(strlen(address) == 17) {
    
    				address[8] = '';
    
    				// 获取内存加载模块的起始地址
    				*start = (void*)strtoul(address, NULL, 16);
    				// 获取内存加载模块的结束地址
    				*end   = (void*)strtoul(address+9, NULL, 16);
    
    				// printf("[%p-%p] %s | %p
    ", *start, *end, ptr, addr);
    
    				// 判断该系统函数的地址是否在该模块的内存范围内
    				if(addr > *start && addr < *end) {
    
    					// 找到该系统函数所在的内存模块
    					// 保存该内存加载的so模块的文件路径
    					strcpy(module, ptr);
    
    					fclose( fp );
    					return 0;
    				}
    			}
    		}
    
    		fclose( fp ) ;
    	}
    
    	return -1;
    }
    
    // 通过指定的内存模块so的路径字符串,获取该内存模块的在目标进程pid中起始地址和结束地址
    int find_module_info_by_name(pid_t pid, const char *module, void** start, void** end) {
    
    	char statline[1024];
    	FILE *fp;
    	char *address, *proms, *ptr, *p;
    
    	if ( pid < 0 ) {
    
    		/* self process */
    		snprintf( statline, sizeof(statline), "/proc/self/maps");
    	} else {
    
    		snprintf( statline, sizeof(statline), "/proc/%d/maps", pid );
    	}
    
    	fp = fopen( statline, "r" );
    
    	if ( fp != NULL ) {
    
    		while ( fgets( statline, sizeof(statline), fp ) ) {
    
    			ptr = statline;
    			address = nexttok(&ptr); // skip address
    			proms = nexttok(&ptr); // skip proms
    			nexttok(&ptr); // skip offset
    			nexttok(&ptr); // skip dev
    			nexttok(&ptr); // skip inode
    
    			while(*ptr != '') {
    
    				if(*ptr == ' ')
    					ptr++;
    				else
    					break;
    			}
    
    			p = ptr;
    			while(*p != '') {
    
    				if(*p == '
    ')
    					*p = '';
    				p++;
    			}
    
    			// 4016a000-4016b000
    			if(strlen(address) == 17) {
    
    				address[8] = '';
    
    				*start = (void*)strtoul(address, NULL, 16);
    				*end   = (void*)strtoul(address+9, NULL, 16);
    
    				// printf("[%p-%p] %s | %p
    ", *start, *end, ptr, addr);
    
    				// 通过内存模块的路径字符串,判读是否是要查找的目标内存so模块
    				if(strncmp(module, ptr, strlen(module)) == 0) {
    
    					fclose( fp ) ;
    
    					return 0;
    				}
    			}
    		}
    
    		fclose( fp ) ;
    	}
    
    	return -1;
    }
    
    
    // 获取目标pid进程中指定函数的调用地址
    void* get_remote_address(pid_t pid, void *local_addr) {
    
    	// 保存加载的内存so模块的文件路径字符串
    	char buf[256];
    	// 当前进程中指定模块的起始地址
    	void* local_start = 0;
    	// 当前进程中指定模块的结束地址
    	void* local_end = 0;
    	// 目标pid进程中指定模块的起始地址
    	void* remote_start = 0;
    	// 目标pid进程中指定模块的结束地址
    	void* remote_end = 0;
    
    	// 获取当前进程中指定系统函数所在的模块的文件路径字符串buf
    	if(find_module_info_by_address(-1, local_addr, buf, &local_start, &local_end) < 0) {
    
    		LOGI("[-] find_module_info_by_address FAIL");
    		return NULL;
    	}
    
    	LOGI("[+] the local module is %s", buf);
    
    	// 通过指定的内存模块so的路径字符串,获取该内存模块的在目标进程pid中起始地址和结束地址
    	if(find_module_info_by_name(pid, buf, &remote_start, &remote_end) < 0) {
    
    
    		LOGI("[-] find_module_info_by_name FAIL");
    		return NULL;
    	}
    
    	// 目标pid进程的local_addr函数的调用地址
    	return (void *)( (uint32_t)local_addr + (uint32_t)remote_start - (uint32_t)local_start );
    }
    
    Android.mk文件

    LOCAL_PATH := $(call my-dir)  
      
    include $(CLEAR_VARS)  
    
    # 编译生成的模块的名称
    LOCAL_MODULE := poison  
    
    # 需要被编译的源码文件 
    LOCAL_SRC_FILES := poison.c 
    	       elf_utils.c 
    	       ptrace_utils.c 
    	       tools.c 
    
    # 支持log日志打印android/log.h里函数调用的需要
    LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog  
    
    # 编译模块生成可执行文件 
    include $(BUILD_EXECUTABLE)  
    Application.mk文件

    # 编译生成的模块运行支持的平台
    APP_ABI := armeabi-v7a  
    # 设置编译连接的工具的版本
    #NDK_TOOLCHAIN_VERSION = 4.9  


    感谢链接

    https://github.com/matrixhawk/Poison

    https://github.com/boyliang/ndk-patch

    http://zke1ev3n.me/2015/12/02/Android-so注入/

    http://blog.csdn.net/qq1084283172/article/details/46859931

    http://www.cnblogs.com/leaven/archive/2011/01/25/1944688.html

    http://bbs.pediy.com/showthread.php?t=141355

    http://blog.csdn.net/jinzhuojun/article/details/9900105



  • 相关阅读:
    .Net/C# 应用程序直接读取本地 Cookies 文件(WinXP SP2 调用 API: InternetGetCookie 无果)
    wininet.dll函数库:不会过期的cookie
    WinForm中TextBox控件循环自动滚动示例
    JScript中Date.getTime转.Net中的DateTime
    js gettime c# ticks
    mysql查看整库个表详情
    rds分区实践
    mysql5.7.21源码安装
    EXPLAIN详解
    C#基础温习(4):C#中string数组和list的相互转换
  • 原文地址:https://www.cnblogs.com/csnd/p/11800652.html
Copyright © 2011-2022 走看看