通过分析Android应用进程的创建启动流程,知道设置应用进程的名称是在(这部分各个版本的系统实现都大同小异,还是看4.4的代码)http://androidxref.com/4.4_r1/xref/frameworks/base/core/java/android/app/ActivityThread.java#handleBindApplication
private void handleBindApplication(AppBindData data) { mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); mProfiler = new Profiler(); mProfiler.profileFile = data.initProfileFile; mProfiler.profileFd = data.initProfileFd; mProfiler.autoStopProfiler = data.initAutoStopProfiler; // send up app name; do this *before* waiting for debugger Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());
所以反射调用即可修改进程名,ddm应该是ddms调试用的,这个不改应该也可以。
try { Method setArgV0 = Process.class.getDeclaredMethod("setArgV0", String.class); setArgV0.setAccessible(true); setArgV0.invoke(null,"com.tencent.mm"); } catch (Throwable e) { e.printStackTrace(); }
结合之前的linux修改进程名,
//字符串包括结尾符号\0不能超过16,而且这个只是改了proc/pid/stat/\status中的字符串 const char* new_name = "mytest"; prctl(PR_SET_NAME, new_name);
或者这样可以改变ps、cmdline的进程名
/* gcc changetitle.c -o changetitle */ #include <unistd.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <stdlib.h> #include <sys/prctl.h> # define MAXLINE 2048 extern char **environ; static char **g_main_Argv = NULL; /* pointer to argument vector */ static char *g_main_LastArgv = NULL; /* end of argv */ void setproctitle_init(int argc, char **argv, char **envp) { int i; for (i = 0; envp[i] != NULL; i++) // calc envp num continue; environ = (char **) malloc(sizeof (char *) * (i + 1)); // malloc envp pointer for (i = 0; envp[i] != NULL; i++) { environ[i] = malloc(sizeof(char) * strlen(envp[i])); strcpy(environ[i], envp[i]); } environ[i] = NULL; g_main_Argv = argv; if (i > 0) g_main_LastArgv = envp[i - 1] + strlen(envp[i - 1]); else g_main_LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); } void setproctitle(const char *fmt, ...) { char *p; int i; char buf[MAXLINE]; extern char **g_main_Argv; extern char *g_main_LastArgv; va_list ap; p = buf; va_start(ap, fmt); vsprintf(p, fmt, ap); va_end(ap); i = strlen(buf); if (i > g_main_LastArgv - g_main_Argv[0] - 2) { i = g_main_LastArgv - g_main_Argv[0] - 2; buf[i] = '\0'; } //修改argv[0] (void) strcpy(g_main_Argv[0], buf); p = &g_main_Argv[0][i]; while (p < g_main_LastArgv) *p++ = '\0'; g_main_Argv[1] = NULL; //调用prctl prctl(PR_SET_NAME,buf); } int main(int argc, char *argv[]) { char argv_buf[MAXLINE] = {0}; // save argv paramters int i; for( i = 1; i < argc; i++) { strcat(argv_buf, argv[i]); strcat(argv_buf, " "); } //修改argv[0]所指向的内存空间的内容 setproctitle_init(argc, argv, environ); //调用prctl修改进程名 setproctitle("%s@%s %s", "littlehann-prog", "ip", argv_buf); for (i = 0; environ[i] != NULL; i++) free(environ[i]); getchar(); return 0; }
对比下Android是如果实现的,http://androidxref.com/4.4_r1/xref/frameworks/native/libs/binder/ProcessState.cpp#305
首先把进程名覆盖了mArgV第一个数组,之后也是调用
prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0);
// Global variables int mArgC; const char* const* mArgV; int mArgLen; void ProcessState::setArgV0(const char* txt) { if (mArgV != NULL) { strncpy((char*)mArgV[0], txt, mArgLen); set_process_name(txt); } }
//http://androidxref.com/4.4_r1/xref/system/core/libcutils/process_name.c#37 void set_process_name(const char* new_name) { #ifdef HAVE_ANDROID_OS char propBuf[PROPERTY_VALUE_MAX]; #endif if (new_name == NULL) { return; } // We never free the old name. Someone else could be using it. int len = strlen(new_name); char* copy = (char*) malloc(len + 1); strcpy(copy, new_name); process_name = (const char*) copy; #if defined(HAVE_PRCTL) if (len < 16) { prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0); } else { prctl(PR_SET_NAME, (unsigned long) new_name + len - 15, 0, 0, 0); } #endif #ifdef HAVE_ANDROID_OS // If we know we are not running in the emulator, then return. if (running_in_emulator == 0) { return; } // If the "running_in_emulator" variable has not been initialized, // then do it now. if (running_in_emulator == -1) { property_get("ro.kernel.qemu", propBuf, ""); if (propBuf[0] == '1') { running_in_emulator = 1; } else { running_in_emulator = 0; return; } } // If the emulator was started with the "-trace file" command line option // then we want to record the process name in the trace even if we are // not currently tracing instructions (so that we will know the process // name when we do start tracing instructions). We do not need to execute // this code if we are just running in the emulator without the "-trace" // command line option, but we don't know that here and this function // isn't called frequently enough to bother optimizing that case. int fd = open(PROCESS_NAME_DEVICE, O_RDWR); if (fd < 0) return; write(fd, process_name, strlen(process_name) + 1); close(fd); #endif }
从名字上猜应该mArgV应该就是main函数接收的argv,我们知道Android应用进程其实都是fork自zygote,而zygote的可执行文件就是app_process
http://androidxref.com/4.4_r1/xref/frameworks/base/cmds/app_process/app_main.cpp#169
int main(int argc, char* const argv[]) { #ifdef __arm__ /* * b/7188322 - Temporarily revert to the compat memory layout * to avoid breaking third party apps. * * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE. * * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466 * changes the kernel mapping from bottom up to top-down. * This breaks some programs which improperly embed * an out of date copy of Android's linker. */ char value[PROPERTY_VALUE_MAX]; property_get("ro.kernel.qemu", value, ""); bool is_qemu = (strcmp(value, "1") == 0); if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) { int current = personality(0xFFFFFFFF); if ((current & ADDR_COMPAT_LAYOUT) == 0) { personality(current | ADDR_COMPAT_LAYOUT); setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1); execv("/system/bin/app_process", argv); return -1; } } unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP"); #endif // These are global variables in ProcessState.cpp mArgC = argc; mArgV = argv; mArgLen = 0; for (int i=0; i<argc; i++) { mArgLen += strlen(argv[i]) + 1; } mArgLen--; AppRuntime runtime; const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm int i = runtime.addVmArguments(argc, argv); // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; const char* parentDir = NULL; const char* niceName = NULL; const char* className = NULL; while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { parentDir = arg; } else if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName = arg + 12; } else { className = arg; break; } } if (niceName && *niceName) { setArgv0(argv0, niceName); set_process_name(niceName); } runtime.mParentDir = parentDir; if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : ""); } else if (className) { // Remainder of args get passed to startup class main() runtime.mClassName = className; runtime.mArgC = argc - i; runtime.mArgV = argv + i; runtime.start("com.android.internal.os.RuntimeInit", application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } }
所以其实Android修改进程名和Linux其实是一样的。
这里就用最简单的方式反射调用setArgV0后,ps查看进程
# ps|grep ent.mm u0_a122 2701 182 1013884 53440 ffffffff 400f773c S com.tencent.mm:push u0_a91 8812 182 914212 42496 ffffffff 400f773c S com.tencent.mm
2701是mm的推送进程,8812为改名后的进程,可见进程名可以骗过了,uid还是出卖了自己。
argv、cmdline欺骗ps
# cat proc/8812/cmdline com.tencent.mm
prctl(PR_SET_NAME, new_name);欺骗stat、status等。
# cat /proc/8812/status Name: com.tencent.mm State: S (sleeping) Tgid: 8812 Pid: 8812 PPid: 182 TracerPid: 0