├── 机审.xmind ├── h5马甲包.md ├── 逆向必备知识.md ├── monkeyDev功能.md ├── 逆向block分析.md ├── 静态分析.md ├── 代码混淆.md ├── frida-ios-dump集成的坑.md ├── cy ├── MS.cy └── myMd.cy ├── Theos开发.md ├── CaptainHook用法.md ├── lldb学习.md └── Cycript用法.md /机审.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaceman/myRe/HEAD/机审.xmind -------------------------------------------------------------------------------- /h5马甲包.md: -------------------------------------------------------------------------------- 1 | ### h5马甲包上线规划 2 | 3 | #### 前言: 由于前阶段提马甲包经常被拒,苹果一系列不规律行为的结果,发现主要原因并不是代码混淆,或者素材原因造成的。 4 | 5 | #### 先总结一下审查上架的流程:机审->分析报告->人审->上架。 6 | 7 | 下图完整描述整个流程: 8 | 9 | ![image-20190405213106222](https://ws1.sinaimg.cn/large/006tNc79gy1g1s2yvydu6j311a0mgmza.jpg) 10 | 11 | #### -------------------------------------------------------------------------------- /逆向必备知识.md: -------------------------------------------------------------------------------- 1 | 逆向必备知识 2 | 3 | + [ssh](https://zh.wikipedia.org/wiki/Secure_Shell):网络协议来实现身份验证。iOS8,9需要在cydia安装openssh。用法:mac电脑 与苹果手机需要在同一wifi下,打开终端:输入ssh root@192.168.1.101(手机IP地址,默认密码为alpine) 4 | + 文件目录: 5 | + 文件权限:所有者,组,其他人。r w x分别为读写执行权限。例如chmod 755 testFile设置testFile 所有者为rwx 组其他人为读rw权限 6 | 7 | + 逆向工具:dumpdecrypted、class-dump、Charles、Reveal、Cycript、LLDB、MonkeyDey等 8 | + 应用构建过程: -------------------------------------------------------------------------------- /monkeyDev功能.md: -------------------------------------------------------------------------------- 1 | ## monkeyDev功能 2 | 3 | + # Logos Tweak 4 | 5 | + # CaptainHook Tweak 6 | 7 | + # Command-line Tool 8 | 9 | + `Config` 这个是cycript的一些脚本下载以及methodtrace配置代码。 10 | 11 | + `LLDBTools` 这个是用于LLDB调试的代码,比如`po pviews()`。 12 | 13 | + `AntiAntiDebug` 这个里面是反反调试的代码 14 | 15 | + `fishhook` 这个是自动集成的fishhook模块 16 | 17 | + Reveal 18 | 19 | + [Cycript](http://www.cycript.org/) 20 | 21 | + ## 动态库调试 22 | 23 | + ## class-dump 24 | 25 | + ## restore-symbol 26 | 27 | + ## 方法跟踪日志 28 | 29 | + ## 增加自己的库 30 | 31 | + ## 集成网络cy脚本 32 | 33 | + ## 集成Frida 34 | 35 | + ## 增加资源 36 | 37 | + ## 更改名字和bundleid 38 | 39 | + ## 生成IPA 40 | 41 | + ## 重签名 42 | 43 | + # CocoaPods -------------------------------------------------------------------------------- /逆向block分析.md: -------------------------------------------------------------------------------- 1 | # 逆向block分析 2 | 3 | ### block的结构 4 | 5 | ```objc 6 | struct __block_impl{ 7 | /** 8 | block在内存中也是类NSObject的结构体, 9 | 结构体开始位置是一个isa指针 10 | */ 11 | 12 | Class isa; 13 | /** 这两个变量暂时不关心 */ 14 | int flags; 15 | int reserved; 16 | /** 17 | 真正的函数指针!! 18 | */ 19 | void (*invoke)(...); 20 | ... 21 | } 22 | ``` 23 | 24 | + 说明下block中的isa指针,根据实际情况会有三种不同的取值,来表示不同类型的block。其中第2种block在运行时才会出现,我们只关注1、3两种,下面就分析这两种isa指针和block符号地址之间的关联 25 | 26 | + _NSConcreteStackBlock:栈上的block,一般block创建时是在栈上分配了一个block结构体的空间,然后对其中的isa等变量赋值 27 | 28 | + _NSConcreteMallocBlock:堆上的block,当block被加入到GCD或者被对象持有时,将栈上的block复制到堆上,此时复制得到的block类型变为了_NSConcreteMallocBlock 29 | 30 | + _NSConcreteGlobalBlock:全局静态的block,当block不依赖于上下文环境,比如不持有block外的变量、只使用block内部的变量的时候,block的内存分配可以在编译期就完成,分配在全局的静态常量区。 31 | -------------------------------------------------------------------------------- /静态分析.md: -------------------------------------------------------------------------------- 1 | # 静态分析 2 | 3 | ### Hopper功能 4 | 5 | + X:查看交叉引用 6 | + G:跳转地址 7 | + Modify - Assemble Instruction 8 | 9 | ### IDA常见功能 10 | 11 | + fn + F5 :伪c 12 | 13 | + "Option + T":字符串搜索 14 | 15 | + G:跳转地址 16 | 17 | + 可重复注释:按";"键进行注释。非重复性注释:按":"进行注释 18 | 19 | + 单击目标变量,按N键:变量重命名 20 | 21 | + X:查看交叉引用 22 | 23 | + 伪代码会存在一些10进制,我们要装换为16进制才有意义,单击数字按H键。 24 | 25 | + 单击目标方法或变量,按Y:类型定义 26 | 27 | + C:被解释成数据还原成代码 28 | 29 | + D:机器码转为代码 30 | 31 | + Option -General - Disassembly:显示机器码 32 | 33 | + Control+F:方法搜索 34 | 35 | 36 | ### ARM汇编_概念 37 | 38 | + AARch64 39 | 40 | + EL0-EL3:不同的异常级别。EL为无权限级别,编写的应用一般都运行在EL0 41 | + R0-R30:31个通用寄存器,每个寄存器都有以下两种访问方式 42 | + 64位:X0-X30,其中X30用于调用程序的link register,保存程序调用完的返回地址 43 | + 32位:W0-W30 44 | + 45 | 46 | + SP:堆栈寄存器,x31访问 47 | + PC:保存当前地址的64位的程序计算器 48 | + LR:指向返回地址,对应为x30 49 | + FP:指向栈贞底部,x29看, 50 | + V0-V32:为32个SIMD&FP寄存器,主要用于浮点数运算 51 | + PSTATE:不是寄存器,进程状态的抽象 52 | 53 | ### 栈帧调用过程 54 | 55 | -------------------------------------------------------------------------------- /代码混淆.md: -------------------------------------------------------------------------------- 1 | ##代码混淆 2 | 3 | 4 | 5 | ###llvm 6 | 7 | ####llvm定义以及原理 8 | 9 | + llvm: 10 | 11 | + 定义:The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.(模块可重用的编译链) 12 | + 主要组成:前端(如gcc,clang) pass 后端 13 | + 前端:获取源代码然后变成某种中间表示 14 | + pass:用来将程序中间表示之间的相互变化,一般情况下用来优化代码。 15 | + 后端:生成实际的机器码 16 | 17 | + 大部分编译器:源代码->前端->pass->后端->机器码 18 | 19 | ![script_1482136450962](https://ws4.sinaimg.cn/large/006tKfTcgy1g12lgzu5ytj30d60240sj.jpg) 20 | 21 | + llvm不同的是,对于不同语言都有中间表示: 22 | 23 | ![script_1482136601642](https://ws4.sinaimg.cn/large/006tKfTcgy1g12lkt5ckvj30rz0ddq3c.jpg) 24 | 25 | 当编译器需要支持多种源代码和目标架构时,基于LLVM的架构,设计一门新的语言只需要去实现一个新的前端就行了,支持新的后端架构也只需要实现一个新的后端就行了。其它部分完成可以复用,就不用再重新设计一次了。 26 | 27 | #### 安装llvm 28 | 29 | clang作为前端: 30 | 31 | svn获取: 32 | 33 | ```objc 34 | svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm 35 | cd llvm/tools 36 | svn co http://llvm.org/svn/llvm-project/cfe/trunk clang 37 | cd ../projects 38 | svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt 39 | cd ../tools/clang/tools 40 | svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra 41 | 42 | ``` 43 | 44 | 编译: 45 | 46 | ```go 47 | mkdir build 48 | cmake /path/to/llvm/source 49 | cmake --build . 50 | ``` 51 | 52 | #### 源码到执行文件 53 | 54 | + 预处理 -> 词法分析 -> Token -> 语法分析 -> AST -> 代码生成 -> LLVM IR -> 优化 -> 生成汇编代码 -> Link -> 目标文件 55 | 56 | 57 | 58 | ### clang 59 | 60 | -------------------------------------------------------------------------------- /frida-ios-dump集成的坑.md: -------------------------------------------------------------------------------- 1 | # frida-ios-dump集成的坑 2 | 3 | 4 | 5 | 按照http://www.alonemonkey.com/2018/01/30/frida-ios-dump/ 的文章集成frida-ios-dump发现过程并不是很顺利。 6 | 7 | ### 环境配置 8 | 9 | 首先上面也说了该工具基于frida,所以首先要在手机和mac电脑上面安装frida,安装方式参数官网的文档: 10 | 11 | 越狱手机的安装frida: 12 | 13 | + 打开cydia 14 | + 添加源:`http://build.frida.re` 15 | + 安装 [Frida](https://www.frida.re/docs/ios) 16 | 17 | 18 | 19 | mac电脑 20 | 21 | + pip install frida-tools 22 | + sudo mkdir /opt/dump && cd /opt/dump && sudo git clone https://github.com/AloneMonkey/frida-ios-dum 23 | + open ~/.zrshrc 添加 alias dump.py="/opt/dump/frida-ios-dump/dump.py"这一行 24 | 25 | 26 | 27 | 28 | 29 | 到这里一切都很顺利 30 | 31 | 32 | 33 | frida-ps -U也没问题,但dump.py **却显示 纳闷~ 34 | 35 | ``` 36 | Traceback (most recent call last): 37 | File "/opt/dump/frida-ios-dump/dump.py", line 10, in 38 | import frida 39 | File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/frida/__init__.py", line 24, in 40 | raise ex 41 | ImportError: No module named _frida 42 | ``` 43 | 44 | 45 | 46 | 原来是pip安装到frida到python3 47 | 48 | 而mac默认是python2启动的 49 | 50 | 解决办法 51 | 52 | + which python 查看python启动路径 我的是:/usr/local/bin/python 53 | + which python3 查看python3启动路径 我的是:/usr/local/bin/python3 54 | + unlink /usr/local/bin/python 55 | + ln -s /usr/local/bin/python3 /usr/local/bin/python 56 | 57 | ok,现在可以dump.py **了,但却出现python2语法错误,修复后就可以愉快的dump了。 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /cy/MS.cy: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2015 Jay Freeman (saurik) 3 | */ 4 | 5 | (function(ms) { 6 | 7 | let GetLibraryPath = function() { 8 | let handle = dlopen(NULL, RTLD_NOLOAD); 9 | if (handle == null) 10 | return null; 11 | 12 | try { 13 | let CYListenServer = dlsym(handle, "CYListenServer"); 14 | if (CYListenServer == null) 15 | return null; 16 | 17 | let info = new Dl_info; 18 | if (dladdr(CYListenServer, info) == 0) 19 | return null; 20 | 21 | let path = info->dli_fname; 22 | let slash = path.lastIndexOf('/'); 23 | if (slash == -1) 24 | return null; 25 | 26 | path = path.substr(0, slash); 27 | 28 | GetLibraryPath = function() { 29 | return path; 30 | }; 31 | 32 | return GetLibraryPath(); 33 | } finally { 34 | dlclose(handle); 35 | } 36 | }; 37 | 38 | var libcycript = dlopen(GetLibraryPath() + "/libcycript.dylib", RTLD_NOLOAD); 39 | if (libcycript == null) { 40 | return; 41 | } 42 | 43 | var libsubstrate = dlopen(GetLibraryPath() + "/libsubstrate.dylib", RTLD_GLOBAL | RTLD_LAZY); 44 | if (libsubstrate == null) { 45 | return; 46 | } 47 | 48 | extern "C" void *MSGetImageByName(const char *); 49 | extern "C" void *MSFindSymbol(void *, const char *); 50 | extern "C" void MSHookFunction(void *, void *, void **); 51 | extern "C" void MSHookMessageEx(Class, SEL, void *, void **); 52 | 53 | var slice = Array.prototype.slice; 54 | 55 | ms.HookFunction = function(func, hook, old) { 56 | var type = typeid(func); 57 | 58 | var pointer; 59 | if (old == null || typeof old === "undefined") 60 | pointer = null; 61 | else { 62 | pointer = new (typedef void **); 63 | *old = function() { return type(*pointer).apply(null, arguments); }; 64 | } 65 | 66 | MSHookFunction(func.valueOf(), type(hook), pointer); 67 | }; 68 | 69 | ms.HookMessage = function(isa, sel, imp, old) { 70 | var type = sel.type(isa); 71 | 72 | var pointer; 73 | if (old == null || typeof old === "undefined") 74 | pointer = null; 75 | else { 76 | pointer = new (typedef void **); 77 | *old = function() { return type(*pointer).apply(null, [this, sel].concat(slice.call(arguments))); }; 78 | } 79 | 80 | MSHookMessageEx(isa, sel, type(function(self, sel) { return imp.apply(self, slice.call(arguments, 2)); }), pointer); 81 | }; 82 | 83 | for(var k in ms) { 84 | if(ms.hasOwnProperty(k)) { 85 | var f = ms[k]; 86 | if(typeof f === 'function') { 87 | Cycript.all[k] = f; 88 | } 89 | } 90 | } 91 | 92 | })(exports); 93 | -------------------------------------------------------------------------------- /Theos开发.md: -------------------------------------------------------------------------------- 1 | # Theos开发 2 | 3 | ### 语法: 4 | 5 | + %hook:hook住一个classs,必须以%end结尾。例子: 6 | 7 | ```objective-c 8 | %hook MYView 9 | - (void)buttonAction:(id)sender{ 10 | %orig(@"我的button"); 11 | } 12 | %end 13 | ``` 14 | 15 | + %orig:执行原来的函数实现,此外,还可以利用`%orig`更改原始函数的参数。例子如上: 16 | 17 | + %log:打印 18 | 19 | + %ground:将`%hook`分组,不属于某个自定义group的`%hook`都会被隐式归类到`%group_ungrouped`中。例子: 20 | 21 | ```go 22 | group iOS1Hook 23 | %hook iOS1Class 24 | - (id)iOS1Method { 25 | id result = %orig; 26 | NSLog(@"This class & method only exist in iOS 1."); 27 | return result; 28 | } 29 | - (id)iOS2Method { 30 | id result = %orig; 31 | NSLog(@"This class & method only exist in iOS 1."); 32 | return result; 33 | } 34 | %end 35 | %end // iOS1Hook 36 | 37 | %hook SpringBoard 38 | -(void)powerDown { 39 | %orig; 40 | } 41 | %end //%group_ungrouped 42 | ``` 43 | 44 | + %init:该指令用于初始化某个`%group`,必须在`%hook`或`%ctor`内调用,参数为%group名,默认是group_ungrouped。例子: 45 | 46 | ```go 47 | - (void)applicationDidFinishLaunching:(id)application { 48 | %orig; 49 | %init; // Equals to %init(_ungrouped) 50 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0 && 51 | kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0) 52 | %init(iOS7Hook); 53 | 54 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) 55 | %init(iOS8Hook); 56 | } 57 | %end 58 | ``` 59 | 60 | + %ctor:tweak的constructor,完成初始化工作;如果不显示定义,Theos会自动生成一个`%ctor`,并在其中调用`%init(_ungrouped)`,如果需要默认调用其他%group或者MSHookFunction就要显示调用: 61 | 62 | ```go 63 | #ifndef kCFCoreFoundationVersionNumber_iOS_8_0 64 | #define kCFCoreFoundationVersionNumber_iOS_8_0 1140.10 65 | #endif 66 | %ctor 67 | { 68 | %init; 69 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0 && 70 | kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0) 71 | %init(iOS7Hook); 72 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) 73 | %init(iOS8Hook); 74 | 75 | MSHookFunction((void *)&AudioServicesPlaySystemSound, 76 | (void *)&replaced_AudioServicesPlaySystemSound, 77 | (void **)&original_AudioServicesPlaySystemSound); 78 | } 79 | ``` 80 | 81 | 82 | + %c :生成一个对象。例子: 83 | 84 | ```go 85 | MYView *myView = [[%c(MYView) alloc]init]; 86 | ``` 87 | 88 | + %new:给一个类增加方法。 其中v@:@代表v:void @:调用者 (:):SEL @:参数类型。例子: 89 | 90 | ```go 91 | %new(v@:@) 92 | - (void)printMessage:(NSString )message{ 93 | 94 | }; 95 | 96 | ``` 97 | 98 | + 如果需要用到源代码的属性或者方法,可以自己新建一个类的.h文件,把属性和方法申明到.h文件,就可以愉快的用啦。 99 | 100 | + 其他的参考[wifi](http://iphonedevwiki.net/index.php/Logos) 101 | 102 | ### 安装:[iOS逆向工程之Theos](https://www.cnblogs.com/ludashi/p/5714095.html) 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /cy/myMd.cy: -------------------------------------------------------------------------------- 1 | (function(utils) { 2 | 3 | utils.constants = { 4 | APPID: NSBundle.mainBundle.bundleIdentifier, //id 5 | APPPATH: NSBundle.mainBundle.bundlePath, //资源路径 6 | APPHOME: NSHomeDirectory(), //沙盒 7 | APPDOC: NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0], 8 | APPLIBRARY: NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0], 9 | APPCACHE: NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] 10 | }; 11 | 12 | utils.pviews = function(){ 13 | return UIApp.keyWindow.recursiveDescription().toString(); //打印视图层次 14 | }; 15 | 16 | utils.pvcs = function(){ //打印当前控制器 17 | return UIWindow.keyWindow().rootViewController._printHierarchy().toString(); 18 | }; 19 | 20 | utils.rp = function(target){//打印响应者 nextResponder 21 | var result = "" + target.toString(); 22 | while(target.nextResponder){ 23 | result += "\n" + target.nextResponder.toString(); 24 | target = target.nextResponder; 25 | } 26 | return result; 27 | }; 28 | 29 | utils.pactions = function(target){ //打印actionsForTarget; 30 | var result = ''; 31 | var objs = target.allTargets.allObjects(); 32 | for(var i = 0; i < objs.length; i++){ 33 | var actions = [target actionsForTarget:objs[i] forControlEvent:0]; 34 | result += objs[i] + " " + [actions componentsJoinedByString:@","]; 35 | } 36 | return result; 37 | } 38 | 39 | 40 | utils.loadFramework = function (target) { //加载资源路径 41 | var h="/System/Library/",t="Frameworks/"+target+".framework"; 42 | return [[NSBundle bundleWithPath:h+t]|| 43 | [NSBundle bundleWithPath:h+"Private"+t] load]; 44 | } 45 | 46 | 47 | utils.tryPrintIvars = function tryPrintIvars(a){ //打印属性 或者*实例对象 48 | var x={}; 49 | for(i in *a) 50 | { 51 | try{ x[i] = (*a)[i]; } catch(e){} 52 | } 53 | return x; 54 | } 55 | 56 | 57 | utils.printMethods = function printMethods(className, isa) { //打印方法,第一个传类对象字符串,第二个可不传。 58 | var count = new new Type("I"); 59 | var classObj = (isa != undefined) ? objc_getClass(className)->isa : 60 | objc_getClass(className); 61 | var methods = class_copyMethodList(classObj, count); 62 | var methodsArray = []; 63 | for(var i = 0; i < *count; i++) { 64 | var method = methods[i]; 65 | methodsArray.push({selector:method_getName(method), 66 | implementation:method_getImplementation(method)}); 67 | } 68 | free(methods); 69 | return methodsArray; 70 | } 71 | 72 | 73 | 74 | 75 | for(var k in utils.constants) { //引入时打印对象变量 76 | Cycript.all[k] = utils.constants[k]; 77 | } 78 | 79 | for(var k in utils) {//引入时打印对象方法 80 | if(utils.hasOwnProperty(k)) { 81 | var f = utils[k]; 82 | if(typeof f === 'function') { 83 | Cycript.all[k] = f; 84 | } 85 | } 86 | } 87 | })(exports); 88 | -------------------------------------------------------------------------------- /CaptainHook用法.md: -------------------------------------------------------------------------------- 1 | # CaptainHook用法 2 | 3 | 4 | 5 | ### 基础设置 6 | 7 | + ## #import 就可以用啦,很方便 8 | 9 | 10 | 11 | ### 用法 12 | 13 | + 声明一个类:在你使用CaptainHook之前必须先声明一个类。例子: 14 | 15 | ```objc 16 | #import //导入头文件 17 | CHDeclareClass(NSString); //声明NSString类 18 | CHConstructor { //CHConstructor 表示在二进制被加载后,立即执行CHConstructor代码块的内容 19 | CHLoadLateClass(NSString);//加载NSString 类 20 | } 21 | ``` 22 | 23 | 24 | 25 | ### 方法挂钩: 26 | 27 | + 先使用**CHMethod**声明一个函数,然后再用**CHHook**钩挂。例子: 28 | 29 | ```objective-c 30 | //标准方法挂钩 31 | #import //导入头文件 32 | CHDeclareClass(NSString); //声明类 33 | CHMethod(2, void, NSString, writeToFile, NSString *, path, atomically, BOOL, flag)//声明一个方法来挂钩。参数1:代表两个参数相当于于CHMethod2(),参数2:为返回类型,参数3:为哪个类的方法,参数4:方法名,参数5:参数的类型,参数6:参数类型名字,参数7:第2个参数的方法名,参数7:参数类型,参数8:参数类型的名字 34 | { 35 | NSLog(@"Writing string to %@: %@", path, self); 36 | CHSuper(2, NSString, writeToFile, path, atomically, flag);//调用原来的实现 37 | } 38 | 39 | CHConstructor // 40 | { 41 | CHLoadClass(NSString); 42 | CHHook(2, NSString, writeToFile, atomically);//挂钩方法。 43 | } 44 | 45 | 46 | ``` 47 | 48 | 49 | + CHDeclareMethod挂钩 50 | 51 | ```objective-c 52 | //覆盖,简单挂钩,无法控制挂钩的时机,也可以作为增加方法使用 53 | #import 54 | CHDeclareClass(NSString); 55 | CHDeclareMethod(2, void, NSString, writeToFile, NSString *, path, atomically, BOOL, flag) 56 | { 57 | NSLog(@"Writing string to %@: %@", path, self); 58 | CHSuper(2, NSString, writeToFile, path, atomically, flag); 59 | } 60 | ``` 61 | 62 | 63 | + 运行时注入新类: 64 | 65 | ```objc 66 | //可以使用CHRegisterClass宏在运行时创建新类: 67 | #import 68 | CHDeclareClass(NSObject); 69 | CHDeclareClass(MyNewClass); 70 | CHMethod(0, id, MyNewClass, init) 71 | { 72 | if ((self = CHSuper(0, MyNewClass, init))) { 73 | NSLog(@"Initted MyNewClass"); 74 | } 75 | return self; 76 | } 77 | 78 | CHConstructor 79 | { 80 | CHAutoreleasePoolForScope(); 81 | CHLoadClass(NSObject); 82 | CHRegisterClass(MyNewClass, NSObject) { 83 | CHHook(0, MyNewClass, init); 84 | }//创建新类 85 | [CHAlloc(MyNewClass) autorelease]; 86 | } 87 | ``` 88 | 89 | + 其他 90 | 91 | + CHOptimizedMethod():挂钩实例方法 92 | 93 | + CHOptimizedClassMethod():挂钩类方法 94 | 95 | + CHPropertyRetainNonatomic()增加属性 96 | 97 | ```objective-c 98 | //增加属性 99 | @interface ViewController : NSObject 100 | @property (nonatomic,retain)NSString *newProperty; 101 | @end 102 | CHDeclareClass(ViewController) 103 | CHPropertyRetainNonatomic(ViewCOntroller,NSString*,newProperty,setNewProperty); 104 | 105 | CHConstructor{ 106 | CHLoadLateClass(ViewController); 107 | CHHook0(ViewController,newProperty); 108 | CHHook1(ViewController,setNewProperty); 109 | } 110 | 111 | ``` 112 | 113 | 114 | ### 简单用法总结: 115 | 116 | + 1.导入#import 头文件 117 | + 2.声明使用的类:CHDeclareClass() 118 | + 3.CHConstructor{}来写加载的类(CHLoadLateClass()) 119 | 120 | + 4.CHMethod()、CHOptimizedMethod()、CHOptimizedClassMethod()声明新函数 121 | + 5.CHConstructor{}下挂钩子(CHHook(),) 122 | + 6.CHSuper():调用原方法实现。 -------------------------------------------------------------------------------- /lldb学习.md: -------------------------------------------------------------------------------- 1 | --- 2 | 勿管世间纷闹,勿管处境。静心做人,静心做事。感谢师傅alonemonkey,感谢同行之人的无私奉献。 3 | --- 4 | 5 | # lldb动态调试学习 6 | 7 | 8 | 9 | ### 准备工作(越狱) 10 | 11 | + 越狱真机调试后在/Developer/usr/bin下找到debugserver 12 | 13 | + 复制debugserver到mac上 14 | 15 | + x新建entitlements.plist文件,签名权限:codesign -s - --entiltilements.plist -f debugserver 16 | 17 | ```plist 18 | 19 | ``` 20 | 21 | 22 | 23 | com.apple.springboard.debugapplications 24 | 25 | get-task-allow 26 | 27 | task_for_pid-allow 28 | 29 | run-unsigned-code 30 | 31 | 32 | 33 | ``` 34 | 35 | + 将debugserver复制回手机 /usr/bin/debugserver 36 | + 打开应用 ps -aux找到进程; 37 | + 附加:根据进程名或id:debugserver *:1234 -a Snapchat(731) 38 | + 然后mac终端输入lldb进去lldb界面 39 | + 然后(iproxy 1234 1234)process connect connect://localhost:1234或process connect connect:手机ip地址:1234 进行连接就ok 40 | 41 | 42 | 43 | + 启动:debugserver -x backboard *:1234 /Applications/MobileSMS.app/MobileSMS 44 | 45 | 46 | 47 | ### lldb常用命令 48 | 49 | + image list -o -f: [序列号] + 模块基地址 + 路径(真正加载的地址) 50 | + 这里说明一下: 内存的真实地址 = 模块的基地址 + ida或hopper查看的地址 51 | + b 内存地址: 或 br s -a 内存地址: (符号还原后: b 方法名): 下断点 52 | + "Control + C" 暂停,终端支持。 53 | + c: 继续 54 | + po :打印对象 55 | + x/s \$x1:将寄存器x1以字符串的方式打印出来 56 | + \$x0 - \$x7: 为寄存器调用的参数, 如果是OC对象\$x0为调用对象,$x1为调用方法。 57 | + memory read -force -f \$sp $fp: 栈参数 58 | + bt: 查看堆栈信息 59 | + register read: 读取寄存器的所有值 60 | + register read \$x0:读取某个寄存器的值 61 | + register write \$x4: 修改寄存器的值 62 | + si:跳到当前指令内部 63 | + ni:跳到当前指令 64 | + finish:返回上层调用栈 65 | + thread return:不用执行下面的代码,之前从当前调用栈返回一个值 66 | + br list: 查看当前列表断点 67 | + br del:删除当前的所有断点 68 | + br del 1.1.1:删除指定编码的断点 69 | + br dis 2.1 使断点2.1失效 70 | + br enable 2.1: 使断点2.1生效 71 | + thread info:输出线程当前信息 72 | + b ptrace -c xxx 满足某个条件后才会中断 73 | 74 | + help "命令名" : 查看命令的详细用法 75 | + apropos "相关命令信息": 搜索相关的命令信息 76 | 77 | ### 高级调试技巧 78 | 79 | + 断点添加命令:在打完断点后,可以添加:br com add "断点编号" ,添加断点触发时执行的命令,以DONE结束。 80 | 81 | + Xcode也可以在以下界面设置 82 | 83 | ![image-20181210112150668](https://ws1.sinaimg.cn/large/006tNbRwly1fy1hgwazbxj307h0je3zm.jpg) 84 | 85 | ![image-20181210111851191](https://ws1.sinaimg.cn/large/006tNbRwly1fy1hdud6uzj30dh08f0u5.jpg) 86 | 87 | + 使用Python脚本: 88 | 89 | + [chisel](https://github.com/facebook/chisel):Facebook的开源的LLDB命令工具,支持pviews,pvc等等命令。[wifi](https://github.com/facebook/chisel/wiki) 90 | + [LLDB](https://github.com/DerekSelander/LLDB):Derek Selander开源工具。 91 | + 脚本使用参考官方:http://lldb.llvm.org/python_reference/index.html 92 | 93 | + Xcode的Debug View Hierarchy:找到所在视图或者视图控制器的Address,再结合 chisel 的 methods "Adderss" 命令找到所要的方法Adress,就可以用br s -a Adress对方法下断点了。省略了,pviews、pvc 、presponder、hide、show等命令,是不是简单了许多~ 94 | 95 | ​ ![image-20181210155034059](https://ws3.sinaimg.cn/large/006tNbRwly1fy1p8hwgivj30jh06jaai.jpg) 96 | 97 | ![image-20181210152911162](https://ws3.sinaimg.cn/large/006tNbRwly1fy1om8sr1qj30770agjs4.jpg) 98 | 99 | 100 | 101 | + Xcode 的Debug Memory Graph:查看对象的内存引用信息 102 | 103 | ![image-20181210164004837](https://ws3.sinaimg.cn/large/006tNbRwly1fy1qo09ixqj30jj06kjrr.jpg) 104 | 105 | + 可以查看对象的内存分配对象,需要在Xcode的Edit Scheme Diagnostics Malloc stack 上打勾。 106 | 107 | ![image-20181210165401439](https://ws1.sinaimg.cn/large/006tNbRwly1fy1r2jcu7ij30780c30tp.jpg) 108 | 109 | + 可以得到内存的实例对象 110 | 111 | + 可以得到内存地址所有被引用的地方,黑色线就是被引用的地方啦(理解程序的逻辑就一目了然呀) 112 | 113 | ![image-20181210165243902](https://ws4.sinaimg.cn/large/006tNbRwly1fy1r16a2fjj30jg0bajsi.jpg) 114 | 115 | 116 | 117 | + 像Cycript一样调用函数,但前面得加e , 例如: e [(MYView \*)0x103d05be0 setHidden:YES],输入c继续,然后就会看到MYView被隐藏掉啦。 118 | + 其他: 119 | + 查看当前模块:Debug - Debug Workflow - Shared Libraries选项 120 | + 查看地址的内存信息:Debug - Debug Workflow - View Momery选项 121 | + 动态调试中,要查看某个函数给别的函数调用的位置,可以先hook住这个函数,然后断言。就可以看到奔溃位置的函数调用啦,很方便~ 122 | 123 | 124 | ### lldb使用思路 125 | 126 | + 用Cycript等工具找到方法内存地址,打断点,确认内存地址是否是正确的。 127 | + 找到想要的方法内存地址,也可以使用Cycript等工具。由[chisel](https://github.com/facebook/chisel)提供的脚本支持 128 | + 查看参数传递,或者引用关系,调用堆栈等执行流程。 -------------------------------------------------------------------------------- /Cycript用法.md: -------------------------------------------------------------------------------- 1 | # cycript 用法大全 2 | 3 | ### Cycript的介绍、原理 4 | 5 | + Cycript是由Cydia创始人[Saurik](:https://git.saurik.com/cycript.git)推出的一款脚本语言,Cycript 混合了Objective-C与javascript语法的解释器,这意味着我们能够在一个命令中用Objective-C或者javascript,或者两者兼用。 6 | 7 | + 利用Libffi、JavaScriptCore实现native代码和解释代码的桥接 8 | 9 | 10 | 11 | 12 | ### Cycript使用 13 | 14 | + 根据new Instance根据地址获取对象,和直接使用#号获取对象 15 | 16 | ```javascript 17 | cy# var btn = #0x10102e630 18 | #">" 19 | cy# var bt1 = new Instance(0x10102e630) 20 | #">" 21 | ``` 22 | 23 | 24 | + choose传递一个类,可以在内存中找出属于这个类的对象 25 | 26 | ```javascript 27 | cy# choose(UIButton) 28 | [#">"] 29 | ``` 30 | 31 | 32 | 33 | + 使用NSLog 34 | 35 | ```javascript 36 | NSLog_ = dlsym(RTLD_DEFAULT, "NSLog") 37 | NSLog = function() { 38 | var types = 'v', args = [], count = arguments.length; 39 | for (var i = 0; i != count; ++i) { 40 | types += '@'; 41 | args.push(arguments[i]); 42 | } 43 | new Functor(NSLog_, types).apply(null, args); 44 | } 45 | ``` 46 | 47 | 48 | + 还有许多集成等等集成到一个[文件](https://raw.githubusercontent.com/blaceman/myRe/master/cy/myMd.cy) 49 | 50 | ```javascript 51 | (function(utils) { 52 | 53 | utils.constants = { 54 | APPID: NSBundle.mainBundle.bundleIdentifier, //id 55 | APPPATH: NSBundle.mainBundle.bundlePath, //资源路径 56 | APPHOME: NSHomeDirectory(), //沙盒 57 | APPDOC: NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0], 58 | APPLIBRARY: NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0], 59 | APPCACHE: NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] 60 | }; 61 | 62 | utils.pviews = function(){ 63 | return UIApp.keyWindow.recursiveDescription().toString(); //打印视图层次 64 | }; 65 | 66 | utils.pvcs = function(){ //打印当前控制器 67 | return UIWindow.keyWindow().rootViewController._printHierarchy().toString(); 68 | }; 69 | 70 | utils.rp = function(target){//打印响应者 nextResponder 71 | var result = "" + target.toString(); 72 | while(target.nextResponder){ 73 | result += "\n" + target.nextResponder.toString(); 74 | target = target.nextResponder; 75 | } 76 | return result; 77 | }; 78 | 79 | utils.pactions = function(target){ //打印actionsForTarget; 80 | var result = ''; 81 | var objs = target.allTargets.allObjects(); 82 | for(var i = 0; i < objs.length; i++){ 83 | var actions = [target actionsForTarget:objs[i] forControlEvent:0]; 84 | result += objs[i] + " " + [actions componentsJoinedByString:@","]; 85 | } 86 | return result; 87 | } 88 | 89 | 90 | utils.loadFramework = function (target) { //加载资源路径 91 | var h="/System/Library/",t="Frameworks/"+target+".framework"; 92 | return [[NSBundle bundleWithPath:h+t]|| 93 | [NSBundle bundleWithPath:h+"Private"+t] load]; 94 | } 95 | 96 | 97 | utils.tryPrintIvars = function tryPrintIvars(a){ //打印属性 或者*实例对象 98 | var x={}; 99 | for(i in *a) 100 | { 101 | try{ x[i] = (*a)[i]; } catch(e){} 102 | } 103 | return x; 104 | } 105 | 106 | 107 | utils.printMethods = function printMethods(className, isa) { //打印方法,第一个传类对象字符串,第二个可不传。 108 | var count = new new Type("I"); 109 | var classObj = (isa != undefined) ? objc_getClass(className)->isa : 110 | objc_getClass(className); 111 | var methods = class_copyMethodList(classObj, count); 112 | var methodsArray = []; 113 | for(var i = 0; i < *count; i++) { 114 | var method = methods[i]; 115 | methodsArray.push({selector:method_getName(method), 116 | implementation:method_getImplementation(method)}); 117 | } 118 | free(methods); 119 | return methodsArray; 120 | } 121 | 122 | 123 | 124 | 125 | for(var k in utils.constants) { //引入时打印对象变量 126 | Cycript.all[k] = utils.constants[k]; 127 | } 128 | 129 | for(var k in utils) {//引入时打印对象方法 130 | if(utils.hasOwnProperty(k)) { 131 | var f = utils[k]; 132 | if(typeof f === 'function') { 133 | Cycript.all[k] = f; 134 | } 135 | } 136 | } 137 | })(exports); 138 | ``` 139 | 140 | + 越狱手机cycript挂钩进程: 141 | 142 | ```javascript 143 | cycript -p 进程id(ps ax | grep Cycript 查看进程id) 144 | ``` 145 | 146 | + cycript支持加载自己的脚本,把cy文件放进/var/root/下 147 | 148 | + cycript -p 进程id(名) /var/root/utils.cy 149 | + cycript -p 进程id(名) 150 | 151 | + monkeyDev Cycript挂钩,monkeyDev运行成功后会打印挂钩信息。 152 | 153 | + **./cycript -r 192.168.1.105(手机IP):6666(端口)** 154 | 155 | + monkeyDev 网络加载cy文件 156 | 157 | + ![image-20181207174155909](https://ws2.sinaimg.cn/large/006tNbRwly1fxybmh7wpjj30yb0llgsm.jpg) 158 | + LoadAtLaunch:是否默认加载到cycript坏境中,对于monkeyDev的ms、md文件起作用,自己的的网络cy文件没有效果,要自己@import myMD(key名)导入。 159 | + priority:优先级 160 | + url:网络的cy文件 161 | + content:cy脚本 162 | 163 | + cycript逆向思路(本地验证): 164 | 165 | + pviews()查看视图层次 166 | + (#0x10102e630(内存地址)).hidden = YES 确定按钮位置 167 | + pactions(#0x10102e630) 打印targetAction 168 | + 手动调用targetAction查看效果 169 | + .... 170 | + rp(#0x10102e630)打印响应者 nextResponder 171 | + 然后printMethods(@"类名")查找方法名 172 | + 找到可疑的方法,调用,查看是否是想要的效果(要猜) --------------------------------------------------------------------------------