对于Android for arm上的so注入(inject)和挂钩(hook),网上已有牛人给出了代码inject。由于实现中的ptrace函数是依赖于平台的,所以不经改动只能用于arm平台。本文将之扩展了一下,使它能够通用于Android的x86和arm平台。Arm平台部分基本重用了inject中的代码,其中因为汇编不好移植且容易出错,所以把shellcode.s用ptrace_call替换掉了,另外保留了mmap,用来传字符串参数,当然也可以通过栈来传,但栈里和其它东西混一起,一弄不好就会隔儿了,所以还是保险点好。最后注意设备要root。

还有就是要在Linux中配置一下NDK编译环境呀~~(这个可以google了~~)

首先创建目录及文件:

文件夹:jni

文件:
    inject.c
    Android.mk
    Application.mk

(说明:由于NDK编译的条件限制,所以我们需要创建的jni文件夹,当然这个文件的名字必须是jni)

注入的核心源代码:

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\n");
return -1;
} int stat = 0;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -1) {
printf("error\n");
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\n");
return -1;
} int stat = 0;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -1) {
printf("error\n");
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]\n", 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.\n", 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 \n",
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\n", 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\n", 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\n",
dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); printf("library path = %s\n", 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\n", 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\n");
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;
target_pid = find_pid_of("/system/bin/surfaceflinger");
if (-1 == target_pid) {
printf("Can't find the process\n");
return -1;
}
//target_pid = find_pid_of("/data/test");
inject_remote_process(target_pid, "/data/libhello.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!"));
return 0;
}

注意上面的/system/bin/surfaceflinger进程我随手写的,你的设备上不一定有。没有的话挑其它的也行,前提是ps命令里能找到。

Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := inject
LOCAL_SRC_FILES := inject.c
#shellcode.s
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
#LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)

Application.mk:

APP_ABI := x86 armeabi-v7a  

运行nkd-build编译成生x86和arm平台下的可执行文件:

再来生成要注入的so,创建目录及文件:
jni
    suf.c
    Android.mk

Application.mk

suf.c:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <elf.h>
#include <fcntl.h> #define LOG_TAG "DEBUG"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) int hook_entry(char * a){
LOGD("Hook success, pid = %d\n", getpid());
LOGD("Hello %s\n", a);
return 0;
}

Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
#LOCAL_ARM_MODE := arm
LOCAL_MODULE := suf
LOCAL_SRC_FILES := suf.c
include $(BUILD_SHARED_LIBRARY)

Application.mk:

APP_ABI := x86 armeabi-v7a  

运行nkd-build编译成生x86和arm平台下的so:

然后就可以跑起来试试了,连接root过的Android设备或者打开模拟器。将inject和libhello.so拷入设备,设执行权限,执行:
先看看被注入进程(surfaceflinger)的mmap,可以看到我们的so已经被加载了,紧接着的那一块就是我们mmap出来的:

从logcat中也可以看到so注入成功,并且以被注入进程的身份执行了so中的代码.

将编译好的文件拷贝到设备的data目录(这里不一定要拷贝到data目录下面,这个可以看自己的需要了)下面:

adb push inject /data/

adb push libsuf.so /data/

然后修改一下文件的权限

chmod 777 inject

然后运行inject,同时我们可以查询一下/system/bin/surfaceflinger进程信息,可以看到,我们将libsuf.so注入到了此进程中.

简单的注入成功,现在我们再来做一个实验,就是应用这套机制来截获surfaceflinger中的eglSwapBuffers调用,然后用我们自己的函数来替换掉原来的eglSwapBuffers调用。关于截系统中的函数调用网上有例子,这里依葫芦画瓢。首先将suf.c改下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/mman.h> #define LOG_TAG "DEBUG"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf) = -1; EGLBoolean new_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
{
LOGD("New eglSwapBuffers\n");
if (old_eglSwapBuffers == -1)
LOGD("error\n");
return old_eglSwapBuffers(dpy, surface);
} 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;
} #define LIBSF_PATH "/system/lib/libsurfaceflinger.so"
int hook_eglSwapBuffers()
{
old_eglSwapBuffers = eglSwapBuffers;
LOGD("Orig eglSwapBuffers = %p\n", old_eglSwapBuffers);
void * base_addr = get_module_base(getpid(), LIBSF_PATH);
LOGD("libsurfaceflinger.so address = %p\n", base_addr); int fd;
fd = open(LIBSF_PATH, O_RDONLY);
if (-1 == fd) {
LOGD("error\n");
return -1;
} 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 = base_addr + shdr.sh_addr;
out_size = shdr.sh_size;
LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size); for (i = 0; i < out_size; i += 4) {
got_item = *(uint32_t *)(out_addr + i);
if (got_item == old_eglSwapBuffers) {
LOGD("Found eglSwapBuffers in got\n");
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) = new_eglSwapBuffers; break;
} else if (got_item == new_eglSwapBuffers) {
LOGD("Already hooked\n");
break;
}
}
if (got_found)
break;
}
}
} free(string_table);
close(fd);
} int hook_entry(char * a){
LOGD("Hook success\n");
LOGD("Start hooking\n");
hook_eglSwapBuffers();
return 0;
}

其实这种查找方法有点简单粗暴,要是正式应用的话可以在动态符号表中查找这个符号的got地址。
接着Android.mk改为:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog -lEGL
#LOCAL_ARM_MODE := arm
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)

Application.mk :

APP_ABI := x86 armeabi-v7a
APP_PLATFORM := android-14

运行ndk-build编译
和上面一样运行,查看logcat,我们可以看到surfaceflinger中调用eglSwapBuffers的地址已被替换成我们的版本:
D/INJECT  ( 2231): [+] Injecting process: 1728  
D/INJECT  ( 2231): [+] get_remote_addr: local[b7e4f000], remote[b7e73000]  
D/INJECT  ( 2231): [+] Remote mmap address: b7e9fe32  
D/INJECT  ( 2231): [+] Calling mmap in target process.  
D/INJECT  ( 2231): [+] Target process returned from mmap, return value=b4f1c000, pc=0   
D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]  
D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]  
D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]  
D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]  
D/INJECT  ( 2231): [+] Get imports: dlopen: b7f84f50, dlsym: b7f84fd0, dlclose: b7f84de0, dlerror: b7f84dc0  
D/INJECT  ( 2231): [+] Calling dlopen in target process.  
D/INJECT  ( 2231): [+] Target process returned from dlopen, return value=b7f995ec, pc=0   
D/INJECT  ( 2231): [+] Calling dlsym in target process.  
D/INJECT  ( 2231): [+] Target process returned from dlsym, return value=b4f17e10, pc=0   
D/INJECT  ( 2231): hook_entry_addr = 0xb4f17e10  
D/DEBUG   ( 1728): Hook success  
D/DEBUG   ( 1728): Start hooking  
D/DEBUG   ( 1728): Orig eglSwapBuffers = 0xb7d4a9c0  
D/DEBUG   ( 1728): libsurfaceflinger.so address = 0xb7f22000  
D/DEBUG   ( 1728): out_addr = b7f7aff4, out_size = 624  
D/DEBUG   ( 1728): Found eglSwapBuffers in got  
...  
D/DEBUG   ( 1728): New eglSwapBuffers  
D/DEBUG   ( 1728): New eglSwapBuffers  
D/DEBUG   ( 1728): New eglSwapBuffers  

...  

Demo下载地址:http://download.csdn.net/detail/jiangwei0910410003/7926427

这篇文章就简单介绍了如何将对surfaceflinger进程进行注入和拦截的操作。这个还是很有用的,后续我们还会对system_process进程进行注入和拦截,那时候还会参考这篇文章。。

Android中实现对/system/bin/surfaceflinger进程进行拦截和注入的更多相关文章

  1. [转]Android中Xposed框架篇—利用Xposed框架实现拦截系统方法

    一.前言 关于Xposed框架相信大家应该不陌生了,他是Android中Hook技术的一个著名的框架,还有一个框架是CydiaSubstrate,但是这个框架是收费的,而且个人觉得不怎么好用,而Xpo ...

  2. Android中获取系统内存信息以及进程信息-----ActivityManager的使用(一)

    本节内容主要是讲解ActivityManager的使用,通过ActivityManager我们可以获得系统里正在运行的activities,包括 进程(Process)等.应用程序/包.服务(Serv ...

  3. [转载] Android中Xposed框架篇---利用Xposed框架实现拦截系统方法

    本文转载自: http://www.wjdiankong.cn/android%E4%B8%ADxposed%E6%A1%86%E6%9E%B6%E7%AF%87-%E5%88%A9%E7%94%A8 ...

  4. Android中使用"running services"查看service进程内存

    从Android 2.0开始,在Settings中加入了一个新的activity("Running Services" activity),它用于显示当前运行的每个Services ...

  5. android中finish和system.exit方法退出的区别

    finish只是将此activity推向后台,并没有释放资源. 而system.exit则是杀死进程,会释放资源

  6. android中SELINUX规则分析和语法简介【转】

    本文转载自:https://blog.csdn.net/LoongEmbedded/article/details/62430039 1. SELINUX是可以理解为一种Android上面的安全机制, ...

  7. android中SELINUX规则分析和语法简介

    1. SELINUX是可以理解为一种android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统我们可以通过配置SELINUX的相关policy,来定制自己的手机的 ...

  8. Android中如何查看内存

    文章参照自:http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-a ...

  9. Android中如何查看内存(上)

    文章参照自:http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-a ...

随机推荐

  1. Python执行和拷贝

    import paramiko from scp import SCPClient class LinuxSSHSCP(object): def __init__(self, ip, username ...

  2. 【leetcode】835. Image Overlap

    题目如下: 解题思路:抛开移动的过程只看移动完成的结果,记图片左上角为顶点,正方形边长为board,要使得两个图片要有重叠,那么一定其中一张图片的顶点和另外一张图片的某一个点重合.假设图片A的顶点A( ...

  3. shell cut从一个文件中提取列

    cut 语法 cut -d 分隔符 -f 列索引 file.txt #将文件file.txt以分隔符.进行分割,并取出第2列.cut -d '.' -f 3- file.txt #将文件file.tx ...

  4. 升级ceph

    参考文档 https://blog.51cto.com/jerrymin/2140258 https://www.virtualtothecore.com/en/upgrade-ceph-cluste ...

  5. UVa 699 The Falling Leaves (树水题)

    Each year, fall in the North Central region is accompanied by the brilliant colors of the leaves on ...

  6. RzPageControl(pagecontrol)

    实现多标签的动态添加,切换,关闭 使用RzPageControl来实现多标签页使用菜单来打开标签页,通过标签页的caption来判断将标签页是否已经被打开过了.1.创建标签页,并判断是否是已经打开过的 ...

  7. 探索Redis设计与实现15:Redis分布式锁进化史

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  8. 【C#、阿里云、Tomcat、XP系统】c#下使用.NET4.0中HttpWebRequest访问Tomcat中HTTPS项目时,在XP系统中超时

    情景: 1.使用Java开发的Web项目,部署在服务器Tomcat中 2.项目使用HTTPS,使用阿里云的PFX证书 阿里云推荐Tomcat配置如下 <Connector port=" ...

  9. 用orm操作sql数据库的优缺点

    一,sql注入问题 二,代码和sql写死在了一起,导致解耦差 三,sql开发人员水平不一,导致sql性能问题 四,开发效率差 使用orm的优点: 一,实现了代码与sql数据的解耦合 二,不需要写原生s ...

  10. tomcat 高并发

    转自 http://blog.csdn.net/feng27156/article/details/19420695 一.容器简化了程序员自身的多线程编程. 各种Web容器,如Tomcat,Resio ...