简单代码注入

1、代码注入目的

  • 了解密码学代码/APP签名原理及重签技术后,能够对其余的应用进行重签、调试,这并非最终目的,咱们要作的是在别人的应用中添加本身的代码,并让APP执行咱们的代码,这样咱们才能理解如何作到恶意代码注入,并清楚知道如何去防御。
  • APP 运行时执行的文件
    • 1.系统的框架,系统库的Macho可执行文件;
      • 非越狱手机没法修改系统库的方法和实现;
      • 越狱手机能够修改(暂时作不到,以观后效);
    • 2.应用包内对应的应用的Macho可执行文件;
      • 能够直接修改Macho文件的内容(暂时不会,须要学习完汇编后直接修改其二进制文件)
    • 3.应用中添加/依赖的framework,dylib等库编译产生的Macho文件;
      • 添加一个本身的framework/dylib库,相对简单,本篇文章将着手于此,简单介绍如何经过添加framework/dylib库,打到注入代码的目的,同时也会介绍相关知识和工具;

2、代码注入原理和实现

  • dyld(the dynamic link editor)是苹果的动态连接器,是苹果操做系统一个重要组成部分,在系统读取程序MachO文件的Header段信息,作好程序准备工做以后,交由dyld负责加载动态库和静态库以及其余工做(后面章节会详细介绍)。linux

  • 动态库:连接时不复制,在程序启动后用动态加载,而后再决议符号,因此理论上动态库只用存在一份,好多个程序均可以动态连接到这个动态库上面,达到了节省内存(不是磁盘是内存中只有一份动态库),还有另一个好处,因为动态库并不绑定到可执行程序上,因此咱们想升级这个动态库就很容易,windows和linux上面通常插件和模块机制都是这样实现的。git

  • 静态库:连接时会被完整的复制到可执行文件中,因此若是两个程序都用了某个静态库,那么每一个二进制可执行文件里面其实都含有这份静态库的代码。github

  • 通常修改原始的程序,是利用代码注入的方式,注入代码就会选择利用FrameWork或者Dylib等三方库的方式注入;windows

  • 一、FrameWork 注入:bash

    • 1.重签后,经过Xcode新建Framwork,将库安装进入APP包,可是程序运行时不会调用执行咱们FrameWork库内的代码;
    • 2.将本身的FrameWork添加到MachO文件中的loadCommands中,须要用工具yololib,将本身写的FrameWork编译后产生的MachO文件写入APP的MachO文件的loadCommands中,经过yololib注入Framwork库路径;
      • 命令:$yololib(空格)MachO文件路径(空格)库路径
      • 全部的Framwork加载都是由DYLD加载进入内存被执行的
      • 注入成功的库路径会写入到MachO文件的LC_LOAD_DYLIB字段中
  • 二、Dylib注入框架

    • 经过Xcode新建Dylib库(注意:Dylib属于MacOS因此须要修改属性)工具

      • 1.修改Base SDK 为iOS;
      • 2.签名信息修改成iOS Developer
    • 添加Target依赖,让Xcode将自定义Dylib文件打包进入APP包。post

      • 1.项目target的Build Phases 添加Copy Files 依赖库,依赖库中依赖刚新建的dylib库
    • 利用yololib进行注入。学习

3、利用 RunTime 进行代码注入,交换原来的实现

  • 一、Method Swizzle

    • 利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法。
    • 正向开发时,通常写在相应类的分类中,主要API:
1.动态添加方法
///添加方法
class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
复制代码
2.交换方法
///获取实例方法
class_getInstanceMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>)
///获取类方法
class_getClassMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>)
///交换方法
method_exchangeImplementations(<#Method _Nonnull m1#>, <#Method _Nonnull m2#>)
复制代码
3.替换方法实现
/// 替换方法实现
class_replaceMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
复制代码
4.设置/获取方法实现
/// 设置方法实现
method_setImplementation(<#Method _Nonnull m#>, <#IMP _Nonnull imp#>)
/// 获取方法实现
method_getImplementation(<#Method _Nonnull m#>)
复制代码
  • 二、Hook的内容

    • 1.重签后运行到手机上的应用,断点调试,找到相应的类名和方法名;
    • 2.利用class-dump将MachO文件内的全部类的描述拷贝出来,即全部.h头文件;
      • 命令:class-dump -H 文件名 -o 输出文件的路径;eg:$class-dump -H WeChat -o ./headers/
    • 3.在LLDB中经过 valueForKey: 获取相应UI控件;
  • 三、多种Hook方式

    • 1.method_exchangeImplementations 方法交换的方式不行
      • oc方法默认参数self是方法的调用者,_cmd方法编号;
      • 没法获取当前类,就没法写成分类;
      • 写在frameWork中的代码,交换时原类或其对象没法调用当前方法;
+ (void)load{
    ///一、方法交换 找不到方法报错
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    Method newMethod = class_getInstanceMethod(self, @selector(nasy_loginAction:));
    method_exchangeImplementations(oldMethod, newMethod);
}
/// 一、方法交换
-(void)nasy_loginAction:(id)btn {
    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    [self nasy_loginAction:nil];
}
复制代码
    • 2.class_addMethod方式:
      • 利用AddMethod方式,让原始方法能够被调用,不至于由于找不到SEL而崩溃
///二、添加方法
+ (void)load{
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    BOOL isAdd = class_addMethod(objc_getClass("JDNewLoginViewController"), @selector(nasy_loginAction:), new_loginAction, "v@:");

    if (isAdd) {
        Method newMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(nasy_loginAction:));
        method_exchangeImplementations(oldMethod, newMethod);
    }

}
void new_loginAction(id self , SEL _cmd){

    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    [self performSelector:@selector(nasy_loginAction:)  withObject:nil];
}
复制代码
    • 3.class_replaceMethod方式:
      • 利用class_replaceMethod,直接给原始的方法替换IMP
/// 三、交换方法实现
+ (void)load{
    oldIMP = class_getMethodImplementation(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    class_replaceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:), new_loginAction, "v@:");
}
IMP ( * oldIMP)(id self,SEL _cmd);
void new_loginAction(id self , SEL _cmd){

    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    oldIMP(self,_cmd);
}

复制代码
    • 4.method_setImplementation方式:
      • 利用method_setImplementation,直接从新赋值原始的新的IMP
/// 四、set  get IMP
+ (void)load{
   oldIMP = class_getMethodImplementation(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    method_setImplementation(oldMethod, new_loginAction);
}
IMP ( * oldIMP)(id self,SEL _cmd);

void new_loginAction(id self , SEL _cmd){

   NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
   oldIMP(self,_cmd);
}

复制代码
相关文章
相关标签/搜索