-
-
[原创] 无需越狱,篡改动态符号表实现hook
-
发表于:
2017-5-14 18:08
18074
-
创造源于需求,这两天酝酿着破解Hopper Disassembler 4.1.5 (HD),弄出了几套hook的方案来怼它。本来是不用这么麻烦的,主要是因为闹了个大乌龙。官网上的4.1.5demo就真的是demo,不能注册成正式版的!搞得我怎么搞都弄不出来,让我心都碎了。破一个demo也是醉了我~~
在这个过程中,用了几个挺牛逼的技术,就拿其中一个跟大家分享。至于hd则是还没破解,找不到正式版的程序,等找到了破解好再分享给大家。
这个技术主要设计到共享库的连接和外部符号重定向原理。动态连接的可执行文件里有重定向指针表,其中分为普通表和lazy表或got表等等,表中是一系列符号的指针。普通表是在程序刚加载时进行符号连接,并填入真实地址。lazy表是在此符号第一次调用时进行连接,lazy表中默认值是指向一段stub,stub根据给定的信息(这个信息帮助linker找到这个符号的相关信息)调用linker的相关功能来进行连接,这之后表中的值就是此符号的真实值,下次调用就直接使用。
程序中还有符号表,符号表里的内容就是描述某个符号的名称、类型、地址、索引、连接方式等信息。通过符号表可以找到特定符号在指针表中的索引。
以上描述中对应的osx系统里的情况就是:符号表是symtab; lazy指针表是__la_symbol_ptr;其中还有个dysymcmd表,用来指出symtab中各个符号的类型。例如dysymcmd中的indirectsymoff段就是描述symtab中那些符号是lazy符号。
以上描述都比较口语化,具体细节可能不对或者不准确,详情请大家提出或者阅读相关文档源码查证。
代码分析:
请看注释
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libproc.h>
#include <unistd.h>
#include <sys/mman.h>
#include <mach-o/dyld.h>
#include <mach-o/arch.h>
#include <mach-o/loader.h>
#include <mach-o/reloc.h>
#include <mach-o/nlist.h>
#include <mach/machine.h>
typedef void* (*type_strdup)(const char * s);
//真实地址
type_strdup origin_strdup;
//代理函数,不能直接调用strdup,会导致死循环
void* proxy_strdup(const char * s) {
printf("Hahahaha, strdup was hooked!\n");
return origin_strdup("Egg");
}
void do_hook() {
uint32_t count = _dyld_image_count();
for (uint32_t i = 0; i < count; i ++) {
//获取文件头,文件头地址就是可执行文件加载地址
const struct mach_header* header = _dyld_get_image_header(i);
const char* image_name = _dyld_get_image_name(i);
// 找到可执行文件
if (strstr(image_name, "demo")) {
printf("[INFO] Found image %s\n", image_name);
struct symtab_command * symcmd = NULL;
struct dysymtab_command *dysymcmd = NULL;
struct segment_command_64 *linkedit = NULL;
struct section_64 *__la_symbol_ptr = NULL;
struct section_64 *__nl_symbol_ptr = NULL;
struct section_64 *__got = NULL;
uint8_t *exe_ptr = (uint8_t*)header;
uint8_t *cmd_ptr =((uint8_t*)header) + sizeof(struct mach_header_64);
// 遍历load command 获得各种数据结构的信息
for (uint32_t j = 0 ; j < header->ncmds; j ++) {
struct load_command *lc = (struct load_command *)cmd_ptr;
if (lc->cmd == LC_SEGMENT_64) {
struct segment_command_64 *sc = (struct segment_command_64*)lc;
// 找到 __DATA 段
if (strcmp(sc->segname, "__DATA") == 0){
uint8_t *sect_ptr = (uint8_t*)sc + sizeof(struct segment_command_64);
for (uint32_t k = 0; k < sc->nsects; k++) {
struct section_64 *sect = (struct section_64 *)sect_ptr;
// 找到 __nl_symbol_ptr 节
if (strcmp(sect->sectname, "__nl_symbol_ptr") == 0){
__la_symbol_ptr = sect;
}
// 找到 __la_symbol_ptr 节
if (strcmp(sect->sectname, "__la_symbol_ptr") == 0){
__nl_symbol_ptr = sect;
}
// 找到 got 节
if (strcmp(sect->sectname, "__got") == 0){
__got = sect;
}
sect_ptr += sizeof(struct section_64);
}
}
//Find __LINKEDIT
if (strcmp(sc->segname, "__LINKEDIT") == 0){
linkedit = (struct segment_command_64*) sc;
}
} // end LC_SEGMENT_64
// 找到符号表
if (lc->cmd == LC_SYMTAB) {
symcmd = (struct symtab_command*)lc;
}
// 找到动态符号表
if (lc->cmd == LC_DYSYMTAB){
dysymcmd = (struct dysymtab_command*)lc;
}
cmd_ptr += lc->cmdsize;
}
//lazy符号指针表
uint64_t *lz_sym_ptr = (uint64_t*)(exe_ptr + __la_symbol_ptr->offset);
uint64_t *nl_sym_ptr = (uint64_t*)(exe_ptr + __nl_symbol_ptr->offset);
// uint64_t *got_ptr = (uint64_t*)(exe_ptr + __got->offset);
//符号表
struct nlist_64 *nlist_ptr = (struct nlist_64*)(exe_ptr + symcmd->symoff);
uint8_t *str_ptr = exe_ptr + symcmd->stroff;
//间接符号索引表
uint32_t *indirect_table = (uint32_t*)(exe_ptr + dysymcmd->indirectsymoff);
uint32_t lz_indirect_idx = __la_symbol_ptr->reserved1;
// 打印所有的符号
// for (uint32_t idx = 0; idx < symcmd->nsyms; idx ++) {
// const char* sym_name = (const char*)str_ptr + nlist_ptr[idx].n_un.n_strx;
// puts(sym_name);
// }
// 遍历间接符号索引表, 找到所有的lazy符号
for (uint32_t idx = lz_indirect_idx; idx < dysymcmd->nindirectsyms; idx ++) {
uint32_t sym_idx = indirect_table[idx];
if (sym_idx == INDIRECT_SYMBOL_LOCAL)
continue;
if(sym_idx == INDIRECT_SYMBOL_ABS)
continue;
// 符号名
const char* sym_name = (const char*)str_ptr + nlist_ptr[sym_idx].n_un.n_strx;
// 找到目标符号
if (strcmp(sym_name, "_strdup") == 0){
printf("[INFO] Hook %s\n", sym_name);
//计算符号索引
uint32_t lz_idx = idx - lz_indirect_idx;
//保存真正地址替换为代理地址
origin_strdup = (type_strdup)lz_sym_ptr[lz_idx];
lz_sym_ptr[lz_idx] = (uint64_t)proxy_strdup;
}
}
printf("[INFO] Hook finish\n");
}
}
}
int main(int argc, char const *argv[])
{
// hook strdup
do_hook();
// hook的strdup 会返回“Egg”而不是"Hello"。
printf("string : %s\n", strdup("Hello"));
return 0;
}
输出
[INFO] Found image /Users/xxxx/symbol_table_hook/demo
[INFO] Hook _strdup
[INFO] Hook finish
Hahahaha, strdup was hooked!
string : Egg
若是写成dylib并且用DYLD_INSERT_LIBRARIES注入的话,就能干很多事情了
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法