標籤:自身 odi orm apple bundle hpa fat 布爾 oid
//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 statisticsint64_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
MachOView源碼(AppController.mm)