Ok, here it is, the new version of qb-sync with lots of new features: new commands, sync multiple IDBs (and thus modules) with a debugger, Windbg remote control shortcuts in IDA, etc.
First, it is now possible to sync multiple IDBs (and thus modules) with a debugger. To achieve this the plugin architecture has evolved. Each IDA's instance of the plugin launches a broker (broker.py basically a socket to stdout pump, used to handle async events with QtCore.QProcess signals). On startup, the broker tries to connect to a new element: the dispatcher (dispatcher.py). If it can't find one, it starts one. Each broker registers itself with the dispatcher (idb name based matching). According to the information received from the debugger (current module's name), the dispatcher enables/disables the appropriate IDB's on the fly and dispatch (lucky enough for a dispatcher) messages to the IDB whose name matches.
In Windbg, to get module's name the previous version used "IDebugSymbols::GetNameByOffset" which sometimes returns dubious results (ex: image00000000_00400000 for 32-bits module on 64-bits OS). The new version uses IDebugSymbols2::GetModuleNameString with parameter DEBUG_MODNAME_LOADED_IMAGE which seems to work like a charm in most situations.
Handling multiple modules in the same IDB has always been sort of tiresome, without even talking about debugging and ASLR. That problem is partly solved: keep one IDB per module, launch an instance of the plugin for each one and let the dispatcher do its job. While you step in the debugger, the appropriate IDA instance gets enabled/disabled on the fly (on Windows 7/8 the IDA windows which gets enabled starts blinking in the taskbar). Debugging/tracing over multiple modules is now quite easy. That is a huge relief.
The first version of the plugin was a typical slave/master architecture; IDA being driven by Windbg, end of the story. This also has changed a little. I've added some feedback controls from IDA to Windbg: in IDA F2, F3, F5, F10, F11 keys are now mapped as Windbg remote control shortcuts. Suppose the debugging session is available (debugger is stopped, waiting for input) and the IDB enabled; you're in IDA's window:
- F2 allows you to set a breakpoint at the cursor address. The great thing here is that the bp address is automatically rebased !
- F3 is the same with one-shot breakpoint, useful to trace out of loops for example.
- F5 lets you resume the debugger.
- F10/F11 control debugger's engine step over/into.
Shift F2/F3 are used to set hardware breakpoints. We can now equally switch between debugger's window to disassembler's window and take the best of the two.
New commands have also been added; I will only cover few of them here:
- modmap/modunmap: these ones are especially useful when debugging "offensive" code that manually loads modules or piece of code (shellcode is a good exemple). modmap tooks advantage of Windbg synthetic module feature (IDebugSymbols3::AddSyntheticModule) which let's you tells the debugger to consider a certain memory range as module "xyz"; may you have an IDB which registers itself with the name "xyz" and you're good to sync.
- modcheck: sometimes a friend of yours complains that the plugin is broken and is not syncing correctly. He was obviously wrong. What actually happened is that the plugin only uses name based matching; your IDB may not be coherent with the module loaded into memory (let's say it has been silently updated). Using modcheck you can compare IDB's input file's and loaded modules' PDB GUIDs. For modules without Debug Directory, file's md5 can be used as well provided that the debuggee is local (can't access the target file system while remote debugging).
In case of doubt or emergency, just use the !synchelp command:
0:000> !synchelp [sync] extension commands help: > !sync <host> = synchronize with <host> or the default value > !syncoff = stop synchronization > !cmt [-a address] <string> = add comment at current eip (or [addr]) in IDA > !rcmt [-a address] <string> = reset comments at current eip (or [addr]) in IDA > !fcmt [-a address] <string> = add a function comment for 'f = get_func(eip)' (or [addr]) in IDA > !cmd <string> = execute command <string> and add its output as comment at current eip in IDA > !bc <||on|off|set 0xBBGGRR> = enable/disable path coloring in IDA color a single instruction at current eip if called without argument 'set' is used with an hex rgb code (ex: 0xFFFFFF) > !idblist = display list of all IDB clients connected to the dispatcher > !idbn <n> = set active idb to the n_th client. n should be a valid decimal value > !syncmodauto <on|off> = enable/disable idb auto switch based on module name > !jmpto <expression> = evaluate expression and sync IDA with result address (switch idb and rebase address if necessary) > !jmpraw <expression> = evaluate expression and sync IDA with result address (use current idb, no idb switch or address rebase) > !modcheck <||md5> = check current module pdb info or md5 with respect to idb's input file > !modmap <base> <size> <name> = map a synthetic module over memory range specified by base and size params > !modunmap <base> = unmap a synthetic module at base address > !bpcmds <||save|load|> = .bpcmds wrapper, save and reload .bpcmds output to current idb display saved data if called with no argument
An experimental OllyDbg2 support has been developed. For now only graph sync is available. More work on this may be done when OllyDbg2's final plugin API will be released. GDB support has been updated as well. IDA plugin is (mostly) agnostic and adding support for other debuggers is really straightforward.
Even though a lot of attention has been paid to make the plugin reliable, it could not hurt to preventively snapshot your IDB's for safety purpose.
See README for more information about how to build/install/use the plugin. Project is still in beta, feedbacks or suggestions/comments about potential bugs are appreciated. Source code is available (GNU GPL v3 license):
Hail to Bruce, Damien, Seb, Kévin and @Ivanlef0u for their help, feedbacks and thoughts.