zoukankan      html  css  js  c++  java
  • 使用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
    ",/* 行号 */__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
        ",/* 行号 */__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]));
          }
      }
      
      • 效果:
  • 相关阅读:
    bzoj violet系列 (2708~2725)
    bzoj4692: Beautiful Spacing
    896.Montonic Array
    56. Merge Intervals
    767. Reorganize String
    872. Leaf-Similar Trees
    使用Spring MVC开发RESTful API(续)
    690. Employee Importance
    429. N-ary Tree Level Order Traversal
    使用Spring MVC开发RESTful API
  • 原文地址:https://www.cnblogs.com/CH520/p/14007974.html
Copyright © 2011-2022 走看看