Apple开发_使用Hook屏蔽NSLog 及 pirntf 的打印

1、场景说明

  • 为何需要hook日志:
    • 控制台输出的日志太多太杂了,导致看不见关键信息.
    • 或者有些输出的日志search不到源码,但是需要知道从哪儿调用的?(比如想知道哪个第三方库调用的)

2、解决办法

  • 2.1 控制台右下角文本过滤

  • 2.2 设置xcode环境变量

    • OS_ACTIVITY_MODE 为 Disable,将会所有log不可见,不论系统log还是自行输出log

    • 效果说明:屏蔽的只是对NSLog及NSLog的封装打印对象。而系统中好多的自动打印是使用了NSLog,使用该方法可以屏蔽系统打印。
    • 代码中操作不规范造成的系统自动打印
  • 2.3 自定义宏代替NSLog宏

// DEBUG  模式下打印日志, 当前行
#ifdef DEBUG
	// 02 输出格式:第868行: 要打印的信息
	#define CHLog(...) fprintf(stderr,"第%d行:%s\n",/* 行号 */__LINE__, /* 动态参数 */[[NSString stringWithFormat:__VA_ARGS__] UTF8String]);
// 发布状态
#else
	#define  CHLog(...)
#endif
  • 2.4 第三方的库里面的打印

    • 还有一些Log可能是在第三方的库里面打印的,所以我们无法采用第三种方式。我们可以采用method_swizzle来进行方法替换,但是NSLog和printf是函数,这时候就需要用到fishhook来进行函数实现替换。
    • ① 原理:
      • 简单说就是修改系统函数地址(自定义函数不可hook),达到函数实现交换的目的
    • ② 替换对象
      • ⓐ 替换NSLog
      // 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
      static void (*sys_nslog)(NSString *format, ...);
      // 定义一个新的函数
      void my_nslog(NSString *format, ...) {
          va_list vl;
          va_start(vl, format);
          NSString *str = [[NSString alloc] initWithFormat:format arguments:vl];
          va_end(vl);
      
          // 调用原始的:此处类似method_swizzle
          //sys_nslog([NSString stringWithFormat:@"hook => %@", str]);
      }
      
      • ⓑ 替换NSLogv,有些极少的情况,可能还需要hook NSLogv()
      // 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
      static void (*orig_nslogv)(NSString *format,va_list args);
      // 定义一个新的函数
      void my_nslogv(NSString *format,va_list args) {
          //orig_nslogv(format,args);
      }
      
      • ⓒ 替换printf
      // C语言原有printf
      static void (*orig_printf)(const char *path, ...);
      // 定义一个新的函数
      void my_printf(const char *format,...) {
          // 什么都不写就是屏蔽打印
          //orig_printf(format);
      }
      
    • ③ 具体用法:
      • 给自己的工具类开个分类,添加以下代码
      • CHTool+Log.m
      #import "CHTool+Log.h"
      
      #include "fishhook.h"
      
      @implementation CHTool (Log)
      
      // 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
      static void (*sys_nslog)(NSString *format, ...);
      // 定义一个新的函数
      void my_nslog(NSString *format, ...) {
          va_list vl;
          va_start(vl, format);
          NSString *str = [[NSString alloc] initWithFormat:format arguments:vl];
          va_end(vl);
      
          // 调用原始的:此处类似method_swizzle
          //sys_nslog([NSString stringWithFormat:@"hook => %@", str]);
      }
      
      // 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
      static void (*orig_nslogv)(NSString *format,va_list args);
      // 定义一个新的函数
      void my_nslogv(NSString *format,va_list args) {
          //orig_nslogv(format,args);
      }
      
      // C语言原有printf
      static void (*orig_printf)(const char *path, ...);
      // 定义一个新的函数
      void my_printf(const char *format,...) {
          // 什么都不写就是屏蔽打印
          //orig_printf(format);
      }
      
      // 关闭以下对象控制的日志打印
      + (void)stop_AllLog {
          // 启动后执行下面这句话即可实现NSLog() hook
          struct rebinding nslog_rebinding = {"NSLog", my_nslog, (void*)&sys_nslog};
          // 1代表后面有一个函数实现需要重新绑定
          rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);
          
          // 启动后执行下面这句话即可实现NSLogv() hook
          struct rebinding nslogv_rebinding = {"NSLogv",my_nslogv,(void*)&orig_nslogv};
          // 1代表后面有一个函数实现需要重新绑定
          rebind_symbols((struct rebinding[1]){nslogv_rebinding}, 1);
          
          // 启动后执行下面这句话即可实现printf() hook
          struct rebinding printf_rebinding = {"printf", my_printf, (void*)&orig_printf};
          // 1代表后面有一个函数实现需要重新绑定
          rebind_symbols((struct rebinding[1]){printf_rebinding}, 1);
      }
      
      @end
      
      • CHTool+Log.h
      #import "CHTool.h"
      
      NS_ASSUME_NONNULL_BEGIN
      
      @interface CHTool (Log)
      
      // DEBUG  模式下打印日志, 当前行
      #ifdef DEBUG
      	// 02 输出格式:第868行: 要打印的信息
      	#define CHLog(...) fprintf(stderr,"第%d行:%s\n",/* 行号 */__LINE__, /* 动态参数 */[[NSString stringWithFormat:__VA_ARGS__] UTF8String]);
      // 发布状态
      #else
      	#define  CHLog(...)
      #endif
      // 关闭所有的日志打印
      + (void)stop_AllLog;
      
      @end
      
      • 据我自己的观察,fprintf 方法第三方用的比较少,所以保留成我自己的打印方法。
    • ④ 应用启动入口调用该方法就可以了
    #import <UIKit/UIKit.h>
    
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            // 关闭所有的日志打印
            [CHTool stop_AllLog];
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    • 效果:
  • 2.5 fishhook源码

posted @ 2020-11-19 22:10  CH520  阅读(628)  评论(0编辑  收藏  举报