首页 技术 正文
技术 2022年11月16日
0 收藏 746 点赞 2,767 浏览 7030 个字

前文介绍了导入表hook,现在来说下导出表的hook。导出表的hook的流程如下。
1、获取动态库基值   

 void* get_module_base(pid_t pid, const char* module_name){
FILE* fp;
long addr = ;
char* pch;
char filename[];
char line[]; // 格式化字符串得到 "/proc/pid/maps"
if(pid < ){
snprintf(filename, sizeof(filename), "/proc/self/maps");
}else{
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
} // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息
fp = fopen(filename, "r");
if(fp != NULL){
// 每次一行,读取文件 /proc/pid/maps中内容
while(fgets(line, sizeof(line), fp)){
// 查找指定的so模块
if(strstr(line, module_name)){
// 分割字符串
pch = strtok(line, "-");
// 字符串转长整形
addr = strtoul(pch, NULL, ); // 特殊内存地址的处理
if(addr == 0x8000){
addr = ;
}
break;
}
}
}
fclose(fp);
return (void*)addr;
}

2、计算program header table实际地址

通过ELF文件头获取到程序表头的偏移地址及表头的个数

     Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
if (memcmp(header->e_ident, "\177ELF", ) != ) {
return ;
}
int phOffset = header->e_phoff;
int phNumber = header->e_phnum;
int phPhyAddr = phOffset + base_addr; int i = ; Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
if (phdr_table == )
{
LOGD("[+] phdr_table address : 0");
return ;
}

3、遍历program header table,找到类型为PT_DYNAMIC的区段(动态链接段),ptype等于2即为dynamic,获取到p_offset

这里需要参照程序表头结构体的相关信息,程序表头结构体结构如下:

struct Elf32_Phdr {
Elf32_Word p_type; // Type of segment
Elf32_Off p_offset; // File offset where segment is located, in bytes
Elf32_Addr p_vaddr; // Virtual address of beginning of segment
Elf32_Addr p_paddr; // Physical address of beginning of segment (OS-specific)
Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero)
Elf32_Word p_memsz; // Num. of bytes in mem image of segment (may be zero)
Elf32_Word p_flags; // Segment flags
Elf32_Word p_align; // Segment alignment constraint
};

因此得到dynamic段对应的地址:

 for (i = ; i < phNumber; i++)
{
if (phdr_table[i].p_type == PT_DYNAMIC)
{
dynamicAddr = phdr_table[i].p_vaddr + base_addr;
dynamicSize = phdr_table[i].p_memsz;
break;
}
}

4、开始遍历dynamic段结构,d_tag为6即为GOT表地址

同样需要参考动态链接段每项的结构体:

typedef struct dynamic {
Elf32_Sword d_tag;
union {
Elf32_Sword d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;

遍历方法为:

 for(i=; i < dynamicSize / ; i++)
{
int val = dynamic_table[i].d_un.d_val;
if (dynamic_table[i].d_tag == )
{
symbolTableAddr = val + base_addr;
break;
}
}

5、遍历GOT表,查找GOT表中标记的目标函数地址,替换为新函数的地址。

我们需要知道符号表的结构:

/* Symbol Table Entry */
typedef struct elf32_sym {
Elf32_Word st_name; /* name - index into string table */
Elf32_Addr st_value; /* symbol value */
Elf32_Word st_size; /* symbol size */
unsigned char st_info; /* type and binding */
unsigned char st_other; /* 0 - no defined meaning */
Elf32_Half st_shndx; /* section header index */
} Elf32_Sym;

然后替换成目标函数的st_value值,即偏移地址

 while()
{
//LOGD("[+] func Addr : %x", symTab[i].st_value);
if(symTab[i].st_value == oldFunc)
{
//st_value 保存的是偏移地址
symTab[i].st_value = newFunc;
LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
break;
}
i++;
}

注意点:

1、我们知道代码段一般都只会设置为可读可执行的,因此需要使用mprotect改变内存页为可读可写可执行;
2、如果执行完这些操作后hook并没有生效,可能是由于缓存的原因,需要使用cacheflush函数对该内存进行操作。
3、获取目标函数的偏移地址,可以通过dlsym得到绝对地址,再减去基址。

我们以hook libvivosgmain.so中的check_signatures函数为例,完整代码如下:

 #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 <dlfcn.h>
#include <sys/mman.h> #define LOG_TAG "INJECT"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) int (*old_check_signatures)();
int new_check_signatures(){
LOGD("[+] New call check_signatures.\n");
if(old_check_signatures == -){
LOGD("error.\n");
}
return old_check_signatures();
} void* get_module_base(pid_t pid, const char* module_name){
FILE* fp;
long addr = ;
char* pch;
char filename[];
char line[]; // 格式化字符串得到 "/proc/pid/maps"
if(pid < ){
snprintf(filename, sizeof(filename), "/proc/self/maps");
}else{
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
} // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息
fp = fopen(filename, "r");
if(fp != NULL){
// 每次一行,读取文件 /proc/pid/maps中内容
while(fgets(line, sizeof(line), fp)){
// 查找指定的so模块
if(strstr(line, module_name)){
// 分割字符串
pch = strtok(line, "-");
// 字符串转长整形
addr = strtoul(pch, NULL, ); // 特殊内存地址的处理
if(addr == 0x8000){
addr = ;
}
break;
}
}
}
fclose(fp);
return (void*)addr;
} #define LIB_PATH "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"
int hook_check_signatures(){ // 获取目标pid进程中"/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"模块的加载地址
void* base_addr = get_module_base(getpid(), LIB_PATH);
LOGD("[+] libvivosgmain.so address = %p \n", base_addr); //计算program header table实际地址
Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
if (memcmp(header->e_ident, "\177ELF", ) != ) {
return ;
} void* handle = dlopen("/data/app-lib/com.bbk.appstore-2/libvivosgmain.so", RTLD_LAZY);
//获取原函数地址
void* funcaddr = dlsym(handle, "check_signatures");
LOGD("[+] libvivosgmain.so check_signatures address = %p \n", (int)funcaddr); int phOffset = header->e_phoff;
int phNumber = header->e_phnum;
int phPhyAddr = phOffset + base_addr;
LOGD("[+] phOffset : %x", phOffset);
LOGD("[+] phNumber : %x", phNumber);
LOGD("[+] phPhyAddr : %x", phPhyAddr);
int i = ; Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
if (phdr_table == )
{
LOGD("[+] phdr_table address : 0");
return ;
} /*
// Program header for ELF32.
struct Elf32_Phdr {
Elf32_Word p_type; // Type of segment
Elf32_Off p_offset; // File offset where segment is located, in bytes
Elf32_Addr p_vaddr; // Virtual address of beginning of segment
Elf32_Addr p_paddr; // Physical address of beginning of segment (OS-specific)
Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero)
Elf32_Word p_memsz; // Num. of bytes in mem image of segment (may be zero)
Elf32_Word p_flags; // Segment flags
Elf32_Word p_align; // Segment alignment constraint
};
*/
//遍历program header table,ptype等于2即为dynamic,获取到p_offset
unsigned long dynamicAddr = ;
unsigned int dynamicSize = ; for (i = ; i < phNumber; i++)
{
if (phdr_table[i].p_type == PT_DYNAMIC)
{
dynamicAddr = phdr_table[i].p_vaddr + base_addr;
dynamicSize = phdr_table[i].p_memsz;
break;
}
}
LOGD("[+] Dynamic Addr : %x", dynamicAddr);
LOGD("[+] Dynamic Size : %x", dynamicSize); /*
typedef struct dynamic {
Elf32_Sword d_tag;
union {
Elf32_Sword d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
*/
//开始遍历dynamic段结构,d_tag为6即为GOT表地址
int symbolTableAddr = ;
Elf32_Dyn* dynamic_table = (Elf32_Dyn*)(dynamicAddr); for(i=; i < dynamicSize / ; i++)
{
int val = dynamic_table[i].d_un.d_val;
if (dynamic_table[i].d_tag == )
{
symbolTableAddr = val + base_addr;
break;
}
}
LOGD("Symbol Table Addr : %x", symbolTableAddr); /*
typedef struct elf32_sym {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
*/
//遍历GOT表,查找GOT表中标记的check_signatures函数地址,替换为new_check_signatures的地址
int giveValuePtr = ;
int fakeValuePtr = ;
int newFunc = (int)new_check_signatures - (int)base_addr;
int oldFunc = (int)funcaddr - (int)base_addr;
i = ;
LOGD("[+] newFunc Addr : %x", newFunc);
LOGD("[+] oldFunc Addr : %x", oldFunc); // 获取当前内存分页的大小
uint32_t page_size = getpagesize();
// 获取内存分页的起始地址(需要内存对齐)
uint32_t mem_page_start = (uint32_t)(((Elf32_Addr)symbolTableAddr)) & (~(page_size - ));
LOGD("[+] mem_page_start = %lx, page size = %lx\n", mem_page_start, page_size);
mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
Elf32_Sym* symTab = (Elf32_Sym*)(symbolTableAddr);
while()
{
//LOGD("[+] func Addr : %x", symTab[i].st_value);
if(symTab[i].st_value == oldFunc)
{
//st_value 保存的是偏移地址
symTab[i].st_value = newFunc;
LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
break;
}
i++;
}
mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_EXEC); return ;
} int hook_entry(char* a){
LOGD("[+] Start hooking.\n");
hook_check_signatures();
return ;
}

我们还是通过执行“Android so注入( inject)和Hook技术学习(一)”文中的inject程序将本程序注入到目标进程进行hook,运行结果如下:

Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

参考资料:

https://blog.csdn.net/u011247544/article/details/78564564

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,104
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,580
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,428
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,835
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,918