对于Linux/Android环境下的一些反外挂简单思路
外挂技术中, 最常用到的Hooking, 代码注入 等技术
通过ptrace函数读取到目标进程的内存信息, 注入dll, 劫持系统函数
针对这些手段, 有一些比较常见的防御手段
- 模块完整性保护 : 定时扫描进程中使用的so库, 如果出现白名单之外的so库, 那么就说明遭到了代码注入
- 调试标记符号读取 : 读取/proc/pid/status文件, 文件中的TracerPid如果不为0, 说明进程正在被调试
- 函数指令内容检测 : 进程初始化时, 读取一部分系统关键函数的指令, 计算并保存指令的CRC32值, 之后定时扫描这些系统函数, CRC32校验失败说明这些函数的指令遭到了修改
- hook 系统函数 : 进程初始化时, hook一部分系统函数, 修改其内容, 监控这些函数调用
模块完整性保护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| char maps_file[32]; sprintf(maps_file, "/proc/%d/maps", getpid()); FILE* fp = fopen(maps_file, "r"); if (fp == NULL) { perror("fopen"); return 1; } char line[256]; while (fgets(line, sizeof(line), fp) != NULL) { char* path = strstr(line, ".so"); if (path != NULL) { char* end = strstr(path, " "); *end = '\0'; printf("%s\n", path); } } fclose(fp);
|
调试标记符号读取
1 2 3 4 5 6 7 8 9 10
| int pid = getpid(); std::ifstream status_file("/proc/" + std::to_string(pid) + "/status"); std::string line; while (getline(status_file, line)) { if (line.find("TracerPid:") == 0) { int tracer_pid = stoi(line.substr(line.find("\t") + 1)); return tracer_pid != 0; } }
|
函数指令内容检测
1 2 3 4 5 6 7
| void * handle = dlopen(so, RTLD_LAZY); void * addr = dlsym(handle, method); Dl_info dlinfo; dladdr(addr, &dlinfo); size_t size = (unsigned int)((char*)dlinfo.dli_saddr - (char*)dlinfo.dli_fbase); _crc32(dlinfo.dli_fbase, size);
|
hook
github上有很多开源的hook框架, 例如xHook, frida等等, 这里不做详细说明