为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

破解Hopper Disassembler v3.7.8 for mac的艰难历程

2018-04-25 16页 doc 40KB 109阅读

用户头像

is_633423

暂无简介

举报
破解Hopper Disassembler v3.7.8 for mac的艰难历程破解Hopper Disassembler v3.7.8 for mac的艰难历程 破解Hopper Disassembler v3.7.8 for mac的艰难历 程 Hopper Disassembler是一个很牛的逆向分析工具,虽然我个人觉得比不上IDA,但是聊胜于无,感觉字符处理得不错。 这个工具在2.x时代是很容西xx的,但是到了3.x翅膀就硬了。 我这里xx的是最新的3.7.8,在这个版本中自我保护手段是很多的,主要是: 1.无法用gdb lldb载入分析,一附加就出错退出。 2、Section偏移量篡改...
破解Hopper Disassembler v3.7.8 for mac的艰难历程
破解Hopper Disassembler v3.7.8 for mac的艰难历程 破解Hopper Disassembler v3.7.8 for mac的艰难历 程 Hopper Disassembler是一个很牛的逆向分析工具,虽然我个人觉得比不上IDA,但是聊胜于无,感觉字符处理得不错。 这个工具在2.x时代是很容西xx的,但是到了3.x翅膀就硬了。 我这里xx的是最新的3.7.8,在这个版本中自我保护手段是很多的,主要是: 1.无法用gdb lldb载入分析,一附加就出错退出。 2、Section偏移量篡改。 3、加密了__TEXT里的代码,字符串,还有objc相关的信息。 这样的结果就是,无法进行动态分析,无法载入静态分析。 以下Hopper Disassembler简称hd 想破解它,首先就要修复其Section。 关于Mach-O的结构,不懂的百度一下吧。还有就是用010 Editor来编辑简直好用到不行 要修复Section,我们先分析以下内存布局,根据其布局来修复 这是otool -l输出的__TEXT Segment的信息 代码: Load command 1 cmd LC_SEGMENT_64 cmdsize 1032 segname __TEXT vmaddr 0x0000000100000000 vmsize 0x00000000003d1000 fileoff 0 filesize 4001792 maxprot 0x00000007 initprot 0x00000007 nsects 12 flags 0x0 主要看vmaddr 和fileoff,vmaddr是这个二进制文件加载到内存中的位置。fileoff是加载到内存里的数据在文件中得偏移量,这里是0.也就是说从头加载。 为什么要看这里呢,这是因为根据分析发现hd主要是对Section的offet进行修改,修改为任意值。Section中得offset和fileoff类似,也是指在文件中得偏移量。静态分析工具需要这个偏移量来定位一些静态信息,如果将其修改,静态分析就无法继续。还有就是修改Section对运行时是没有影响的。这样我们就得知了fileoff和vmaddr。 我们再看一下__TEXT Segment的__text Section,以下是otool -l输出的__text的信息。 代码: Section sectname __text segname __TEXT addr 0x0000000100006fa0 size 0x00000000003022ea offset 28576 align 2^4 (16) reloff 0 nreloc 0 flags 0x80000400 reserved1 0 reserved2 0 addr同样是这个Section在内存中的位置,offset是在文件中的偏移量。可以这么算 offset = fileoff + addr - vmaddr 将offset回填就行了。 按照上面说的方法,逐一修正所有的Section就ok了。还要注意的地方就是各个Segment的大小filesize 和vmsize是否相等。 至此Section的修复就算完成了。 第二步,就是恢复被加密的Section的内容。 因为无法附加调试,vm_read的动态基址获取很麻烦,于是我这里选择指定DYLD_INSERT_LIBRARIES 环境变量的方式注入一个dylib,在dylib的构造方法里启动一个线程,在线程中等待运行时Section的解 密完成。一旦发现解密完成之后就把解密后的内容dump到硬盘上。 代码: /* export DYLD_FORCE_FLAT_NAMESPACE=1 export DYLD_INSERT_LIBRARIES=~/xd.dylib */ #include #include #include #include #include "pthread.h" #include #include #include #include #include #include mach_vm_address_t getBasicAddress(mach_port_t task){ mach_vm_size_t region_size = 0; mach_vm_address_t region = NULL; int ret = 0; /* Get region boundaries */ #if defined(_MAC64) || defined(__LP64__) vm_region_basic_info_data_64_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64; if ((ret = mach_vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task)) != KERN_SUCCESS) { printf("mach_vm_region() message %s!\n",mach_error_string(ret)); return NULL; } #else vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; if ((ret = vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task)) != KERN_SUCCESS) { printf("vm_region() message %s!\n",mach_error_string(ret)); return NULL; } #endif return region; } vm_size_t readRemotoMemory(char *buf,vm_size_t len,mach_port_t task,vm_address_t address) { vm_size_t outSize = 0; int ret = vm_read_overwrite(task,address,len,(vm_address_t)buf,&outSize); if (ret != 0) { printf("vm_read_overwrite() message %s!\n",mach_error_string(ret)); return 0; } return outSize; } //int main(int argc, char const *argv[]) void* handler(void *p) { //int pid = 16057; int pid = getpid(); char buffer[512]; mach_vm_address_t address = 0; mach_port_t task = 0; int waitTime = 15; while(waitTime){ sleep(1); printf("thread waiting! %d\n",waitTime); waitTime --; } int ret = task_for_pid(mach_task_self(),pid,&task); if (ret != 0) { printf("task_for_pid() message %s!\n",mach_error_string(ret)); return NULL; } address = getBasicAddress(task); printf("pid : %d\n",pid); printf("task : %x\n", task); printf("address : %llx\n", address); if (address == 0) { printf("getBasicAddress() faild!\n"); return NULL; } uint32_t writeSize = 0; FILE *fp = fopen("dump.bin","wb"); readRemotoMemory(buffer,512,task,address); printf("%x\n",*(uint*)buffer); while (writeSize <= 0x1F8E){ readRemotoMemory(buffer,512,task,address); //printf("%x\n",*(uint*)buffer); address += 512; writeSize += writeSize; fwrite(buffer,512,1,fp); } return NULL; } void __attribute__((constructor)) init() { int err; pthread_t ntid; err = pthread_create(&ntid, NULL, handler, NULL); if (err != 0) { printf("can't create thread: %s\n", strerror(err)); return ; } } 这是我此次使用的dump的代码。将其编译成dylib,插入DYLD_INSERT_LIBRARIES后启动hd就行了。详细做法请看其代码。 dump出来的文件,其实就是一个解密后的二进制镜像。里面包吨有解密了得Section。其文件结构也是一个mach-o文件。 这里我用010 Editor神器将dump出来的Section逐一复制粘贴到原本里hd里。我做的时候只还原了__TEXT Segment的Section,__Data Segment的没有理会。而且这样子修复后的hd,是可以运行的,只不过显示主界面大约一秒钟之后就闪退了。而且其反调试功能还在工作,还是无法动态调试。 这样就算解密完成了,经过这样的处理,就能顺利地加载到IDA分析了。 最后一步就是破解了 本人比较懒,有快捷的方法我也不啰嗦。因为要完全还原hd的可执行文件太麻烦,于是我这里使用运行时内存补丁来将其破解。 经过ida的分析,找到几处关键点checkRegistrationLicense,和checkRegistrationToken,然后用这里,,的工具生成补丁。 补丁的加载方式还是DYLD_INSERT_LIBRARIES插入dylib,~~太懒了没办法,补丁代码如下 代码: /* export DYLD_FORCE_FLAT_NAMESPACE=1 export DYLD_INSERT_LIBRARIES=~/chd.dylib */ #include #include #include #include #include "pthread.h" #include #include #include #include #include #include #include "libkern/OSCacheControl.h" mach_vm_address_t getBasicAddress(int pid){ mach_vm_size_t region_size = 0; mach_vm_address_t region = 0; mach_port_t task = 0; int ret = 0; ret = task_for_pid(mach_task_self(),pid,&task); if (ret != 0) { printf("task_for_pid() message %s!\n",mach_error_string(ret)); return 0; } /* Get region boundaries */ #if defined(_MAC64) || defined(__LP64__) vm_region_basic_info_data_64_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64; if ((ret = mach_vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task)) != KERN_SUCCESS) { printf("mach_vm_region() message %s!\n",mach_error_string(ret)); return 0; } #else vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; if ((ret = vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task)) != KERN_SUCCESS) { printf("vm_region() message %s!\n",mach_error_string(ret)); return NULL; } #endif return region; } vm_size_t readRemotoMemory(char *buf,vm_size_t len,int pid,vm_address_t address) { vm_size_t outSize = 0; mach_port_t task = 0; int ret = task_for_pid(mach_task_self(),pid,&task); if (ret != 0) { printf("task_for_pid() message %s!\n",mach_error_string(ret)); return 0; } ret = vm_read_overwrite(task,address,len,(vm_address_t)buf,&outSize); if (ret != 0) { printf("vm_read_overwrite() message %s!\n",mach_error_string(ret)); return 0; } return outSize; } // int FakeCode(char *addr, char code) { mach_port_t task; mach_vm_size_t region_size = 0; mach_vm_address_t region = (vm_address_t)addr; /* Get region boundaries */ #if defined(_MAC64) || defined(__LP64__) vm_region_basic_info_data_64_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64; if (mach_vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task) != 0) { return 0; } #else vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; if (vm_region(mach_task_self(), ®ion, ®ion_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task) != 0) { return 0; } #endif /* Change memory protections to rw- */ if (vm_protect(mach_task_self(), region, region_size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY) != KERN_SUCCESS) { //_LineLog(); return 0; } /* Actually perform the write */ *addr = code; /* Flush CPU data cache to save write to RAM */ sys_dcache_flush(addr, sizeof(code)); /* Invalidate instruction cache to make the CPU read patched instructions from RAM */ sys_icache_invalidate(addr, sizeof(code)); /* Change memory protections back to r-x */ vm_protect(mach_task_self(), region, region_size, 0, VM_PROT_EXECUTE | VM_PROT_READ); return 1; } //int main(int argc, char const *argv[]) void* handler(void *p) { //int pid = 16057; int pid = getpid(); char buffer[512]; mach_vm_address_t address = 0; address = getBasicAddress(pid); //printf("Target pid : %d\n",pid); //printf("Base address : %llx\n", address); if (address == 0) { printf("getBasicAddress() faild!\n"); return NULL; } //Demo char *demo = (char*)address + 0x329a9e; demo[0] = ' '; demo[1] = ' '; demo[2] = ' '; demo[3] = ' '; //Demo version char *dv = (char*)address + 0x329E8C; dv[0] = 'F'; dv[1] = 'u'; dv[2] = 'l'; dv[3] = 'l'; //Waiting for decode __text sleep(1); //checkRegistrationLicense: //xor ebx,ebx => mov $1,%bl //xor edi,edi => inc %edi *(uint32_t*)(address + 0xb9b7) = 0xc7ff01b3; //checkRegistrationToken // xor r14d,r14d => inc r14d *(uint8_t*)(address + 0xb974) = 0x41; *(uint8_t*)(address + 0xb974 + 1) = 0xff; *(uint8_t*)(address + 0xb974 + 2) = 0xc6; return NULL; } void __attribute__((constructor)) init() { int err; pthread_t ntid; err = pthread_create(&ntid, NULL, handler, NULL); if (err != 0) { printf("can't create thread: %s\n", strerror(err)); return ; } } 这个补丁是结合具体情况来写的,看代码就知道,是直接用指针来修改内存的。这样子做在这个例子可行,但是在别的情况就可能不行了。为什么呢,因为在正常情况,__TEXT段是只读可执行的,直接修改会报错的。但是我们这里为什么可以直接修改呢,因为我们前面说过,__TEXT的内容是被加密的,运行后将会被解密,解密回填的时候必然要对其进行写操作。于是hd在运行时就自行对这些内存增加了写权限。结果却没有恢复原来的权限,于是就方便了我们,要是我,我也不会恢复原来的权限的,太麻烦,没必要,。 这里仅对内存做了两处补丁,这样子破解可能还不完全,我也没发现哪里不能用,不过能用就是了。没有了注册窗口,没有了调试面板的限制,没有Demo的水印等。 最后打包的时候,将MacOS下的Hopper Disassembler v3重命名为Hopper Disassembler v3_,然后新建一个shell脚本,名字叫Hopper Disassembler v3,脚本代码如下 代码: #!/bin/bash HD_PATH="`dirname "${0}"`" HD_BIN="`dirname "${0}"`"/Hopper\ Disassembler\ v3_ export DYLD_INSERT_LIBRARIES="${HD_PATH}/chd.dylib" "$HD_BIN" 我这里的补丁叫chd.dylib,将其放到MacOS下就OK了。简直完美。 若有不对之处敬请指正。 很多同学说搞不定,在此奉上传送门: 本文出处:
/
本文档为【破解Hopper Disassembler v3&#46;7&#46;8 for mac的艰难历程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索