WinDBG Scripting, finding ROP gadgets

Finding all ROP gadgets with windbg using only windbg scripting? It can be done.

!for_each_module ".if(not(wo(dwo(${@#Base}+0x3c)+${@#Base}+46+18) & 0x40)) {r @$t3 = @#End - @#Base;.foreach /s (retn \"C2 C3\") {.foreach (f {s -[1]b @#Base L@$t3 ${retn}}) {.for(r @$t0 = 1; @$t0 < 4; r @$t0 = @$t0 + 1) {r @$t1 = 0;.foreach (g {.catch {u f - @$t0 L@$t0+1}}) {.if($spat(\"${g}\", \"*ret*\") != 0) {r @$t1 = 1}};.if(@$t1 == 1) {.printf \"---------------------- size %x\", @$t0;.echo;.catch {u f - @$t0 L@$t0+1}}}}}}"

or broken down:

!for_each_module " .if(not(wo(dwo(${@#Base}+0x3c)+${@#Base}+46+18) & 0x40)) { r @$t3 = @#End - @#Base; .foreach /s (retn \"C2 C3\") { .foreach (f {s -[1]b @#Base L@$t3 ${retn}}) { .for(r @$t0 = 1; @$t0 < 4; r @$t0 = @$t0 + 1) { r @$t1 = 0; .foreach (g {.catch {u f - @$t0 L@$t0+1}}) { .if($spat(\"${g}\", \"*ret*\") != 0) { r @$t1 = 1 } }; .if(@$t1 == 1) { .printf \"---------------------- size %x\", @$t0; .echo; .catch {u f - @$t0 L@$t0+1} } } } } } "

or even further broken down:

.if(not(wo(dwo(${@#Base}+0x3c)+${@#Base}+46+18) & 0x40)) { r @$t3 = @#End - @#Base;

for each module read the offset to the PE Headerdwo(${@#Base}+0x3c) then add that offset to the base, then move up 0x18 to reach the Optional PE Header, then fetch the word at offset 0x46 of the Optional PE Header (DllCharacteristics) then check for the IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 flag. Set @$t3 to reflect the size of the image to be used later in the C3 byte search.

this is followed by: .foreach /s (retn \"C2 C3\") which sets the variable 'retn' first to C2 and then to C3

Then .foreach (f {s -[1]b @#Base L@$t3 ${retn}}) will run the command s -[1]b @#Base L@$t3 C2 (or C3 depending on retn) and for ever line in the result its set the variable 'f' to be used later on.

's' will search memory, -[1] means: only list the addresses you find, and 'b' means search for bytes. So we search for all occurences of 'C3' and fetch the addresses into 'f'

.for(r @$t0 = 1; @$t0 < 4; r @$t0 = @$t0 + 1) {

We want to dissassmble a few bytes back from the C3 we found, so we do 1 - 3 bytes back from where we found the C3

.foreach (g {.catch {u f - @$t0 L@$t0+1}}) { .if($spat(\"${g}\", \"*ret*\") != 0) { r @$t1 = 1 }

This dissasembles and then checks if we do indeed have a 'ret' in the dissambled code. Future addition: look for ??? in output before the 'ret'

This it work? Yes it does. Sortofish. It will dissasemble more then it should since the 'u' command doesnt take a byte length but and instruction length. And just dumping all the gadgets on the commandline is silly so you might want to add and .logopen and .logclose to the script.

I wrote it mostly for fun and to show it is possible to do a bit more with windbg scripts then just set some breakpoints.

also:

!for_each_module ".if(wo(dwo(${@#Base}+0x3c)+${@#Base}+46+18) & 0x40) { .echo \"${@#ModuleName}: aslr\"; } .else { .echo \"${@#ModuleName} NO ASLR\"; };"

Will dump all the modules and state if they do or dont have ASLR

Or, small enough to fit into 140 chars:

!for_each_module ".if(not(wo(dwo(${@#Base}+0x3c)+${@#Base}+46+18)&0x40)){.echo \"${@#ModuleName} NO ASLR\";};"