Skip to content

Playing around with parsing binaries / DWARF with Go

License

Notifications You must be signed in to change notification settings

vsoch/gosmeagle

Repository files navigation

Go Smeagle

This is mostly for fun - I wanted to see how hard it would be to parse a binary with Go. Right now we use the same logic as objdump to load it, and then print Symbols (and I found an entry to where the Dwarf is).

img/smeagle.gif

🚧️ under development 🚧️

Usage

To build the gosmeagle binary, you can do:

$ make

You can also interact as follows:

$ go run main.go

Parse

Parsing means outputting a corpus to JSON.

$ go run main.go parse libtest.so
{"library":"libtest.so","functions":[{"name":"__printf_chk","type":"Function"},{"parameters":[{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"long int","size":8},{"type":"__int128","size":16}],"name":"bigcall","type":"Function"}]}

or print pretty:

$ go run main.go parse libtest.so --pretty
{
    "library": "libtest.so",
    "functions": [
        {
            "name": "__printf_chk",
            "type": "Function"
        },
        {
            "parameters": [
                {
                    "type": "long int",
                    "size": 8
                },
                {
                    "type": "long int",
                    "size": 8
                },
                {
                    "type": "long int",
                    "size": 8
                },
                {
                    "type": "long int",
                    "size": 8
                },
                {
                    "type": "long int",
                    "size": 8
                },
                {
                    "type": "__int128",
                    "size": 16
                }
            ],
            "name": "bigcall",
            "type": "Function"
        }
    ]
}

Disasm

Disassembling means printing Assembly.

$ go run main.go disasm libtest.so
TEXT register_tm_clones(SB) 

TEXT __do_global_dtors_aux(SB) 

TEXT frame_dummy(SB) 

TEXT bigcall(SB) 
  0x1120		f3			?								
  0x1121		0f			?								
  0x1122		1e			?								
  0x1123		fa			CLI                                  // cli			
  0x1124		4883ec10		SUBQ $0x10, SP                       // sub $0x10,%rsp		
  0x1128		4989c9			MOVQ CX, R9                          // mov %rcx,%r9		
  0x112b		31c0			XORL AX, AX                          // xor %eax,%eax		
  0x112d		4889f1			MOVQ SI, CX                          // mov %rsi,%rcx		
  0x1130		ff742418		PUSHQ 0x18(SP)                       // pushq 0x18(%rsp)	
  0x1134		488d35c50e0000		LEAQ 0xec5(IP), SI                   // lea 0xec5(%rip),%rsi	
  0x113b		ff742428		PUSHQ 0x28(SP)                       // pushq 0x28(%rsp)	
  0x113f		4150			PUSHL R8                             // push %r8		
  0x1141		4989d0			MOVQ DX, R8                          // mov %rdx,%r8		
  0x1144		4889fa			MOVQ DI, DX                          // mov %rdi,%rdx		
  0x1147		bf01000000		MOVL $0x1, DI                        // mov $0x1,%edi		
  0x114c		e8fffeffff		CALL 0x1050                          // callq 0x1050		
  0x1151		4883c428		ADDQ $0x28, SP                       // add $0x28,%rsp		
  0x1155		c3			RET                                  // retq

Note that this library is under development, so stay tuned!

Load

To test loading a json corpus, we have the load command:

$ go run main.go load example/smeagle-output.json

Background

I started this library after discussion (see this thread) and wanting to extend Dwarf a bit and also reproduce Smeagle in Go.

Includes

Since I needed some functionality from debug/dwarf that was not public, the library is included here (with proper license/credit headers) in pkg/debug along with ELF that needs to return a matching type. The changes I made include

  • renaming readType to ReadType so it's public.
  • also renaming sigToType to SigToType so it's public
  • made typeCache public (TypeCache)
  • Added an "Original" (interface) to a CommonType, and then changed ReadType in dwarf/debug/type.go so that each case sets t.Original = t so we can return the original type to further parse (t.Common().Original).
  • Added a StructCache to the dwarf.Data in pkg/debug/dwarf/open.go that is populated in pkg/debug/dwarf/type.go as follows:
// ADDED: save the struct to the struct cache for later lookup
d.StructCache[t.StructName] = t

And then used in parsers/x86_64/parse.go to match a typedef (which only has name and type string) to a fully parsed struct (a struct, union, or class).

Docker

You can also use docker:

$ docker build -t ghcr.io/vsoch/gosmeagle .
$ docker run -it -v $PWD:/data ghcr.io/vsoch/gosmeagle parse /data/libtest.so

About

Playing around with parsing binaries / DWARF with Go

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages