如何动态获取游戏的pid
在读取第三方应用的时候需要使用到pid数据,读取内存,但是pid数据每次app杀死后重启都会变,动态获取pid是必须要的功能,也是基础。
简单获取pid
在Android中其实有api可以获取pid。
# pidof 完整包名
dipper:/ $ su
dipper:/ # pidof com.herocraft.game.free.mig29
25452
这个是在终端上面获取的,在我们写代码之前,都可以先在终端做尝试,然后想办法转成代码。下面就是c++的实现。
/**
* 传递包名
* @param name 目标包名
* @return 返回app的pid
*/
pid_t get_game_pid(char *name) {
FILE *fp;
// 储存拿到的pid
pid_t pid;
// 命令拼接
char cmd[0x100] = "pidof %s";
char exec[128] = {0};
// 把pidof 和包名拼接在一起组成命令
sprintf(exec, cmd, name);
// popen执行命令,等同于终端执行
fp = popen(exec, "r");
// 拿到结构后储存到pid变量中
fscanf(fp, "%d", &pid);
pclose(fp);
return pid;
}
根据我们前面文章配置好的环境一键编译运行:
可以拿到正常app对应的pid了。这种方式可能存在兼容性问题。
获取pid的兼容性方案
在系统的/proc目录中全部进程的pid都创建一个文件夹:
现在杀死app,重新读取一次pid看看他在哪个文件夹中。
dipper:/proc # pidof com.herocraft.game.free.mig29
28910 # 通过pidof拿到了app的pid是28910
dipper:/proc # cd 28910/
dipper:/proc/28910 # ls
attr cpuset map_files oom_score sched_wake_up_idle task
auxv cwd maps oom_score_adj schedstat time_in_state
cgroup environ mem pagemap smaps timerslack_ns
clear_refs exe mountinfo personality smaps_rollup wchan
cmdline fd mounts reclaim stack
comm fdinfo mountstats root stat
concurrent_active_time hang_detection_enabled net sched statm
concurrent_policy_time io ns sched_group_id status
coredump_filter limits oom_adj sched_init_task_load syscall
dipper:/proc/28910 # cat cmdline
com.herocraft.game.free.mig29 # 在pid目录中有一个文件cmdline里面保存了包名
也就是我们遍历/proc/
目录中的所有一级目录的cmdline文件读取他和我们目标的包名判断,如果他们一致,就表示找到了,当前文件所在的目录就是pid了。
上面的规则用代码实现:
int getPID(const char *packageName) {
// 默认pid是-1,如果返回-1就是读取失败
int id = -1;
DIR *dir;
FILE *fp;
char filename[64];
char cmdline[64];
struct dirent *entry;
// 打开proc目录
dir = opendir("/proc");
while ((entry = readdir(dir)) != NULL) {
// 目录名字就是pid,转int
id = atoi(entry->d_name);
if (id != 0) {
// 把目录和cmdline拼接一起读取
sprintf(filename, "/proc/%d/cmdline", id);
fp = fopen(filename, "r");
if (fp) {
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
// 判断传递进来的包名和cmdline中字符串是否一样
if (strcmp(packageName, cmdline) == 0) {
// 如果一致,上面目录就是pid了
return id;
}
}
}
}
closedir(dir);
return -1;
}
这样遍历也能拿到pid
,兼容性高,至于使用那个方案,自己喜欢就行。
下面是完整的代码:
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <dirent.h>
/**
* 传递包名
* @param name 目标包名
* @return 返回app的pid
*/
pid_t get_game_pid(char *name) {
FILE *fp;
// 储存拿到的pid
pid_t pid;
// 命令拼接
char cmd[0x100] = "pidof %s";
char exec[128] = {0};
// 把pidof 和包名拼接在一起组成命令
sprintf(exec, cmd, name);
// popen执行命令,等同于终端执行
fp = popen(exec, "r");
// 拿到结构后储存到pid变量中
fscanf(fp, "%d", &pid);
pclose(fp);
return pid;
}
int getPID(const char *packageName) {
// 默认pid是-1,如果返回-1就是读取失败
int id = -1;
DIR *dir;
FILE *fp;
char filename[64];
char cmdline[64];
struct dirent *entry;
// 打开proc目录
dir = opendir("/proc");
while ((entry = readdir(dir)) != NULL) {
// 目录名字就是pid,转int
id = atoi(entry->d_name);
if (id != 0) {
// 把目录和cmdline拼接一起读取
sprintf(filename, "/proc/%d/cmdline", id);
fp = fopen(filename, "r");
if (fp) {
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
// 判断传递进来的包名和cmdline中字符串是否一样
if (strcmp(packageName, cmdline) == 0) {
// 如果一致,上面目录就是pid了
return id;
}
}
}
}
closedir(dir);
return -1;
}
int main() {
printf("读取游戏的pid\n");
pid_t game_pid = get_game_pid("com.herocraft.game.free.mig29");
printf("pid: %d\n", game_pid);
int pid = getPID("com.herocraft.game.free.mig29");
printf("pid_proc: %d\n", pid);
return 0;
}
总结
1、动态读取app的pid。
2、这些代码都是模板,写一次就足够了,可以自己收集起来统一存放到一个头文件中。
3、下一篇是读取app的内存中的数据。