Cherry referring to my last name kirschju.re Forward and Reverse Engineering

Exporting Vectorized Function Graphs from IDA Pro

I recently faced the problem that I wanted to place the control flow graph of a compiled function in a PDF file while still somewhat satisfying aesthetic aspirations. I get that taking a screenshot from IDA’s graph view widget does the trick most of the time, but the job becomes tedious if the exported function is larger than the screen size (implying pixel-fiddling in your favorite image editor as a consequence). In case one wants to maintain a resolution that would allow future readers to zoom onto details of a more complex graph, one quickly ends up sewing together dozens of screenshots.

IDA offers to export the displayed function graph as Geometric Description Language (GDL) file, but all spatial information such as basic block positioning and edge routing is not part of the exported file. The result is a rather poor-looking graph, that serves the purpose, but isn’t particularly enjoyable to look at. For example, the following function

Screenshot of a simple Control Flow Graph as rendered by IDA Pro

when exported to GDL and converted to PNG with graph-easy

graph-easy --from gdl --input=graph.gdl --png --output=converted_from_gdl.png

looks as below:

Messy GDL Graph exported from IDA Pro and converted to PNG

This—in my humble optinion—isn’t particularly a treat to the eyes.

I therefore took the time to write a small plugin for IDA Pro that retrieves the graphical parameters from a particular graph view and exports them to JSON. For example the (shortened) JSON of the function above looks as follows:

{
    "font_flags" : 1,
    "font_name" : "Droid Sans Mono",
    "font_size" : 12,
    "functions" : 
    [
        {
            "basic_blocks" : 
            [
                {
                    "addr_end" : 13825,
                    "addr_start" : 13808,
                    "bottom" : 273,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQM7IF9faW50NjQgX19mYXN0Y2FsbCBzdWJfMzVGMChfX2dpZF90IGdpZCkCAwA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "ARsBKDAwMDAwMDAwMDAwMDM1RjABGgEoMDAwMDAwMDAwMDAwMzVGMHN1Yl8zNUYwAhogcHJvYyBuZWFyAhsA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "OyBfX3Vud2luZCB7AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwdXNoAgUgICAgASkBIXIxMgIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXIxMmQCIQIpAQksAgkgASoBDDECDAIqAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwdXNoAgUgICAgASkBIXJicAIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIWVicAIhAikBCSwCCSABKgEhZWRpAiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwdXNoAgUgICAgASkBIXJieAIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV0ZXN0AgUgICAgASkBIXNpbAIhAikBCSwCCSABKgEhc2lsAiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVqegIFICAgICAgASBzaG9ydCACIAEpARoBKDAwMDAwMDAwMDAwMDM2NTBsb2NfMzY1MAIaAikA"
                        }
                    ],
                    "id" : 0,
                    "left" : 130,
                    "right" : 598,
                    "top" : 0
                },
                {
                    "addr_end" : 13839,
                    "addr_start" : 13825,
                    "bottom" : 424,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=ie+J8+hm6///SIXAdBE=",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIWVkaQIhAikBCSwCCSABKgEhZWJwAiECKiAgICAgICAgAQI7IGdpZAICAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIWVieAIhAikBCSwCCSABKgEhZXNpAiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVjYWxsAgUgICAgASkBJQEoMDAwMDAwMDAwMDAwMjE3MF9nZXRncmdpZAIlAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV0ZXN0AgUgICAgASkBIXJheAIhAikBCSwCCSABKgEhcmF4AiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVqegIFICAgICAgASBzaG9ydCACIAEpARoBKDAwMDAwMDAwMDAwMDM2MjBsb2NfMzYyMAIaAikA"
                        }
                    ],
                    "id" : 1,
                    "left" : 157,
                    "right" : 482,
                    "top" : 303
                },
                {
                    "addr_end" : 13847,
                    "addr_start" : 13839,
                    "bottom" : 861,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=ie+J8+hm6///SIXAdBE=SIs4QYnc60s=",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJkaQIhAikBCSwCCSABKgEJWwIJASFyYXgCIQEJXQIJAioA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXIxMmQCIQIpAQksAgkgASoBIWVieAIhAioA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVqbXACBSAgICAgASBzaG9ydCACIAEpARoBKDAwMDAwMDAwMDAwMDM2NjJsb2NfMzY2MgIaAikA"
                        }
                    ],
                    "id" : 2,
                    "left" : 0,
                    "right" : 248,
                    "top" : 778
                },
                {
                    "addr_end" : 13904,
                    "addr_start" : 13856,
                    "bottom" : 746,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=ie+J8+hm6///SIXAdBE=SIs4QYnc60s=ugUAAABIjTVkUgAAMf9FMeTouur//0iJ6TH2Mf9IicIxwOjp7P//Zg8fhAAAAAAA",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "ASgwMDAwMDAwMDAwMDAzNjIwARoBKDAwMDAwMDAwMDAwMDM2MjBsb2NfMzYyMAIaAQk6AgkgICAgICAgICAgICAgICABAjsgY2F0ZWdvcnkCAgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIWVkeAIhAikBCSwCCSABKgEMNQIMAioA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVsZWECBSAgICAgASkBIXJzaQIhAikBCSwCCSABKgEGASgwMDAwMDAwMDAwMDA4ODkwYUNhbm5vdEZpbmROYW1lXzACBgIqIAEEOyABKDAwMDAwMDAwMDAwMDg4OTAiY2Fubm90IGZpbmQgbmFtZSBmb3IgZ3JvdXAgSUQgJWx1IgIEAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV4b3ICBSAgICAgASkBIWVkaQIhAikBCSwCCSABKgEhZWRpAiECKiAgICAgICAgAQI7IGRvbWFpbm5hbWUCAgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV4b3ICBSAgICAgASkBIXIxMmQCIQIpAQksAgkgASoBIXIxMmQCIQIqAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVjYWxsAgUgICAgASkBJQEoMDAwMDAwMDAwMDAwMjBGMF9kY2dldHRleHQCJQIpAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJjeAIhAikBCSwCCSABKgEhcmJwAiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV4b3ICBSAgICAgASkBIWVzaQIhAikBCSwCCSABKgEhZXNpAiECKiAgICAgICAgAQI7IGVycm51bQICAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV4b3ICBSAgICAgASkBIWVkaQIhAikBCSwCCSABKgEhZWRpAiECKiAgICAgICAgAQI7IHN0YXR1cwICAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJkeAIhAikBCSwCCSABKgEhcmF4AiECKiAgICAgICAgAQI7IGZvcm1hdAICAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQV4b3ICBSAgICAgASkBIWVheAIhAikBCSwCCSABKgEhZWF4AiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVjYWxsAgUgICAgASkBJQEoMDAwMDAwMDAwMDAwMjMzMF9lcnJvcgIlAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVub3ACBSAgICAgASkBIHdvcmQgcHRyAiAgAQlbAgkBIXJheAIhAQkrAgkBIXJheAIhAQkrAgkBHzAwMDAwMDAwaAIfAQldAgkCKQA="
                        }
                    ],
                    "id" : 3,
                    "left" : 137,
                    "right" : 891,
                    "top" : 454
                },
                {
                    "addr_end" : 13922,
                    "addr_start" : 13904,
                    "bottom" : 918,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=ie+J8+hm6///SIXAdBE=SIs4QYnc60s=ugUAAABIjTVkUgAAMf9FMeTouur//0iJ6TH2Mf9IicIxwOjp7P//Zg8fhAAAAAAASInvSI011owAAOgRAwAASInH",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "ASgwMDAwMDAwMDAwMDAzNjUwARoBKDAwMDAwMDAwMDAwMDM2NTBsb2NfMzY1MAIaAQk6AgkA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJkaQIhAikBCSwCCSABKgEhcmJwAiECKgA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVsZWECBSAgICAgASkBIXJzaQIhAikBCSwCCSABKgEkASgwMDAwMDAwMDAwMDBDMzMwdW5rX0MzMzACJAIqAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVjYWxsAgUgICAgASkBGgEoMDAwMDAwMDAwMDAwMzk3MHN1Yl8zOTcwAhoCKQA="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJkaQIhAikBCSwCCSABKgEhcmF4AiECKiAgICAgICAgAQI7IHMCAgA="
                        }
                    ],
                    "id" : 4,
                    "left" : 258,
                    "right" : 561,
                    "top" : 778
                },
                {
                    "addr_end" : 13942,
                    "addr_start" : 13922,
                    "bottom" : 1202,
                    "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==QVRBvAEAAABVif1TQIT2dE8=ie+J8+hm6///SIXAdBE=SIs4QYnc60s=ugUAAABIjTVkUgAAMf9FMeTouur//0iJ6TH2Mf9IicIxwOjp7P//Zg8fhAAAAAAASInvSI011owAAOgRAwAASInHSIs1P4wAAOiS6///RIngW11BXMM=",
                    "compressed" : false,
                    "disasm_lines" : 
                    [
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "ASgwMDAwMDAwMDAwMDAzNjYyARoBKDAwMDAwMDAwMDAwMDM2NjJsb2NfMzY2MgIaAQk6AgkgICAgICAgICAgICAgICABAjsgc3RyZWFtAgIA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIXJzaQIhAikBCSwCCSABKgEhY3MCIQEJOgIJAQcBKDAwMDAwMDAwMDAwMEMyQThzdGRvdXQCBwIqAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVjYWxsAgUgICAgASkBJQEoMDAwMDAwMDAwMDAwMjIwMF9mcHV0c191bmxvY2tlZAIlAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVtb3YCBSAgICAgASkBIWVheAIhAikBCSwCCSABKgEhcjEyZAIhAioA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwb3ACBSAgICAgASkBIXJieAIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwb3ACBSAgICAgASkBIXJicAIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVwb3ACBSAgICAgASkBIXIxMgIhAikA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AQVyZXRuAgUA"
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "OyB9IC8vIHN0YXJ0cyBhdCAzNUYwAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "ARsBKDAwMDAwMDAwMDAwMDM1RjABGgEoMDAwMDAwMDAwMDAwMzVGMHN1Yl8zNUYwAhogZW5kcAIbAA=="
                        },
                        {
                            "bg_color" : 4294967295,
                            "text" : "AA=="
                        }
                    ],
                    "id" : 5,
                    "left" : 101,
                    "right" : 459,
                    "top" : 948
                }
            ],
            "bitness" : 64,
            "bytes" : "eJwBhgB5/0FUQbwBAAAAVYn9U0CE9nRPie+J8+hm6///SIXAdBFIizhBidzrS2YPH4QAAAAAALoFAAAASI01ZFIAADH/RTHk6Lrq//9Iiekx9jH/SInCMcDo6ez//2YPH4QAAAAAAEiJ70iNNdaMAADoEQMAAEiJx0iLNT+MAADokuv//0SJ4FtdQVzDc4I7MQ==",
            "color" : 4294967295,
            "edges" : 
            [
                {
                    "color" : 188,
                    "coords" : 
                    [
                        "354 273",
                        "354 288",
                        "319 288",
                        "319 303"
                    ],
                    "dest_id" : 1,
                    "source_id" : 0
                },
                {
                    "color" : 37120,
                    "coords" : 
                    [
                        "374 273",
                        "374 288",
                        "901 288",
                        "901 764",
                        "419 764",
                        "419 778"
                    ],
                    "dest_id" : 4,
                    "source_id" : 0
                },
                {
                    "color" : 188,
                    "coords" : 
                    [
                        "309 424",
                        "309 439",
                        "122 439",
                        "122 760",
                        "124 760",
                        "124 778"
                    ],
                    "dest_id" : 2,
                    "source_id" : 1
                },
                {
                    "color" : 37120,
                    "coords" : 
                    [
                        "329 424",
                        "329 439",
                        "514 439",
                        "514 454"
                    ],
                    "dest_id" : 3,
                    "source_id" : 1
                },
                {
                    "color" : 13320960,
                    "coords" : 
                    [
                        "124 861",
                        "124 933",
                        "270 933",
                        "270 948"
                    ],
                    "dest_id" : 5,
                    "source_id" : 2
                },
                {
                    "color" : 13320960,
                    "coords" : 
                    [
                        "514 746",
                        "514 760",
                        "399 760",
                        "399 778"
                    ],
                    "dest_id" : 4,
                    "source_id" : 3
                },
                {
                    "color" : 13320960,
                    "coords" : 
                    [
                        "409 918",
                        "409 933",
                        "290 933",
                        "290 948"
                    ],
                    "dest_id" : 5,
                    "source_id" : 4
                }
            ],
            "end" : 13942,
            "error" : "",
            "flags" : 21504,
            "frame_pointer_delta" : 0,
            "frame_size" : 24,
            "has_graph" : true,
            "name" : "sub_35F0",
            "start" : 13808,
            "valid" : true
        }
    ]
}

The JSON then is consumed by a small python script that produces a scalable vector graphic (SVG):

Scalable Vector Graphic showing the function graph

This isn’t pixel-perfect either, but the result is good enough for my needs. To embed the graph into a PDF, I turned the SVG into a PDF via rsvg-convert:

rsvg-convert -f pdf -o output.pdf input.svg