grap: define and match graph patterns within binaries
grap takes patterns and binary files, uses a Casptone-based disassembler to obtain the control flow graphs from the binaries, then matches the patterns against them.
Patterns are user-defined graphs with instruction conditions ("opcode is xor and arg1 is eax") and repetition conditions (3 identical instructions, basic blocks...).
grap is available as a standalone tool with a disassembler and python bindings, and as an IDA plugin which takes advantage of the disassembly done by IDA and the reverser.
Support: * Files: disassembly of PE, ELF and raw binary, further files should work within IDA * Architecture: x86 and x86_64
This document describes how to build and install grap on a Linux distribution.
You may also read:
Besides compilers (build-essential), the following dependencies must be installed:
Thus on Ubuntu / Debian, this should work :
sudo apt-get install build-essential cmake bison flex libboost-regex-dev libboost-system-dev libboost-filesystem-dev libseccomp-dev python3-dev python3-pefile python3-pyelftools python3-capstone swig
Please note that those were tested for the latest Ubuntu LTS (18.04.3). Packages may differ depending on your distribution.
The following commands will build and install the project:
mkdir build; cd build/as we advise you to build the project in a dedicated directory
cmake ../src/; makewill build with cmake and make
sudo make installwill install grap into /usr/local/bin/
SWIG might fail to find python3 if your default version is python2, this can be overcome by switching to python3 as default. For instance on Ubuntu:
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10
The tool can be launched by using the following command:
$ grap [options] pattern test_paths
Below are a few examples of supported options:
grap -h: describes supported options
One can let grap infer a pattern from a string. Only few options are supported but this is useful for prototyping:
grap "opcode is xor and arg1 contains '['" (test.exe): look for a xor with a memory write
grap -v "sub->xor->sub" (test.exe): -v will output the path of the inferred pattern
Choose how the binaries are disassembled:
grap -od (pattern.grapp) samples/*: disassemble files in folder samples/ with no attempt at matching
grap -f (pattern.grapp) (test.exe): force re-disassembling the binary, then matches it against pattern.grapp
grap --raw (pattern.grapp) (test.bin): disassembling raw file (use --raw-64 for 64 bits binaries)
Control the verbosity of the output:
grap -q -sa (pattern.grapp) (samples/*.grapcfg): match disassembled files, show matching and non matching files, one per line
grap -m (pattern.grapp) (test.grapcfg): show all matched nodes
Choose where the disassembled file(s) (.grapcfg) are written; match multiple files against multiple patterns:
grap patterns/basic_block_loop.grapp -o ls.grapcfg /bin/ls: disassemble ls into ls.grapp and looks for basic block loops
grap (pattern1.grapp) -p (pattern2.grapp) (test.exe): match against multiple pattern files
grap -r -q patterns/ /bin/ -o /tmp/: disassemble all files from /bin/ into /tmp/ and matches them against all .grapp patterns from patterns/ (recursive option -r applies to /bin/, not to patterns/)
The following pattern detects a decryption loop consisting of a xor followed by sub found in a Backspace sample: ``` digraph decryptionmd54ee00c46da143ba70f7e6270960823be { A [cond=true, repeat=3] B [cond="opcode is xor and arg2 is 0x11"] C [cond="opcode is sub and arg2 is 0x25"] D [cond=true, repeat=3] E [cond="opcode beginswith j and nchildren == 2"]
A -> B B -> C C -> D D -> E E -> A [childnumber=2] } ```
Note that pattern files can contain multiple pattern graphs.
You may find additional pattern examples in two directories:
On malware samples:
Python bindings usage:
You will find more documentation in the doc/ folder:
The syntax of pattern and test graphs is detailed in the file grap_graphs.pdf within the release section.
grap is licensed under the MIT license. The full license text can be found in LICENSE.