既往不恋,纵情向前ios
原文连接git
//Logs an error message to the Apple System Log facility.
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
复制代码
根据苹果的文档介绍,NSLog的做用是输出信息到标准的Error控制台和 苹果的日志系统(ASL,Apple System Log)里面(iOS 10以前)。github
iOS10以后,苹果使用新的统一日志系统(Unified Logging System)来记录日志,全面取代ASL的方式,此种方式,是把日志集中存放在内存和数据库里,并提供单1、高效和高性能的接口去获取系统全部级别的消息传递。objective-c
新的统一日志系统没有ASL那样的接口可让咱们取出所有日志。数据库
NSLog在调试阶段,日志会输出到到Xcode中,而在iOS真机上,它会输出到系统的/var/log/syslog
这个文件中。浏览器
在平常开发中,不少人喜欢使用NSLog来输出调试信息,可是都知道NSLog是比较消耗性能呢,NSLog输出的内容或次数多了以后,甚至会影响App的体验。缓存
因而乎,比较常见的手段是,线上不使用NSLog,DEBUG下才真正使用NSLog。bash
#if DEBUG
#define MYLOG(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, ##VA_ARGS);
#else
#define MYLOG(fmt,...) {}
#endif
复制代码
参考CocoaLumberjack中的DDASLLogCapture实现服务器
+ (void)start {
//...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[self captureAslLogs];
});
}
复制代码
+ (void)captureAslLogs {
//....
}
复制代码
[DDLog log: message:]
方法将日志记录下来。+ (void)aslMessageReceived:(aslmsg)msg {
//...
}
复制代码
说明:以上方法不会影响Xcode控制台的输出,无侵入。app
captureAslLogs
中经过notify_register_dispatch
来注册监听进程间的系统通知;notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
{
//...
});
复制代码
/*
* ASL notifications
* Sent by syslogd to advise clients that new log messages have been
* added to the ASL database.
*/
#define kNotifyASLDBUpdate "com.apple.system.logger.message"
复制代码
#define kNotifyVFSLowDiskSpace "com.apple.system.lowdiskspace"
复制代码
APP运行时,Mach-O文件被dyld(动态加载器)加载进内存
ASLR(地址空间布局随机化)让Mach-O被加载时内存地址随机分配
苹果的PIC位置与代码独立技术,让Mach-O调用系统库函数时,先在Mach-O表中的_DATA段
创建一个指针指向外部库函数,dyld加载MachO时知道外部库函数的调用地址,会动态的把_DATA段的指针指向外部库函数
fishhook可以替换NSLog等库函数,这事是由于Mach-O的符号表里有NSLog等,能够经过符号表找到NSLog字符串。
说明:具体原理参考iOS逆向工程 - fishhook原理
实现代码以下:
//申明一个函数指针用于保存原NSLog的真实函数地址
static void (*orig_nslog)(NSString *format, ...);
//NSLog重定向
void redirect_nslog(NSString *format, ...) {
//能够添加本身的处理,好比输出到本身的持久化存储系统中
//继续执行原来的 NSLog
va_list va;
format = [NSString stringWithFormat:@"[hook success]%@",format];
va_start(va, format);
NSLogv(format, va);
va_end(va);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
struct rebinding nslog_rebinding = {"NSLog",redirect_nslog,(void*)&orig_nslog};
rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);
NSLog(@"%@, hello word!",@"ss");
}
return
}
//[hook success]ss, hello word!
复制代码
NSPipe
建立一个管道,pipe有读端和写端,而后经过dup2
将标准输入重定向到pipe的写端。再经过NSFileHandle
监听pipe的读端,最后再处理读出的信息。- (void)redirectSTD:(int )fd {
NSPipe * pipe = [NSPipe pipe] ;
NSFileHandle *pipeReadHandle = [pipe fileHandleForReading] ;
int pipeFileHandle = [[pipe fileHandleForWriting] fileDescriptor];
dup2(pipeFileHandle, fd) ;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(redirectNotificationHandle:)
name:NSFileHandleReadCompletionNotification
object:pipeReadHandle] ;
[pipeReadHandle readInBackgroundAndNotify];
}
- (void)redirectNotificationHandle:(NSNotification *)nf {
NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ;
//能够添加本身的处理,能够将内容显示到View,或者是存放到另外一个文件中等等
//todo
[[nf object] readInBackgroundAndNotify];
}
//使用
[self redirectSTD:STDERR_FILENO];
复制代码