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]));
          }
      }
      
      • 效果:
  • 相关阅读:
    Vue学习手记01-安装和项目创建
    [Powershell] FTP Download File
    [PowerShell] Backup Folder and Files Across Network
    SSRS 请求并显示SharePoint人员和组字段
    Beta 冲刺 (2/7)
    Beta 冲刺 (1/7)
    BETA 版冲刺前准备
    事后诸葛亮
    Alpha 答辩总结
    α冲刺 (10/10)
  • 原文地址:https://www.cnblogs.com/CH520/p/14007974.html
Copyright © 2011-2022 走看看