欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创] 无需越狱,篡改动态符号表实现hook

[原创] 无需越狱,篡改动态符号表实现hook

创造源于需求,这两天酝酿着破解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注入的话,就能干很多事情了

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功