搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
算法编织者
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
Eclipse 连接SQL_eclipse中.sql
2
Openstack报错:Connection to the hypervisor is broken on host: compute 的解决办法_hypervisorunavailable: connection to the hyperviso
3
安卓逆向——Frida安装和使用_frida12.11.6
4
【Python】函数Д_python def函数
5
springboot整合Java High Level REST Client调动elasticsearch进行增删改查_spring elasticsearch rest high level增删改查
6
Codeforces 919 (div2) a-c题题解
7
利用Docker Compose快速部署FastGPT知识库问答_fastgpt本地部署
8
C语言 printf() 详解 非常详细_c printf
9
Java学习 - 使用类与对象创建一个自己的登录界面_java swing登录注册界面包和类设计
10
解决Ubuntu 20.04打开Terminal闪退问题
当前位置:
article
> 正文
破解Hopper Disassembler v3.7.8 for mac的艰难历程_hopper disassembler 安装 “hopper disassembler”意外退出。
作者:算法编织者 | 2024-01-29 18:55:56
赞
踩
hopper disassembler 安装 “hopper disassembler”意外退出。
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 <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include "pthread.h"
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_vm.h>
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,然后用这里(http://malokch.xicp.net/?post=25)的工具生成补丁。
补丁的加载方式还是DYLD_INSERT_LIBRARIES插入dylib,~~太懒了没办法,补丁代码如下
代码:
/*
export DYLD_FORCE_FLAT_NAMESPACE=1
export DYLD_INSERT_LIBRARIES=~/chd.dylib
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include "pthread.h"
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_vm.h>
#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了。简直完美。
若有不对之处敬请指正。
很多同学说搞不定,在此奉上传送门:http://pan.baidu.com/s/1bn94SDx
本文出处:
http://www.szboozou.com/zhuanti/biancheng/2015-04-28/491.html
本文内容由网友自发贡献,转载请注明出处:
【wpsshop博客】
推荐阅读
article
纯
Python
实现
人工智能
_
python
ai
...
很久以前微信流行过一个小游戏:打飞机,这个游戏简单又无聊。在2017年来临之际,我就实现一个超级弱智的
人工智能
(AI),...
赞
踩
article
【
Hadoop
】
HA
高
可用
搭建
保姆级教程(大二学长
的
万字笔记)_
ha
doop
ha
搭建
...
Hadoop
HA
高
可用
搭建
保姆级教程,来自正在学习大数据导论
的
大二学长
的
万字笔记,干得不能再干
的
干货分享!_
ha
doo...
赞
踩
article
C
语言
函数
的
封装
_
c
语言
如何
封装
自己的
函数
...
函数
封装
实现_
c
语言
如何
封装
自己的
函数
c
语言
如何
封装
自己的
函数
1、使用冒泡排序法比较大小 //...
赞
踩
article
python
内置
函数_
python
内置
函数...
python
内置
函数大全_
python
内置
函数
python
内置
函数 ...
赞
踩
article
python
获取
app
信息
的库_
python
爬取 “得到”
App
电子书
信息
...
前言文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。...
赞
踩
article
用
git
把
本地
文件
推送
到
远程
仓库
_
git
提交
到
线上
文件
不全...
要
把
实验的项目交
到
GitHub上,因为用
git
提交
的问题之前一直有点迷糊,今天再整理一下一.先
提交
到
本地
仓库
1.之前的g...
赞
踩
article
数字
图像
处理
(
实践
篇)二十九
OpenCV
-
Python
在
图像
中检测矩形、
正方形
和三角形的
实践
...
数字
图像
处理
(
实践
篇)二十九
OpenCV
-
Python
在
图像
中检测矩形、
正方形
和三角形的
实践
数字
图像
处理
(
实践
篇)二十...
赞
踩
article
如何在
ubuntu
系统下进行SSH远程登录
服务器
和
文件
传输
?_
ubunut
远程
ssh
怎么传
文件
...
本篇博文是关于如何在
ubuntu
服务器
上进行编程和传输
文件
相关操作,学到的只是一些皮毛,还希望读者进行拓展和指正。
服务器
...
赞
踩
article
如何
使用
Docker
本地
搭建
Traefik
服务并实现
公网
访问
管理
界面...
Træfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件,能轻易的部署微服务。它支持多种后端 (
Docker
...
赞
踩
article
映射
同伦
等价
(一般
的
+
空间
偶
的
)
_
同伦
映射
...
在数学中,
同伦
(Homotopy)
的
概念在拓扑上描述了两个对象间
的
“连续变化”。两个拓扑
空间
如果可以通过一系列连续
的
形变...
赞
踩
article
Flink
项目
搭建与
使用
_建立一个
flink
项目
...
Flink
项目
搭建与
使用
前言本文不会介绍
flink
的概念与原理,如果对于
Flink
还不了解,先去看看
flink
的基础知识...
赞
踩
article
关于
Spring
Boot
2
.x升3.x的那些事_
springboot
2
升级
到3...
手头上有个项目,准备从
Spring
Boot
2
.x
升级
到3.x,
升级
后发现编译器报了一堆错误。一般来说大版本
升级
,肯定...
赞
踩
article
Doker
多克创新(
taobao
=
》
Doker
)发福利了,最高8折
优惠
!
!
!
长期有效
!
!
...
凡官方旗舰店产品,每个产品下订单的前10名,6 折
优惠
,10名之后,9.5折
优惠
,付款之前请加官方微信,并将订单截图发送...
赞
踩
article
Github
打不开
怎么办
,教你分
分钟
搞定...
首先打开自己的浏览器 选择 设置->系统->打开您计算机的代理设置(如下图以google为例)将手动设置代理下的”使用代...
赞
踩
article
DRF
之
序列化
组件...
序列化
qs对象,单个对象 做
序列化
给前端反
序列化
数据校验:前端传入数据---> 校验数据是否合法反
序列化
---> 前端...
赞
踩
article
Flink
基于
时间
窗口
定时
输出到
ElasticSearch
中并做到真正不丢
数据
_
flink
sink
...
介绍
Flink
定时
输出到外部存储介质,有两种办法实现,在RichSinkFunction中实现SinkFunction的...
赞
踩
article
selenium
使用,
qq
邮箱
登陆
_
selenium
qq
邮箱
登录...
“”"from
selenium
import webdriverfrom test
_
product.test
_
case...
赞
踩
article
回归预测 |
MATLAB
实现
CNN
(
卷积
神经
网络
)多输入单输出_
matlab
中
卷积
神经
输出层...
回归预测 |
MATLAB
实现
CNN
(
卷积
神经
网络
)多输入单输出目录回归预测 |
MATLAB
实现
CNN
(
卷积
神经
网络
)...
赞
踩
article
[
iOS
逆向 6]
分析
与
调试
_
hopper
disassembler
查看
调用
关系...
获取 AppiTools越狱后,iTools 软件内可以看到手机安装了哪些应用,选中某个应用,点导出即可。注意:.ipa...
赞
踩
article
智能
家电
网
关
开发(分析)_
智能
网
关
开发...
介绍
智能
家庭
网
关
功能 电源管理 统一管理
智能
家电
设备 对设备的启动、设置、定时等操作 电路自动保护(防护设备短路) 故...
赞
踩
相关标签
人工智能
AI
机器学习
深度学习
神经网络
大数据
hadoop
笔记
c语言
排序算法
算法
windows
python
经验分享
python获取app信息的库
git
github
数字图像处理
OpenCV
linux
ubuntu
docker
eureka
容器