OpenDis - A Framework for Binary Static AnalysisOpenDis borns as a tool to make easier the life of a security researcher looking for vulnerabilities in binaries. Many tools exists to accomplish the same thing but the vast majority of these are closed source (at least) and, commonly, very expensive commercial tools.
How to use OpenDisThe main tool is called "dis.py" (very original). You will find the script in $INGUMA_DIR/dis. The following is the output when you invokes dis.py without passing parameters:
Description of the arguments-s: If you're interested in writting your own scripts using the OpenDis framework you will need, first, to save the binary in the OpenDis database format. Use the -s=filename argument to create a database.
-p: Translates assembler code to pseudo-c format. It doesn't work properly.
-r: Check for the most common error prone blocks, calls, etc... Will print an alert (CHECK:) highlighting the possible vulnerable assembly blocks.
-v: OpenDis will try to find variables, showing these as local_var_size, where size is the size of the variable. This flag disables the feature.
-a: OpenDis tries to find function arguments as well as variables althought it doesn't work so well at the moment. Using this flag disables the feature.
-j: Report the most interesting calls found in the assembly code. When developing exploits for an overflow, format, etc... you will need to find points to jump to, in example, if your shellcode is stored in %ebx, you need to find a way to do call *%ebx or jmp *%ebx. OpenDis reports all the most interesting calls and if these addresses appears to be usables or not.
-ph: The FreePascal hack. Shows only code before pascalmain removing many uninteresting code, not written by the developer but by the fpc compiler.
-mh: Only shows code found prior to the function main. Commonly, compiler generated code goes after the main function. Consider it a hack that sometimes works.
-i: If you're not interested in the constants found in the binary you can specify this flag to ignore the .rodata section.
-rh: When looking for the text of a constant sometimes OpenDis fails to find the valid start offset. This hack will look afterward in the .rodata section for the 0x00 hexadecimal character considering it the start of the constant.
-d: OpenDis tries to remove many compiler generated functions. If you're interested in taking a look to these functions you can use this flag. Not so util, IMHO.
-c: Very usefull when you're decompiling binaries not for x86 processors. Specify the processor the binary was compiled for and OpenDis (and internally objdump and nm) will take specific actions for these kind of processors. The toolkit supports "advanced" options for x86, sparc and avr processors.
Imagine the following very vulnerable C program:
OpenDis will generate the following output when invoked passing as the unique argument the name of the compiled binary file:
As you can see the code is more readable that raw assembly. Next, try some "advanced" feature of OpenDis, the automatic detection of vulnerable/error prone constructions. Invoke the script "dis.py" passing the argument -r. Notice the extralines at the bottom of the output:
Is not cool? The detection of these constructions, at least, in small programs is very easy. In the first assembly block, the strcpy overflow, we can find the instruction lea local_size_10, %eax, which specifies the size of some statically sized array. Two lines afterward the call to strcpy is made. If we can pass an argument larger than 10 (local_size_10) we will overflow the buffer of our vulnerable program.
In the second block OpenDis found that the printf call is made with a variable. It may (or may not) be a vulnerability, as is in our example.
Well, we have 2 vulnerbilities in our code, and these vulnerabilities were found by OpenDis. In large static analysis projects you will find more false positives than real ones but, well, at least OpenDis points us where to start finding vulns.
How to use the FrameworkAs of version 0.0.6 of Inguma, you will find 2 usage examples of the API: dbprint.py and asmdiff.py. The first script (dbprint.py) takes as the first parameter an OpenDis format database and prints to stdout the complete assembler code.
The second example (and the most interesting one) tries to find differences between 2 binary versions of the same program, library or object file. I use it very 3 months, in example, to known what changes were made to Oracle patches (the CPU, Critical Patch Update) with more or less luck.
Using the Framework's APIOpenDis databases are simple [c]pickle format objects which contains many classes stored in the asmclasses.py file so, to use the API, at the top of your script add the following 2 Python code lines:
Next, open the database file and load the pickle object. The call will return 2 objects: the .rodata section (class CRoData) and the complete program (class CProgram).
The obj object is a list of the program's functions (returning CProcedure type objects) so you can iterate over all functions, search specific functions, etc... Every procedure will have their respective assembly lines and you can iterate over all lines using the lines property of the CProcedure's class. Every procedure, also, has the following properties:
While iterating over all lines in a procedure you will need to known what is the corresponding assembly code, label position (i.e., main+0x12), etc... The following are the available properties of the CLine class:
With these objects and properties you can easily write scripts to search for specific constructions, calls, etc... in about seconds. The following full example simply prints the complete assembly code given a database: