zoukankan      html  css  js  c++  java
  • Android native层动态库注射

    1.简单介绍

    本文解说在Android native层。root权限下。注射动态库到目标进程,从而hook目标进程中动态库的函数的实现方式。

    文中的源代码所有来源于网络。我仅仅是略微加以整理。

    环境:Android4.2 源代码下编译,模拟器中执行。

    2.代码构成

    包括三个模块的代码:

    1.inject程序:把动态库libhookhelper注射到目标进程

    2.libhookhelper:动态库代码。此部分代码用于改动目标进程中目标函数所在的got表的信息。从而替换掉目标函数。

    理解这部分须要一点点elf格式的知识。文章最后会给出链接文档以作參考。

    3.libtest:这部分含实用于測试的目标进程的代码。

    目标进程调用一个动态库实现打印字符串的功能。无实际意义。仅作測试。

    3.代码

    3.1 inject程序与Android.mk文件:

    inject.c代码例如以下:

    #include <stdio.h>    
    #include <stdlib.h>    
    #include <asm/user.h>    
    #include <asm/ptrace.h>    
    #include <sys/ptrace.h>    
    #include <sys/wait.h>    
    #include <sys/mman.h>    
    #include <dlfcn.h>    
    #include <dirent.h>    
    #include <unistd.h>    
    #include <string.h>    
    #include <elf.h>    
    #include <android/log.h>    
    
    #if defined(__i386__)    
    #define pt_regs         user_regs_struct    
    #endif    
    
    #define ENABLE_DEBUG 1    
    
    #if ENABLE_DEBUG    
    #define  LOG_TAG "inject"    
    #define  LOGD(fmt, args...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, fmt, ##args)    
    #define DEBUG_PRINT(format,args...)     
    LOGD(format, ##args)    
    #else    
    #define DEBUG_PRINT(format,args...)    
    #endif    
    
    #define CPSR_T_MASK     ( 1u << 5 )    
    
    const char *libc_path = "/system/lib/libc.so";    
    const char *linker_path = "/system/bin/linker";    
    
    int ptrace_readdata(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;    
    }    
    
    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;    
    
    	j = size / 4;    
    	remain = size % 4;    
    
    	laddr = data;    
    
    	for (i = 0; i < j; i ++) {    
    		memcpy(d.chars, laddr, 4);    
    		ptrace(PTRACE_POKETEXT, pid, dest, d.val);    
    
    		dest  += 4;    
    		laddr += 4;    
    	}    
    
    	if (remain > 0) {    
    		d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0);    
    		for (i = 0; i < remain; i ++) {    
    			d.chars[i] = *laddr ++;    
    		}    
    
    		ptrace(PTRACE_POKETEXT, pid, dest, d.val);    
    	}    
    
    	return 0;    
    }    
    
    #if defined(__arm__)    
    int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)    
    {    
    	uint32_t i;    
    	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 *)¶ms[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;        
    
    	if (ptrace_setregs(pid, regs) == -1     
    		|| ptrace_continue(pid) == -1) {    
    		printf("error
    ");    
    		return -1;    
    	}    
    
    	int stat = 0;  
    	waitpid(pid, &stat, WUNTRACED);  
    	while (stat != 0xb7f) {  
    		if (ptrace_continue(pid) == -1) {  
    			printf("error
    ");  
    			return -1;  
    		}  
    		waitpid(pid, &stat, WUNTRACED);  
    	}  
    
    	return 0;    
    }    
    
    #elif defined(__i386__)    
    long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct user_regs_struct * regs)    
    {    
    	regs->esp -= (num_params) * sizeof(long) ;    
    	ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));    
    
    	long tmp_addr = 0x00;    
    	regs->esp -= sizeof(long);    
    	ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));     
    
    	regs->eip = addr;    
    
    	if (ptrace_setregs(pid, regs) == -1     
    		|| ptrace_continue( pid) == -1) {    
    		printf("error
    ");    
    		return -1;    
    	}    
    
    	int stat = 0;  
    	waitpid(pid, &stat, WUNTRACED);  
    	while (stat != 0xb7f) {  
    		if (ptrace_continue(pid) == -1) {  
    			printf("error
    ");  
    			return -1;  
    		}  
    		waitpid(pid, &stat, WUNTRACED);  
    	}  
    
    	return 0;    
    }    
    #else     
    #error "Not supported"    
    #endif    
    
    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;    
    }    
    
    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;    
    }    
    
    int ptrace_continue(pid_t pid)    
    {    
    	if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {    
    		perror("ptrace_cont");    
    		return -1;    
    	}    
    
    	return 0;    
    }    
    
    int ptrace_attach(pid_t pid)    
    {    
    	if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {    
    		perror("ptrace_attach");    
    		return -1;    
    	}    
    
    	int status = 0;    
    	waitpid(pid, &status , WUNTRACED);    
    
    	return 0;    
    }    
    
    int ptrace_detach(pid_t pid)    
    {    
    	if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {    
    		perror("ptrace_detach");    
    		return -1;    
    	}    
    
    	return 0;    
    }    
    
    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", 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, 16 );    
    
    				if (addr == 0x8000)    
    				    addr = 0;    
    
    				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(-1, module_name);    
    	remote_handle = get_module_base(target_pid, module_name);    
    
    	DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]
    ", local_handle, remote_handle);    
    
    	void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);    
    
    #if defined(__i386__)    
    	if (!strcmp(module_name, libc_path)) {    
    		ret_addr += 2;    
    	}    
    #endif    
    	return ret_addr;    
    }    
    
    int find_pid_of(const char *process_name)    
    {    
    	int id;    
    	pid_t pid = -1;    
    	DIR* dir;    
    	FILE *fp;    
    	char filename[32];    
    	char cmdline[256];    
    
    	struct dirent * entry;    
    
    	if (process_name == NULL)    
    		return -1;    
    
    	dir = opendir("/proc");    
    	if (dir == NULL)    
    		return -1;    
    
    	while((entry = readdir(dir)) != NULL) {    
    		id = atoi(entry->d_name);    
    		if (id != 0) {    
    			sprintf(filename, "/proc/%d/cmdline", id);    
    			fp = fopen(filename, "r");    
    			if (fp) {    
    				fgets(cmdline, sizeof(cmdline), fp);    
    				fclose(fp);    
    
    				if (strcmp(process_name, cmdline) == 0) {    
    				    /* process found */    
    				    pid = id;    
    				    break;    
    				}    
    			}    
    		}    
    	}    
    
    	closedir(dir);    
    	return pid;    
    }    
    
    long ptrace_retval(struct pt_regs * regs)    
    {    
    #if defined(__arm__)    
    	return regs->ARM_r0;    
    #elif defined(__i386__)    
    	return regs->eax;    
    #else    
    #error "Not supported"    
    #endif    
    }    
    
    long ptrace_ip(struct pt_regs * regs)    
    {    
    #if defined(__arm__)    
    	return regs->ARM_pc;    
    #elif defined(__i386__)    
    	return regs->eip;    
    #else    
    #error "Not supported"    
    #endif    
    }    
    
    int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)     
    {    
    	DEBUG_PRINT("[+] Calling %s in target process.
    ", func_name);    
    	if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -1)    
    		return -1;    
    
    	if (ptrace_getregs(target_pid, regs) == -1)    
    		return -1;    
    	DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x 
    ",     
    		func_name, ptrace_retval(regs), ptrace_ip(regs));    
    	return 0;    
    }    
    
    int inject_remote_process(pid_t target_pid, const char *library_path, 
    	const char *function_name, const char *param, size_t param_size)    
    {    
    	int ret = -1;    
    	void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;    
    	void *local_handle, *remote_handle, *dlhandle;    
    	uint8_t *map_base = 0;    
    	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
    ", target_pid);    
    
    	if (ptrace_attach(target_pid) == -1)    
    		goto exit;    
    
    	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, libc_path, (void *)mmap);    
    	DEBUG_PRINT("[+] Remote mmap address: %x
    ", mmap_addr);    
    
    	/* 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    
    
    	if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)    
    		goto exit;    
    
    	map_base = ptrace_retval(&regs);    
    
    	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 );    
    	dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror );    
    
    	DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x
    ",    
    	dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);    
    
    	printf("library path = %s
    ", library_path);    
    	ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);    
    
    	parameters[0] = map_base;       
    	parameters[1] = RTLD_NOW| RTLD_GLOBAL;     
    
    	if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)    
    		goto exit;    
    
    	void * sohandle = ptrace_retval(&regs);    
    
    	#define FUNCTION_NAME_ADDR_OFFSET       0x100    
    	ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);    
    	parameters[0] = sohandle;       
    	parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;     
    
    	if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, &regs) == -1)    
    		goto exit;    
    
    	void * hook_entry_addr = ptrace_retval(&regs);    
    	DEBUG_PRINT("hook_entry_addr = %p
    ", hook_entry_addr);    
    
    	#define FUNCTION_PARAM_ADDR_OFFSET      0x200    
    	ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);    
    	parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;      
    	
    	if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, &regs) == -1)    
    		goto exit;        
    
    	
    	printf("Press enter to dlclose and detach
    ");    
    	getchar();    
    	parameters[0] = sohandle;       
    	
    	if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, &regs) == -1)    
    		goto exit;  
    	/* restore */    
    	ptrace_setregs(target_pid, &original_regs); 
    	ptrace_detach(target_pid);    
    	ret = 0;    
    
    exit:    
    	return ret;    
    }    
    
    int main(int argc, char** argv) {    
    	pid_t target_pid;
    	if(argc!=2) {
    		printf("invalid prarams");
    	}
    	target_pid = find_pid_of(argv[1]);    
    	if (-1 == target_pid) {  
    		printf("Can't find the process
    ");  
    		return -1;  
    	}  
       
    	inject_remote_process(target_pid, "/system/lib/libhookhelper.so", "hook_entry",  "I'm parameter!", strlen("I'm parameter!"));    
    	return 0;  
    }  

    Android.mk文件

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_SRC_FILES := inject.c
    LOCAL_SHARED_LIBRARIES := libcutils libutils libdl
    LOCAL_MODULE := inject
    
    include $(BUILD_EXECUTABLE)

    3.2 libhookhelper代码

    hookhelper代码例如以下:

    #include <unistd.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <android/log.h>  
    #include <elf.h>  
    #include <fcntl.h>  
    #include <sys/mman.h>
    #include <linker.h>
    #include <dlfcn.h>
    
    #define LOG_TAG "hookhelper"  
    #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)    
    
    extern "C" {
    //external function declaration
    extern void print();
    
    void newPrint()  
    {  
        LOGD("new Print
    ");
        print();
    }  
    
    #define LIBSF_PATH      "/system/bin/cqlmain"
    #define FUNCTIONNAME    "print"
    
    void *g_OriginalFunc = (void*)print;
    void *g_NewFunc = (void*)newPrint;
    
    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", 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, 16 );  
      
                    if (addr == 0x8000)  
                        addr = 0;  
      
                    break;  
                }  
            }  
      
            fclose(fp) ;  
        }  
      
        return (void *)addr;  
    } 
    
    void replaceFunc(void *handle,const char *name, void* pNewFun, void** pOldFun)  
    {  
    
        if(!handle)  
            return;  
    
        soinfo *si = (soinfo*)handle;     
        Elf32_Sym *symtab = si->symtab;    
        const char *strtab = si->strtab;    
        Elf32_Rel *rel = si->plt_rel;  
        unsigned count = si->plt_rel_count;   
        unsigned idx;   
    
        bool fit = 0;
    
        for(idx=0; idx<count; idx++)   
        {    
            unsigned int type = ELF32_R_TYPE(rel->r_info);    
            unsigned int sym = ELF32_R_SYM(rel->r_info);    
            unsigned int reloc = (unsigned)(rel->r_offset + si->base);    
            char *sym_name = (char *)(strtab + symtab[sym].st_name);   
    
            if(strcmp(sym_name, name)==0)   
            {   
                uint32_t page_size = getpagesize();  
                uint32_t entry_page_start = reloc& (~(page_size - 1));  
                mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
    
                *pOldFun = (void *)*((unsigned int*)reloc);
                *((unsigned int*)reloc)= (unsigned int)pNewFun;
                LOGD("find %s function at address: %p
    ",name,(void*)*pOldFun);
                fit = 1;
                break;
            }   
            rel++;    
        }  
    
        if(fit) {
            LOGD("not find :%s in plt_rel
    ",FUNCTIONNAME);
        }
    } 
    
    void replaceFun2() {   
        void * base_addr = get_module_base(getpid(), LIBSF_PATH);
        
        LOGD("%s address = %p
    ", LIBSF_PATH,base_addr);    
      
        int fd;    
        fd = open(LIBSF_PATH, O_RDONLY);    
        if (-1 == fd) {    
            LOGD("error
    ");    
            return;    
        }    
      
        Elf32_Ehdr ehdr;    
        read(fd, &ehdr, sizeof(Elf32_Ehdr));    
      
        unsigned long shdr_addr = ehdr.e_shoff;      
        int shnum = ehdr.e_shnum;      
        int shent_size = ehdr.e_shentsize;      
        unsigned long stridx = ehdr.e_shstrndx;      
      
        Elf32_Shdr shdr;    
        lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);      
        read(fd, &shdr, shent_size);      
      
        char * string_table = (char *)malloc(shdr.sh_size);      
        lseek(fd, shdr.sh_offset, SEEK_SET);      
        read(fd, string_table, shdr.sh_size);    
        lseek(fd, shdr_addr, SEEK_SET);      
      
        int i;      
        uint32_t out_addr = 0;    
        uint32_t out_size = 0;    
        uint32_t got_item = 0;  
        int32_t got_found = 0;    
    
        for (i = 0; i < shnum; i++) {      
            read(fd, &shdr, shent_size);      
            if (shdr.sh_type == SHT_PROGBITS) {    
    
                int name_idx = shdr.sh_name;            
                if (strcmp(&(string_table[name_idx]), ".got.plt") == 0
                    || strcmp(&(string_table[name_idx]), ".got") == 0) {
                    
                    out_addr = (uint32_t)(base_addr + shdr.sh_addr);      
                    out_size = shdr.sh_size;    
                      
                    for (i = 0; i < out_size; i += 4) {      
                        got_item = *(uint32_t *)(out_addr + i);
                        if (got_item  == (uint32_t)g_OriginalFunc) {      
                            LOGD("found %s in got
    ",FUNCTIONNAME); 
                            
                            got_found = 1;  
      
                            uint32_t page_size = getpagesize();  
                            uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));  
                            mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
                            
                            *(uint32_t *)(out_addr + i) = (uint32_t)g_NewFunc;
                            //break;      
                        } else if (got_item == (uint32_t)g_NewFunc) {      
                            LOGD("Already hooked
    ");
                            got_found = 1;
                            break;      
                        } 
                    }
                    
                    if (got_found)
                        break;   
                }     
            }      
        }      
    
        free(string_table);      
        close(fd);
    }
    
    
    int hook()                                                  
    {                                                               
        #if 1                                              
        replaceFunc( dlopen( LIBSF_PATH, RTLD_GLOBAL ),             
                FUNCTIONNAME, g_NewFunc, (void**)&g_OriginalFunc);  
        #else                                                       
        replaceFun2();                                              
        #endif                                                      
        return 0;                                                   
    }                                                               
    
    
    
    int hook_entry(char * a) {  
        hook();
        return 0;  
    }
    
    }
    

    Android.mk文件例如以下:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_SRC_FILES := hookhelper.cpp
    LOCAL_C_INCLUDES += bionic/linker 
    
    LOCAL_SHARED_LIBRARIES := libcutils libutils libprint libdl
    LOCAL_MODULE    := libhookhelper
    
    include $(BUILD_SHARED_LIBRARY)

    3.3 libtest文件例如以下:

    main.c:

    #include <stdio.h>
    //#include "print.h"
    int main() {
    	extern void print();
    	while(1) {
    		print();
    		sleep(2);
    	}
    	return 0;
    }
    

    print.c文件例如以下:

    #include<cutils/log.h>
    #undef LOG_TAG
    #define LOG_TAG "test"  
    #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)    
    
    void print() {
    	LOGD("my print :%p
    ",(void*)print);
    }
    

    Android.mk文件例如以下:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_SRC_FILES := print.c
    LOCAL_SHARED_LIBRARIES := libcutils libutils liblog
    LOCAL_MODULE    := libprint
    
    include $(BUILD_SHARED_LIBRARY)
    
    include $(CLEAR_VARS)
    
    LOCAL_SRC_FILES := main.c
    LOCAL_SHARED_LIBRARIES := libprint
    LOCAL_MODULE := cqlmain
    
    include $(BUILD_EXECUTABLE)

    4. 參考文档

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

    http://www.xfocus.net/articles/200208/438.html

    http://blog.csdn.net/myarrow/article/details/9630377


  • 相关阅读:
    数据库 concat 与 ||
    mysql时间戳详解及运用
    mysql数据库事务的操作与理解
    数据分析实战——03丨Python基础语法:开始你的Python之旅
    数据分析实战——02丨学习数据挖掘的最佳路径是什么?
    数据分析实战——01丨数据分析全景图及修炼指南
    数据分析实战——开篇词 | 你为什么需要数据分析能力?
    从0开始学大数据学习笔记——37.如何对数据进行分类和预测?
    坚毅(GRIT)阅读笔记
    Make over monday – 每周动手实践的Tableau社区网站
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5377843.html
Copyright © 2011-2022 走看看