├── README └── fixobjc.idc /README: -------------------------------------------------------------------------------- 1 | This is an attempt to improve the original fixobjc.idc script by Willem Jan Hengeveld. 2 | 3 | For now it's only compatible with Mach-O 32bits binaries for Mac OS X. 4 | 5 | My goal is to make it compatible with all Mac OS X and iOS binaries. 6 | 7 | fG! 8 | 9 | -------------------------------------------------------------------------------- /fixobjc.idc: -------------------------------------------------------------------------------- 1 | // vim: ft=cpp sw=4 ts=4 et 2 | /* 3 | * _ (`-') <-.(`-') 4 | * <-. (_) (OO )_.-> .-> __( OO) _ 5 | * (`-')-----.,-(`-')(_| \_)--.(`-')----. '-'---.\ <-.--. \-,-----. 6 | * (OO|(_\---'| ( OO)\ `.' / ( OO).-. '| .-. (/ (`-'| ,| | .--./ 7 | * / | '--. | | ) \ .') ( _) | | || '-' `.) (OO |(_| /_) (`-') 8 | * \_) .--'(| |_/ .' \ \| |)| || /`'. |,--. | | || |OO ) 9 | * `| |_) | |'->/ .'. \ ' '-' '| '--' /| '-' /(_' '--'\ 10 | * `--' `--' `--' '--' `-----' `------' `-----' `-----' 11 | * 12 | * IDA IDC Script that processes the objective-C typeinfo, and names methods accordingly 13 | * 14 | * New additions, modifications and bug fixes by fG! 15 | * (C) fG!, 2012 - reverser@put.as - http://reverse.put.as 16 | * 17 | * Original script by: 18 | * (C) 2003-2008 Willem Jan Hengeveld 19 | * Web: http://www.xs4all.nl/~itsme/projects/ida/ 20 | * 21 | * v0.1 - 28/08/2012 22 | * ChangeLog: 23 | * - Add a version number 24 | * - Add xrefs to instance methods (I really hate to have to go backwards, too many clicks!) 25 | * - Commented some of the code, start cleaning fixmes 26 | * - Start adding configurable options 27 | * - Rename methods as IDA does with [Class selector] format 28 | * This will work with binaries which IDA can correctly process their obj-c info 29 | * - Fix the cfstring segment (wasn't working due to wrong segment name) 30 | * - change comment type for message and class refs 31 | * - Correctly rename class methods +[] format 32 | * - Add xrefs to class methods 33 | * 34 | */ 35 | 36 | // configurable options - YOU CAN MESS AROUND HERE 37 | #define TYPE_INFORMATION NO // add type information to names?, I don't like it :-) 38 | // STOP MESSING AROUND BELOW HERE ;-) 39 | 40 | #define UNLOADED_FILE 1 41 | #include 42 | 43 | #define YES 1 44 | #define NO 0 45 | #define DEBUG 0 46 | 47 | #define STRUCT_OBJC_METHOD_SIZE 12 // sizeof(struct objc_method) 48 | 49 | // this script processes the objective C typeinfo tables, 50 | // and names functions accordingly. 51 | // greatly improving the disassembly of objective C programs 52 | 53 | static String(ea) 54 | { 55 | return GetString(ea, -1, ASCSTR_C); 56 | } 57 | 58 | static create_mthnames(ea0, ea1, name, type) 59 | { 60 | auto ea; 61 | for (ea=ea0 ; ea seg __cat_inst_meth 213 | MakeName(Dword(ea+0x8), form("catmths_%s", name)); 214 | create_mthnames(Dword(ea+0x8)+8, Dword(ea+0x8)+8+12*Dword(Dword(ea+0x8)+4), name, "(cat)"); 215 | } 216 | // todo: class methods -> __cat_cls_meth 217 | } 218 | } 219 | 220 | Message("[INFO] Processing __module_info segment\n"); 221 | segea=SegByBase(SegByName("__module_info")); 222 | for (ea= SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 223 | { 224 | if (GuessType(ea)=="__module_info_struct") 225 | { 226 | MakeName(Dword(ea+0xC), form("symtab_%X", Dword(ea+0xC))); 227 | } 228 | } 229 | 230 | Message("[INFO] Processing __cfstring segment\n"); 231 | segea=SegByBase(SegByName("__cfstring")); 232 | for (ea= SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 233 | { 234 | if (GuessType(ea)=="__CFString") 235 | { 236 | if (!MakeName(ea, "cfs_"+Name(Dword(ea+8)))) 237 | { 238 | i=0; 239 | while (!MakeName(ea, form("cfs_%s_%d",Name(Dword(ea+8)),i))) 240 | i++; 241 | } 242 | // no more need for this since IDA already comments with the string contents 243 | // for (rea=DfirstB(ea) ; rea!=BADADDR ; rea=DnextB(ea,rea)) 244 | // { 245 | // MakeComm(rea, String(Dword(ea+8))); 246 | // } 247 | } 248 | } 249 | 250 | Message("[INFO] Processing __message_refs segment\n"); 251 | segea=SegByBase(SegByName("__message_refs")); 252 | for (ea= SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 253 | { 254 | if (!MakeName(ea, "msg_"+Name(Dword(ea)))) 255 | { 256 | i=0; 257 | while (!MakeName(ea, form("msg_%s_%d",Name(Dword(ea)),i))) 258 | i++; 259 | } 260 | for (rea=DfirstB(ea) ; rea!=BADADDR ; rea=DnextB(ea,rea)) 261 | { 262 | MakeComm(rea, "message: \""+String(Dword(ea))+"\""); 263 | } 264 | } 265 | 266 | Message("[INFO] Processing __cls_refs segment\n"); 267 | segea=SegByBase(SegByName("__cls_refs")); 268 | for (ea= SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 269 | { 270 | if (!MakeName(ea, "cls_"+Name(Dword(ea)))) 271 | { 272 | i=0; 273 | while (!MakeName(ea, form("cls_%s_%d",Name(Dword(ea)),i))) 274 | i++; 275 | } 276 | for (rea=DfirstB(ea) ; rea!=BADADDR ; rea=DnextB(ea,rea)) 277 | { 278 | MakeComm(rea, "class: \""+String(Dword(ea))+"\""); 279 | } 280 | } 281 | 282 | Message("[INFO] Processing __instance_vars segment\n"); 283 | segea=SegByBase(SegByName("__instance_vars")); 284 | for (ea= SegStart(segea) ; ea and vtbl_ from ivars+methods 341 | // todo: create __cfString_struct in seg 342 | // todo: create align 40h between __class items and __meta_class item s 343 | // todo: create align 20h between __cls_meth, __inst_meth, __instance_vars, __symbols 344 | // todo: create dword arrays for __eh_frame 345 | // todo: const_coal contains obj defs + ptr to vtables too 346 | // todo: rename __pointers -> { __data -> __cString ptrs, __data -> __cfString, ... } 347 | 348 | // add xrefs to methods 349 | // FIXME: there are at least two different structures used in this segment so needs some research 350 | // segea=SegByBase(SegByName("__cat_inst_meth")); 351 | // add_catinst_methods_xrefs(segea); 352 | Message("[INFO] Processing __inst_meth segment\n"); 353 | segea=SegByBase(SegByName("__inst_meth")); 354 | add_inst_methods_xrefs(segea); 355 | 356 | Message("[INFO] Processing __cls_meth segment\n"); 357 | segea=SegByBase(SegByName("__cls_meth")); 358 | add_inst_methods_xrefs(segea); 359 | 360 | Message("[INFO] Everything finished!\n"); 361 | } 362 | 363 | /* 364 | * function that will add xrefs to category instance methods so we can easily find who's calling each method 365 | */ 366 | static add_catinst_methods_xrefs(segea) 367 | { 368 | auto cstring, msg_ref, xref, function, ea, advance; 369 | // start process valid code/data until the end of this segment 370 | for (ea= SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 371 | { 372 | if (Dword(ea) == 0x0 && Dword(ea+4) != 0) 373 | { 374 | advance = 8; 375 | } 376 | // get the pointer to the correspondent C string we will use to find xrefs 377 | cstring = Dword(ea); 378 | // bypass empty strings, usually the header fields of class/methods 379 | if (SegName(cstring) != "__cstring") 380 | { 381 | continue; 382 | } 383 | // the function name we will add the xrefs to 384 | function = Dword(ea+8); 385 | // now try to find the pointer to message_refs, it's usually the first data xref 386 | // if there's no pointer to message_refs then there's no xrefs to this method (seems to hold true!) 387 | msg_ref = DfirstB(cstring); 388 | // test if it belongs to __message_refs and if not try to find it, if available 389 | if (SegName(msg_ref) != "__message_refs") 390 | { 391 | while(msg_ref != BADADDR) 392 | { 393 | msg_ref = DnextB(cstring, msg_ref); 394 | if (SegName(msg_ref) == "__message_refs") 395 | { 396 | break; 397 | } 398 | } 399 | } 400 | // if there's no ptr to message_refs then move to next method 401 | if (msg_ref == BADADDR) 402 | { 403 | continue; 404 | } 405 | 406 | // start looking up who's referencing the method 407 | // and add the xref back to the method 408 | for (xref = DfirstB(msg_ref); xref != BADADDR; xref = DnextB(msg_ref, xref)) 409 | { 410 | add_dref(xref, function, dr_O); 411 | } 412 | } 413 | } 414 | 415 | /* 416 | * function that will add xrefs to instance methods so we can easily find who's calling each method 417 | */ 418 | static add_inst_methods_xrefs(segea) 419 | { 420 | auto cstring, msg_ref, xref, function, ea; 421 | // start process valid code/data until the end of this segment 422 | // NOTE: we are bruteforcing by using NextHead instead of parsing the structure 423 | // the speed advantage of parsing the structure appears to be very small so let's keep it this way 424 | for (ea=SegStart(segea) ; ea!=BADADDR ; ea=NextHead(ea,SegEnd(segea))) 425 | { 426 | // Message("Processing %x\n", ea); 427 | // get the pointer to the correspondent C string we will use to find xrefs 428 | cstring = Dword(ea); 429 | // bypass empty strings, usually the header fields of class/methods 430 | if (SegName(cstring) != "__cstring") 431 | { 432 | continue; 433 | } 434 | // the function name we will add the xrefs to 435 | function = Dword(ea+8); 436 | // now try to find the pointer to message_refs, it's usually the first data xref 437 | // if there's no pointer to message_refs then there's no xrefs to this method (seems to hold true!) 438 | msg_ref = DfirstB(cstring); 439 | // test if it belongs to __message_refs and if not try to find it, if available 440 | if (SegName(msg_ref) != "__message_refs") 441 | { 442 | while(msg_ref != BADADDR) 443 | { 444 | msg_ref = DnextB(cstring, msg_ref); 445 | if (SegName(msg_ref) == "__message_refs") 446 | { 447 | break; 448 | } 449 | } 450 | } 451 | // if there's no ptr to message_refs then move to next method 452 | if (msg_ref == BADADDR) 453 | { 454 | continue; 455 | } 456 | 457 | // start looking up who's referencing the method 458 | // and add the xref back to the method 459 | for (xref = DfirstB(msg_ref); xref != BADADDR; xref = DnextB(msg_ref, xref)) 460 | { 461 | add_dref(xref, function, dr_O); 462 | } 463 | } 464 | } 465 | 466 | static main() 467 | { 468 | fix__objc_binary(); 469 | } --------------------------------------------------------------------------------