// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Project: The Cheat // // File: AppController.m // Created: Wed Aug 13 2003 // // Copyright: 2003 Chaz McGarvey. All rights reserved. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #import "AppController.h" #include #include // defines #define PID_SELECTED [[[processList objectAtIndex:[processPopup indexOfSelectedItem]] objectForKey:@"NSApplicationProcessIdentifier"] intValue] #define TYPE_SELECTED [typePopup indexOfSelectedItem] #define SIZE_SELECTED [sizePopup indexOfSelectedItem] @implementation AppController - (id)init { if ( self = [super init] ) { } return self; } - (void)awakeFromNib { NSNotificationCenter *nc = [[NSWorkspace sharedWorkspace] notificationCenter]; [self rebuildProcessList]; [self updateProcessPopup]; [self updateTypePopup]; [self updateSizePopup]; [self updateChangeButton]; [self updateStatusText]; [nc addObserver:self selector:@selector(processListChanged:) name:@"NSWorkspaceDidLaunchApplicationNotification" object:nil]; [nc addObserver:self selector:@selector(processListChanged:) name:@"NSWorkspaceDidTerminateApplicationNotification" object:nil]; [self reset]; } - (void)reset { if ( cheating ) { cheating = NO; [addressList release], addressList = nil; // update the interface [typePopup setEnabled:YES]; [sizePopup setEnabled:YES]; [searchTextField setStringValue:@""]; [changeTextField setStringValue:@""]; [addressTable reloadData]; } } - (void)firstSearch:(id)nothing { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; pid_t pid = (pid_t)PID_SELECTED; vm_map_t task; kern_return_t result; vm_address_t address = 0x0; vm_size_t size = 0; vm_region_basic_info_data_t info; mach_msg_type_number_t infoCnt = 8; mach_port_t object_name = 0; char unsigned *data; vm_size_t dataCnt; char unsigned *string8bit = (char unsigned *)[[searchTextField stringValue] lossyCString]; long unsigned stringSize = strlen( string8bit ); char integer8bit = (char)[searchTextField intValue]; short integer16bit = (short)[searchTextField intValue]; long integer32bit = (long)[searchTextField intValue]; long long integer64bit = (long long)[searchTextField intValue]; float float32bit = (float)[searchTextField floatValue]; double float64bit = (double)[searchTextField doubleValue]; BOOL done = NO; if ( (result = task_for_pid( current_task(), pid, &task)) != KERN_SUCCESS ) { NSLog( @"task_for_pid returned error: %i", result ); return; } addressList = [[NSMutableArray alloc] init]; while ( !done ) { if ( (result = vm_region( task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)(&info), &infoCnt, &object_name )) != KERN_SUCCESS ) { if ( result != KERN_INVALID_ADDRESS ) { NSLog( @"vm_region returned error: %i", result ); } done = YES; } //NSLog( @"address: %X, size: %i", address, size ); if ( (info.protection & VM_PROT_READ) && ((info.protection & VM_PROT_WRITE) >> 1) ) { data = (char unsigned *)malloc( size ); dataCnt = size; if ( (result = vm_read_overwrite( task, address, size, (vm_address_t)data, &dataCnt )) != KERN_SUCCESS && result != KERN_PROTECTION_FAILURE ) { NSLog( @"vm_read_overwrite returned error: %i", result ); free( data ); done = YES; } if ( result == KERN_SUCCESS ) { long unsigned i, max = (long unsigned)dataCnt; //NSLog( @"data: %X, size: %i", (vm_address_t)data, dataCnt ); switch ( TYPE_SELECTED ) { case TYPE_STRING: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { long unsigned maxString = max - stringSize; for ( i = 0; i < maxString; i += sizeof(char unsigned) ) { if ( strncmp( string8bit, data+i, stringSize ) == 0 ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; } break; case TYPE_INTEGER: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { for ( i = 0; i < max; i += sizeof(char) ) { if ( integer8bit == *((char *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; case SIZE_16_BIT: { for ( i = 0; i < max; i += sizeof(short) ) { if ( integer16bit == *((short *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; case SIZE_32_BIT: { for ( i = 0; i < max; i += sizeof(long) ) { if ( integer32bit == *((long *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; case SIZE_64_BIT: { for ( i = 0; i < max; i += sizeof(long long) ) { if ( integer64bit == *((long long *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; } break; case TYPE_FLOAT: switch ( SIZE_SELECTED+2 ) { case SIZE_32_BIT: { for ( i = 0; i < max; i += sizeof(float) ) { if ( float32bit == *((float *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; case SIZE_64_BIT: { for ( i = 0; i < max; i += sizeof(double) ) { if ( float64bit == *((double *)(data+i)) ) { [addressList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } } break; } break; } } free( data ); } address += size; } searching = NO; // update the interface [statusBar stopAnimation:self]; [self updateProcessPopup]; [self updateSearchButton]; [self updateTypePopup]; [self updateSizePopup]; [self updateChangeButton]; [self updateStatusText]; [addressTable reloadData]; [pool release]; } - (void)search:(id)nothing { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; pid_t pid = (pid_t)PID_SELECTED; vm_map_t task; kern_return_t result; vm_address_t address = 0x0; vm_size_t size = 0; vm_region_basic_info_data_t info; mach_msg_type_number_t infoCnt = 8; mach_port_t object_name = 0; char unsigned *data; vm_size_t dataCnt; char unsigned *string8bit = (char unsigned *)[[searchTextField stringValue] lossyCString]; long unsigned stringSize = strlen( string8bit ); char integer8bit = (char)[searchTextField intValue]; short integer16bit = (short)[searchTextField intValue]; long integer32bit = (long)[searchTextField intValue]; long long integer64bit = (long long)[searchTextField intValue]; float float32bit = (float)[searchTextField floatValue]; double float64bit = (double)[searchTextField doubleValue]; long unsigned j, max = [addressList count]; NSMutableArray *newList = [[NSMutableArray alloc] init]; if ( (result = task_for_pid( current_task(), pid, &task)) != KERN_SUCCESS ) { NSLog( @"task_for_pid returned error: %i", result ); return; } for ( j = 0; j < max; j++ ) { long unsigned item = [[addressList objectAtIndex:j] unsignedLongValue]; address = (vm_address_t)item; if ( (result = vm_region( task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)(&info), &infoCnt, &object_name )) != KERN_SUCCESS ) { if ( result != KERN_INVALID_ADDRESS ) { NSLog( @"vm_region returned error: %i", result ); } break; } //NSLog( @"address: %X, size: %i", address, size ); if ( (info.protection & VM_PROT_READ) && ((info.protection & VM_PROT_WRITE) >> 1) ) { data = (char unsigned *)malloc( size ); dataCnt = size; if ( (result = vm_read_overwrite( task, address, size, (vm_address_t)data, &dataCnt )) != KERN_SUCCESS && result != KERN_PROTECTION_FAILURE ) { NSLog( @"vm_read_overwrite returned error: %i", result ); free( data ); break; } if ( result == KERN_SUCCESS ) { long unsigned i = item - (long unsigned)address; if ( i < (long unsigned)dataCnt ) { //NSLog( @"data: %X, size: %i", (vm_address_t)data, dataCnt ); switch ( TYPE_SELECTED ) { case TYPE_STRING: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { if ( strncmp( string8bit, data+i, stringSize ) == 0 ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; } break; case TYPE_INTEGER: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { if ( integer8bit == *((char *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; case SIZE_16_BIT: { if ( integer16bit == *((short *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; case SIZE_32_BIT: { if ( integer32bit == *((long *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; case SIZE_64_BIT: { if ( integer64bit == *((long long *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; } break; case TYPE_FLOAT: switch ( SIZE_SELECTED+2 ) { case SIZE_32_BIT: { if ( float32bit == *((float *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; case SIZE_64_BIT: { if ( float64bit == *((double *)(data+i)) ) { [newList addObject:[NSNumber numberWithUnsignedLong:(long unsigned)address + i]]; } } break; } break; } } } free( data ); } } [addressList release]; addressList = newList; searching = NO; // update the interface [statusBar stopAnimation:self]; [self updateProcessPopup]; [self updateSearchButton]; [self updateTypePopup]; [self updateSizePopup]; [self updateChangeButton]; [self updateStatusText]; [addressTable reloadData]; [pool release]; } - (void)change { pid_t pid = (pid_t)PID_SELECTED; vm_map_t task; kern_return_t result; char unsigned *string8bit = (char unsigned *)[[changeTextField stringValue] lossyCString]; long unsigned stringSize = strlen( string8bit ); char integer8bit = (char)[changeTextField intValue]; short integer16bit = (short)[changeTextField intValue]; long integer32bit = (long)[changeTextField intValue]; long long integer64bit = (long long)[changeTextField intValue]; float float32bit = (float)[changeTextField floatValue]; double float64bit = (double)[changeTextField doubleValue]; NSEnumerator *enumerator = [addressTable selectedRowEnumerator]; NSNumber *row; if ( (result = task_for_pid( current_task(), pid, &task)) != KERN_SUCCESS ) { NSLog( @"task_for_pid returned error: %i", result ); return; } while ( row = [enumerator nextObject] ) { long unsigned item = [[addressList objectAtIndex:[row intValue]] unsignedLongValue]; //NSLog( @"address: %X", item ); switch ( TYPE_SELECTED ) { case TYPE_STRING: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)string8bit, (mach_msg_type_number_t)stringSize ); } break; } break; case TYPE_INTEGER: switch ( SIZE_SELECTED ) { case SIZE_8_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&integer8bit), sizeof(char) ); } break; case SIZE_16_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&integer16bit), sizeof(short) ); } break; case SIZE_32_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&integer32bit), sizeof(long) ); } break; case SIZE_64_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&integer64bit), sizeof(long long) ); } break; } break; case TYPE_FLOAT: switch ( SIZE_SELECTED+2 ) { case SIZE_32_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&float32bit), sizeof(float) ); } break; case SIZE_64_BIT: { result = vm_write( task, (vm_address_t)item, (vm_offset_t)(&float64bit), sizeof(double) ); } break; } break; } } } - (void)updateProcessPopup { if ( searching ) { [processPopup setEnabled:NO]; } else { [processPopup setEnabled:YES]; } } - (void)updateTypePopup { if ( cheating || searching ) { [typePopup setEnabled:NO]; } else { int selected = [typePopup indexOfSelectedItem]; [typePopup setEnabled:YES]; [typePopup removeAllItems]; [typePopup addItemWithTitle:@"String"]; [typePopup addItemWithTitle:@"Integer"]; [typePopup addItemWithTitle:@"Float"]; [typePopup selectItemAtIndex:selected]; } } - (void)updateSizePopup { if ( cheating || searching ) { [sizePopup setEnabled:NO]; } else { [sizePopup setEnabled:YES]; [sizePopup removeAllItems]; switch ( TYPE_SELECTED ) { case TYPE_STRING: [sizePopup addItemWithTitle:@" 8-bit"]; break; case TYPE_INTEGER: [sizePopup addItemWithTitle:@" 8-bit"]; [sizePopup addItemWithTitle:@"16-bit"]; [sizePopup addItemWithTitle:@"32-bit"]; [sizePopup addItemWithTitle:@"64-bit"]; break; case TYPE_FLOAT: [sizePopup addItemWithTitle:@"32-bit"]; [sizePopup addItemWithTitle:@"64-bit"]; break; } } } - (void)updateSearchButton { if ( searching ) { [searchTextField setEnabled:NO]; [searchButton setEnabled:NO]; } else { [searchTextField setEnabled:YES]; [searchButton setEnabled:YES]; } } - (void)updateChangeButton { if ( [addressTable selectedRow] == -1 || searching ) { [changeTextField setEnabled:NO]; [changeButton setEnabled:NO]; } else { [changeTextField setEnabled:YES]; [changeButton setEnabled:YES]; } } - (void)updateStatusText { if ( searching ) { [statusText setStringValue:@"Searching..."]; } else if ( !cheating ) { [statusText setStringValue:[NSString stringWithFormat:@"PID: %i", PID_SELECTED]]; } else // cheating { [statusText setStringValue:[NSString stringWithFormat:@"Found: %i", [addressList count]]]; } [statusText display]; } - (void)processListChanged:(NSNotification *)note { if ( cheating && [[note name] isEqualToString:@"NSWorkspaceDidTerminateApplicationNotification"] ) { int pid = PID_SELECTED; int other = [[[note userInfo] objectForKey:@"NSApplicationProcessIdentifier"] intValue]; // check to make sure the program we were cheating wasn't the one that quit if ( pid == other ) { // it was, so let's take care of it NSBeginAlertSheet( @"", @"OK", nil, nil, window, nil, nil, nil, 0, @"The application that was being cheated has quit." ); [self reset]; } } [self rebuildProcessList]; [self updateProcessPopup]; [self updateStatusText]; } - (void)rebuildProcessList { NSString *selected = [[processPopup titleOfSelectedItem] retain]; int i, max; [processList release]; processList = [[[NSWorkspace sharedWorkspace] launchedApplications] retain]; max = [processList count]; [processPopup setImagePosition:NSImageOverlaps]; [processPopup removeAllItems]; for ( i = 0; i < max; i++ ) { NSString *name = [[processList objectAtIndex:i] objectForKey:@"NSApplicationName"]; NSString *path = [[processList objectAtIndex:i] objectForKey:@"NSApplicationPath"]; NSImage *image = [[NSWorkspace sharedWorkspace] iconForFile:path]; [processPopup addItemWithTitle:name]; [image setScalesWhenResized:YES]; [image setSize:NSMakeSize( 16.0, 16.0 )]; [[processPopup itemAtIndex:i] setImage:image]; if ( [selected isEqualToString:[processPopup itemTitleAtIndex:i]] ) { [processPopup selectItemAtIndex:i]; } } [selected release]; } - (void)dealloc { [self reset]; [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; [processList release]; [super dealloc]; } - (IBAction)processPopup:(id)sender { [self reset]; [self updateStatusText]; } - (IBAction)typePopup:(id)sender { [self updateSizePopup]; } - (IBAction)searchButton:(id)sender { if ( [[searchTextField stringValue] isEqualToString:@""] ) { NSBeep(); return; } searching = YES; // update the interface [statusBar startAnimation:self]; [self updateProcessPopup]; [self updateSearchButton]; [self updateTypePopup]; [self updateSizePopup]; [self updateChangeButton]; [self updateStatusText]; if ( !cheating ) { cheating = YES; [NSThread detachNewThreadSelector:@selector(firstSearch:) toTarget:self withObject:nil]; } else { [NSThread detachNewThreadSelector:@selector(search:) toTarget:self withObject:nil]; } /* { pid_t pid = (pid_t)PID_SELECTED; vm_map_t task; kern_return_t result; //int waitStatus; addressList = [[NSMutableArray alloc] init]; result = task_for_pid( current_task(), pid, &task ); if ( result == KERN_SUCCESS ) NSLog( @"KERN_SUCCESS" ); else if ( result == KERN_INVALID_ADDRESS ) NSLog( @"KERN_INVALID_ADDRESS" ); else if ( result == KERN_INVALID_ARGUMENT ) NSLog( @"KERN_INVALID_ARGUMENT" ); else if ( result == KERN_PROTECTION_FAILURE ) NSLog( @"KERN_PROTECTION_FAILURE" ); else if ( result == KERN_NO_SPACE ) NSLog( @"KERN_NO_SPACE" ); if ( ptrace( PT_ATTACH, pid, 0, 0 ) != -1 ) { if ( waitpid( pid, &waitStatus, WUNTRACED ) == pid ) { if ( WIFSTOPPED(waitStatus) ) { NSLog( @"process stopped" ); } else { NSLog( @"process didn't stop" ); } { vm_address_t address = 0x1b000; vm_size_t size = 0; vm_region_basic_info_data_t info; mach_msg_type_number_t infoCnt = 8; mach_port_t object_name = 0; BOOL canRead, canWrite, canExecute; char unsigned *data; vm_size_t dataCnt; NSLog( @"pid: %i, task: %i", pid, task ); result = vm_region( task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)(&info), &infoCnt, &object_name ); NSLog( @"info count: %i", (int)infoCnt ); if ( result == KERN_SUCCESS ) NSLog( @"KERN_SUCCESS" ); else if ( result == KERN_INVALID_ADDRESS ) NSLog( @"KERN_INVALID_ADDRESS" ); else if ( result == KERN_INVALID_ARGUMENT ) NSLog( @"KERN_INVALID_ARGUMENT" ); else if ( result == KERN_PROTECTION_FAILURE ) NSLog( @"KERN_PROTECTION_FAILURE" ); else if ( result == KERN_NO_SPACE ) NSLog( @"KERN_NO_SPACE" ); NSLog( @"address: %X, size: %i", address, size ); canRead = info.protection & VM_PROT_READ; canWrite = (info.protection & VM_PROT_WRITE) >> 1; canExecute = (info.protection & VM_PROT_EXECUTE) >> 2; if ( canRead ) NSLog( @"can read" ); if ( canWrite ) NSLog( @"can write" ); if ( canExecute ) NSLog( @"can execute" ); data = (char unsigned *)malloc( size ); dataCnt = size; result = vm_read_overwrite( task, address, size, (vm_address_t)data, &dataCnt ); if ( result == KERN_SUCCESS ) NSLog( @"KERN_SUCCESS" ); else if ( result == KERN_INVALID_ADDRESS ) NSLog( @"KERN_INVALID_ADDRESS" ); else if ( result == KERN_INVALID_ARGUMENT ) NSLog( @"KERN_INVALID_ARGUMENT" ); else if ( result == KERN_PROTECTION_FAILURE ) NSLog( @"KERN_PROTECTION_FAILURE" ); else if ( result == KERN_NO_SPACE ) NSLog( @"KERN_NO_SPACE" ); NSLog( @"data: %X, size: %i", (vm_address_t)data, dataCnt ); free( data ); } } else { NSLog( @"waitpid() failed" ); } ptrace( PT_DETACH, pid, 0, 0 ); } else { NSLog( @"ptrace() failed" ); } }*/ } - (IBAction)changeButton:(id)sender { [self change]; } - (int)numberOfRowsInTableView:(NSTableView *)table { if ( cheating && !searching ) return [addressList count]; return 0; } - (id)tableView:(NSTableView *)table objectValueForTableColumn:(NSTableColumn *)column row:(int)row { return [NSString stringWithFormat:@"%X", [[addressList objectAtIndex:row] unsignedLongValue]]; } - (void)tableView:(NSTableView *) setObjectValue:(id)object forTableColumn:(NSTableColumn *)column row:(int)row { return; } - (void)tableViewSelectionDidChange:(NSNotification *)note { [self updateChangeButton]; } @end