From 4afe92ec1dfa5696c809b2d28e4c19c11a1fbbc1 Mon Sep 17 00:00:00 2001 From: BangNguyen Date: Fri, 25 Feb 2022 19:09:47 +0800 Subject: [PATCH] Support exporting Mach-o segment via copy action --- DataController.h | 3 + DataController.mm | 20 +++ DataSources.h | 2 + DataSources.mm | 220 +++++++++++++++++----------- Document.mm | 44 +++++- machoview.xcodeproj/project.pbxproj | 6 +- 6 files changed, 202 insertions(+), 93 deletions(-) diff --git a/DataController.h b/DataController.h index 53c0de9..02c0533 100644 --- a/DataController.h +++ b/DataController.h @@ -55,6 +55,7 @@ struct MVNodeSaver; @property (nonatomic) NSString * valueStr; +(MVColoumns *) coloumnsWithData:(NSString *)col0 :(NSString *)col1 :(NSString *)col2 :(NSString *)col3; +-(NSString *)fullString; @end @@ -95,6 +96,8 @@ struct MVNodeSaver; @property (nonatomic) FILE * swapFile; - (NSUInteger) rowCountToDisplay; +- (NSString *)allContents; +- (void)showAllRows; - (MVRow *) getRowToDisplay:(NSUInteger)rowIndex; - (void) popRow; diff --git a/DataController.mm b/DataController.mm index 646d7ea..1295ed9 100644 --- a/DataController.mm +++ b/DataController.mm @@ -89,6 +89,10 @@ -(id)initWithData:(NSString *)col0 :(NSString *)col1 :(NSString *)col2 :(NSStrin return self; } +-(NSString *)fullString { + return [NSString stringWithFormat:@"%@ %@ %@ %@", offsetStr, dataStr, descriptionStr, valueStr]; +} + //----------------------------------------------------------------------------- +(MVColoumns *) coloumnsWithData:(NSString *)col0 :(NSString *)col1 :(NSString *)col2 :(NSString *)col3 { @@ -455,6 +459,22 @@ - (NSUInteger)rowCountToDisplay return [displayRows count]; } +- (NSString *)allContents { + NSMutableString *str = [NSMutableString new]; + for(MVRow *row in displayRows) { + if (row.coloumns == nil ){ + [row loadFromFile:swapFile]; + } + [str appendString:row.coloumns.fullString]; + [str appendString:@"\n"]; + } + return str; +} + +- (void)showAllRows { + NSLog(@"%@", [self allContents]); +} + //---------------------------------------------------------------------------- - (MVRow *)getRowToDisplay: (NSUInteger)rowIndex { diff --git a/DataSources.h b/DataSources.h index 7559033..0403ce1 100644 --- a/DataSources.h +++ b/DataSources.h @@ -13,5 +13,7 @@ extern NSString * const MVScannerErrorMessage; @interface MVDataSourceDetails : NSObject; +- (NSString *)fullBinaryData:(NSTableView *)tableView; +- (NSString *)fullDetailData:(NSTableView *)tableView; @end diff --git a/DataSources.mm b/DataSources.mm index dc87fba..2cb2aff 100644 --- a/DataSources.mm +++ b/DataSources.mm @@ -81,6 +81,15 @@ @implementation MVDataSourceDetails #pragma mark NSTableView must-have delegates +- (NSInteger)numberOfBinaryRows:(MVNode *)node { + NSInteger numRows = node.dataRange.length / 16; + if (node.dataRange.length % 16 != 0) + { + ++numRows; + } + return numRows; +} + - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { MVDocument * document = [[[aTableView window] windowController] document]; @@ -89,16 +98,11 @@ - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView // if there is no details, then provide binary dump if (selectedNode.details == nil) { - NSInteger numRows = selectedNode.dataRange.length / 16; - if (selectedNode.dataRange.length % 16 != 0) - { - ++numRows; - } - return numRows; + return [self numberOfBinaryRows:selectedNode]; } - return selectedNode.details.rowCountToDisplay; } + //---------------------------------------------------------------------------- - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex @@ -119,87 +123,11 @@ - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColu // if it has no details then show binary data at given range if (selectedNode.details == nil) { - NSUInteger offset = selectedNode.dataRange.location + rowIndex * 16; - - // file offset - if (colIndex == OFFSET_COLUMN) - { - NSString * cellContent = [NSString stringWithFormat:@"%.8lX", offset]; - if ([document isRVA] == YES) - { - id layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey]; - return [layout performSelector:@selector(convertToRVA:) withObject:cellContent]; - } - return cellContent; - } - - // binary data - uint8_t buffer[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - - NSUInteger len = MIN(selectedNode.dataRange.length - rowIndex * 16, (NSUInteger)16); - - memcpy(buffer, (uint8_t *)[document.dataController.fileData bytes] + offset, len); - - if (colIndex == DATA_LO_COLUMN) - { - NSUInteger index = (len > 8 ? 8 : len); - - return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ", - buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]] - substringToIndex:index*3]; - } - - if (colIndex == DATA_HI_COLUMN) - { - NSUInteger index = (len > 8 ? len - 8 : 0); - - return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ", - buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15]] - substringToIndex:index*3]; - } - - // textual data (where possible) - for (NSUInteger i = 0; i < len; ++i) - { - // keep the output in ASCII - if (buffer[i] < 32 || buffer[i] > 126) - { - buffer[i] = '.'; - } - } - - return NSSTRING(buffer); + return [self getBinaryString:rowIndex column:colIndex doc:document]; } // if it has descripion then show it - MVRow * row = [selectedNode.details getRowToDisplay:rowIndex]; - if (row != nil) - { - NSString * cellContent = [row coloumnAtIndex:colIndex]; - - // special column is the offset column: - // if RVA is selected then subtitute the content on the fly - if (colIndex == OFFSET_COLUMN && [cellContent length] > 0) - { - if ([document isRVA] == YES) - { - id layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey]; - cellContent = [layout performSelector:@selector(convertToRVA:) withObject:cellContent]; - } - } - - // put formatting on display text - NSColor * color = [row.attributes objectForKey:MVTextColorAttributeName]; - if (color != nil) - { - NSDictionary * attributes = [NSDictionary dictionaryWithObject:color forKey:NSForegroundColorAttributeName]; - return [[NSAttributedString alloc] initWithString:cellContent - attributes:attributes]; - } - return cellContent; - } - - return nil; + return [self getDetailString:rowIndex column:colIndex doc:document]; } //---------------------------------------------------------------------------- @@ -308,6 +236,128 @@ - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTable } //---------------------------------------------------------------------------- +#pragma mark Utils + +- (NSString *)getBinaryString:(NSInteger)rowIndex column:(NSInteger)colIndex doc:(MVDocument *)document { + MVNode * selectedNode = document.dataController.selectedNode; + NSUInteger offset = selectedNode.dataRange.location + rowIndex * 16; + + // file offset + if (colIndex == OFFSET_COLUMN) + { + NSString * cellContent = [NSString stringWithFormat:@"%.8lX", offset]; + if ([document isRVA] == YES) + { + id layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey]; + return [layout performSelector:@selector(convertToRVA:) withObject:cellContent]; + } + return cellContent; + } + + // binary data + uint8_t buffer[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + NSUInteger len = MIN(selectedNode.dataRange.length - rowIndex * 16, (NSUInteger)16); + + memcpy(buffer, (uint8_t *)[document.dataController.fileData bytes] + offset, len); + + if (colIndex == DATA_LO_COLUMN) + { + NSUInteger index = (len > 8 ? 8 : len); + + return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ", + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]] + substringToIndex:index*3]; + } + + if (colIndex == DATA_HI_COLUMN) + { + NSUInteger index = (len > 8 ? len - 8 : 0); + + return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ", + buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15]] + substringToIndex:index*3]; + } + + // textual data (where possible) + for (NSUInteger i = 0; i < len; ++i) + { + // keep the output in ASCII + if (buffer[i] < 32 || buffer[i] > 126) + { + buffer[i] = '.'; + } + } + + return NSSTRING(buffer); +} + +- (id)getDetailString:(NSInteger)rowIndex column:(NSInteger)colIndex doc:(MVDocument *)document { + MVNode * selectedNode = document.dataController.selectedNode; + MVRow * row = [selectedNode.details getRowToDisplay:rowIndex]; + if (row != nil) + { + NSString * cellContent = [row coloumnAtIndex:colIndex]; + + // special column is the offset column: + // if RVA is selected then subtitute the content on the fly + if (colIndex == OFFSET_COLUMN && [cellContent length] > 0) + { + if ([document isRVA] == YES) + { + id layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey]; + cellContent = [layout performSelector:@selector(convertToRVA:) withObject:cellContent]; + } + } + + // put formatting on display text + NSColor * color = [row.attributes objectForKey:MVTextColorAttributeName]; + if (color != nil) + { + NSDictionary * attributes = [NSDictionary dictionaryWithObject:color forKey:NSForegroundColorAttributeName]; + return [[NSAttributedString alloc] initWithString:cellContent + attributes:attributes]; + } + return cellContent; + } + return nil; +} + +- (NSString *)fullBinaryData:(NSTableView *)tableView { + MVDocument * document = [[[tableView window] windowController] document]; + MVNode * selectedNode = document.dataController.selectedNode; + + NSInteger numRow = [self numberOfBinaryRows:selectedNode]; + NSMutableString *result = [NSMutableString new]; + for (NSInteger i = 0; i @@ -49,6 +50,9 @@ - (void)mouseDown:(NSEvent *)theEvent //============================================================================ +@interface MVTable() +@end + @implementation MVTableView //---------------------------------------------------------------------------- @@ -131,6 +135,30 @@ - (void)cancelOperation:(id)sender } } +- (void)copy:(NSMenuItem *)menuItem { + MVDataSourceDetails *ds = self.dataSource; + if (![ds isKindOfClass:[MVDataSourceDetails class]]) { + assert(0); + return; + } + + MVDocument * document = [[[self window] windowController] document]; + MVNode * selectedNode = document.dataController.selectedNode; + + [[NSPasteboard generalPasteboard] clearContents]; + if (selectedNode.details) { + [[NSPasteboard generalPasteboard] setString:[ds fullDetailData:self] forType:NSPasteboardTypeString]; + } else { + [[NSPasteboard generalPasteboard] setString:[ds fullBinaryData:self] forType:NSPasteboardTypeString]; + } +} + +#pragma-mark NSMenuItemValidation + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + return YES; +} + @end //============================================================================ @@ -455,18 +483,22 @@ - (void)handleThreadStateChanged:(NSNotification *)notification { if (OSAtomicIncrement32(&threadCount) == 1) { - [progressIndicator setUsesThreadedAnimation:YES]; - [progressIndicator startAnimation:nil]; - [stopButton setHidden:NO]; + dispatch_async(dispatch_get_main_queue(), ^{ + [progressIndicator setUsesThreadedAnimation:YES]; + [progressIndicator startAnimation:nil]; + [stopButton setHidden:NO]; + }); } } else if ([threadState isEqualToString:MVStatusTaskTerminated] == YES) { if (OSAtomicDecrement32(&threadCount) == 0) { - [progressIndicator stopAnimation:nil]; - [statusText setStringValue:@""]; - [stopButton setHidden:YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + [progressIndicator stopAnimation:nil]; + [statusText setStringValue:@""]; + [stopButton setHidden:YES]; + }); } } } diff --git a/machoview.xcodeproj/project.pbxproj b/machoview.xcodeproj/project.pbxproj index afa40ac..17031b6 100644 --- a/machoview.xcodeproj/project.pbxproj +++ b/machoview.xcodeproj/project.pbxproj @@ -726,7 +726,7 @@ ); PRIVATE_HEADERS_FOLDER_PATH = ""; PRODUCT_NAME = MachOView; - SDKROOT = macosx10.9; + SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = ""; VALID_ARCHS = "$(inherited)"; VERSIONING_SYSTEM = ""; @@ -781,7 +781,7 @@ ); PRIVATE_HEADERS_FOLDER_PATH = ""; PRODUCT_NAME = MachOView; - SDKROOT = macosx10.9; + SDKROOT = macosx; SKIP_INSTALL = NO; USER_HEADER_SEARCH_PATHS = ""; VALID_ARCHS = "$(inherited)"; @@ -793,6 +793,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES; CODE_SIGN_IDENTITY = ""; @@ -818,6 +819,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES; CODE_SIGN_IDENTITY = "";