MachOView源码(AppController.mm)

//AppController.h

/*
 *  AppController.h
 *  MachOView
 *
 *  Created by psaghelyi on 15/06/2010.
 *
 */

#import <Cocoa/Cocoa.h>

@class MVPreferenceController;

@interface MVAppController : NSObject <NSApplicationDelegate,NSOpenSavePanelDelegate>
{
  MVPreferenceController * preferenceController;
}
//偏好设置
- (IBAction)showPreferencePanel:(id)sender;
//附加
- (IBAction)attach:(id)sender;

@end

//AppController.mm

 /*
 *  AppController.mm
 *  MachOView
 *
 *  Created by psaghelyi on 15/06/2010.
 *
 */

#import "Common.h"
#import "AppController.h"
#import "DataController.h"
#import "Document.h"
#import "PreferenceController.h"
#import "Attach.h"

// counters for statistics
int64_t nrow_total;  // number of rows (loaded and empty)
int64_t nrow_loaded; // number of loaded rows

//============================================================================
@implementation MVAppController

//----------------------------------------------------------------------------
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
  return NO;
}

//----------------------------------------------------------------------------
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
  return NO;
}

//----------------------------------------------------------------------------
//new 方式 没写
- (IBAction)newDocument:(id)sender
{
  NSLog(@"Not yet possible");
}

//----------------------------------------------------------------------------
//是否在运行
- (BOOL)isOnlyRunningMachOView
{
    //获取进程的相关信息
  NSProcessInfo * procInfo = [NSProcessInfo processInfo];
    //获取程序程序包(app包就是一个mainBundle)
  NSBundle * mainBundle = [NSBundle mainBundle];
    //获取当前进程的CFBundleShortVersionString
    //返回指定键关联的接收器中的属性信息列表
  NSString * versionString = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

  NSUInteger numberOfInstance = 0;
    //返回一个工作空间
    //提供服务
    //1)打开,操作文件/设备,获取文件/设备信息
    //2)跟踪文件,设备以及数据库的变动
    //3)设置或获取文件的 Finder 信息
    //4)操作应用程序
  NSWorkspace * workspace = [NSWorkspace sharedWorkspace];
    //获取当前运行的 apps
  for (NSRunningApplication * runningApplication in [workspace runningApplications])
  {
    //检查进程名称是否匹配
    //executableURL获取bundle 类实例的 可执行的URL
    //lastPathComponent获取路径中的最后一个文件名
    NSString * fileName = [[runningApplication executableURL] lastPathComponent];
      //判断名称是否相等
    if ([fileName isEqualToString: [procInfo processName]] == NO)
    {
      continue;
    }

    //检查版本字符串是否匹配
    //通过 url 获取bundle
    NSBundle * bundle = [NSBundle bundleWithURL:[runningApplication bundleURL]];
      //判断是否与当前进程的相等
    if ([versionString isEqualToString:[bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]] == YES && ++numberOfInstance > 1)
    {
      return NO;
    }
  }

  return YES;
}

//----------------------------------------------------------------------------
//附加方式打开同时解析 mach-o header
- (IBAction)attach:(id)sender
{
    //alert
  NSAlert *alert = [NSAlert alertWithMessageText:@"Insert PID to attach to:"
                                   defaultButton:@"Attach"
                                 alternateButton:@"Cancel"
                                     otherButton:nil
                       informativeTextWithFormat:@""];
   //初始化一个文本框
  NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
    //设置默认字符串
  [input setStringValue:@""];
    //设置弹框的 view为input
  [alert setAccessoryView:input];
    //返回按钮值
  NSInteger button = [alert runModal];
  if (button == NSAlertDefaultReturn)
  {
      //设置为不可编辑
    [input validateEditing];
      //获取 pid
    pid_t targetPid = [input intValue];
    NSLog(@"Attach to process %d", targetPid);
      //入口地址
    mach_vm_address_t mainAddress = 0;
      //找到入口地址
    if (find_main_binary(targetPid, &mainAddress))
    {
      NSLog(@"Failed to find main binary address!");
      return;
    }
    uint64_t aslr_slide = 0;
    uint64_t imagesize = 0;
      //获取镜像大小
    if ( (imagesize = get_image_size(mainAddress, targetPid, &aslr_slide)) == 0 )
    {
      NSLog(@"[ERROR] Got image file size equal to 0!");
      return;
    }
    //分配macho所需要的缓冲区大小(文件)
    uint8_t *readbuffer = (uint8_t*)malloc(imagesize);
    if (readbuffer == NULL)
    {
      NSLog(@"Can't allocate mem for dumping target!");
      return;
    }
    //最后读取这些部分并将它们的内容转储到缓冲区中
    if (dump_binary(mainAddress, targetPid, readbuffer, aslr_slide))
    {
      NSLog(@"Main binary memory dump failed!");
      free(readbuffer);
      return;
    }
    //将缓冲区内容转储到临时文件以使用NSDocument model
      //拼接临时文件名+进程名 如:"/var/folders/l8/3kn0r51s3qdbbnxb9ng2gvb40000gn/T/MachOView_2.4.XXXXXXXXXXX"
    const char *tmp = [[MVDocument temporaryDirectory] UTF8String];
      //用于保存数数据的临时文件绝对路径名的缓冲区
    char *dumpFilePath = (char*)malloc(strlen(tmp)+1);
    if (dumpFilePath == NULL)
    {
      NSLog(@"Can't allocate mem for temp filename path!");
      free(readbuffer);
      return;
    }
      //拷贝
    strcpy(dumpFilePath, tmp);
    int outputFile = 0;
      //在系统中以唯一的文件名创建一个文件并打开
    if ( (outputFile = mkstemp(dumpFilePath)) == -1 )
    {
      NSLog(@"mkstemp failed!");
      free(dumpFilePath);
      free(readbuffer);
      return;
    }
    //写入
    if (write(outputFile, readbuffer, imagesize) == -1)
    {
      NSLog(@"[ERROR] Write error at %s occurred!\n", dumpFilePath);
      free(dumpFilePath);
      free(readbuffer);
      return;
    }
    NSLog(@"\n[OK] Full binary dumped to %s!\n\n", dumpFilePath);
    close(outputFile);
    //打开文件
    [self application:NSApp openFile:[NSString stringWithCString:dumpFilePath encoding:NSUTF8StringEncoding]];
    //删除临时转储文件
    NSFileManager * fileManager = [NSFileManager defaultManager];
    [fileManager removeItemAtPath:[NSString stringWithCString:dumpFilePath encoding:NSUTF8StringEncoding] error:NULL];
    free(dumpFilePath);
    free(readbuffer);
  }
  else if (button == NSAlertAlternateReturn)
  {
   //
  }
  else
  {
    NSAssert1(NO, @"Invalid input dialog button %ld", button);
  }
}

//----------------------------------------------------------------------------
//open方式打开
- (IBAction)openDocument:(id)sender
{
    //打开文件查看
  NSOpenPanel *openPanel = [NSOpenPanel openPanel];
    //是否显示面板封装的文件的目录
  [openPanel setTreatsFilePackagesAsDirectories:YES];
    //设置为多选模式
  [openPanel setAllowsMultipleSelection:YES];
    //不可选择文件夹
  [openPanel setCanChooseDirectories:NO];
    //设置可以选择文件
  [openPanel setCanChooseFiles:YES];
    //设置代理为自身 调用 shouldShowFilename过滤打开面板中的文件
  [openPanel setDelegate:self];
    //告警风格窗口
    //completionHandler参数为事件响应
  [openPanel beginSheetModalForWindow:nil 
   completionHandler:^(NSInteger result) 
   {
       //判断点击 ok
     if (result != NSOKButton) 
     {
       return;
     }
       //关闭面板之前,可能会出现错误
       //移除窗口
     [openPanel orderOut:self];
       //遍历所选的文件路径
     for (NSURL * url in [openPanel URLs])
     {
         //全部打开(通过多文档模式)
       [self application:NSApp openFile:[url path]];
     }
   }];
}

//----------------------------------------------------------------------------
//协议-----过滤文件
- (BOOL)panel:(id)sender shouldShowFilename:(NSString*)filename
{
    //文件 url
  NSURL * url = [NSURL fileURLWithPath:filename];

  // 判断是否为文件夹
  NSNumber * isDirectory = nil;
  [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
  if ([isDirectory boolValue] == YES) 
  {
    return YES;
  }

  // 跳过符号链接等
    //判断是否为一些符号链接
  NSNumber * isRegularFile = nil;
  [url getResourceValue:&isRegularFile forKey:NSURLIsRegularFileKey error:NULL];
  if ([isRegularFile boolValue] == NO) 
  {
    return NO;
  }

  // 检查文件的magic
    //文件句柄
  NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:filename];
   // NSLog(@"fileHandleForReadingAtPath----- %@!\n\n", filename);
  //读取指定的字符数
  NSData * magicData = [fileHandle readDataOfLength:8];
    // NSLog(@"magicData----- %@!\n\n", magicData);
    //关闭句柄
  [fileHandle closeFile];
  //判断长度
  if ([magicData length] < sizeof(uint32_t))
  {
    return NO;
  }
  //比较
  uint32_t magic = *(uint32_t*)[magicData bytes];
    //NSLog(@"magic----- %8x!\n\n", magic);
  if (magic == MH_MAGIC || magic == MH_MAGIC_64 || 
      magic == FAT_CIGAM || magic == FAT_MAGIC)
  {
    return YES;
  }

  if ([magicData length] < sizeof(uint64_t))
  {
    return NO;
  }

  if (*(uint64_t*)[magicData bytes] == *(uint64_t*)"!<arch>\n")
  {
    return YES;
  }

  return NO;
}

//----------------------------------------------------------------------------
//程序将启动
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
  BOOL isFirstMachOView = [self isOnlyRunningMachOView];

  //禁用状态恢复功能,这对MachOView来说不是很有用
  if([[NSUserDefaults standardUserDefaults] objectForKey: @"ApplePersistenceIgnoreState"] == nil)
      [[NSUserDefaults standardUserDefaults] setBool: YES forKey:@"ApplePersistenceIgnoreState"];

  // load user's defaults for preferences
//  if([[NSUserDefaults standardUserDefaults] objectForKey: @"UseLLVMDisassembler"] != nil)
//    qflag = [[NSUserDefaults standardUserDefaults] boolForKey:@"UseLLVMDisassembler"];

  //获取一个文件管理者
  NSFileManager * fileManager = [NSFileManager defaultManager];
  NSString * tempDir = [MVDocument temporaryDirectory];

  __autoreleasing NSError * error;

  //删除以前被遗忘的临时文件
  if (isFirstMachOView && [fileManager fileExistsAtPath:tempDir isDirectory:NULL] == YES)
  {
      //移除
    if ([fileManager removeItemAtPath:tempDir error:&error] == NO)
    {
      [NSApp presentError:error];
    }
  }

  //为临时文件创建占位符
    //返回一个布尔值,该值指示一个文件或目录是否存在于指定路径
  if ([fileManager fileExistsAtPath:tempDir isDirectory:NULL] == NO)
  {
      //创建一个带有给定属性的指定目录路径
    if ([fileManager createDirectoryAtPath:tempDir
               withIntermediateDirectories:NO
                                attributes:nil
                                     error:&error] == NO)
    {
        //调用错误对话框
      [NSApp presentError:error];
    }
  }
}

//----------------------------------------------------------------------------
//程序已启动
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
#ifdef MV_STATISTICS
  nrow_total = nrow_loaded = 0;
    //开线程开始打印(用于调试忽视)
  [NSThread detachNewThreadSelector:@selector(printStat) toTarget:self withObject:nil];
#endif 

  //默认一开始就打开文件对话框
  if ([[NSUserDefaults standardUserDefaults] objectForKey:@"OpenAtLaunch"] != nil)
  {
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OpenAtLaunch"] == YES)
    {
      if ([[[NSDocumentController sharedDocumentController] documents] count] == 0)
      {
        [self openDocument:nil];
      }
    }
  }
}

//----------------------------------------------------------------------------
//程序将结束
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
  BOOL isLastMachOView = [self isOnlyRunningMachOView];

  if (isLastMachOView == YES)
  {
    //删除临时文件
    NSFileManager * fileManager = [NSFileManager defaultManager];
      //返回当前用户的临时目录
    NSString * tempDir = [MVDocument temporaryDirectory];
      //NSLog(@"Can't allocate mem for temp filename path! %@",tempDir);
      //删除
    [fileManager removeItemAtPath:tempDir error:NULL];
  }
}

//----------------------------------------------------------------------------
//回调函数 打开文件
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
  NSLog (@"open file: %@", filename);

  __autoreleasing NSError *error;
    //获取一个多文档控制器
  NSDocumentController * documentController = [NSDocumentController sharedDocumentController];
    //NSDocumentController 创建管理多个MVDocument
    //MVDocument 创建管理多个NSWindowController
    //NSWindowController
    //通过文件路径创建一个文档 同时显示(通过代理实现细节)
    //MVDocument必须实现的函数有:
    //窗口名称
    //- (NSString *)windowNibName;
    //指定窗口控制器
    //- (void)windowControllerDidLoadNib:(NSWindowController *)aController;
    //必须实现创建和返回受支持类型的文档数据,通常是将数据写入NSData对象的文件。
    //- (NSData *)dataRepresentationOfType:(NSString *)aType;
    //必须实现将NSData对象(包含特定类型的文档数据)转换为文档的内部数据结构,以便文档准备显示其内容。NSData对象通常是从读取文档文件的文档中产生的。
    //- (BOOL)loadDataRepresentation:(NSData *)data:(NSString *)aType;

  MVDocument * document = [documentController openDocumentWithContentsOfURL:[NSURL fileURLWithPath:filename] 
                                                                    display:YES 
                                                                      error:&error];

  //判断是否能打开文档
  if (!document) 
  {
    [NSApp presentError:error];
    return NO;
  }

  return YES;
}

//----------------------------------------------------------------------------
//线程回调
-(void) printStat
{
  for (;;)
  {
    NSLog(@"stat: %lld/%lld rows in memory\n",nrow_loaded,nrow_total);
    [NSThread sleepForTimeInterval:1];
  }
}

//----------------------------------------------------------------------------
//系统偏好设置 (显示一个nib)
- (IBAction)showPreferencePanel:(id)sender
{
    if (!preferenceController)
    {
        preferenceController = [[MVPreferenceController alloc] init];
    }
    [preferenceController showWindow:self];
}

@end

转载于:https://blog.51cto.com/haidragon/2142007

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值