四 代码注入

nx_001.jpeg

上一篇文章中,在上一篇文章中,我们讲了iOS应用重签名技术,同时我们也把微信的越狱ipa包安装到了我们的手机上了,那么,这一章,我们就来讨论讨论代码注入相关的话题吧。

接下来本文会从以下几点进行阐述:

  • Framework注入
  • yololib手动注入
  • Dylib注入
  • MethodSwizzle
  • 破坏微信注册

准备工作: 本篇文章需要用到以下工具
越狱微信7.0.2 提取码:hyya
MacchOView
yololib
class-dump

1.Framework注入

一般修改原始的程序,是利用代码注入的方式,注入代码就会选择利用FrameWork或者Dylib等三方库的方式注入。

什么是Framework这里就不多加叙述,我参考这个网站,非常详细,看不懂你直接@我。点这里:Framework最强讲解

接下来直接演示如何创建一个Framework,并且介绍跟咱们Hook微信有关的基础原理。

1.1 新建项目,完成重签名过程

根据上一篇文章,我们学会了脚本自动代码重签名,这也是本次要用到的内容,不理解的小伙伴可以移步看看iOS代码重签名相关。

首先,我们要创建一个项目,名称叫做FrameworkInject,并且使用脚本对项目重签名。(这里我们还是签上一篇文章中介绍的微信项目)。

由于上一篇文章我们简单的介绍了下项目的目录结构,所以 这里我们使用Xcode快捷键 command+b 编译一下,可以看到项目的目录下出现了Temp文件夹,依次打开 Temp->Payload->WeChat右键显示包内容,找到 WeChat 的可执行文件,使用 MachOView 将WeChat打开,下一步我们简单分析下 MachO这个可执行文件。

1.2 MachO文件简单分析,注意库加载逻辑

1.将 WeChat 文件使用 MachOView 打开会得到如下的界面:

四 MachO文件预览.png

2.此时我们只需要关注 Load Commands中的内容就可以了,通过观察我们会发现,Load Commands下面有许多 LC_LOAD_DYLIB(**)的东西,这里就是这个MachO文件执行时需要加载的库的地址了(这里对MachO文件不做过的的分析,后面会出文章重新分析),可以看到有系统库也有WeChat自己定义的库,如图:TXLiteXX这个库就是腾讯自己的库。
四 MachO文件预览Load Commands.png

注意:微信用的第三方库有的是在app包里面,有的是在iOS系统里,当程序加载的时候回去 LC_LOAD_DYLIB 这里面查找库的路径,只有在这里的库才会被加载,否则不加载。

1.3 注入自己的三方库。

1.在当前项目中新建framework,打开项目,选择Targets,选择下面的添加按钮。
四 Framework注入1.png

2.选择iOS下面的Framework。
四 Framework注入2.png

3.添加自己的Framework。
四 Framework注入3.png

注意:
在我们用XCode新建CZHook的时候,其实XCode帮我们做了一部操作:创建CZHook时候,同时将CZHook链接到我们的项目中(这是后期的XCode新增功能,早年的XCode这一步是需要我们自己做的)
如图:

四 Framework注入4.png

4.common+b Build一下,会发现在已经Build出来的文件中的Frameworks下已经有CZHook了,已经已经表明CZHook被Copy我们的ipa文件了。如图:
四 Framework注入5.png

但是CZHook在ipa文件中,并不代表着CZHook就可以被我们的可执行文件所执行,因为CZHook并没有没导报入我们的可执行文件,只有在这个可行执行文件的某一个地方做好标记,告知可执行文件,在适当的时候需要加载外部的CZHook,才能够正常运行。
而这个地方所说的可执行文件就是MachO文件,我们可以利用工具MachOView来查看MachO中到底有什么内容。结果肯定是找不到的,所以,下一步我们来将CZHook文件标记到我们的 MachO文件中。

5.这里我们就需要用到终端命令行工具 yololib

将下载下来的yololib.zip解压后得到的yololib放在‎⁨目录/usr⁩/local⁩/bin⁩下,这样我们在终端中就可以使用yololib命令了

四 Framework注入6.png

以下命令就是将FYHook注入WeChat的命令

// yololib 「MachO路径」 「CZHook相对MachO的路径」
yololib WeChat Frameworks/CZHook.framework/CZHook
1.4 使用yololib手动注入。

这里有个细节需要注意下,我们使用脚本重新签名,每次重新签名的文件都在目录Temp下,所以我们修改Temp下的文件是没有用的。

这里我们要修改APP目录下的ipa才可以。

1.解压APP目录下的*.ipa包,依次进入Payload->WeChat显示包内容。

2.在当前文件夹下执行命令 yololib WeChat Frameworks/CZHook.framework/CZHook。在MachOView中查看,出现下图则说明成功。
四 Framework注入7.png

3.推出到Payload文件目录下执行 zip -ry WeChat.ipa Payload,完成之后将 ipa替换到 APP文件目录下。

4.在 CZHook 中添加文件继承与NSObject,并添加以下代码

+(void)load{
    NSLog(@"来了 。。。。 ");
}

如图:
四 Framework注入8.png

5.运行项目,当项目输出 来了 。。。。这句话的时候说明我们手动注入代码成功。

参考代码01FrameworkInject

2.Dylib注入

接下来我们来讲一下Dylib注入,Dylib也是常见的注入方法,只是Dylib注入不属于iOS注入的方法,而是属于Mac的注入方法,以下是步骤:

1.首先,我们要创建一个项目,名称叫做DylibInject,并且使用脚本对项目重签名。

2.创建新的TARGET,这里选择 macOS 的 Library.
四 Dylib注入1.png
注意:
1. Dylib是给 macOS 使用的库,所以默认不支持iOS使用,我们需要手动设置两个地方。
2. 选择 CZHook -> Build Settings,将Base SDK 改成 iOS。此时我们会发现这个库支持运行在手机上了。
3. 修改CZHook的签名信息,选择 CZHook -> Build Settings,选择 ALL 搜索 sign,会发现 Code Signing Identity是使用的Mac develop证书签名的,我们要把它改成iOS develop证书签名。如下图:
四 Dylib注入3.png

四 Dylib注入2.png

3.此时,先选择CZHook,command+b Build一下,把CZHook库编译出来,然后选择我们的项目工程,再次Build一次,然后查看Products下的*.app,选择show in finder 查看信息。

4.此时查看 libCZHook.dylib 库是有的,但是我们要去看安装包中的Framework中有没有libCZHook.dylib这个库。目前是没有这个库的。

5.原因是我们需要自己把libCZHook.dylib这个库添加到我们的可执行文件中,操作方法如下:

  • 在TARGETS中选择我们的工程项目
  • 选中Build Phases,点击加号选择New Copy Files Phase。添加我们的libCZHook.dylib库


    四 Dylib注入4.png

6.此时 Build一下,此时会发现libCZHook.dylib出现在我们项目可执行文件中了,但是没有在Frameworks这个文件夹中。

7.然后我们去脚本中注入libCZHook.dylib这个库。在脚本最后添加

yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libCZHook.dylib"

8.此时,我们command+r 运行一下,我运行成功,没有问题。

注意:

  • 假如这里报错,在 dyld 加载的时候报错,我们要查看报错的信息,看看是否加载了,或者没有加载成功,这里可能会遇到这个库被加载了,但是没有加载成功(可能是签名信息有误,没有成功)。
  • 原因是我们使用脚本对Frameworks中的库多签名了一次,这里就没有必要使用Xcode再次帮我们拷贝一份新的库了,所以这里我们需要自己手动把这个包放到Framework中去,然后根据上一篇文章的方法把ipa重新打包一下。

参考代码02DylibInject

3.MethodSwizzle 简介

在前面的案例中,我们不停的注入代码,添加自己的代码,添加自己的逻辑,此时我们会用到Objective-C中的MethodSwizzle,这里我们简单介绍一下。

利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法

在OC中,SEL 和 IMP 之间的关系,就好像一本书的“目录”
SEL 是方法编号,就像“标题”一样。IMP是方法实现的真实地址,就像“页码”一样。他们是一一对应的关系。
如图:

四 MethodSwizzle1.png

Runtime提供了交换两个SEL和IMP对应关系的函数.

OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

通过这个函数交换两个SEL和IMP对应关系的技术,我们称之为Method Swizzle(方法欺骗)

四 MethodSwizzle2.png

3.1 Objecive-C 消息案例(简单解析)

在OC中,函数调用我们可以理解成消息转发,就是给某一个对象发送一个消息,如下图:
四 MethodSwizzle3.png

在简单了解一下后,我们来撸一个真实的案例如下:

1.使用NSUrl发送请求。这个请求是没有问题的。
NSURL *url2 = [NSURL URLWithString:@"www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSLog(@"%@",request);

2.模拟NSUrl有中文的情况。这时url不转码的话会为空,此时报错。
NSURL *url2 = [NSURL URLWithString:@"www.baidu.com/中文"];
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSLog(@"%@",request);

3.此时我们怎么处理呢? 因为使用 URLWithString:的地方比较多,找起来很不好处理,这时我们可以用消息转发的方式,我们自己给他添加转码的逻辑。hook 怎个项目中URLWithString的方法。

4.使用分类处理。

分类代码如图:
四 MethodSwizzle4.png
// 代码如下
+(void)load{
    // 获取方法
    Method urlWithStr = class_getClassMethod(self, @selector(URLWithString:));
    
    Method czUrlWithStr = class_getClassMethod(self, @selector(CZUrlWithString:));
    
    // 交换方法
    method_exchangeImplementations(urlWithStr, czUrlWithStr);
}

+(instancetype)CZUrlWithString:(NSString *)str{
    // 调用系统原来的方法
    NSURL *url = [NSURL CZUrlWithString:str];
    // 判断url是否为空,转码
    if (url == nil) {
        str = [str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    }
    url = [NSURL CZUrlWithString:str];
    
    return url;
}

参考代码03MethodSwizzleDemo

4.ViewDebug、LLDB、class-dump分析微信登录页面

4.1.class-dump介绍和简单使用

class-dump,是可以把Objective-C运行时的声明的信息导出来的工具。其实就是可以导出.h文件。用class-dump可以把未经加密的app的头文件导出来。

将class-dump拷贝到Mac的目录/usr⁩/local⁩/bin⁩下,这样我们在终端中就可以使用class-dump命令了。
四 class_dump1.png

1.在左面创建空的文件夹 WeChatHeadrs。并进入到当前的文件夹。

2.进入到 WeChat的可执行文件夹中,运行命令将WeChat所有的头文件导出来。

// class-dump -H 「app的MachO文件」 -o 「输入的目录」
class-dump -H WeChat -o /Users/XX/desktop/WeChatHeaders
四 class_dump2.png

4.2.通过ViewDebug、LLDB来找到微信的登录按钮,并Hook其方法。

4.2.1 尝试Hook微信的注册方法。

任务

1.先将微信项目跑起来,通过ViewDebug找到微信的注册按钮。
2.向微信的注册按钮事件中添加一句话。

操作如下:

1.运行我们的注入项目,参照本篇文章第一部分。
四 ViewDebug调试1.png

2.选中注册按钮,这时会发现注册按钮的Clase Name为FixTitleColorButton这个是微信自定义的按钮。

3.按钮的Target保存了按钮添加在哪个视图,通过底下的po命令输出,我们会发现按钮被添加到了控制器WCAccountLoginControlLogic上,分析Action地址发现按钮执行的函数叫做onFirstViewRegester

4.这时就要用到我们通过class-dump获取到的头文件了,搜索 @interface WCAccountLoginControlLogic文件。找到 onFirstViewRegester。如图:

四 ViewDebug调试2.png

5.接下来,我们就来Hook onFirstViewRegester这个方法了。

Hook代码:
+(void)load{
    NSLog(@"来了 。。。。 ");
    //错误写法,注意 "WCAccountLoginControlLogic" 前面不能加@
//    Method oldMethod = class_getInstanceMethod(objc_getClass(@"WCAccountLoginControlLogic"), @selector(onFirstViewRegester));

    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegester));
    
    Method newMethod = class_getInstanceMethod(self, @selector(CZHookOnFirstViewRegester));

    method_exchangeImplementations(oldMethod, newMethod);
}

-(void)CZHookOnFirstViewRegester{
    NSLog(@"检测到异常,不能注册!!!!!");
}

参考代码04FrameworkInject(动态调试Hook微信注册按钮)

4.2.2 尝试Hook微信的登录账号、密码。

操作步骤类似于上面的4.2.1,结果如下图:
四 ViewDebug调试3.png

接下来我们来分析下登录注册文本框事件。

由以上内容的出,我们现在所在的类中,具体的文本框代码不清楚,这时就需要我们去分析头文件了。

首先,我们来找一下WCAccountMainLoginViewController这个类的头文件,看看有什么收获吧。

1.如图,我们找到了类似账号、密码登录的控件,但是此时并不敢确定,还是要用ViewDug去调试一下,发现文本框的类型并不是WCAccountTextFieldItem类型

四 ViewDebug调试4.png

2.我们接着去找WCAccountTextFieldItem的头问津啊查看情况,会发现WCAccountTextFieldItem里面的信息更少了,但是我们发现这个类继承于WCBaseTextFieldItem,我们去看看吧。

四 ViewDebug调试5.png

3.在 WCBaseTextFieldItem中我们发现了WCUITextField类型的变量m_textField,此时和我们要找的控件类型一致了,然而,还是让我们去验证一下吧 。。

四 ViewDebug调试6.png

4.我们通过KVC的方式从WCAccountMainLoginViewController获取WCAccountTextFieldItem

po [(WCAccountMainLoginViewController *)0x13606fa00 valueForKey:@"_textFieldUserNameItem"]

5.从WCAccountTextFieldItem中获取WCUITextField

po [(WCAccountTextFieldItem *)0x281c18d20 valueForKey:@"m_textField"]

最终结果如下:
四 ViewDebug调试7.png

6.最终,我们的效果如下:
四 ViewDebug调试8.png
部分代码:
#import "CZHookInject.h"
#import <objc/runtime.h>

@implementation CZHookInject

+(void)load{
    Method oldMyNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    
    Method newMyNext = class_getInstanceMethod(self, @selector(hook_myNext));
    
    method_exchangeImplementations(oldMyNext, newMyNext);
}

-(void)hook_myNext{
    NSString *userNmaeStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
    
    NSLog(@"输入的用户名是:%@",userNmaeStr);
}

7.当获取完用户的密码的时候,我们需要代码走之前的逻辑,这时我们需要调用原来的方法,但是怎么调用呢?是调用hook_myNext方法吗?会发生什么呢?

参考代码05FrameworkInject(动态调试Hook微信账号密码)

4.2.3 解决崩溃

崩溃原因介绍:当我们调用hook_myNext的方式时,WCAccountMainLoginViewController的方法列表中没有hook_myNext方法的实现,所以崩溃了,这里不做多解释。

解决崩溃的方法:

  1. 通过动态添加方法解决
  2. 通过方法替换完成交换
  3. 通过getIMP和setIMP方式解决

添加方法的方式解决
代码如下:

/* 添加方法
 +(void)load{
     Method oldMyNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
     
     // 添加新方法
     BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_myNext), hook_myNext, @"v@:");

     // 获取新添加的方法
     Method newMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_myNext));
     
     method_exchangeImplementations(oldMyNext, newMethod);
 }

 void hook_myNext(id self,SEL _cmd) {

     NSString *userNmaeStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];

     NSLog(@"输入的用户名是:%@",userNmaeStr);

     [self performSelector:hook_myNext];
 }

替换方法

1.定义一个函数指针,保存之前的函数
 2.获取之前的函数
 3.替换之前的函数
 
 缺点:
 如果你的交换没有onNext方法,使用 replaceMethod 会默认帮你添加一个空方法
 
代码:
IMP (*old_onNext)(id self,SEL _cmd);

+(void)load{
    // 1,拿到原始的imp
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    
    // 替换imp
    class_replaceMethod(objc_getClass(@"WCAccountMainLoginViewController"), @selector(onNext), my_next, @"v@:");
}

void my_next(id self,SEL _cmd){
    NSString *userNameStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
    NSLog(@"输入的用户名是:%@",userNameStr);
    
    // 调用之前的函数
    old_onNext(self,_cmd);
}

通过getIMP和setIMP方式解决

setIMP 和 getIMP 方法
/*
 1.拿到旧的方法的IMP
 2.保存旧的IMP
 3.设置IMP
 */
 
IMP (*old_onNext)(id self,SEL _cmd);

+(void)load{
    // 1.拿到原始的method,以下两句代码效果一样,下面的不会报警告
//    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
    
    // 2.保存旧的IMP
    old_onNext = method_getImplementation(onNext);
    
    // 3.设置IMP
    method_setImplementation(onNext, (IMP)my_next);
}

void my_next(id self,SEL _cmd){
    NSString *userNameStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
    NSLog(@"输入的用户名是:%@",userNameStr);
    
    // 调用之前的函数
    old_onNext(self,_cmd);
}

参考代码06FrameworkInject(解决hook的崩溃)

警告!
非越狱状态,玩逆向微信不要真的登录,有被警告甚至封号风险

参考文章:
作者:一缕清风扬万里
原文地址:https://www.jianshu.com/p/31232eef35c5

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350