首页
社区
课程
招聘
[原创] 无需越狱,篡改动态符号表实现hook
发表于: 2017-5-14 18:08 18074

[原创] 无需越狱,篡改动态符号表实现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虚拟机自动化脱壳的方法

收藏
免费 5
支持
分享
最新回复 (9)
雪    币: 2575
活跃值: (487)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
2
看起来比较牛逼
2017-5-14 20:08
0
雪    币: 1176
活跃值: (1234)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
3
我有正式版  哈哈      正式版需要demo  +  密钥  联网升级
2017-5-14 21:54
0
雪    币: 259
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习了
2017-5-17 23:12
0
雪    币: 266
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gqm
5
小郑弟弟  又淘气了
2017-5-18 22:29
0
雪    币: 208
活跃值: (469)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
学习了,看这个想到了Android  EFL  GOT表的hook,感觉道理都是相通的
2017-5-21 22:28
0
雪    币: 244
活跃值: (169)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
好东西,学习下
2017-6-15 17:42
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
赞~~
2017-6-20 11:46
0
雪    币: 30
活跃值: (41)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
看一次学习一次
2017-7-17 10:24
0
雪    币: 214
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
2022-2-22 14:42
0
游客
登录 | 注册 方可回帖
返回
//