Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Starting from Visual Studio 2010 Ultimate there is a cool feature called DGML (Directed Graph Markup Language).
I wrote a small script to convert the disassembled code from WinDBG into a DGML.
In order to use it, simply type the following commands under a debug session:
.shell -o LoadLibraryA.dgml -ci "uf kernel32!LoadLibraryA" cscript.exe /nologo dasm2dgml.js
A DGML file will be generated with the given name, and here is what it looks like:
Here is the source code:
var EBB = [];
var hypertext=function(s){
var r=[],L=s.length;
for(var i=0;i<L;i++){
var c=s.charAt(i);
switch(c){
case '"':r.push('"');break;
case '&':r.push('&');break;
case '<':r.push('<');break;
case '>':r.push('>');break;
default:r.push(c);}}
return r.join('');
};
var map=function(f,v){var L=v.length,r=[];for(var i=0;i<L;i++)r.push(f(v[i]));return r;};
(function(){
var blk;
var CExtendedBasicBlock = function(name, previous, next){
this.Address = '';
this.Code = [];
this.Name = name;
this.Previous = previous;
this.Next = next;
};
while(true)
{
if(WScript.StdIn.AtEndOfStream)
break;
var strSourceLine = WScript.StdIn.ReadLine().replace(/(^\s+)|(\s+$)/g, '');
if(!strSourceLine)
continue;
if(strSourceLine.match(/.*:$/))
{
blk = new CExtendedBasicBlock(strSourceLine.slice(0, -1));
EBB.push(blk);
}
else
{
blk.Address = blk.Address || strSourceLine.match(/^[^\s]+/)[0];
blk.Code.push(strSourceLine.replace(/[^\s]*\s+/, '').replace(/[^\s]*\s+/, ''));
}
}
})();
EBB = EBB.sort(function(x, y){ return x.Address == y.Address ? 0 : x.Address > y.Address ? 1 : -1; });
for(var i = 1; i < EBB.length; i++)
{
EBB[i].Previous = EBB[i - 1];
EBB[i].Previous.Next = EBB[i];
}
WScript.Echo('<DirectedGraph Background="#FFFFFF" GraphDirection="TopToBottom" xmlns="https://schemas.microsoft.com/vs/2009/dgml">');
WScript.Echo(' <Nodes>');
map(function(blk){
var content = hypertext(blk.Name + ' (' + blk.Address + ')') + '
';
map(function(instruction){
content += '
' + hypertext(instruction);
}, blk.Code);
WScript.Echo(' <Node Id="' + hypertext(blk.Name) + '" Label="' + content + '" />');
}, EBB);
WScript.Echo(' </Nodes>');
WScript.Echo(' <Links>');
map(function(blk){
map(function(instruction){
map(function(x){
var idx = instruction.indexOf(x.Name);
idx = idx >= 0 ? instruction.charAt(idx + x.Name.length) : -1;
if(idx == '' || idx == ' ')
WScript.Echo(' <Link Source="' + hypertext(blk.Name) + '" Target="' + hypertext(x.Name) + '" />');
}, EBB);
}, blk.Code);
if(blk.Next && !(blk.Code[blk.Code.length - 1].match(/^[^\s]+/)[0] in {jmp: 0, ret: 0}))
WScript.Echo(' <Link Category="FallThrough" Source="' + hypertext(blk.Name) + '" Target="' + hypertext(blk.Next.Name) + '" />');
}, EBB);
WScript.Echo(' </Links>');
WScript.Echo(' <Styles>');
WScript.Echo(' <Style TargetType="Node">');
WScript.Echo(' <Setter Property="FontFamily" Value="Consolas" />');
WScript.Echo(' <Setter Property="FontSize" Value="11" />');
WScript.Echo(' <Setter Property="Background" Value="White" />');
WScript.Echo(' <Setter Property="NodeRadius" Value="2" />');
WScript.Echo(' </Style>');
WScript.Echo(' <Style TargetType="Link">');
WScript.Echo(' <Condition Expression="HasCategory(\'FallThrough\')" />');
WScript.Echo(' <Setter Property="Background" Value="Red" />');
WScript.Echo(' <Setter Property="Stroke" Value="Red" />');
WScript.Echo(' </Style>');
WScript.Echo(' </Styles>');
WScript.Echo('</DirectedGraph>');
Notes:
- This script cannot generate 100% accurate control flow diagram, you will have to do further analysis (e.g. jmp eax).
- I haven't got a chance to test under WOA (ARM32), so I leave it as a homework for our readers.
Enjoy:)