一、got hook检测
// 检测原理:检测系统lib库的so文件的指定函数地址与本进程so的导入函数地址是否相等。比如libc库的open函数。这种方式需要先计算好got起始地址,导入表函数位置。
// 代码还未完成,后续补上
二、maps检测
// 检查maps中是否存在inlinehook、gothook框架的so文件
UNEXPORT bool AntiHook::scan_line(char *map) {
char *(path[]) = {
"libarthook_native.so",
"libsandhook.edxp.so",
"libsandhook-native.so",
"libsandhook.so",
"libxposed_art.so",
"libfrida-gadget.so",
"libmemtrack_real.so",
"frida-agent-64.so",
"libva++.so",
"librfbinder-cpp.so",
"frida-agent-32.so",
"libva-native.so",
"libwhale.edxp.so",
"libriru_edxp.so",
"libriru_snet-tweak-riru.so",
"libriru_edxposed.so"
};
for (int i = 0; i < sizeof(path) / sizeof(char*); i++){
if (Str::strstr(map, path[i]) != nullptr){
return true;
}
}
return false;
}
UNEXPORT void AntiHook::read_line_scan(string str){
char* split = (char*)"\n";
string strs = str + split; // 在字符串末尾也加入分隔符,方便截取最后一段
size_t pos = strs.find(split);
while (pos != strs.npos)
{
string temp = strs.substr(0, pos);
LOGI("read_line_scan maps -> %s", temp.c_str());
if (scan_line(const_cast<char *>(temp.c_str()))){
//TODO 找到 hook 框架
LOGI("---------------------------read_line_scan find hook -> %s", temp.c_str());
return;
}
//去掉已分割的字符串,在剩下的字符串中进行分割
strs = strs.substr(pos + 1, strs.size());
pos = strs.find(split);
}
}
UNEXPORT void AntiHook::check_maps() {
Thread::lock_mutex();
Env::thread_share_switch_maps = true;
Thread::unLock_mutex();
string str = Syscall::readFile((char*)"/proc/self/maps");
Thread::lock_mutex();
Env::thread_share_switch_maps = false;
Thread::unLock_mutex();
if (str != "null"){
read_line_scan(str);
}
}
三、inlinehook 框架检测
// 原理:.text段的内容在ELF加载前后没有发生变化,对比内存中的.text段和文件中的.text的crc是否相等
#ifdef __LP64__
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#else
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#endif
UNEXPORT void AntiHook::check_inlinehook(){
#ifdef __arm__
const char *libc_path = "/apex/com.android.runtime/lib/bionic/libc.so"; // 安卓10下的路径
const char *libart_path = "/apex/com.android.runtime/lib/libart.so"; // 安卓10下的路径
#elif defined(__aarch64__)
const char *libc_path = "/apex/com.android.runtime/lib64/bionic/libc.so"; // 安卓10下的路径
const char *libart_path = "/apex/com.android.runtime/lib64/libart.so"; // 安卓10下的路径
#else
#error "Arch unknown, please port me"
#endif
if (compare_text_section_crc(libc_path)){
// TODO 发现 inlinehook 框架
}
if (compare_text_section_crc(libart_path)){
// TODO 发现 inlinehook 框架
}
}
UNEXPORT bool AntiHook::compare_text_section_crc(const char *path) {
FILE *maps;
char buff[256];
char* load_addr;
int size;
int fd = -1, found = 0;
Elf_Ehdr *elf = static_cast<Elf_Ehdr *>(MAP_FAILED);
char* shstr_offset;
char* shoff;
Elf_Shdr *sh;
int crcFile = 0;
int crcMemory = 0;
char* name;
long pageSize;
#define fatal(fmt,args...) do { LOGE(fmt,##args); goto err_exit; } while(0)
maps = fopen("/proc/self/maps", "r");
if(!maps){
fatal("failed to open maps");
}
while(!found && fgets(buff, sizeof(buff), maps)){
if(Str::strstr(buff, const_cast<char *>(path)) != nullptr && Str::strstr(buff,"r--p") != nullptr){
found = 1;
}
}
fclose(maps);
if(!found){
fatal("%s not found in my userspace", path);
}
if(sscanf(buff, "%lx", &load_addr) != 1){
fatal("failed to read load address for %s", path);
}
fd = open(path, O_RDONLY);
if (fd < 0) {
fatal("failed to open %s", path);
}
size = lseek(fd, 0, SEEK_END);
if (size <= 0) {
fatal("lseek() failed for %s", path);
}
elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
fd = -1;
if (elf == MAP_FAILED) {
fatal("elf == MAP_FAILED");
}
shstr_offset = nullptr;
shoff = ((char*) elf) + elf->e_shoff;
for (int i = 0; i < elf->e_shstrndx; i++){
shoff += elf->e_shentsize; //跳转到段字符串表位置
}
sh = (Elf_Shdr *) shoff;
if (sh->sh_type == SHT_STRTAB) { // 段字符串表
shstr_offset = ((char *) elf) + sh->sh_offset;
}
if (shstr_offset != nullptr){
shoff = ((char *) elf) + elf->e_shoff;
for (int k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) {
Elf_Shdr *sh = (Elf_Shdr *) shoff;
switch (sh->sh_type) {
case SHT_PROGBITS:{
name = shstr_offset + sh->sh_name;
if (Str::strstr(name, ".text") != nullptr){ /** 找到 .text 段,对其求crc */
/** so本地文件中 .text 段的crc */
crcFile = CRC::calcCRC(reinterpret_cast<const unsigned char *>(((char *) elf) + sh->sh_offset), sh->sh_size);
LOGI("compare_text_section_crc %s find section -> %s, file crc=%d", path, name, crcFile);
/** so在内存中的 .text 段的crc */
pageSize = sysconf(_SC_PAGESIZE); /** .text段为不可读,需改内存属性 */
if (mprotect((void*)((long)(load_addr + sh->sh_offset) & -pageSize), (sh->sh_size / pageSize + 1) * pageSize, PROT_READ | PROT_WRITE | PROT_EXEC) != 0){
fatal("mprotect failed -> %s", strerror(errno));
}
crcMemory = CRC::calcCRC(reinterpret_cast<const unsigned char *>(((char *) load_addr) + sh->sh_offset), sh->sh_size);
LOGI("compare_text_section_crc %s find section -> %s, memory crc=%d", path, name, crcMemory);
munmap(elf, size);
return crcFile == crcMemory;
}
break;
}
}
}
}
#undef fatal
err_exit:
if(fd >= 0) close(fd);
if(elf != MAP_FAILED) munmap(elf, size);
return false;
}
四、svc hook 检测
// 原理:针对SVC Hook目前就seccomp的bpf最常用,这种就是设定filter过滤器规则,拦截到指定的系统调用号后会产生一个信号量,这种信号量是你设定的,然后就会执行事先使用sigaction注册的函数。我们可以这样检测:注册一个 sigaction 处理逻辑,然后主动使用 sigqueue函数发送信号量,再接收,如果没有接收到说明被seccomp的bpf规则拦截了,SVC已经被Hook了。
五、指定函数二进制代码检测
UNEXPORT bool AntiHook::check_PrettyMethod() {
#ifdef __arm__
string st_PrettyMethod = get_method_code("/apex/com.android.runtime/lib/libart.so", "ArtMethod12PrettyMethod");
if (Str::strstr(const_cast<char *>(st_PrettyMethod.c_str()), "B5 85 B0 04 46 51 48 0D 46 78 44 07 68 38 68 04") == nullptr){
// TODO 发现PrettyMethod二进制代码被修改
LOGI("check_inlinehook PrettyMethod 被 patch");
return true;
}
#elif defined(__aarch64__)
string st_PrettyMethod = get_method_code("/apex/com.android.runtime/lib64/libart.so", "ArtMethod12PrettyMethod");
if (Str::strstr(const_cast<char *>(st_PrettyMethod.c_str()), "FF 43 01 D1 F6 57 02 A9 F4 4F 03 A9 FD 7B 04 A9") == nullptr){
// TODO 发现PrettyMethod二进制代码被修改
LOGI("check_inlinehook PrettyMethod 被 patch");
return true;
}
#else
#error "Arch unknown, please port me"
#endif
return false;
}
UNEXPORT string AntiHook::get_method_code(const char* lib, const char* method, int len){
string st;
void* handle = LoadLibrary::fake_dlopen(lib, RTLD_NOW);
if (handle == nullptr){
LOGE("dlopen failed -> %s", strerror(errno));
return st;
}
void* op = (void*)LoadLibrary::fake_dlsym(handle, method);
if (op == nullptr){
LOGE("dlsym failed -> %s", strerror(errno));
LoadLibrary::fake_dlclose(handle);
return st;
}
long pageSize = sysconf(_SC_PAGESIZE);
void* start = (void*)((long)op & -pageSize);
if (mprotect(start, pageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC) == 0){
LOGI("mprotect success");
}else{
LOGE("mprotect failed -> %s", strerror(errno));
}
char tmp[128] = {0};
sprintf(tmp, "%s %s -> %p\n", lib, method, op);
LOGI("tmp = %s ", tmp);
//st.append(tmp);
unsigned char*code = (unsigned char*)op;
for (int i = 0; i < len; i++){
if (code[i] < 0x10){
sprintf(tmp, "0%X ",code[i]);
}else{
sprintf(tmp, "%X ",code[i]);
}
st.append(tmp);
}
LOGI("get_method_code -> %s", st.c_str());
LoadLibrary::fake_dlclose(handle);
return st;
}
UNEXPORT bool AntiHook::check_open() {
#ifdef __arm__
const char *lib_path = "/apex/com.android.runtime/lib/bionic/libc.so"; // 安卓10的路径
#elif defined(__aarch64__)
const char *lib_path = "/apex/com.android.runtime/lib64/bionic/libc.so"; // 安卓10的路径
#else
#error "Arch unknown, please port me"
#endif
#define CMP_COUNT 8
//文件中的open
struct local_dlfcn_handle *handle = static_cast<local_dlfcn_handle *>(local_dlopen(lib_path));
off_t offset = local_dlsym(handle,"open");
FILE *fp = fopen(lib_path,"rb");
char file_bytes[CMP_COUNT] = {0};
if(fp != nullptr){
fseek(fp,offset,SEEK_SET);
fread(file_bytes,1,CMP_COUNT,fp);
fclose(fp);
}else{
LOGE("fopen failed -> %s", strerror(errno));
}
//内存中的open
void *dl_handle = dlopen(lib_path,RTLD_NOW);
void *sym = dlsym(dl_handle,"open");
long pageSize = sysconf(_SC_PAGESIZE);
void* start = (void*)((long)sym & -pageSize);
if (mprotect(start, pageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC) == 0){
bool is_hook = memcmp(file_bytes,sym,CMP_COUNT) != 0;
local_dlclose(handle);
dlclose(dl_handle);
return is_hook;
}else{
LOGE("mprotect failed -> %s", strerror(errno));
}
local_dlclose(handle);
dlclose(dl_handle);
return false;
}