├── .gitignore ├── .merlin ├── LICENSE.txt ├── Makefile ├── README.md ├── TODO.md ├── _oasis ├── _tags ├── bin ├── elf │ ├── deadbeef.elf │ ├── efbeadde.elf │ └── libbeef.so ├── mach │ ├── deadbeef.mach │ ├── efbeadde.mach │ └── libbeef.mach └── pe │ ├── deadbeef.exe │ ├── efbeadde.exe │ └── libbeef.dll ├── build.sh ├── cee ├── deadbeef.c ├── efbeadde.c ├── libbeef.c ├── other ├── other.c └── other_sstrip ├── configure ├── lib ├── META ├── Rdr.ml ├── RdrGraph.ml ├── RdrObject.ml ├── SymbolMap.ml ├── TransitiveClosure.ml ├── elf │ ├── Elf.ml │ ├── ElfConstants.ml │ ├── ElfCoverage.ml │ ├── ElfDynamic.ml │ ├── ElfHeader.ml │ ├── ElfProgramHeader.ml │ ├── ElfReloc.ml │ ├── ElfSectionHeader.ml │ ├── ElfSymbolTable.ml │ ├── ElfVersion.ml │ ├── META │ ├── elf.mldylib │ └── elf.mllib ├── goblin │ ├── Goblin.ml │ ├── GoblinExport.ml │ ├── GoblinImport.ml │ ├── GoblinTree.ml │ ├── META │ ├── goblin.mldylib │ └── goblin.mllib ├── mach │ ├── META │ ├── Mach.ml │ ├── MachBindOpcodes.ml │ ├── MachConstants.ml │ ├── MachCoverage.ml │ ├── MachCpuTypes.ml │ ├── MachExports.ml │ ├── MachFat.ml │ ├── MachHeader.ml │ ├── MachImports.ml │ ├── MachLoadCommand.ml │ ├── MachLoadCommandMacro.ml │ ├── MachLoadCommandTypes.ml │ ├── MachRebaseOpcodes.ml │ ├── MachSection.ml │ ├── MachSymbolTable.ml │ ├── MachThread64.ml │ ├── MachVersion.ml │ ├── mach.mldylib │ └── mach.mllib ├── pe │ ├── PE.ml │ ├── PEByteCoverage.ml │ ├── PECharacteristic.ml │ ├── PECharacteristic.txt │ ├── PEDataDirectories.ml │ ├── PEExport.ml │ ├── PEHeader.ml │ ├── PEImport.ml │ ├── PEMachineType.ml │ ├── PEMachineType.txt │ ├── PEOptionalHeader.ml │ ├── PESectionTable.ml │ ├── PEUtils.ml │ ├── pe.mldylib │ └── pe.mllib ├── rdr.mldylib ├── rdr.mllib └── utils │ ├── Binary.ml │ ├── ByteCoverage.ml │ ├── Generics.ml │ ├── Input.ml │ ├── Json.ml │ ├── Leb128.ml │ ├── Leb128.mli │ ├── META │ ├── RdrCommand.ml │ ├── RdrPrinter.ml │ ├── RdrStorage.ml │ ├── RdrUtils.ml │ ├── utils.mldylib │ └── utils.mllib ├── myocamlbuild.ml ├── opam ├── descr ├── files │ └── rdr.install ├── findlib └── opam ├── project_deps.png ├── scripts └── consts_to_pattern.py ├── setup.ml └── src ├── Config.ml ├── META ├── ReadElf.ml ├── ReadMach.ml ├── ReadPE.ml ├── main.ml ├── rdrutils.mldylib ├── rdrutils.mllib └── x86 ├── X86Assembly.ml └── X86_64.ml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | _build 3 | cee/* 4 | doc/* 5 | *.png 6 | *.gv 7 | *.sh 8 | stats/* 9 | bin/* 10 | old/* 11 | graph/* 12 | darwin/* 13 | 14 | #oasis stuff 15 | *.native 16 | *.byte 17 | *.docdir 18 | 19 | setup.data 20 | setup.log -------------------------------------------------------------------------------- /.merlin: -------------------------------------------------------------------------------- 1 | S lib 2 | S lib/mach 3 | S lib/elf 4 | S lib/pe 5 | S lib/goblin 6 | S lib/utils 7 | 8 | S src 9 | 10 | B _build/lib 11 | B _build/lib/* 12 | B _build/src 13 | B _build/src/* 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of rdr nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: a3c674b4239234cbbe53afe090018954) 3 | 4 | SETUP = ocaml setup.ml 5 | 6 | build: setup.data 7 | $(SETUP) -build $(BUILDFLAGS) 8 | 9 | doc: setup.data build 10 | $(SETUP) -doc $(DOCFLAGS) 11 | 12 | test: setup.data build 13 | $(SETUP) -test $(TESTFLAGS) 14 | 15 | all: 16 | $(SETUP) -all $(ALLFLAGS) 17 | 18 | install: setup.data 19 | $(SETUP) -install $(INSTALLFLAGS) 20 | 21 | uninstall: setup.data 22 | $(SETUP) -uninstall $(UNINSTALLFLAGS) 23 | 24 | reinstall: setup.data 25 | $(SETUP) -reinstall $(REINSTALLFLAGS) 26 | 27 | clean: 28 | $(SETUP) -clean $(CLEANFLAGS) 29 | 30 | distclean: 31 | $(SETUP) -distclean $(DISTCLEANFLAGS) 32 | 33 | setup.data: 34 | $(SETUP) -configure $(CONFIGUREFLAGS) 35 | 36 | configure: 37 | $(SETUP) -configure $(CONFIGUREFLAGS) 38 | 39 | .PHONY: build doc test all install uninstall reinstall clean distclean configure 40 | 41 | # OASIS_STOP 42 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Feature TODOs 2 | 3 | This is a non-exhaustive list of some features that would be nice. 4 | 5 | 1. 32-bit ELF and 32-bit mach. 6 | Honestly, not too interested in implementing this, but will be more than happy to accept a pull request which does implement it. It shouldn't be too hard, just more tedious and boring than anything. Philosophically, 32-bit binaries in my opinion are dead, or at least should be, and the only reason we still have them are because of crappy windows game designers linking against 32-bit libraries and porting their shoddy code to linux, and strangely enough, hacker dudes who write reams of tutorials on 32-bit binary structure, disassembly, etc. The faster we usher their death on in the real world, the better. Nevertheless, practicality rules, and if there is enough demand, should eventually implement... 7 | 8 | 2. Versioned and/or named symbol maps. So, `rdr -m ` to use a specific map, say built with `rdr -b `; similarly, versioning maps so that upgrades don't cause segfaults :disappointed:. 9 | 10 | 3. Faster map; don't just marshall, use a custom binary trie, similar to the export trie in mach binaries, to do fast symbol lookups ourselves. Much of the time spent looking up symbols is the initial load of the 70 meg file to marshall into the map; the lookup is a fraction of that time. I'd prefer to just walk the binary tree with an open fd in `O(log(n))` every time, instead of the large overhead of reading the _entire_ file in; this becomes especially prevalent on linux when `rdr -i ` is invoked. Write now the `ToL` ref cell is a _hack_. And of course, the larger the trie, especially with something like `rdr -r -b "/usr/bin/ /usr/local"` might become prohibitive w.r.t load times, since the map could in principle be hundreds of megs. 11 | 12 | 4. x86 interpreter. Yea, I know it's crazy; but this is the feature I'll be working on. Maybe it's too much, but I want it, for other reasons... Even a simple one would be worth the time and wheel reinvention of creating an x86 backend/AST. 13 | 14 | 5. Of course the interpreter would also need a disassembler; for philosophical reasons I'd prefer the backend disassembler **not** use GNU BFD or llvm; the philosophical reason is essentially outlined in [a blog post](http://www.m4b.io/2015-05-04-the-fault-is-not-in-our-stars.html). The gist is that multiple redundancy is scientific; think of it as triangulation of the semantics of an instruction set. Of course interim solutions (hacks) are welcome to get functionality off the ground, i.e., the -D flag in `rdr -m -D -f printf`. 15 | 16 | 6. A completely unsafe, hacky compiler for some unspecified binary/assembly-ish language. Rationale: so I mess around with instructions a lot; assembly doesn't work, because I want to input, for example, `0x66 0xe8 0x44 0xff 0x7f 0xff 0xff`, and see what the _silicon_ says that instruction sequence is. Right now I load up a precompiled C binary with integer sigils or repeated instruction sequences, and replace them with whatever I want in a hex editor. It's laborious and not very rewarding. Prototype cycles are slow. A more structured approach would be nice. Most importantly, whatever name this assembly/binary language has _must_ be awesome. 17 | 18 | 7. Regexp `-f` searching. 19 | 20 | 8. `c++filt` reverse searching; you give it a `c++filt` string, and it reverses it. Also, `c++filt` output. Can be hacky and call `c++filt` for now. 21 | 22 | # Bug TODOs 23 | 24 | * symlinks in `darwin/usr/lib/` for various dylibs (`libz.dylib`, `libsymsea.dylib`) were resolved at copy time into their links; as a result, redundant searches are displayed for `_deflate`, etc. Need to scrub them for testing 25 | * add different message if using marshalled map, like searching map marshall, or what the map marshal was generated from, etc 26 | * In order to implement this, require perhaps a record with meta data like name, when generated, what from, date, etc. 27 | * And everything else that is wrong 28 | -------------------------------------------------------------------------------- /_oasis: -------------------------------------------------------------------------------- 1 | OASISFormat: 0.4 2 | Plugins: META (0.4), DevFiles (0.4) 3 | Name: rdr 4 | Version: 3.0.0 5 | Synopsis: Lightweight, cross platform binary parsing and analysis library 6 | Authors: m4b 7 | Homepage: http://github.com/m4b/rdr 8 | Maintainers: 9 | License: BSD-3-clause 10 | Description:`rdr` is an OCaml tool/library for doing cross-platform analysis of binaries, 11 | by printing headers, locating entry points, showing import and export 12 | symbols, their binary offsets and size, etc. 13 | 14 | It also features a symbol map which allows fast lookups for arbitrary 15 | symbols, and their associated data, on your system 16 | (the default search location are binaries in /usr/lib). 17 | 18 | The latest release also makes `rdr` a package which you can link against 19 | and use in your own projects. 20 | 21 | See the README at http://github.com/m4b/rdr for more details. 22 | 23 | Features: 24 | 25 | * 64-bit Linux, Mach-o, 32-bit PE binary analysis 26 | * Searchable symbol-map of all the symbols on your system, including binary 27 | offset, size, and exporting library 28 | * Print imports and exports of binaries 29 | * Make pretty graphs, at the binary or symbol map level 30 | * Byte Coverage algorithm which marks byte sequences as understood (or not) 31 | and provides other meta-data 32 | 33 | 34 | Library "utils" 35 | Path: lib/utils 36 | BuildTools: ocamlbuild 37 | FindLibParent: rdr 38 | FindLibName: utils 39 | CompiledObject: best 40 | BuildDepends: unix, str 41 | Modules: 42 | RdrUtils, 43 | Binary, 44 | Input, 45 | Leb128, 46 | ByteCoverage, 47 | Generics, 48 | RdrPrinter, 49 | RdrCommand, 50 | RdrStorage 51 | 52 | Library "mach" 53 | Path: lib/mach 54 | BuildTools: ocamlbuild 55 | FindLibParent: rdr 56 | FindLibName: mach 57 | CompiledObject: best 58 | Modules: 59 | Mach, 60 | MachBindOpcodes, 61 | MachCpuTypes, 62 | MachFat, 63 | MachThread64, 64 | MachLoadCommand, 65 | MachLoadCommandTypes, 66 | MachConstants, 67 | MachExports, 68 | MachHeader, 69 | MachImports, 70 | MachSection, 71 | MachSymbolTable, 72 | MachRebaseOpcodes, 73 | MachVersion, 74 | MachCoverage, 75 | MachLoadCommandMacro 76 | BuildDepends: 77 | rdr.utils 78 | 79 | Library "elf" 80 | Path: lib/elf 81 | FindLibParent: rdr 82 | FindLibName: elf 83 | BuildTools: ocamlbuild 84 | CompiledObject: best 85 | Modules: 86 | Elf, 87 | ElfHeader, 88 | ElfProgramHeader, 89 | ElfSectionHeader, 90 | ElfVersion, 91 | ElfConstants, 92 | ElfDynamic, 93 | ElfReloc, 94 | ElfSymbolTable, 95 | ElfCoverage 96 | BuildDepends: 97 | rdr.utils 98 | 99 | Library "pe" 100 | Path: lib/pe 101 | FindLibParent: rdr 102 | FindLibName: pe 103 | BuildTools: ocamlbuild 104 | CompiledObject: best 105 | Modules: 106 | PE, 107 | PEHeader, 108 | PEOptionalHeader, 109 | PESectionTable, 110 | PEDataDirectories, 111 | PEImport, 112 | PEExport, 113 | PEMachineType, 114 | PECharacteristic, 115 | PEByteCoverage, 116 | PEUtils 117 | BuildDepends: 118 | rdr.utils 119 | 120 | Library "goblin" 121 | Path: lib/goblin 122 | BuildTools: ocamlbuild 123 | FindLibParent: rdr 124 | FindLibName: goblin 125 | CompiledObject: best 126 | Modules: 127 | Goblin, 128 | GoblinExport, 129 | GoblinImport, 130 | GoblinTree 131 | BuildDepends: 132 | rdr.utils, 133 | rdr.mach, 134 | rdr.elf, 135 | rdr.pe 136 | 137 | Library "rdr" 138 | Path: lib 139 | FindLibName: rdr 140 | BuildTools: ocamlbuild 141 | CompiledObject: best 142 | Modules: 143 | Rdr, 144 | RdrObject, 145 | RdrGraph, 146 | SymbolMap, 147 | TransitiveClosure 148 | BuildDepends: 149 | rdr.goblin, 150 | rdr.utils, 151 | rdr.mach, 152 | rdr.elf, 153 | rdr.pe 154 | 155 | Library "rdrutils" 156 | Path: src 157 | BuildTools: ocamlbuild 158 | Install: false 159 | CompiledObject: best 160 | Modules: 161 | Config, 162 | ReadMach, 163 | ReadElf, 164 | ReadPE 165 | BuildDepends: 166 | rdr.pe, 167 | rdr.elf, 168 | rdr.mach, 169 | rdr.goblin, 170 | rdr.utils, 171 | rdr, 172 | str, 173 | unix 174 | 175 | Executable "rdr" 176 | Path: src 177 | MainIs: main.ml 178 | BuildTools: ocamlbuild 179 | CompiledObject: best 180 | BuildDepends: 181 | rdrutils, 182 | str -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 6f72290905f846d782e04758586f28ab) 3 | # Ignore VCS directories, you can use the same kind of rule outside 4 | # OASIS_START/STOP if you want to exclude directories that contains 5 | # useless stuff for the build process 6 | true: annot, bin_annot 7 | <**/.svn>: -traverse 8 | <**/.svn>: not_hygienic 9 | ".bzr": -traverse 10 | ".bzr": not_hygienic 11 | ".hg": -traverse 12 | ".hg": not_hygienic 13 | ".git": -traverse 14 | ".git": not_hygienic 15 | "_darcs": -traverse 16 | "_darcs": not_hygienic 17 | # Library utils 18 | "lib/utils/utils.cmxs": use_utils 19 | : pkg_str 20 | : pkg_unix 21 | # Library mach 22 | "lib/mach/mach.cmxs": use_mach 23 | : pkg_str 24 | : pkg_unix 25 | : use_utils 26 | # Library elf 27 | "lib/elf/elf.cmxs": use_elf 28 | : pkg_str 29 | : pkg_unix 30 | : use_utils 31 | # Library pe 32 | "lib/pe/pe.cmxs": use_pe 33 | : pkg_str 34 | : pkg_unix 35 | : use_utils 36 | # Library goblin 37 | "lib/goblin/goblin.cmxs": use_goblin 38 | : pkg_str 39 | : pkg_unix 40 | : use_elf 41 | : use_mach 42 | : use_pe 43 | : use_utils 44 | # Library rdr 45 | "lib/rdr.cmxs": use_rdr 46 | : pkg_str 47 | : pkg_unix 48 | : use_elf 49 | : use_goblin 50 | : use_mach 51 | : use_pe 52 | : use_utils 53 | # Library rdrutils 54 | "src/rdrutils.cmxs": use_rdrutils 55 | # Executable rdr 56 | : pkg_str 57 | : pkg_unix 58 | : use_elf 59 | : use_goblin 60 | : use_mach 61 | : use_pe 62 | : use_rdr 63 | : use_rdrutils 64 | : use_utils 65 | : pkg_str 66 | : pkg_unix 67 | : use_elf 68 | : use_goblin 69 | : use_mach 70 | : use_pe 71 | : use_rdr 72 | : use_rdrutils 73 | : use_utils 74 | # OASIS_STOP 75 | : -traverse 76 | : -traverse 77 | -------------------------------------------------------------------------------- /bin/elf/deadbeef.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/elf/deadbeef.elf -------------------------------------------------------------------------------- /bin/elf/efbeadde.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/elf/efbeadde.elf -------------------------------------------------------------------------------- /bin/elf/libbeef.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/elf/libbeef.so -------------------------------------------------------------------------------- /bin/mach/deadbeef.mach: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/mach/deadbeef.mach -------------------------------------------------------------------------------- /bin/mach/efbeadde.mach: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/mach/efbeadde.mach -------------------------------------------------------------------------------- /bin/mach/libbeef.mach: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/mach/libbeef.mach -------------------------------------------------------------------------------- /bin/pe/deadbeef.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/pe/deadbeef.exe -------------------------------------------------------------------------------- /bin/pe/efbeadde.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/pe/efbeadde.exe -------------------------------------------------------------------------------- /bin/pe/libbeef.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/bin/pe/libbeef.dll -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ocamlbuild.native -lib unix -lib str src/Rdr.native && mv Rdr.native rdr 3 | -------------------------------------------------------------------------------- /cee/deadbeef.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(){ 4 | int x = 0xdeadbeef; 5 | printf("0x%x\n", x); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /cee/efbeadde.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(){ 4 | int x = 0xefbeadde; 5 | printf("0x%x\n", x); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /cee/libbeef.c: -------------------------------------------------------------------------------- 1 | int OBEBBLBI = 0xb1b1eb0b; 2 | int BIBLEBOB = 0x0bebb1b1; 3 | const long kefbeadde = 0xdeadbeef; 4 | const long kdeadbeef = 0xefbeadde; 5 | 6 | long long LEL = 0xffffffffffffffff; 7 | const long long kLEL = 0xffffffffffffffff; 8 | 9 | long int beef_maximum(long int x, long int y){ 10 | return x > y ? x : y; 11 | } 12 | 13 | float beef_float(float x, float y){ 14 | return x * y; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /cee/other: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/cee/other -------------------------------------------------------------------------------- /cee/other.c: -------------------------------------------------------------------------------- 1 | extern int printf (const char *__restrict __format, ...); 2 | 3 | int main (){ 4 | unsigned long lerpDerp = 40; 5 | 6 | printf("other libc: %lu\n", lerpDerp); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /cee/other_sstrip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/cee/other_sstrip -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # OASIS_START 4 | # DO NOT EDIT (digest: dc86c2ad450f91ca10c931b6045d0499) 5 | set -e 6 | 7 | FST=true 8 | for i in "$@"; do 9 | if $FST; then 10 | set -- 11 | FST=false 12 | fi 13 | 14 | case $i in 15 | --*=*) 16 | ARG=${i%%=*} 17 | VAL=${i##*=} 18 | set -- "$@" "$ARG" "$VAL" 19 | ;; 20 | *) 21 | set -- "$@" "$i" 22 | ;; 23 | esac 24 | done 25 | 26 | ocaml setup.ml -configure "$@" 27 | # OASIS_STOP 28 | -------------------------------------------------------------------------------- /lib/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 127233ddbb59488f5dacd5adbca956b4) 3 | version = "3.0.0" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library" 6 | requires = "rdr.goblin rdr.utils rdr.mach rdr.elf rdr.pe" 7 | archive(byte) = "rdr.cma" 8 | archive(byte, plugin) = "rdr.cma" 9 | archive(native) = "rdr.cmxa" 10 | archive(native, plugin) = "rdr.cmxs" 11 | exists_if = "rdr.cma" 12 | package "utils" ( 13 | version = "3.0.0" 14 | description = 15 | "Lightweight, cross platform binary parsing and analysis library" 16 | requires = "unix str" 17 | archive(byte) = "utils.cma" 18 | archive(byte, plugin) = "utils.cma" 19 | archive(native) = "utils.cmxa" 20 | archive(native, plugin) = "utils.cmxs" 21 | exists_if = "utils.cma" 22 | ) 23 | 24 | package "pe" ( 25 | version = "3.0.0" 26 | description = 27 | "Lightweight, cross platform binary parsing and analysis library" 28 | requires = "rdr.utils" 29 | archive(byte) = "pe.cma" 30 | archive(byte, plugin) = "pe.cma" 31 | archive(native) = "pe.cmxa" 32 | archive(native, plugin) = "pe.cmxs" 33 | exists_if = "pe.cma" 34 | ) 35 | 36 | package "mach" ( 37 | version = "3.0.0" 38 | description = 39 | "Lightweight, cross platform binary parsing and analysis library" 40 | requires = "rdr.utils" 41 | archive(byte) = "mach.cma" 42 | archive(byte, plugin) = "mach.cma" 43 | archive(native) = "mach.cmxa" 44 | archive(native, plugin) = "mach.cmxs" 45 | exists_if = "mach.cma" 46 | ) 47 | 48 | package "goblin" ( 49 | version = "3.0.0" 50 | description = 51 | "Lightweight, cross platform binary parsing and analysis library" 52 | requires = "rdr.utils rdr.mach rdr.elf rdr.pe" 53 | archive(byte) = "goblin.cma" 54 | archive(byte, plugin) = "goblin.cma" 55 | archive(native) = "goblin.cmxa" 56 | archive(native, plugin) = "goblin.cmxs" 57 | exists_if = "goblin.cma" 58 | ) 59 | 60 | package "elf" ( 61 | version = "3.0.0" 62 | description = 63 | "Lightweight, cross platform binary parsing and analysis library" 64 | requires = "rdr.utils" 65 | archive(byte) = "elf.cma" 66 | archive(byte, plugin) = "elf.cma" 67 | archive(native) = "elf.cmxa" 68 | archive(native, plugin) = "elf.cmxs" 69 | exists_if = "elf.cma" 70 | ) 71 | # OASIS_STOP 72 | 73 | -------------------------------------------------------------------------------- /lib/Rdr.ml: -------------------------------------------------------------------------------- 1 | module Elf = Elf 2 | module Mach = Mach 3 | module Goblin = Goblin 4 | module Utils = RdrUtils 5 | module Object = RdrObject 6 | module Map = SymbolMap 7 | module Graph = RdrGraph 8 | module TransitiveClosure = TransitiveClosure 9 | -------------------------------------------------------------------------------- /lib/RdrObject.ml: -------------------------------------------------------------------------------- 1 | type t = 2 | | Mach of bytes 3 | | Elf of bytes 4 | | PE32 of bytes 5 | | Unknown of string * string 6 | 7 | let get ?verbose:(verbose=false) filename = 8 | let ic = open_in_bin filename in 9 | if (in_channel_length ic < 4) then 10 | (* 4 bytes, less than any magic number we're looking for *) 11 | begin 12 | close_in ic; 13 | Unknown (filename, "less than 4 bytes") 14 | end 15 | else 16 | (* BEGIN Binary cases *) 17 | let magic = Input.input_i32be ic in 18 | if (verbose) then 19 | Printf.printf "opening %s with magic: 0x%x\n" filename magic; 20 | (* MACH FAT *) 21 | if (magic = Mach.Fat.kFAT_MAGIC) (* cafe babe *) then 22 | let nfat_arch = Input.input_i32be ic in 23 | if (nfat_arch > 4) then (* hack to avoid java class file errors which have same magic num *) 24 | begin 25 | close_in ic; 26 | Unknown (filename, "Mach-o fat too many archs (probably a java class file)") 27 | end 28 | else 29 | let sizeof_arch_bytes = nfat_arch * Mach.Fat.sizeof_fat_arch in 30 | let fat_arch_bytes = Bytes.create sizeof_arch_bytes in 31 | really_input ic fat_arch_bytes 0 sizeof_arch_bytes; 32 | let offset = 33 | Mach.Fat.get_x86_64_binary_offset fat_arch_bytes nfat_arch in 34 | match offset with 35 | | Some (offset, size) -> 36 | seek_in ic offset; 37 | let magic = Input.input_i32be ic in 38 | if (magic = Mach.Header.kMH_CIGAM_64) then 39 | begin 40 | seek_in ic offset; 41 | let binary = Bytes.create size in 42 | really_input ic binary 0 size; 43 | close_in ic; 44 | Mach binary 45 | end 46 | else 47 | begin 48 | close_in ic; Unknown (filename, "Mach-o fat has no 64 bit binaries") 49 | end 50 | | None -> 51 | close_in ic; 52 | Unknown (filename, "Mach-o fat has no binaries") 53 | (* MACH *) 54 | else if (magic = Mach.Header.kMH_CIGAM_64) then 55 | (* backwards cause we read the 32bit int big E style *) 56 | begin 57 | seek_in ic 0; 58 | let binary = Bytes.create (in_channel_length ic) in 59 | really_input ic binary 0 (in_channel_length ic); 60 | close_in ic; 61 | Mach binary 62 | end 63 | (* ELF *) 64 | else if (magic = Elf.Header.kMAGIC_ELF) then 65 | begin 66 | seek_in ic 0; 67 | let binary = Bytes.create (in_channel_length ic) in 68 | really_input ic binary 0 (in_channel_length ic); 69 | close_in ic; 70 | if (Elf.Header.check_64bit binary) then 71 | Elf binary 72 | else 73 | Unknown (filename, "ELF binary is not 64-bit") 74 | end 75 | (* PE *) 76 | else 77 | let dos_magic = (magic lsr 16) land 0xffff in (* grabs the leftmost 31:16 bits *) 78 | if (dos_magic = PE.Header.kDOS_CIGAM) then 79 | begin 80 | seek_in ic PE.Header.kPE_POINTER_OFFSET; 81 | (* TODO: determine 64-bit here *) 82 | let pe_offset = Input.input_i32 ic in 83 | seek_in ic pe_offset; 84 | let coff_magic = Input.input_i32be ic in 85 | (* Printf.printf "0x%x\n" coff_magic; *) 86 | if (coff_magic = PE.Header.kCOFF_MAGIC) then 87 | begin 88 | seek_in ic 0; 89 | let binary = Bytes.create (in_channel_length ic) in 90 | really_input ic binary 0 (in_channel_length ic); 91 | close_in ic; 92 | PE32 binary 93 | end 94 | else 95 | Unknown (filename, (Printf.sprintf "Not a PE32 binary: 0x%x and 0x%x" dos_magic coff_magic)) 96 | end 97 | else 98 | begin 99 | close_in ic; 100 | if (verbose) then 101 | Printf.printf "ignoring binary: %s\n" filename; 102 | Unknown (filename, (Printf.sprintf "unknown magic number 0x%x" magic)) 103 | end 104 | 105 | let try_goblin ~coverage filename = 106 | match get filename with 107 | | Elf binary -> 108 | Goblin.Elf.to_goblin ~coverage ~use_tree:true filename binary 109 | | Mach binary -> 110 | Goblin.Mach.to_goblin ~coverage filename binary 111 | | PE32 binary -> 112 | Goblin.PE.to_goblin ~coverage filename binary 113 | | Unknown (_,error) -> raise @@ 114 | Failure (Printf.sprintf " %s failed with %s" filename error) 115 | -------------------------------------------------------------------------------- /lib/SymbolMap.ml: -------------------------------------------------------------------------------- 1 | (* 2 | TODO: 3 | (-2): Add meta data to marshalled symbolmap/tol 4 | (-1): /usr/lib/libmcheck.a with magic: 0x7f454c46 --- why does the .a file have an elf magic number? 5 | (0) : Fix the suffix problem; doesn't work very well with linux, and osx frameworks won't get recognized either... 6 | (1) : add this offset/polymorphic sorting, etc., to macho binary analysis to infer the size of objects per library...! 7 | *) 8 | 9 | open Unix 10 | 11 | (* also implement .a i guess 12 | --- ios will have a lot of those, burned into the binary, fun fun *) 13 | let libraryish_suffixes = [".dylib"; ".so"; ".dll"; ".exe"] 14 | (* 15 | TODO: banned suffixes, remove after solving problem with: 16 | /usr/lib/libmcheck.a with magic: 0x7f454c46 17 | *) 18 | let banned_suffixes = [".a"] 19 | 20 | let has_libraryish_suffix string = 21 | List.fold_left (fun acc suffix -> 22 | acc || (Filename.check_suffix string suffix)) false libraryish_suffixes 23 | 24 | let has_banned_suffix string = 25 | List.fold_left (fun acc suffix -> 26 | acc || (Filename.check_suffix string suffix)) false banned_suffixes 27 | 28 | (* osx specific framework reading; needs some (a lot) of tuning *) 29 | let read_frameworks ~verbose dir stack = 30 | let dir_fd = Unix.opendir dir in 31 | let count = ref 0 in 32 | try 33 | while true do 34 | let f = dir ^ (Unix.readdir dir_fd) in 35 | let f_stat = Unix.lstat f in (* sees symbolic links *) 36 | match f_stat.st_kind with 37 | | Unix.S_DIR -> 38 | if (Filename.check_suffix f "framework") then 39 | let framework = Filename.chop_suffix (Filename.basename f) ".framework" in 40 | Stack.push (f ^ Filename.dir_sep ^ framework) stack 41 | else 42 | (); 43 | | _ -> (); 44 | done 45 | with 46 | | End_of_file -> 47 | Unix.closedir dir_fd; 48 | if (verbose) then Printf.printf "Pushed %d libs from %s\n" !count dir 49 | | Unix_error (error,s1,s2) -> 50 | Unix.closedir dir_fd; 51 | if (verbose) then Printf.printf "Error: %s %s %s\n" (error_message error) s1 s2 52 | 53 | let rec read_framework_dirs ~verbose:verbose ~frameworks:frameworks stack = 54 | match frameworks with 55 | | [] -> stack 56 | | dir::dirs -> 57 | let dir = if (Filename.check_suffix dir Filename.dir_sep) then 58 | dir 59 | else 60 | dir ^ Filename.dir_sep 61 | in 62 | read_frameworks ~verbose:verbose dir stack; 63 | read_framework_dirs ~verbose:verbose ~frameworks:dirs stack 64 | 65 | let build_lib_stack ~recursive:recursive ~verbose:verbose ~dirs:dirs = 66 | let stack = Stack.create() in 67 | let rec dir_loop dirs stack = 68 | match dirs with 69 | | [] -> stack 70 | | dir::dirs -> 71 | (* add the / if missing, as a convenience to the user *) 72 | let dir = if (Filename.check_suffix dir Filename.dir_sep) then 73 | dir 74 | else 75 | dir ^ Filename.dir_sep 76 | in 77 | dir_loop dirs (read_dir dir stack) 78 | and read_dir dir stack = 79 | let dir_fd = Unix.opendir dir in 80 | let not_done = ref true in 81 | let count = ref 0 in 82 | while (!not_done) do 83 | try 84 | let f = dir ^ (Unix.readdir dir_fd) in 85 | let f_stat = Unix.lstat f in (* lstat can see symbolic links *) 86 | match f_stat.st_kind with 87 | | Unix.S_REG -> 88 | let base = Filename.basename f in 89 | (* if (has_libraryish_suffix base) then *) 90 | if (not (has_banned_suffix base)) then 91 | begin 92 | if (verbose) then Printf.printf "using %s\n" f; 93 | incr count; 94 | Stack.push f stack 95 | end; 96 | | Unix.S_DIR -> 97 | if (recursive && not (Filename.check_suffix f ".")) then 98 | (* because read_dir returns the stack, maybe make this unit? *) 99 | ignore @@ read_dir (f ^ Filename.dir_sep) stack 100 | else 101 | (); 102 | (* ignore symbolic links and other file types *) 103 | | _ -> (); 104 | with 105 | | End_of_file -> 106 | not_done := false; 107 | Unix.closedir dir_fd; 108 | if (verbose) then Printf.printf "Pushed %d libs from %s\n" !count dir 109 | | Unix_error (error,s1,s2) -> 110 | (* probably surpress this error? *) 111 | if (verbose) then Printf.eprintf "Error: %s %s %s\n" (error_message error) s1 s2 112 | done; 113 | stack 114 | in dir_loop dirs stack 115 | 116 | let output_stats tbl = 117 | let oc = open_out (RdrStorage.get_path "stats") in 118 | output_string oc "symbol,count\n"; 119 | Hashtbl.iter (fun a b -> 120 | let string = Printf.sprintf "%s,%d\n" a b in 121 | output_string oc string; 122 | ) tbl; 123 | close_out oc 124 | 125 | (* =================== *) 126 | (* BUILDING *) 127 | (* =================== *) 128 | let build ~verbose:verbose ~graph:graph ~libs:libstack = 129 | let tbl = Hashtbl.create ((Stack.length libstack) * 100) in 130 | if (verbose) then Printf.printf "Building map...\n"; 131 | if (verbose) then 132 | begin 133 | Printf.printf "Total libs: %d\n" (Stack.length libstack); 134 | Printf.printf "... Done\n" 135 | end; 136 | let rec loop map lib_deps = 137 | if (Stack.is_empty libstack) then 138 | begin 139 | output_stats tbl; 140 | RdrGraph.graph_lib_dependencies ~use_dot_storage:true lib_deps; 141 | if (graph) then 142 | RdrGraph.graph_library_dependencies 143 | ~use_sfdp:(RdrCommand.is_linux()) ~use_dot_storage:true; 144 | map 145 | end 146 | else 147 | let library = Stack.pop libstack in 148 | let binary = 149 | try 150 | match RdrObject.get ~verbose:false library with 151 | | RdrObject.Mach binary -> 152 | Some (Mach.get ~coverage:false binary |> Goblin.Mach.from library) 153 | | RdrObject.Elf binary -> 154 | let elf = Elf.get ~coverage:false binary in 155 | Some (Goblin.Elf.from ~use_tree:false library elf) 156 | | RdrObject.PE32 binary -> 157 | Some (PE.get ~coverage:false binary |> Goblin.PE.from library) 158 | | RdrObject.Unknown (lib,error) -> 159 | None 160 | with _ -> 161 | None 162 | in 163 | match binary with 164 | | None -> 165 | loop map lib_deps 166 | | Some goblin -> 167 | let imports = goblin.Goblin.imports in 168 | Array.iter 169 | (fun import -> 170 | let symbol = import.Goblin.Import.name in 171 | if (Hashtbl.mem tbl symbol) then 172 | let count = Hashtbl.find tbl symbol in 173 | Hashtbl.replace tbl symbol (count + 1) 174 | else 175 | Hashtbl.add tbl symbol 1 176 | ) imports; 177 | let exports = goblin.Goblin.exports in 178 | let map' = 179 | Array.fold_left 180 | (fun acc export -> 181 | let name = export.Goblin.Export.name in 182 | try 183 | let branches = Goblin.Tree.find name acc in 184 | Goblin.Tree.add 185 | name 186 | ({Goblin.Tree.library; aliases=[goblin.Goblin.name]; export}::branches) 187 | acc 188 | with 189 | | Not_found -> 190 | Goblin.Tree.add 191 | name 192 | [{Goblin.Tree.library; aliases=[goblin.Goblin.name]; export}] 193 | acc 194 | ) map exports in 195 | loop map' ((goblin.Goblin.name, goblin.Goblin.libs)::lib_deps) 196 | in loop Goblin.Tree.empty [] 197 | 198 | (* unit testing *) 199 | (* 200 | let findf libs elem = 201 | let lib = Goblin.Symbol.find_symbol_lib elem |> fst in 202 | List.mem lib libs 203 | 204 | let sort = Goblin.Symbol.sort_symbols 205 | 206 | let map = Goblin.Tree.get () 207 | let flat = flatten_polymorphic_map_to_list map 208 | let sflat = Goblin.Symbol.sort_symbols flat 209 | let small = List.find_all (findf ["/usr/lib/libz.1.dylib"; "/usr/lib/libobjc.A.dylib"]) flat 210 | *) 211 | 212 | -------------------------------------------------------------------------------- /lib/TransitiveClosure.ml: -------------------------------------------------------------------------------- 1 | open Goblin 2 | open Goblin.Import 3 | open RdrUtils.Printer 4 | 5 | let debug = false 6 | 7 | (* TODO: 8 | * add stats 9 | * add graphing ? 10 | *) 11 | 12 | (* 13 | with coverage 14 | 15 | real 0m0.150s 16 | user 0m0.103s 17 | sys 0m0.040s 18 | 19 | no coverage 20 | real 0m0.148s 21 | user 0m0.099s 22 | sys 0m0.044s 23 | *) 24 | 25 | 26 | type transitive_symbol = { 27 | name: string; 28 | providers: string list; 29 | } 30 | 31 | let pp_transitive_symbol ppf ts = 32 | Format.fprintf ppf "@[%s => " 33 | ts.name; 34 | pp_seq ~brackets:true ppf pp_string ts.providers; 35 | Format.close_box() 36 | 37 | module S = Set.Make(struct 38 | type t = string 39 | let compare ts1 ts2 = compare ts1 ts2 40 | end) 41 | 42 | module M = Map.Make(String) 43 | 44 | let pp_set ppf (set:S.t) = 45 | S.iter (fun elem -> Format.fprintf ppf "@ "; pp_string ppf elem) set 46 | (* S.iter (pp_transitive_symbol ppf) set *) 47 | 48 | let add filter (map, set) (imports:Goblin.Import.t array) = 49 | Array.fold_left (fun (set,map) import -> 50 | if (List.mem import.lib filter) then 51 | let name = import.name in 52 | let set = S.add name set in 53 | let map = 54 | if (M.mem name map) then 55 | let providers = M.find name map in 56 | M.add name (import.lib::providers) map 57 | else 58 | map 59 | in 60 | set,map 61 | else 62 | set,map 63 | ) (set,map) imports 64 | 65 | let add_s set filter (imports:Goblin.Import.t array) = 66 | Array.fold_left (fun set import -> 67 | if (List.mem import.lib filter) then 68 | let name = import.name in 69 | let set = S.add name set in 70 | set 71 | else 72 | set 73 | ) set imports 74 | 75 | let lib_fallback_paths = 76 | if (RdrCommand.is_osx()) then 77 | ["/usr/local/lib/"; "/usr/lib/"; "/lib/";] 78 | else if (RdrCommand.is_linux()) then 79 | ["/usr/local/lib/"; "/usr/lib/";] 80 | else 81 | (* ... windows ? *) 82 | ["/usr/lib/";] 83 | 84 | let rec close_libraries set visited filter libs = 85 | let ppf = Format.std_formatter in 86 | if (debug) then Format.fprintf Format.std_formatter "@[@ "; 87 | let solution = Array.fold_left (fun (set,visited) lib -> 88 | if (debug) then begin 89 | ignore @@ read_line() end; 90 | let goblin,visited = 91 | if (Sys.file_exists lib) then 92 | if (not @@ S.mem lib visited) then 93 | let visited = S.add lib visited in 94 | if (debug) then 95 | begin 96 | Format.fprintf ppf "@ NOT Visited %s@ NEW Visited Set:@[@ " lib; 97 | pp_set Format.std_formatter visited; 98 | Format.fprintf ppf "@]" 99 | end; 100 | Some (RdrObject.try_goblin ~coverage:false lib),visited 101 | else 102 | begin 103 | if (debug) then Format.fprintf ppf "@ Visited %s" lib; 104 | None,visited 105 | end 106 | else 107 | let name = Filename.basename lib in 108 | let fallback_libs = List.map ( fun x -> x ^ name) lib_fallback_paths in 109 | let fallbacks_visited = 110 | List.fold_left 111 | (fun acc lib -> (S.mem lib visited) || acc) false fallback_libs 112 | in 113 | if (debug) then 114 | begin 115 | Format.fprintf Format.std_formatter "@ NOT FOUND %s@ Searching:@ " name; 116 | Format.open_vbox 2; 117 | Format.print_space(); 118 | pp_slist Format.std_formatter fallback_libs; 119 | Format.print_space(); 120 | Format.close_box() 121 | end; 122 | if (not fallbacks_visited) then 123 | fallback_search visited fallback_libs 124 | else 125 | None,visited 126 | in 127 | match goblin with 128 | | Some goblin -> 129 | let set = add_s set filter goblin.imports in 130 | if (debug) then begin 131 | Format.fprintf Format.std_formatter "@ New Set(%d)@ " (S.cardinal set); 132 | pp_set ppf set; 133 | Format.fprintf Format.std_formatter "@ Scanning %s's libaries:@ " (Filename.basename lib); 134 | Format.open_vbox 2; 135 | Format.print_space(); 136 | pp_slist Format.std_formatter (Array.to_list goblin.libs); Format.print_space(); 137 | Format.close_box() 138 | end; 139 | if (goblin.Goblin.libs = [||]) then 140 | set,visited 141 | else 142 | close_libraries set visited filter goblin.libs 143 | | None -> 144 | (* TODO: refactor so actually unfound libraries are warned against, this is a serious issue if so *) 145 | (* Format.eprintf "@ lib %s not found@." lib; flush stdout; *) 146 | set,visited 147 | ) (set,visited) libs 148 | in 149 | if (debug) then Format.fprintf Format.std_formatter "@]"; 150 | solution 151 | and fallback_search visited libs = 152 | match libs with 153 | | [] -> None,visited 154 | | lib::libs -> 155 | try 156 | if (not @@ S.mem lib visited) then 157 | Some (RdrObject.try_goblin ~coverage:false lib),(S.add lib visited) 158 | else 159 | None,visited 160 | with _ -> 161 | fallback_search visited libs 162 | 163 | (* solution *) 164 | type t = { 165 | symbols: string list; 166 | transitive_library_dependencies: string list; 167 | filter: string list; 168 | } 169 | 170 | let pp ppf t = 171 | Format.fprintf ppf "@["; 172 | Format.fprintf ppf "@ @[Transitive Closure Filter (%d)@ " (List.length t.filter); 173 | pp_slist ppf t.filter; 174 | Format.fprintf ppf "@]"; 175 | Format.fprintf ppf "@ @[Transitive Library Dependencies (%d)@ " (List.length t.transitive_library_dependencies); 176 | RdrUtils.Printer.pp_slist ppf t.transitive_library_dependencies; 177 | Format.fprintf ppf "@]"; 178 | Format.fprintf ppf "@ @[Symbols (%d)@ " (List.length t.symbols); 179 | RdrUtils.Printer.pp_slist ppf t.symbols; 180 | Format.fprintf ppf "@]@]@." 181 | 182 | let print t = 183 | pp Format.std_formatter t 184 | 185 | (* TODO: do not need to scan the goblin's libraries (in case they added libraries that aren't needed), just the set of libraries created from every import's exporting library *) 186 | let compute filename filter = 187 | let goblin = RdrObject.try_goblin ~coverage:false filename in 188 | let set = 189 | Array.fold_left 190 | (fun acc (import:Goblin.Import.t) -> 191 | if (List.mem import.lib filter) then 192 | let name = import.name in 193 | (* let providers = [import.lib] in *) 194 | S.add name acc 195 | (* S.add {name; providers} acc *) 196 | else 197 | acc 198 | ) S.empty goblin.imports 199 | in 200 | let symbols,transitive_library_dependencies = 201 | close_libraries set S.empty filter goblin.libs 202 | in 203 | {symbols = S.elements symbols; 204 | transitive_library_dependencies = S.elements transitive_library_dependencies; 205 | filter; 206 | } 207 | -------------------------------------------------------------------------------- /lib/elf/Elf.ml: -------------------------------------------------------------------------------- 1 | module Header = ElfHeader 2 | module ProgramHeader = ElfProgramHeader 3 | module SectionHeader = ElfSectionHeader 4 | module Reloc = ElfReloc 5 | module Constants = ElfConstants 6 | module Dynamic = ElfDynamic 7 | module SymbolTable = ElfSymbolTable 8 | module Coverage = ElfCoverage 9 | 10 | let debug = false 11 | 12 | type t = { 13 | header: Header.t; 14 | program_headers: ProgramHeader.t; 15 | section_headers: SectionHeader.t; 16 | _dynamic: Dynamic.t; 17 | dynamic_symbols: SymbolTable.t; 18 | symbol_table: SymbolTable.t; 19 | relocations: Reloc.t; 20 | is_lib: bool; 21 | is_64: bool; 22 | soname: string; 23 | interpreter: string; 24 | libraries: string list; 25 | size: int; 26 | byte_coverage: ByteCoverage.t; 27 | entry: int64; 28 | raw_code: bytes; (* list *) 29 | } 30 | 31 | let get ?coverage:(coverage=true) ?meta_only:(meta_only=false) binary = 32 | let header = Header.get_elf_header64 binary in 33 | let entry = Int64.of_int header.Header.e_entry in 34 | let is_64 = Header.is_64bit header.Header.e_ident in 35 | if (debug) then Header.print_elf_header64 header; 36 | let program_headers = 37 | ProgramHeader.get_program_headers 38 | binary 39 | header.Header.e_phoff 40 | header.Header.e_phentsize 41 | header.Header.e_phnum 42 | in 43 | if (debug) then ProgramHeader.print_program_headers program_headers; 44 | let interpreter = ProgramHeader.get_interpreter binary program_headers in 45 | if (debug) then Printf.printf "interpreter: %s\n" interpreter; 46 | let slide_sectors = 47 | ProgramHeader.get_slide_sectors program_headers 48 | in 49 | if (debug) then 50 | begin 51 | Printf.printf "slide sectors\n"; 52 | ProgramHeader.print_slide_sectors slide_sectors; 53 | end; 54 | let section_headers = 55 | SectionHeader.get_section_headers 56 | binary 57 | header.Header.e_shoff 58 | header.Header.e_shentsize 59 | header.Header.e_shnum 60 | in 61 | if (debug) then SectionHeader.print_section_headers section_headers; 62 | let size = Bytes.length binary in 63 | if (debug) then Printf.printf "size: 0x%x\n" size; 64 | let is_lib = (Header.is_lib header) in 65 | if (debug) then Printf.printf "is_lib: %b\n" is_lib; 66 | let symbol_table = 67 | SymbolTable.get_symbol_table binary section_headers 68 | |> SymbolTable.sort 69 | in 70 | (* if (debug) then SymbolTable.print_symbol_table symbol_table; *) 71 | let _dynamic = Dynamic.get_dynamic binary program_headers in 72 | (* if (debug) then Dynamic.print_dynamic _dynamic; *) 73 | let symtab_offset, strtab_offset, strtab_size = 74 | Dynamic.get_dynamic_symbol_offset_data _dynamic slide_sectors 75 | in 76 | if (debug) then 77 | Printf.printf "symtab_offset: 0x%x strtab_offset: 0x%x strtab_size: 0x%x\n" 78 | symtab_offset strtab_offset strtab_size; 79 | (* broken right here for /usr/lib/go/pkg/tool/linux_amd64/cgo *) 80 | let dynamic_strtab = 81 | Dynamic.get_dynamic_strtab binary strtab_offset strtab_size 82 | in 83 | (* if (debug) then Printf.printf "dynamic_strtab: %s\n" dynamic_strtab; *) 84 | let libraries = Dynamic.get_libraries _dynamic dynamic_strtab in 85 | let dynamic_symbols = 86 | Dynamic.get_dynamic_symbols 87 | binary 88 | slide_sectors 89 | symtab_offset 90 | strtab_offset 91 | strtab_size 92 | |> SymbolTable.sort 93 | in 94 | let soname = 95 | try 96 | let offset = Dynamic.get_soname_offset _dynamic in 97 | Binary.string binary (strtab_offset + offset) 98 | with Not_found -> "" (* we're not a dylib *) 99 | in 100 | let relocations = 101 | Dynamic.get_reloc_data _dynamic slide_sectors 102 | |> Reloc.get_relocs64 binary 103 | in 104 | if (debug) then Reloc.print_relocs64 relocations; 105 | let byte_coverage = 106 | if (coverage) then 107 | ElfCoverage.compute_byte_coverage header program_headers section_headers size binary 108 | else 109 | ByteCoverage.null 110 | in 111 | (* TODO: fix *) 112 | let raw_code = if (meta_only) then 113 | Bytes.create 0 114 | else 115 | Bytes.create 0 in 116 | { 117 | header; 118 | program_headers; 119 | section_headers; 120 | size; 121 | _dynamic; 122 | dynamic_symbols; 123 | symbol_table; 124 | relocations; 125 | is_lib; 126 | is_64; 127 | soname; 128 | interpreter; 129 | libraries; 130 | raw_code; 131 | entry; 132 | byte_coverage; 133 | } 134 | 135 | let print elf = 136 | Header.print_elf_header64 ~verbose:true elf.header; 137 | ProgramHeader.print_program_headers elf.program_headers; 138 | SectionHeader.print_section_headers elf.section_headers; 139 | Dynamic.print_dynamic elf._dynamic; 140 | SymbolTable.print elf.dynamic_symbols; 141 | SymbolTable.print elf.symbol_table; 142 | Reloc.print_relocs64 elf.relocations; 143 | ByteCoverage.print elf.byte_coverage 144 | -------------------------------------------------------------------------------- /lib/elf/ElfConstants.ml: -------------------------------------------------------------------------------- 1 | (* Legal values for e_type (object file type). *) 2 | 3 | let kET_NONE = 0 (* No file type *) 4 | let kET_REL = 1 (* Relocatable file *) 5 | let kET_EXEC = 2 (* Executable file *) 6 | let kET_DYN = 3 (* Shared object file *) 7 | let kET_CORE = 4 (* Core file *) 8 | let kET_NUM = 5 (* Number of defined types *) 9 | let kET_LOOS = 0xfe00 (* OS-specific range start *) 10 | let kET_HIOS = 0xfeff (* OS-specific range end *) 11 | let kET_LOPROC = 0xff00 (* Processor-specific range start *) 12 | let kET_HIPROC = 0xffff (* Processor-specific range end *) 13 | 14 | (* Legal values for e_machine (architecture). *) 15 | 16 | let kEM_NONE = 0 (* No machine *) 17 | let kEM_M32 = 1 (* AT&T WE 32100 *) 18 | let kEM_SPARC = 2 (* SUN SPARC *) 19 | let kEM_386 = 3 (* Intel 80386 *) 20 | let kEM_68K = 4 (* Motorola m68k family *) 21 | let kEM_88K = 5 (* Motorola m88k family *) 22 | let kEM_860 = 7 (* Intel 80860 *) 23 | let kEM_MIPS = 8 (* MIPS R3000 big-endian *) 24 | let kEM_S370 = 9 (* IBM System/370 *) 25 | let kEM_MIPS_RS3_LE = 10 (* MIPS R3000 little-endian *) 26 | 27 | let kEM_PARISC = 15 (* HPPA *) 28 | let kEM_VPP500 = 17 (* Fujitsu VPP500 *) 29 | let kEM_SPARC32PLUS = 18 (* Sun's "v8plus" *) 30 | let kEM_960 = 19 (* Intel 80960 *) 31 | let kEM_PPC = 20 (* PowerPC *) 32 | let kEM_PPC64 = 21 (* PowerPC 64-bit *) 33 | let kEM_S390 = 22 (* IBM S390 *) 34 | 35 | let kEM_V800 = 36 (* NEC V800 series *) 36 | let kEM_FR20 = 37 (* Fujitsu FR20 *) 37 | let kEM_RH32 = 38 (* TRW RH-32 *) 38 | let kEM_RCE = 39 (* Motorola RCE *) 39 | let kEM_ARM = 40 (* ARM *) 40 | let kEM_FAKE_ALPHA = 41 (* Digital Alpha *) 41 | let kEM_SH = 42 (* Hitachi SH *) 42 | let kEM_SPARCV9 = 43 (* SPARC v9 64-bit *) 43 | let kEM_TRICORE = 44 (* Siemens Tricore *) 44 | let kEM_ARC = 45 (* Argonaut RISC Core *) 45 | let kEM_H8_300 = 46 (* Hitachi H8/300 *) 46 | let kEM_H8_300H = 47 (* Hitachi H8/300H *) 47 | let kEM_H8S = 48 (* Hitachi H8S *) 48 | let kEM_H8_500 = 49 (* Hitachi H8/500 *) 49 | let kEM_IA_64 = 50 (* Intel Merced *) 50 | let kEM_MIPS_X = 51 (* Stanford MIPS-X *) 51 | let kEM_COLDFIRE = 52 (* Motorola Coldfire *) 52 | let kEM_68HC12 = 53 (* Motorola M68HC12 *) 53 | let kEM_MMA = 54 (* Fujitsu MMA Multimedia Accelerator*) 54 | let kEM_PCP = 55 (* Siemens PCP *) 55 | let kEM_NCPU = 56 (* Sony nCPU embeeded RISC *) 56 | let kEM_NDR1 = 57 (* Denso NDR1 microprocessor *) 57 | let kEM_STARCORE = 58 (* Motorola Start*Core processor *) 58 | let kEM_ME16 = 59 (* Toyota ME16 processor *) 59 | let kEM_ST100 = 60 (* STMicroelectronic ST100 processor *) 60 | let kEM_TINYJ = 61 (* Advanced Logic Corp. Tinyj emb.fam*) 61 | let kEM_X86_64 = 62 (* AMD x86-64 architecture *) 62 | let kEM_PDSP = 63 (* Sony DSP Processor *) 63 | 64 | let kEM_FX66 = 66 (* Siemens FX66 microcontroller *) 65 | let kEM_ST9PLUS = 67 (* STMicroelectronics ST9+ 8/16 mc *) 66 | let kEM_ST7 = 68 (* STmicroelectronics ST7 8 bit mc *) 67 | let kEM_68HC16 = 69 (* Motorola MC68HC16 microcontroller *) 68 | let kEM_68HC11 = 70 (* Motorola MC68HC11 microcontroller *) 69 | let kEM_68HC08 = 71 (* Motorola MC68HC08 microcontroller *) 70 | let kEM_68HC05 = 72 (* Motorola MC68HC05 microcontroller *) 71 | let kEM_SVX = 73 (* Silicon Graphics SVx *) 72 | let kEM_ST19 = 74 (* STMicroelectronics ST19 8 bit mc *) 73 | let kEM_VAX = 75 (* Digital VAX *) 74 | let kEM_CRIS = 76 (* Axis Communications 32-bit embedded processor *) 75 | let kEM_JAVELIN = 77 (* Infineon Technologies 32-bit embedded processor *) 76 | let kEM_FIREPATH = 78 (* Element 14 64-bit DSP Processor *) 77 | let kEM_ZSP = 79 (* LSI Logic 16-bit DSP Processor *) 78 | let kEM_MMIX = 80 (* Donald Knuth's educational 64-bit processor *) 79 | let kEM_HUANY = 81 (* Harvard University machine-independent object files *) 80 | let kEM_PRISM = 82 (* SiTera Prism *) 81 | let kEM_AVR = 83 (* Atmel AVR 8-bit microcontroller *) 82 | let kEM_FR30 = 84 (* Fujitsu FR30 *) 83 | let kEM_D10V = 85 (* Mitsubishi D10V *) 84 | let kEM_D30V = 86 (* Mitsubishi D30V *) 85 | let kEM_V850 = 87 (* NEC v850 *) 86 | let kEM_M32R = 88 (* Mitsubishi M32R *) 87 | let kEM_MN10300 = 89 (* Matsushita MN10300 *) 88 | let kEM_MN10200 = 90 (* Matsushita MN10200 *) 89 | let kEM_PJ = 91 (* picoJava *) 90 | let kEM_OPENRISC = 92 (* OpenRISC 32-bit embedded processor *) 91 | let kEM_ARC_A5 = 93 (* ARC Cores Tangent-A5 *) 92 | let kEM_XTENSA = 94 (* Tensilica Xtensa Architecture *) 93 | let kEM_ALTERA_NIOS2 = 113 (* Altera Nios II *) 94 | let kEM_AARCH64 = 183 (* ARM AARCH64 *) 95 | let kEM_TILEPRO = 188 (* Tilera TILEPro *) 96 | let kEM_MICROBLAZE = 189 (* Xilinx MicroBlaze *) 97 | let kEM_TILEGX = 191 (* Tilera TILE-Gx *) 98 | let kEM_NUM = 192 99 | 100 | (* If it is necessary to assign new unofficial EM_* values, please 101 | pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the 102 | chances of collision with official or non-GNU unofficial values. *) 103 | 104 | let kEM_ALPHA = 0x9026 105 | 106 | let etype_to_string etype = 107 | match etype with 108 | | 0 -> "NONE" 109 | | 1 -> "REL" 110 | | 2 -> "EXEC" 111 | | 3 -> "DYN" 112 | | 4 -> "CORE" 113 | | 5 -> "NUM" 114 | | 0xfe00 -> "LOOS" 115 | | 0xfeff -> "HIOS" 116 | | 0xff00 -> "LOPROC" 117 | | 0xffff -> "HIPROC" 118 | | _ -> "UKNOWN TYPE" 119 | 120 | let machine_to_string machine = 121 | match machine with 122 | | 0 -> "NONE" 123 | | 1 -> "M32" 124 | | 2 -> "SPARC" 125 | | 3 -> "386" 126 | | 4 -> "68K" 127 | | 5 -> "88K" 128 | | 7 -> "860" 129 | | 8 -> "MIPS" 130 | | 9 -> "S370" 131 | | 10 -> "MIPS_RS3_LE" 132 | | 15 -> "PARISC" 133 | | 17 -> "VPP500" 134 | | 18 -> "SPARC32PLUS" 135 | | 19 -> "960" 136 | | 20 -> "PPC" 137 | | 21 -> "PPC64" 138 | | 22 -> "S390" 139 | | 36 -> "V800" 140 | | 37 -> "FR20" 141 | | 38 -> "RH32" 142 | | 39 -> "RCE" 143 | | 40 -> "ARM" 144 | | 41 -> "FAKE_ALPHA" 145 | | 42 -> "SH" 146 | | 43 -> "SPARCV9" 147 | | 44 -> "TRICORE" 148 | | 45 -> "ARC" 149 | | 46 -> "H8_300" 150 | | 47 -> "H8_300H" 151 | | 48 -> "H8S" 152 | | 49 -> "H8_500" 153 | | 50 -> "IA_64" 154 | | 51 -> "MIPS_X" 155 | | 52 -> "COLDFIRE" 156 | | 53 -> "68HC12" 157 | | 54 -> "MMA" 158 | | 55 -> "PCP" 159 | | 56 -> "NCPU" 160 | | 57 -> "NDR1" 161 | | 58 -> "STARCORE" 162 | | 59 -> "ME16" 163 | | 60 -> "ST100" 164 | | 61 -> "TINYJ" 165 | | 62 -> "X86_64" 166 | | 63 -> "PDSP" 167 | | 66 -> "FX66" 168 | | 67 -> "ST9PLUS" 169 | | 68 -> "ST7" 170 | | 69 -> "68HC16" 171 | | 70 -> "68HC11" 172 | | 71 -> "68HC08" 173 | | 72 -> "68HC05" 174 | | 73 -> "SVX" 175 | | 74 -> "ST19" 176 | | 75 -> "VAX" 177 | | 76 -> "CRIS" 178 | | 77 -> "JAVELIN" 179 | | 78 -> "FIREPATH" 180 | | 79 -> "ZSP" 181 | | 80 -> "MMIX" 182 | | 81 -> "HUANY" 183 | | 82 -> "PRISM" 184 | | 83 -> "AVR" 185 | | 84 -> "FR30" 186 | | 85 -> "D10V" 187 | | 86 -> "D30V" 188 | | 87 -> "V850" 189 | | 88 -> "M32R" 190 | | 89 -> "MN10300" 191 | | 90 -> "MN10200" 192 | | 91 -> "PJ" 193 | | 92 -> "OPENRISC" 194 | | 93 -> "ARC_A5" 195 | | 94 -> "XTENSA" 196 | | 113 -> "ALTERA_NIOS2" 197 | | 183 -> "AARCH64" 198 | | 188 -> "TILEPRO" 199 | | 189 -> "MICROBLAZE" 200 | | 191 -> "TILEGX" 201 | | 192 -> "NUM" 202 | | 0x9026 -> "ALPHA" 203 | | _ -> "UNKNOWN MACHINE" 204 | 205 | 206 | -------------------------------------------------------------------------------- /lib/elf/ElfCoverage.ml: -------------------------------------------------------------------------------- 1 | open ByteCoverage 2 | 3 | let debug = false 4 | 5 | (* add more, and let melding happen *) 6 | let known_program_headers = 7 | [(ElfProgramHeader.kPT_INTERP, String); 8 | (ElfProgramHeader.kPT_NOTE, String); 9 | (ElfProgramHeader.kPT_DYNAMIC, Symbol); 10 | (ElfProgramHeader.kPT_GNU_EH_FRAME, PlatformSpecific); 11 | ] 12 | 13 | let compute_program_header_coverage kind data ph = 14 | match ph with 15 | | Some ph -> 16 | if (debug) then 17 | Printf.printf "called: %s\n" 18 | (ElfProgramHeader.ptype_to_string 19 | ph.ElfProgramHeader.p_type); 20 | let size = ph.ElfProgramHeader.p_filesz in 21 | let range_start = ph.ElfProgramHeader.p_offset in 22 | let range_end = size + range_start in 23 | let extra = ElfProgramHeader.ptype_to_string ph.ElfProgramHeader.p_type in 24 | ByteCoverage.add 25 | (create_data 26 | ~tag:String 27 | ~r1:range_start 28 | ~r2:range_end 29 | ~extra:extra 30 | ~understood:true 31 | ) data 32 | | None -> data 33 | 34 | let compute_program_header_coverage phs data = 35 | if (ElfProgramHeader.is_empty phs) then 36 | data 37 | else 38 | let f = (fun data (header_type, tag) -> 39 | let header = ElfProgramHeader.get_header header_type phs in 40 | compute_program_header_coverage tag data header 41 | ) in 42 | (fun data -> List.fold_left f data known_program_headers) data 43 | 44 | let known_sections = 45 | [(ElfSectionHeader.kSHT_SYMTAB, SymbolTable); 46 | (ElfSectionHeader.kSHT_STRTAB, StringTable); 47 | (ElfSectionHeader.kSHT_PROGBITS, Code); 48 | (ElfSectionHeader.kSHT_NOBITS, Semantic); 49 | (ElfSectionHeader.kSHT_INIT_ARRAY, Code); 50 | (ElfSectionHeader.kSHT_FINI_ARRAY, Code); 51 | (ElfSectionHeader.kSHT_DYNAMIC, Symbol); 52 | (ElfSectionHeader.kSHT_HASH, Symbol); 53 | (ElfSectionHeader.kSHT_GNU_HASH, Symbol); 54 | (ElfSectionHeader.kSHT_RELA, Rela); 55 | (ElfSectionHeader.kSHT_DYNSYM, SymbolTable); 56 | (ElfSectionHeader.kSHT_NOTE, PlatformSpecific); 57 | (ElfSectionHeader.kSHT_GNU_verdef, PlatformSpecific); 58 | (ElfSectionHeader.kSHT_GNU_verneed, PlatformSpecific); 59 | ] 60 | 61 | let compute_section_coverage stype tag data section = 62 | if (debug) then 63 | Printf.printf "called: %s\n" 64 | (ElfSectionHeader.shtype_to_string 65 | section.ElfSectionHeader.sh_type); 66 | let size = section.ElfSectionHeader.sh_size in 67 | let range_start = section.ElfSectionHeader.sh_offset in 68 | let range_end = 69 | if (stype = ElfSectionHeader.kSHT_NOBITS) then 70 | range_start 71 | else 72 | size + range_start 73 | in 74 | let extra = 75 | section.ElfSectionHeader.name ^ " // " ^ 76 | ElfSectionHeader.shtype_to_string 77 | section.ElfSectionHeader.sh_type 78 | in 79 | ByteCoverage.add 80 | (create_data 81 | ~tag:tag 82 | ~r1:range_start 83 | ~r2:range_end 84 | ~extra:extra 85 | ~understood:true 86 | ) data 87 | 88 | (* add a platform specific post process, for e.g. NOBITS *) 89 | let compute_section_header_coverage h shs data = 90 | if (ElfSectionHeader.is_empty shs) then 91 | data 92 | else 93 | begin 94 | let size = h.ElfHeader.e_shentsize * h.ElfHeader.e_shnum in 95 | let range_start = h.ElfHeader.e_shoff in 96 | let range_end = size + range_start in 97 | ByteCoverage.add 98 | (create_data 99 | ~tag:Meta 100 | ~r1:range_start 101 | ~r2:range_end 102 | ~extra:"Section Headers" 103 | ~understood:true 104 | ) data 105 | end 106 | |> 107 | begin 108 | let f = (fun data (section_type, tag) -> 109 | let sections = ElfSectionHeader.get_sections section_type shs in 110 | List.fold_left (compute_section_coverage section_type tag) data sections 111 | ) in 112 | (fun data -> List.fold_left f data known_sections) 113 | end 114 | 115 | let compute_byte_coverage h phs shs elf_size binary :ByteCoverage.t = 116 | let ehsize = h.ElfHeader.e_ehsize in 117 | let phsize = 118 | (h.ElfHeader.e_phentsize * h.ElfHeader.e_phnum) 119 | in 120 | ByteCoverage.add 121 | (create_data 122 | ~tag:Meta 123 | ~r1:0 124 | ~r2:h.ElfHeader.e_ehsize 125 | ~extra:"ELF Header" 126 | ~understood:true 127 | ) 128 | ByteCoverage.empty 129 | |> ByteCoverage.add 130 | (create_data 131 | ~tag:Meta 132 | ~r1:ehsize 133 | ~r2:(phsize+ehsize) 134 | ~extra:"Program Headers" 135 | ~understood:true 136 | ) 137 | |> compute_program_header_coverage phs 138 | |> compute_section_header_coverage h shs 139 | |> ByteCoverage.create elf_size binary 140 | -------------------------------------------------------------------------------- /lib/elf/ElfHeader.ml: -------------------------------------------------------------------------------- 1 | open Binary 2 | (* 3 | #directory "/Users/matthewbarney/git/rdr/_build/lib/utils/";; 4 | #directory "/Users/matthewbarney/git/rdr/_build/lib/elf/";; 5 | #load "Binary.cmo";; 6 | #load "ElfConstants.cmo";; 7 | *) 8 | 9 | type e_ident = 10 | { 11 | ei_magic: int; (* 7fELF 4 bytes *) 12 | ei_class: int; (* 1 = 32 bit, 2 = 64 bit, 1 byte *) 13 | ei_data: int; (* 1 = little endian, 2 = big endian, 1 byte *) 14 | ei_version: int; (* 1 byte *) 15 | ei_osabi: int; (* often set to zero for all platforms linux = 3 1 byte *) 16 | ei_abiversion: int; (* after kernel 2.6 set to zero 1 byte *) 17 | ei_pad: int; (* 7 bytes of padding, making next offset 0x10 *) 18 | } 19 | 20 | let sizeof_e_ident = 16 (* bytes *) 21 | 22 | let kMAGIC_ELF = 0x7f454c46 23 | let kCIGAM_ELF = 0x464c457f 24 | 25 | (* 64 bit is default; 32 will be t32 *) 26 | type t = 27 | { 28 | e_ident: e_ident; 29 | e_type: int; (* 2 bytes 1 = relocatable, 2 = executable, 3 = shared 4 = core *) 30 | e_machine: int; (* 3E = x86-64 B7 = AArch64 *) (* 2 bytes *) 31 | e_version: int; (* 4 bytes, set to 1 *) 32 | e_entry: int; (* either 4 or 8 bytes if 32-bit or 64-bit *) 33 | e_phoff: int; (* Points to the start of the program header table. It usually follows the file header immediately making the offset 0x40 for 64-bit ELF executables. *) 34 | e_shoff: int; (* Points to the start of the section header table. *) 35 | e_flags: int; (* 4 bytes *) 36 | e_ehsize: int; (* remaining are 2 bytes; Contains the size of this header, normally 64 bytes (0x40) for 64-bit and 52 for 32-bit format. *) 37 | e_phentsize: int (* Contains the size of a program header table entry. *); 38 | e_phnum: int (* Contains the number of entries in the program header table. *); 39 | e_shentsize: int (* Contains the size of a section header table entry. *); 40 | e_shnum: int (* Contains the number of entries in the section header table. *); 41 | e_shstrndx: int (* Contains index of the section header table entry that contains the section names. *); 42 | } 43 | 44 | let sizeof_elf_header64 = sizeof_e_ident + 48 (* bytes *) 45 | 46 | let e_ident_to_string e_ident = 47 | Printf.sprintf "magic: 0x%x \nclass: 0x%x \ndata: 0x%x \nversion: 0x%x \nosabi: 0x%x \nabiversion: 0x%x\n" 48 | e_ident.ei_magic 49 | e_ident.ei_class 50 | e_ident.ei_data 51 | e_ident.ei_version 52 | e_ident.ei_osabi 53 | e_ident.ei_abiversion 54 | 55 | let print_verbose_elf_header64 header = 56 | Printf.printf "%stype: %d machine: 0x%x version: %d entry: 0x%x\nphoff: 0x%x shoff: 0x%x flags: 0x%x ehsize: 0x%x\nphentsize: 0x%x phnum: 0x%x shentsize: 0x%x shnum: 0x%x shstrndx: 0x%x\n" (e_ident_to_string header.e_ident) 57 | header.e_type 58 | header.e_machine 59 | header.e_version 60 | header.e_entry 61 | header.e_phoff 62 | header.e_shoff 63 | header.e_flags 64 | header.e_ehsize 65 | header.e_phentsize 66 | header.e_phnum 67 | header.e_shentsize 68 | header.e_shnum 69 | header.e_shstrndx 70 | 71 | let print_elf_header64 ?verbose:(verbose=false) header = 72 | if (verbose) then 73 | print_verbose_elf_header64 header 74 | else 75 | Printf.printf "ELF %s %s @ 0x%x\n" 76 | (ElfConstants.machine_to_string header.e_machine) 77 | (ElfConstants.etype_to_string header.e_type) 78 | (header.e_entry) 79 | 80 | (* hack to check whether 64 bit without consuming stuff *) 81 | let check_64bit bytes = 82 | let ei_class = Binary.u8 bytes 4 in (* 4 bytes in past 7fELF *) 83 | ei_class = 2 84 | 85 | let is_lib header = header.e_type = 3 86 | let is_supported header = header.e_type = 2 || header.e_type = 3 (* executable or dy lib *) 87 | 88 | let get_e_ident bytes = 89 | let one = u8 bytes 0 in 90 | let two = (u8 bytes 1) lsl 8 in 91 | let three = (u8 bytes 2) lsl 16 in 92 | let four = (u8 bytes 3) lsl 24 in 93 | let ei_magic = one lor two lor three lor four in 94 | let ei_class,o = u8o bytes 4 in 95 | let ei_data,o = u8o bytes o in 96 | let ei_version,o = u8o bytes o in 97 | let ei_osabi,o = u8o bytes o in 98 | let ei_abiversion,o = u8o bytes o in 99 | let ei_pad = 0x0 in 100 | {ei_magic; ei_class; ei_data; ei_version; ei_osabi; ei_abiversion; ei_pad;} 101 | 102 | let is_64bit e_ident = e_ident.ei_class = 2 103 | 104 | let get_elf_header64 binary = 105 | let e_ident = get_e_ident binary in 106 | let o = sizeof_e_ident in 107 | let e_type,o = u16o binary o in 108 | let e_machine,o = u16o binary o in 109 | let e_version,o = u32o binary o in 110 | let e_entry,o = u64o binary o in 111 | let e_phoff,o = u64o binary o in 112 | let e_shoff,o = u64o binary o in 113 | let e_flags,o = u32o binary o in 114 | let e_ehsize,o = u16o binary o in 115 | let e_phentsize,o = u16o binary o in 116 | let e_phnum,o = u16o binary o in 117 | let e_shentsize,o = u16o binary o in 118 | let e_shnum,o = u16o binary o in 119 | let e_shstrndx,o = u16o binary o in 120 | { 121 | e_ident; e_type; e_machine; e_version; e_entry; e_phoff; 122 | e_shoff; e_flags; e_ehsize; e_phentsize; e_phnum; 123 | e_shentsize; e_shnum; e_shstrndx; 124 | } 125 | 126 | let set_e_ident bytes ident offset = 127 | Binary.set_uint_be bytes ident.ei_magic 4 offset (* this should be big-endian i believe *) 128 | |> Binary.set_uint bytes ident.ei_class 1 129 | |> Binary.set_uint bytes ident.ei_data 1 130 | |> Binary.set_uint bytes ident.ei_version 1 131 | |> Binary.set_uint bytes ident.ei_osabi 1 132 | |> Binary.set_uint bytes ident.ei_abiversion 1 133 | |> Binary.set_uint bytes ident.ei_pad 7 134 | 135 | let e_ident_to_bytes ident = 136 | let b = Bytes.create sizeof_e_ident in 137 | ignore @@ set_e_ident b ident 0; 138 | b 139 | 140 | let set bytes header offset = 141 | set_e_ident bytes header.e_ident offset 142 | |> Binary.set_uint bytes header.e_type 2 143 | |> Binary.set_uint bytes header.e_machine 2 144 | |> Binary.set_uint bytes header.e_version 4 145 | |> Binary.set_uint bytes header.e_entry 8 146 | |> Binary.set_uint bytes header.e_phoff 8 147 | |> Binary.set_uint bytes header.e_shoff 8 148 | |> Binary.set_uint bytes header.e_flags 4 149 | |> Binary.set_uint bytes header.e_ehsize 2 150 | |> Binary.set_uint bytes header.e_phentsize 2 151 | |> Binary.set_uint bytes header.e_phnum 2 152 | |> Binary.set_uint bytes header.e_shentsize 2 153 | |> Binary.set_uint bytes header.e_shnum 2 154 | |> Binary.set_uint bytes header.e_shstrndx 2 155 | 156 | let to_bytes header = 157 | let b = Bytes.create sizeof_elf_header64 in 158 | let offset = set b header 0 in 159 | ignore offset; 160 | b 161 | 162 | (* 163 | let h0 = 164 | {e_ident = 165 | {ei_magic = 1179403647; ei_class = 2; ei_data = 1; ei_version = 1; 166 | ei_osabi = 0; ei_abiversion = 0; ei_pad = 0}; 167 | e_type = 2; e_machine = 183; e_version = 1; e_entry = 4319080; e_phoff = 64; 168 | e_shoff = 845672; e_flags = 0; e_ehsize = 64; e_phentsize = 56; e_phnum = 7; 169 | e_shentsize = 64; e_shnum = 27; e_shstrndx = 26} 170 | 171 | let h01 = to_bytes h0 |> get_elf_header64 = h0 172 | 173 | let h1 = "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00\x01\x00\x00\x00\x68\xe7\x41\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x68\xe7\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x07\x00\x40\x00\x1b\x00\x1a\x00" 174 | 175 | let h101 = get_elf_header64 h1 |> to_bytes = h1 176 | *) 177 | 178 | -------------------------------------------------------------------------------- /lib/elf/ElfReloc.ml: -------------------------------------------------------------------------------- 1 | (* 2 | typedef struct { 3 | Elf32_Addr r_offset; 4 | uint32_t r_info; 5 | int32_t r_addend; 6 | } Elf32_Rela; 7 | 8 | typedef struct { 9 | Elf64_Addr r_offset; 10 | uint64_t r_info; 11 | int64_t r_addend; 12 | } Elf64_Rela; 13 | *) 14 | 15 | (* 16 | #define ELF64_R_SYM(i) ((i) >> 32) 17 | #define ELF64_R_TYPE(i) ((i) & 0xffffffff) 18 | *) 19 | 20 | type rela64 = 21 | { 22 | r_offset: int; (* 8 bytes *) 23 | r_info: int; (* 8 bytes *) 24 | r_addend: int; (* 8 bytes, signed *) 25 | symindex: int; 26 | symtype: int; 27 | } 28 | 29 | let sizeof_rela64 = 24 (* bytes *) 30 | 31 | (* 64-bit default, 32t will be 32 bits *) 32 | type t = rela64 list 33 | 34 | let set_rela64 bytes rela offset = 35 | Binary.set_uint bytes rela.r_offset 8 offset 36 | |> Binary.set_uint bytes rela.r_info 8 37 | |> Binary.set_uint bytes rela.r_addend 8 38 | 39 | let set bytes relocs offset = 40 | List.fold_left (fun acc rela -> set_rela64 bytes rela acc) offset relocs 41 | 42 | let to_bytes relocs = 43 | let b = Bytes.create (List.length relocs * sizeof_rela64) in 44 | ignore @@ set b relocs 0; 45 | b 46 | 47 | let get_sym64 i = i asr 32 48 | let get_type64 i = i land 0xffffffff 49 | 50 | let print_rela64 r = 51 | Printf.printf 52 | "r_offset: 0%x r_info: 0x%x r_addend: 0x%x index: %d type: 0x%x\n" 53 | r.r_offset 54 | r.r_info 55 | r.r_addend 56 | r.symindex 57 | r.symtype 58 | 59 | let print_relocs64 = List.iter print_rela64 60 | 61 | let get_reloc64 binary o = 62 | let r_offset,o = Binary.u64o binary o in 63 | let r_info,o = Binary.u64o binary o in 64 | let r_addend,_ = Binary.u64o binary o in 65 | let symindex = get_sym64 r_info in 66 | let symtype = get_type64 r_info in 67 | {r_offset; r_info; r_addend; symindex; symtype;} 68 | 69 | let rec get_reloc64_loop binary boundary pos acc = 70 | if (pos >= boundary) then 71 | acc 72 | else 73 | let reloc = get_reloc64 binary pos in 74 | get_reloc64_loop 75 | binary boundary (pos + sizeof_rela64) (reloc::acc) 76 | 77 | let get_relocs64 binary (relasz,rela,pltrelsz,jmprel) = 78 | let nl = get_reloc64_loop binary (rela+relasz) rela [] in 79 | let l = get_reloc64_loop binary (pltrelsz+jmprel) jmprel [] in 80 | l @ nl |> List.rev 81 | 82 | let get_size index relocs = 83 | try 84 | (List.nth relocs index).r_offset 85 | with _ -> 0x0 86 | -------------------------------------------------------------------------------- /lib/elf/ElfSectionHeader.ml: -------------------------------------------------------------------------------- 1 | (* 2 | typedef struct { 3 | uint32_t sh_name; 4 | uint32_t sh_type; 5 | uint64_t sh_flags; 6 | Elf64_Addr sh_addr; 7 | Elf64_Off sh_offset; 8 | uint64_t sh_size; 9 | uint32_t sh_link; 10 | uint32_t sh_info; 11 | uint64_t sh_addralign; 12 | uint64_t sh_entsize; 13 | } Elf64_Shdr; 14 | *) 15 | 16 | type section_header = 17 | { 18 | mutable name: string; 19 | sh_name: int; (* 4 *) 20 | sh_type: int; (* 4 *) 21 | sh_flags: int; (* 8 *) 22 | sh_addr: int; (* 8 *) 23 | sh_offset: int; (* 8 *) 24 | sh_size: int; (* 8 *) 25 | sh_link: int; (* 4 *) 26 | sh_info: int; (* 4 *) 27 | sh_addralign: int; (* 8 *) 28 | sh_entsize: int; (* 8 *) 29 | } 30 | 31 | let sizeof_section_header = 64 (* bytes *) 32 | 33 | (* 64 bits; t32 will be 32-bits *) 34 | type t = section_header array 35 | 36 | let kSHT_NULL= 0(* Section header table entry unused *) 37 | let kSHT_PROGBITS= 1(* Program data *) 38 | let kSHT_SYMTAB= 2(* Symbol table *) 39 | let kSHT_STRTAB= 3(* String table *) 40 | let kSHT_RELA= 4(* Relocation entries with addends *) 41 | let kSHT_HASH= 5(* Symbol hash table *) 42 | let kSHT_DYNAMIC= 6(* Dynamic linking information *) 43 | let kSHT_NOTE= 7(* Notes *) 44 | let kSHT_NOBITS= 8(* Program space with no data (bss) *) 45 | let kSHT_REL= 9(* Relocation entries, no addends *) 46 | let kSHT_SHLIB= 10(* Reserved *) 47 | let kSHT_DYNSYM= 11(* Dynamic linker symbol table *) 48 | let kSHT_INIT_ARRAY= 14(* Array of constructors *) 49 | let kSHT_FINI_ARRAY= 15(* Array of destructors *) 50 | let kSHT_PREINIT_ARRAY= 16(* Array of pre-constructors *) 51 | let kSHT_GROUP= 17(* Section group *) 52 | let kSHT_SYMTAB_SHNDX= 18(* Extended section indeces *) 53 | let kSHT_NUM= 19(* Number of defined types. *) 54 | let kSHT_LOOS= 0x60000000(* Start OS-specific. *) 55 | let kSHT_GNU_ATTRIBUTES= 0x6ffffff5(* Object attributes. *) 56 | let kSHT_GNU_HASH= 0x6ffffff6(* GNU-style hash table. *) 57 | let kSHT_GNU_LIBLIST= 0x6ffffff7(* Prelink library list *) 58 | let kSHT_CHECKSUM= 0x6ffffff8(* Checksum for DSO content. *) 59 | let kSHT_LOSUNW= 0x6ffffffa(* Sun-specific low bound. *) 60 | let kSHT_SUNW_move= 0x6ffffffa 61 | let kSHT_SUNW_COMDAT= 0x6ffffffb 62 | let kSHT_SUNW_syminfo= 0x6ffffffc 63 | let kSHT_GNU_verdef= 0x6ffffffd(* Version definition section. *) 64 | let kSHT_GNU_verneed= 0x6ffffffe(* Version needs section. *) 65 | let kSHT_GNU_versym= 0x6fffffff(* Version symbol table. *) 66 | let kSHT_HISUNW= 0x6fffffff(* Sun-specific high bound. *) 67 | let kSHT_HIOS= 0x6fffffff(* End OS-specific type *) 68 | let kSHT_LOPROC= 0x70000000(* Start of processor-specific *) 69 | let kSHT_HIPROC= 0x7fffffff(* End of processor-specific *) 70 | let kSHT_LOUSER= 0x80000000(* Start of application-specific *) 71 | let kSHT_HIUSER= 0x8fffffff(* End of application-specific *) 72 | 73 | let shtype_to_string shtype = 74 | match shtype with 75 | | 0 -> "NULL" 76 | | 1 -> "PROGBITS" 77 | | 2 -> "SYMTAB" 78 | | 3 -> "STRTAB" 79 | | 4 -> "RELA" 80 | | 5 -> "HASH" 81 | | 6 -> "DYNAMIC" 82 | | 7 -> "NOTE" 83 | | 8 -> "NOBITS" 84 | | 9 -> "REL" 85 | | 10 -> "SHLIB" 86 | | 11 -> "DYNSYM" 87 | | 14 ->"INIT_ARRAY" 88 | | 15 -> "FINI_ARRAY" 89 | | 16 -> "PREINIT_ARRAY" 90 | | 17 -> "GROUP" 91 | | 18 -> "SYMTAB_SHNDX" 92 | | 19 -> "NUM" 93 | | 0x60000000 -> "LOOS" 94 | | 0x6ffffff5 -> "GNU_ATTRIBUTES" 95 | | 0x6ffffff6 -> "GNU_HASH" 96 | | 0x6ffffff7 -> "GNU_LIBLIST" 97 | | 0x6ffffff8 -> "CHECKSUM" 98 | | 0x6ffffffa -> "LOSUNW" (* SUNW_move *) 99 | | 0x6ffffffb ->"SUNW_COMDAT" 100 | | 0x6ffffffc -> "SHT_SUNW_syminfo" 101 | | 0x6ffffffd -> "GNU_verdef" 102 | | 0x6ffffffe -> "GNU_verneed" 103 | | 0x6fffffff -> "GNU_versym" (* HISUNW, END HIOS *) 104 | | 0x70000000 -> "LOPROC" 105 | | 0x7fffffff -> "HIPROC" 106 | | 0x80000000 -> "LOUSER" 107 | | 0x8fffffff -> "HIUSER" 108 | | _ -> "UNKNOWN SECTION HEADER TYPE" 109 | 110 | (* Legal values for sh_flags (section flags). *) 111 | 112 | let kSHF_WRITE = (1 lsl 0) (* Writable *) 113 | let kSHF_ALLOC = (1 lsl 1) (* Occupies memory during execution *) 114 | let kSHF_EXECINSTR = (1 lsl 2) (* Executable *) 115 | let kSHF_MERGE = (1 lsl 4) (* Might be merged *) 116 | let kSHF_STRINGS = (1 lsl 5) (* Contains nul-terminated strings *) 117 | let kSHF_INFO_LINK = (1 lsl 6) (* `sh_info' contains SHT index *) 118 | let kSHF_LINK_ORDER = (1 lsl 7) (* Preserve order after combining *) 119 | let kSHF_OS_NONCONFORMING = (1 lsl 8) (* Non-standard OS specific handling required *) 120 | let kSHF_GROUP = (1 lsl 9) (* Section is member of a group. *) 121 | let kSHF_TLS = (1 lsl 10) (* Section hold thread-local data. *) 122 | let kSHF_MASKOS = 0x0ff00000 (* OS-specific. *) 123 | let kSHF_MASKPROC = 0xf0000000 (* Processor-specific *) 124 | let kSHF_ORDERED = (1 lsl 30) (* Special ordering requirement (Solaris). *) 125 | let kSHF_EXCLUDE = (1 lsl 31) (* Section is excluded unless referenced or allocated (Solaris).*) 126 | 127 | let is_empty shs = shs = [||] 128 | 129 | let to_string sh = 130 | Printf.sprintf "%-18s %-11s \n\tflags: 0x%02x addr: 0x%08x offset 0x%04x size: 0x%03x link: %2d info: %2d addralign: %2d entsize: %d" 131 | (if (sh.name <> "") then 132 | sh.name 133 | else Printf.sprintf "0x%x" sh.sh_name) 134 | (shtype_to_string sh.sh_type) 135 | sh.sh_flags 136 | sh.sh_addr 137 | sh.sh_offset 138 | sh.sh_size 139 | sh.sh_link 140 | sh.sh_info 141 | sh.sh_addralign 142 | sh.sh_entsize 143 | 144 | let print_section_headers shs = 145 | Printf.printf "Section Headers (%d):\n" @@ Array.length shs; 146 | Array.iteri (fun i sh -> Printf.printf "(%2d) %s\n" i @@ to_string sh) shs 147 | 148 | let get_section_header binary offset = 149 | let sh_name,o = Binary.u32o binary offset in 150 | let sh_type,o = Binary.u32o binary o in 151 | let sh_flags,o = Binary.u64o binary o in 152 | let sh_addr,o = Binary.u64o binary o in 153 | let sh_offset,o = Binary.u64o binary o in 154 | let sh_size,o = Binary.u64o binary o in 155 | let sh_link,o = Binary.u32o binary o in 156 | let sh_info,o = Binary.u32o binary o in 157 | let sh_addralign,o = Binary.u64o binary o in 158 | let sh_entsize,o = Binary.u64o binary o in 159 | let name = "" in 160 | { 161 | name; 162 | sh_name; 163 | sh_type; 164 | sh_flags; 165 | sh_addr; 166 | sh_offset; 167 | sh_size; 168 | sh_link; 169 | sh_info; 170 | sh_addralign; 171 | sh_entsize; 172 | } 173 | 174 | let rec find_shstrtab shs = 175 | match shs with 176 | | [] -> None 177 | | sh::shs -> 178 | begin 179 | if ((sh.sh_flags) = 0 && (sh.sh_type = kSHT_STRTAB)) then 180 | Some sh 181 | else 182 | find_shstrtab shs 183 | end 184 | 185 | let update_section_headers_with_names binary shs = 186 | match find_shstrtab shs with 187 | | Some sh -> 188 | let base_offset = sh.sh_offset in 189 | List.iter (fun sh -> 190 | (* must be very careful, the strtab _relies_ on null termination of strings, and performs optimizations, like jumping into the middle of a string, e.g., rela.plt and just .plt to perform a kind of trie-esque string saving format... *) 191 | sh.name <- Binary.string binary (base_offset + sh.sh_name); 192 | ) shs; 193 | shs 194 | | None -> 195 | (* just return the unmodifed section headers if we can't find the shstrtab *) 196 | shs 197 | 198 | (* TODO: use list? *) 199 | let get_section_headers binary shoff shentsize shnum = 200 | let rec loop count offset acc = 201 | if (count >= shnum) then 202 | List.rev acc |> update_section_headers_with_names binary |> Array.of_list 203 | else 204 | let ph = get_section_header binary offset in 205 | loop (count + 1) (offset + shentsize) (ph::acc) 206 | in loop 0 shoff [] 207 | 208 | let find_section_by_type section_type shs = 209 | let section = ref None in 210 | for i = 0 to (Array.length shs) - 1 do 211 | if (shs.(i).sh_type = section_type) then 212 | section := Some shs.(i) 213 | done; 214 | section 215 | 216 | let get_sections section_type shs = 217 | Array.fold_left (fun acc elem -> 218 | if (elem.sh_type = section_type) then 219 | (elem::acc) 220 | else 221 | acc) [] shs 222 | 223 | let get_dynamic_section shs = 224 | Array.fold_left (fun acc elem -> 225 | if (elem.sh_type = kSHT_DYNAMIC) then 226 | Some elem 227 | else 228 | acc) None shs 229 | 230 | 231 | let set_section_header bytes sh offset = 232 | Binary.set_uint bytes sh.sh_name 4 offset 233 | |> Binary.set_uint bytes sh.sh_type 4 234 | |> Binary.set_uint bytes sh.sh_flags 8 235 | |> Binary.set_uint bytes sh.sh_addr 8 236 | |> Binary.set_uint bytes sh.sh_offset 8 237 | |> Binary.set_uint bytes sh.sh_size 8 238 | |> Binary.set_uint bytes sh.sh_link 4 239 | |> Binary.set_uint bytes sh.sh_info 4 240 | |> Binary.set_uint bytes sh.sh_addralign 8 241 | |> Binary.set_uint bytes sh.sh_entsize 8 242 | 243 | let set bytes shs offset = 244 | Array.fold_left (fun acc sh -> set_section_header bytes sh acc) offset shs 245 | 246 | let to_bytes shs = 247 | let b = Bytes.create ((Array.length shs) * sizeof_section_header) in 248 | ignore @@ set b shs 0; 249 | b 250 | 251 | -------------------------------------------------------------------------------- /lib/elf/ElfSymbolTable.ml: -------------------------------------------------------------------------------- 1 | (* builds the debug symbol table; will be empty if stripped *) 2 | 3 | open ElfSectionHeader 4 | 5 | (* Legal values for ST_BIND subfield of st_info (symbol binding). *) 6 | let kSTB_LOCAL = 0 (* Local symbol *) 7 | let kSTB_GLOBAL = 1 (* Global symbol *) 8 | let kSTB_WEAK = 2 (* Weak symbol *) 9 | let kSTB_NUM = 3 (* Number of defined types. *) 10 | let kSTB_LOOS = 10 (* Start of OS-specific *) 11 | let kSTB_GNU_UNIQUE = 10 (* Unique symbol. *) 12 | let kSTB_HIOS = 12 (* End of OS-specific *) 13 | let kSTB_LOPROC = 13 (* Start of processor-specific *) 14 | let kSTB_HIPROC = 15 (* End of processor-specific *) 15 | 16 | (* Legal values for ST_TYPE subfield of st_info (symbol type). *) 17 | 18 | let kSTT_NOTYPE = 0 (* Symbol type is unspecified *) 19 | let kSTT_OBJECT = 1 (* Symbol is a data object *) 20 | let kSTT_FUNC = 2 (* Symbol is a code object *) 21 | let kSTT_SECTION = 3 (* Symbol associated with a section *) 22 | let kSTT_FILE = 4 (* Symbol's name is file name *) 23 | let kSTT_COMMON = 5 (* Symbol is a common data object *) 24 | let kSTT_TLS = 6 (* Symbol is thread-local data object*) 25 | let kSTT_NUM = 7 (* Number of defined types. *) 26 | let kSTT_LOOS = 10 (* Start of OS-specific *) 27 | let kSTT_GNU_IFUNC = 10 (* Symbol is indirect code object *) 28 | let kSTT_HIOS = 12 (* End of OS-specific *) 29 | let kSTT_LOPROC = 13 (* Start of processor-specific *) 30 | let kSTT_HIPROC = 15 (* End of processor-specific *) 31 | (* ================ *) 32 | 33 | let symbol_bind_to_string bind = 34 | match bind with 35 | | 0 -> "LOCAL" 36 | | 1 -> "GLOBAL" 37 | | 2 -> "WEAK" 38 | | 3 -> "NUM " 39 | (* | 10 -> "LOOS" *) 40 | | 10 -> "GNU_UNIQUE" (* not sure what this is *) 41 | | 12 -> "HIOS" 42 | | 13 -> "LOPROC" 43 | | 15 -> "HIPROC" 44 | | _ -> "UNKNOWN SYMBOL BIND" 45 | 46 | let symbol_type_to_string sttype = 47 | match sttype with 48 | | 0 -> "NOTYPE" 49 | | 1 -> "OBJECT" 50 | | 2 -> "FUNC" 51 | | 3 -> "SECTION" 52 | | 4 -> "FILE" 53 | | 5 -> "COMMON" 54 | | 6 -> "TLS " 55 | | 7 -> "NUM " 56 | (* | 10 -> "LOOS" *) 57 | | 10 -> "GNU_IFUNC" (* this is an "indirect function" which chooses an implementation at runtime *) 58 | | 12 -> "HIOS" 59 | | 13 -> "LOPROC" 60 | | 15 -> "HIPROC" 61 | | _ -> "UNKNOWN SYMBOL TYPE" 62 | 63 | (* 64 | * How to extract and insert information held in the st_info field. 65 | 66 | #define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) 67 | #define ELF32_ST_TYPE(val) ((val) & 0xf) 68 | #define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 69 | *) 70 | 71 | let get_bind info = info lsr 4 72 | let get_type info = info land 0xf 73 | 74 | (* Symbol visibility specification encoded in the st_other field. *) 75 | let kSTV_DEFAULT = 0 (* Default symbol visibility rules *) 76 | let kSTV_INTERNAL = 1 (* Processor specific hidden class *) 77 | let kSTV_HIDDEN = 2 (* Sym unavailable in other modules *) 78 | let kSTV_PROTECTED = 3 (* Not preemptible, not exported *) 79 | 80 | let symbol_visibility_to_string visibility = 81 | match visibility with 82 | | 0 -> "DEFAULT" 83 | | 1 -> "INTERNAL" 84 | | 2 -> "HIDDEN" 85 | | 3 -> "PROTECTED" 86 | | _ -> "UKNOWN SYMBOL VISIBILITY" 87 | 88 | (* How to extract and insert information held in the st_other field. *) 89 | 90 | let get_visibility other = other land 0x03 91 | 92 | (* =========================== *) 93 | (* 94 | typedef struct { 95 | uint32_t st_name; 96 | unsigned char st_info; 97 | unsigned char st_other; 98 | uint16_t st_shndx; 99 | Elf64_Addr st_value; 100 | uint64_t st_size; 101 | } Elf64_Sym; 102 | *) 103 | 104 | type symbol_entry = 105 | { 106 | mutable name: string; 107 | st_name: int; (* 4 *) 108 | st_info: int; (* 1 *) 109 | st_other: int; (* 1 *) 110 | st_shndx: int; (* 2 *) 111 | st_value: int; (* 8 *) 112 | st_size: int; (* 8 *) 113 | } 114 | 115 | let sizeof_symbol_entry = 24 116 | 117 | type t = symbol_entry list 118 | 119 | let set_symbol_entry bytes sym offset = 120 | Binary.set_uint bytes sym.st_name 4 offset 121 | |> Binary.set_uint bytes sym.st_info 1 122 | |> Binary.set_uint bytes sym.st_other 1 123 | |> Binary.set_uint bytes sym.st_shndx 2 124 | |> Binary.set_uint bytes sym.st_value 8 125 | |> Binary.set_uint bytes sym.st_size 8 126 | 127 | let set bytes syms offset = 128 | List.fold_left (fun acc sym -> set_symbol_entry bytes sym acc) offset syms 129 | 130 | let to_bytes syms = 131 | let b = Bytes.create (List.length syms * sizeof_symbol_entry) in 132 | ignore @@ set b syms 0; 133 | b 134 | 135 | let symbol_to_string symbol = 136 | Printf.sprintf "%s 0x%x %s %s %s size: %d index: %d " 137 | symbol.name 138 | symbol.st_value 139 | (get_bind symbol.st_info |> symbol_bind_to_string) 140 | (get_type symbol.st_info |> symbol_type_to_string) 141 | (get_visibility symbol.st_other |> symbol_visibility_to_string) 142 | symbol.st_size 143 | symbol.st_shndx 144 | 145 | let print (entries:t) = 146 | Printf.printf "Symbol Table (%d):\n" @@ List.length entries; 147 | List.iter 148 | (fun elem -> 149 | Printf.printf "%s\n" @@ symbol_to_string elem) 150 | entries 151 | 152 | let get_symbol_entry bytes offset = 153 | let name = "" in 154 | let st_name = Binary.u32 bytes offset in 155 | let st_info = Binary.u8 bytes (offset + 4) in 156 | let st_other = Binary.u8 bytes (offset + 5) in 157 | let st_shndx = Binary.u16 bytes (offset + 6) in 158 | let st_value = Binary.u64 bytes (offset + 8) in 159 | let st_size = Binary.u64 bytes (offset + 16) in 160 | { 161 | name; 162 | st_name; 163 | st_info; 164 | st_other; 165 | st_shndx; 166 | st_value; 167 | st_size; 168 | } 169 | 170 | let get_symbol_entry_adjusted bytes masks offset = 171 | let name = "" in 172 | let st_name = Binary.u32 bytes offset in 173 | let st_info = Binary.u8 bytes (offset + 4) in 174 | let st_other = Binary.u8 bytes (offset + 5) in 175 | let st_shndx = Binary.u16 bytes (offset + 6) in 176 | let st_value = Binary.u64 bytes (offset + 8) |> ElfProgramHeader.adjust masks in 177 | let st_size = Binary.u64 bytes (offset + 16) in 178 | { 179 | name; 180 | st_name; 181 | st_info; 182 | st_other; 183 | st_shndx; 184 | st_value; 185 | st_size; 186 | } 187 | 188 | let get_symtab bytes offset size = 189 | let len = size + offset in 190 | let rec loop pos acc = 191 | if (pos >= len) then 192 | acc 193 | else 194 | loop (pos + sizeof_symbol_entry) ((get_symbol_entry bytes pos)::acc) 195 | in loop offset [] 196 | 197 | (* adjusted by vm addr mask list offsets *) 198 | let get_symtab_adjusted bytes masks offset size = 199 | let len = size + offset in 200 | let rec loop pos acc = 201 | if (pos >= len) then 202 | acc 203 | else 204 | loop (pos + sizeof_symbol_entry) ((get_symbol_entry_adjusted bytes masks pos)::acc) 205 | in loop offset [] 206 | 207 | (* update the symbol name using the symbol table data offset into the binary *) 208 | let amend_symbol_table binary offset size symbol_table = 209 | List.iter (fun sym -> 210 | sym.name <- Binary.string binary (offset + sym.st_name); 211 | ) symbol_table; 212 | symbol_table 213 | 214 | let get_symbol_table binary section_headers = 215 | match get_sections ElfSectionHeader.kSHT_SYMTAB section_headers with 216 | | [] -> [] 217 | | sh::shs -> 218 | let offset = sh.sh_offset in 219 | let size = sh.sh_size in 220 | let strtab = section_headers.(sh.sh_link) in 221 | get_symtab binary offset size |> amend_symbol_table binary strtab.sh_offset strtab.sh_size 222 | 223 | let get_symbol_table_with_offsets binary offset size strtab_offset strtab_size = 224 | get_symtab binary offset size |> amend_symbol_table binary strtab_offset strtab_size 225 | 226 | let get_symbol_table_adjusted binary masks offset size strtab_offset strtab_size = 227 | get_symtab_adjusted binary masks offset size 228 | |> amend_symbol_table binary strtab_offset strtab_size 229 | |> List.rev (* otherwise relocs backwards *) 230 | 231 | let sort symbols = 232 | List.sort 233 | (fun a b -> 234 | Pervasives.compare 235 | a.st_value 236 | b.st_value 237 | ) symbols 238 | 239 | let is_local symbol = 240 | (get_bind symbol.st_info) = 0 (* local *) 241 | 242 | let is_import symbol = 243 | symbol.st_value = 0x0 244 | && symbol.st_shndx = 0 245 | && symbol.name <> "" (* ignore first \0 symbol *) 246 | 247 | let is_export symbol = 248 | let bind = get_bind symbol.st_info in 249 | let t = get_type symbol.st_info in 250 | (bind = 1 (* global *) || (bind = 2 (* weak *) && 251 | (t = 1 (* func *) 252 | || t = 10 (* gnu_ifunc *) 253 | || t = 2 ))) (* object *) 254 | && symbol.st_value <> 0 255 | 256 | let is_ifunc symbol = (get_bind symbol.st_info) = 10 257 | -------------------------------------------------------------------------------- /lib/elf/ElfVersion.ml: -------------------------------------------------------------------------------- 1 | open Binary 2 | 3 | type elf32_verdef = { 4 | vd_version: int [@size 4]; 5 | vd_flags: int [@size 4]; 6 | vd_ndx: int [@size 4]; 7 | vd_cnt: int [@size 4]; 8 | vd_hash: int [@size 4]; 9 | vd_aux: int [@size 4]; 10 | vd_next: int [@size 4]; 11 | } 12 | 13 | type elf64_verdef = { 14 | vd_version: int [@size 8]; 15 | vd_flags: int [@size 8]; 16 | vd_ndx: int [@size 8]; 17 | vd_cnt: int [@size 8]; 18 | vd_hash: int [@size 8]; 19 | vd_aux: int [@size 8]; 20 | vd_next: int [@size 8]; 21 | } 22 | 23 | type elf32_verdaux = { 24 | vda_name: int [@size 4]; 25 | vda_next: int [@size 4]; 26 | } 27 | 28 | type elf64_verdaux = { 29 | vda_name: int [@size 8]; 30 | vda_next: int [@size 8]; 31 | } 32 | 33 | type elf32_verneed = { 34 | vn_version: int [@size 4]; 35 | vn_cnt: int [@size 4]; 36 | vn_file: int [@size 4]; 37 | vn_aux: int [@size 4]; 38 | vn_next: int [@size 4]; 39 | } 40 | 41 | type elf64_verneed = { 42 | vn_version: int [@size 8]; 43 | vn_cnt: int [@size 8]; 44 | vn_file: int [@size 8]; 45 | vn_aux: int [@size 8]; 46 | vn_next: int [@size 8]; 47 | } 48 | 49 | type elf32_vernaux = { 50 | vna_hash: int [@size 4]; 51 | vna_flags: int [@size 4]; 52 | vna_other: int [@size 4]; 53 | vna_name: int [@size 4]; 54 | vna_next: int [@size 4]; 55 | } 56 | 57 | type elf64_vernaux = { 58 | vna_hash: int [@size 8]; 59 | vna_flags: int [@size 8]; 60 | vna_other: int [@size 8]; 61 | vna_name: int [@size 8]; 62 | vna_next: int [@size 8]; 63 | } 64 | 65 | let kVER_DEF_NONE = 0 66 | 67 | let kVER_DEF_CURRENT = 1 68 | 69 | let kVER_DEF_NUM = 2 70 | 71 | let kVER_FLG_BASE = 0x1 72 | 73 | let kVER_FLG_WEAK = 0x2 74 | 75 | let kVER_NDX_LOCAL = 0 76 | 77 | let kVER_NDX_GLOBAL = 1 78 | 79 | let kVER_NDX_LORESERVE = 0xff00 80 | 81 | let kVER_NDX_ELIMINATE = 0xff01 82 | 83 | let kVER_NEED_NONE = 0 84 | 85 | let kVER_NEED_CURRENT = 1 86 | 87 | let kVER_NEED_NUM = 2 88 | 89 | let kVER_FLG_WEAK = 0x2 90 | 91 | let get_elf32_verdef binary offset :elf32_verdef = 92 | let vd_version,o = Binary.u32o binary offset in 93 | let vd_flags,o = Binary.u32o binary o in 94 | let vd_ndx,o = Binary.u32o binary o in 95 | let vd_cnt,o = Binary.u32o binary o in 96 | let vd_hash,o = Binary.u32o binary o in 97 | let vd_aux,o = Binary.u32o binary o in 98 | let vd_next = Binary.u32 binary o in 99 | {vd_version;vd_flags;vd_ndx;vd_cnt;vd_hash;vd_aux;vd_next;} 100 | 101 | let get_elf64_verdef binary offset :elf64_verdef = 102 | let vd_version,o = Binary.u64o binary offset in 103 | let vd_flags,o = Binary.u64o binary o in 104 | let vd_ndx,o = Binary.u64o binary o in 105 | let vd_cnt,o = Binary.u64o binary o in 106 | let vd_hash,o = Binary.u64o binary o in 107 | let vd_aux,o = Binary.u64o binary o in 108 | let vd_next = Binary.u64 binary o in 109 | {vd_version;vd_flags;vd_ndx;vd_cnt;vd_hash;vd_aux;vd_next;} 110 | 111 | let get_elf32_verdaux binary offset :elf32_verdaux = 112 | let vda_name,o = Binary.u32o binary offset in 113 | let vda_next = Binary.u32 binary o in 114 | {vda_name;vda_next;} 115 | 116 | let get_elf64_verdaux binary offset :elf64_verdaux = 117 | let vda_name,o = Binary.u64o binary offset in 118 | let vda_next = Binary.u64 binary o in 119 | {vda_name;vda_next;} 120 | 121 | let get_elf32_verneed binary offset :elf32_verneed = 122 | let vn_version,o = Binary.u32o binary offset in 123 | let vn_cnt,o = Binary.u32o binary o in 124 | let vn_file,o = Binary.u32o binary o in 125 | let vn_aux,o = Binary.u32o binary o in 126 | let vn_next = Binary.u32 binary o in 127 | {vn_version;vn_cnt;vn_file;vn_aux;vn_next;} 128 | 129 | let get_elf64_verneed binary offset :elf64_verneed = 130 | let vn_version,o = Binary.u64o binary offset in 131 | let vn_cnt,o = Binary.u64o binary o in 132 | let vn_file,o = Binary.u64o binary o in 133 | let vn_aux,o = Binary.u64o binary o in 134 | let vn_next = Binary.u64 binary o in 135 | {vn_version;vn_cnt;vn_file;vn_aux;vn_next;} 136 | 137 | let get_elf32_vernaux binary offset :elf32_vernaux = 138 | let vna_hash,o = Binary.u32o binary offset in 139 | let vna_flags,o = Binary.u32o binary o in 140 | let vna_other,o = Binary.u32o binary o in 141 | let vna_name,o = Binary.u32o binary o in 142 | let vna_next = Binary.u32 binary o in 143 | {vna_hash;vna_flags;vna_other;vna_name;vna_next;} 144 | 145 | let get_elf64_vernaux binary offset :elf64_vernaux = 146 | let vna_hash,o = Binary.u64o binary offset in 147 | let vna_flags,o = Binary.u64o binary o in 148 | let vna_other,o = Binary.u64o binary o in 149 | let vna_name,o = Binary.u64o binary o in 150 | let vna_next = Binary.u64 binary o in 151 | {vna_hash;vna_flags;vna_other;vna_name;vna_next;} 152 | -------------------------------------------------------------------------------- /lib/elf/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: dc13e6ca3cd0fe7a183086218f6af14f) 3 | version = "1.1" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library with no dependencies" 6 | requires = "goblin utils" 7 | archive(byte) = "elf.cma" 8 | archive(byte, plugin) = "elf.cma" 9 | archive(native) = "elf.cmxa" 10 | archive(native, plugin) = "elf.cmxs" 11 | exists_if = "elf.cma" 12 | # OASIS_STOP 13 | 14 | -------------------------------------------------------------------------------- /lib/elf/elf.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: ce24dc89c5ac84c2d6a3a945d434939d) 3 | Elf 4 | ElfHeader 5 | ElfProgramHeader 6 | ElfSectionHeader 7 | ElfVersion 8 | ElfConstants 9 | ElfDynamic 10 | ElfReloc 11 | ElfSymbolTable 12 | ElfCoverage 13 | # OASIS_STOP 14 | -------------------------------------------------------------------------------- /lib/elf/elf.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: ce24dc89c5ac84c2d6a3a945d434939d) 3 | Elf 4 | ElfHeader 5 | ElfProgramHeader 6 | ElfSectionHeader 7 | ElfVersion 8 | ElfConstants 9 | ElfDynamic 10 | ElfReloc 11 | ElfSymbolTable 12 | ElfCoverage 13 | # OASIS_STOP 14 | -------------------------------------------------------------------------------- /lib/goblin/GoblinExport.ml: -------------------------------------------------------------------------------- 1 | type reexport = 2 | | From of string 3 | | Rename of string * string 4 | 5 | let pp_reexport ppf reex = 6 | match reex with 7 | | From lib -> 8 | Format.fprintf ppf " => %s" lib 9 | | Rename (lib, rename) -> 10 | Format.fprintf ppf " => %s.%s" lib rename 11 | 12 | let show_reexport reex = 13 | pp_reexport Format.str_formatter reex; Format.flush_str_formatter() 14 | 15 | type t = 16 | { 17 | name: string; (* name of the exported symbol *) 18 | offset: int; (* offset into the containing binary's byte array *) 19 | size: int; (* size of the routine, in bytes *) 20 | reexport: reexport option; 21 | } 22 | 23 | let pp ppf export = 24 | let reex = 25 | match export.reexport with 26 | | Some reexport -> 27 | show_reexport reexport 28 | | None -> "" 29 | in 30 | Format.fprintf ppf "@[%16x %s (%d)%s@]" export.offset export.name export.size reex 31 | 32 | let show export = 33 | pp Format.str_formatter export; Format.flush_str_formatter() 34 | 35 | let print export = 36 | pp Format.std_formatter export; Format.print_newline() 37 | -------------------------------------------------------------------------------- /lib/goblin/GoblinImport.ml: -------------------------------------------------------------------------------- 1 | type t = 2 | { 3 | name: string; (* name of the imported symbol *) 4 | lib: string; (* library which contains the binary *) 5 | is_lazy: bool; 6 | mutable idx: int; (* the index into some goblin's export code section, typically generated via the dynamic linker *) 7 | offset: int; (* offset into (tol#find import.lib).code *) 8 | size: int; (* size of the imported symbol, in bytes *) 9 | } 10 | 11 | let pp ppf import = 12 | let squiggle = if (import.is_lazy) then "~>" else "->" in 13 | Format.fprintf ppf "@[%16x %s (%d) %s %s@]" 14 | import.offset 15 | import.name 16 | import.size 17 | squiggle 18 | import.lib 19 | 20 | let show import = 21 | pp Format.str_formatter import; Format.flush_str_formatter() 22 | 23 | let print import = 24 | pp Format.std_formatter import; Format.print_newline() 25 | -------------------------------------------------------------------------------- /lib/goblin/GoblinTree.ml: -------------------------------------------------------------------------------- 1 | (* Trie of Life is in goblin because it's cross platform *) 2 | (* Map from symbolname -> Goblin.Symbol *) 3 | 4 | module SymbolMap = Map.Make(String) 5 | 6 | open GoblinExport 7 | 8 | type branch = { 9 | library: string; 10 | aliases: string list; 11 | export: GoblinExport.t 12 | } 13 | 14 | let pp_branch ppf branch = 15 | RdrPrinter.pp_h 16 | Format.str_formatter 17 | ~brackets:true 18 | RdrPrinter.pp_string branch.aliases; 19 | let aliases = Format.flush_str_formatter() in 20 | Format.fprintf 21 | ppf "@[%a -> %s %s@]" 22 | GoblinExport.pp branch.export 23 | branch.library 24 | aliases 25 | 26 | let show_branch branch = 27 | pp_branch Format.str_formatter branch; Format.flush_str_formatter() 28 | 29 | let print_branch branch = 30 | pp_branch Format.std_formatter branch; Format.print_newline() 31 | 32 | type t = branch list SymbolMap.t 33 | 34 | let num_symbols map = SymbolMap.cardinal map 35 | 36 | let fold = SymbolMap.fold 37 | 38 | let find = SymbolMap.find 39 | 40 | let add name export map = 41 | SymbolMap.add name export map 42 | 43 | let empty:t = SymbolMap.empty 44 | 45 | let is_empty = SymbolMap.is_empty 46 | 47 | let singleton: 't SymbolMap.t ref = ref empty 48 | 49 | let flatten map = 50 | fold 51 | (fun key values acc -> 52 | (* list.fold acc in different arg pos than map.fold arg wtf *) 53 | List.fold_left 54 | (fun acc data -> 55 | data::acc) acc values 56 | ) map [] 57 | 58 | let show_flat list = 59 | let b = Buffer.create ((List.length list) * 15) in 60 | let rec loop = 61 | function 62 | | [] -> Buffer.contents b 63 | | branch::branches -> 64 | Buffer.add_string b 65 | @@ show_branch branch; 66 | Buffer.add_string b "\n"; 67 | loop branches 68 | in loop list 69 | 70 | let sort_symbols 71 | ?compare_libs:(compare_libs=false) 72 | list = 73 | List.sort (fun a b -> 74 | (* first compare libs, export must have a lib *) 75 | (* yea... but not just for exports anymore, so either need to return empty lib or check for Not_found... *) 76 | let l1 = a.library in 77 | let l2 = b.library in 78 | let n1 = a.export.name in 79 | let n2 = b.export.name in 80 | if (not compare_libs || l1 = l2) then 81 | try 82 | let o1 = a.export.offset in 83 | try 84 | let o2 = b.export.offset in 85 | if (o1 = o2) then 86 | Pervasives.compare n1 n2 87 | else 88 | Pervasives.compare o1 o2 89 | with Not_found -> 90 | 1 91 | with Not_found -> 92 | -1 93 | else 94 | Pervasives.compare l1 l2 95 | ) list 96 | 97 | let get_libraries symbol map = 98 | try 99 | let branches = SymbolMap.find symbol map in 100 | Generics.list_with_stringer 101 | ~newline:true ~omit_singleton_braces:true 102 | (fun branch -> branch.library) 103 | branches 104 | with Not_found -> 105 | "Unknown" 106 | 107 | (* aliases are case sensitive - PE will be case insensitive *) 108 | let resolve_import ~case_sensitive:sensitive (branches: branch list) libraries = 109 | let libraries = if (sensitive) then 110 | libraries else List.map String.lowercase libraries 111 | in 112 | let rec loop acc branches = 113 | match branches with 114 | | [] -> 115 | begin 116 | match acc with 117 | | [] -> "Unresolved" 118 | | library::[] -> library 119 | | library::libraries -> 120 | Printf.sprintf 121 | "Multiple Libraries Resolved\n%s" 122 | @@ Generics.list_to_string acc 123 | end 124 | | branch::branches -> 125 | let lib = if (sensitive) then 126 | branch.library else String.lowercase branch.library 127 | in 128 | if (List.mem lib libraries 129 | || List.exists 130 | (fun alias -> 131 | if (sensitive) then 132 | List.mem alias libraries 133 | else 134 | List.mem (String.lowercase alias) libraries 135 | ) 136 | branch.aliases 137 | ) then 138 | loop (branch.library::acc) branches 139 | else 140 | loop acc branches 141 | in loop [] branches 142 | 143 | let resolve_library ~case_sensitive ~name ~libraries ~tree = 144 | try 145 | let branches = find name tree in 146 | resolve_import ~case_sensitive branches libraries 147 | with Not_found -> "Unknown" 148 | 149 | (* 150 | let resolve_ordinal ~ordinal ~lib ~tree = 151 | let bindings = SymbolMap.bindings in 152 | let rec loop bindings = 153 | match bindings with 154 | | [] -> None 155 | | (key, branches)::bindings -> 156 | (* if lib = some branches.library && ordinal = some branches' exports' ordinal then that branches' exports' name *) 157 | *) 158 | 159 | let print_map map = 160 | SymbolMap.iter ( 161 | fun name branches -> 162 | Printf.printf "%s -> %s\n" name 163 | @@ Generics.list_with_stringer 164 | (fun branch -> 165 | branch.library) 166 | branches) map 167 | 168 | exception Not_built 169 | 170 | let get () = 171 | let f = RdrStorage.get_path "gtree" in 172 | if (Sys.file_exists f) then 173 | if (is_empty !singleton) then 174 | begin 175 | let ic = open_in_bin f in 176 | let map = Marshal.from_channel ic in 177 | close_in ic; 178 | singleton := map; 179 | map 180 | end 181 | else 182 | !singleton 183 | else 184 | raise Not_built 185 | -------------------------------------------------------------------------------- /lib/goblin/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: c06468a8578c3570ca857b0f4da94952) 3 | version = "1.1" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library with no dependencies" 6 | requires = "utils" 7 | archive(byte) = "goblin.cma" 8 | archive(byte, plugin) = "goblin.cma" 9 | archive(native) = "goblin.cmxa" 10 | archive(native, plugin) = "goblin.cmxs" 11 | exists_if = "goblin.cma" 12 | # OASIS_STOP 13 | 14 | -------------------------------------------------------------------------------- /lib/goblin/goblin.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 670c48f5bf7db3c3c4af46d1e765c8d1) 3 | Goblin 4 | GoblinExport 5 | GoblinImport 6 | GoblinTree 7 | # OASIS_STOP 8 | -------------------------------------------------------------------------------- /lib/goblin/goblin.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 670c48f5bf7db3c3c4af46d1e765c8d1) 3 | Goblin 4 | GoblinExport 5 | GoblinImport 6 | GoblinTree 7 | # OASIS_STOP 8 | -------------------------------------------------------------------------------- /lib/mach/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 3a9fea97bc0f9f339ba064f211798e15) 3 | version = "1.1" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library with no dependencies" 6 | requires = "utils goblin" 7 | archive(byte) = "mach.cma" 8 | archive(byte, plugin) = "mach.cma" 9 | archive(native) = "mach.cmxa" 10 | archive(native, plugin) = "mach.cmxs" 11 | exists_if = "mach.cma" 12 | # OASIS_STOP 13 | 14 | -------------------------------------------------------------------------------- /lib/mach/Mach.ml: -------------------------------------------------------------------------------- 1 | module BindOpcodes = MachBindOpcodes 2 | module CpuTypes = MachCpuTypes 3 | module Fat = MachFat 4 | module LoadCommand = MachLoadCommand 5 | module Constants = MachConstants 6 | (* todo remove plural to match elf? *) 7 | module Exports = MachExports 8 | module Header = MachHeader 9 | module Imports = MachImports 10 | module Section = MachSection 11 | module SymbolTable = MachSymbolTable 12 | module RebaseOpcodes = MachRebaseOpcodes 13 | module Version = MachVersion 14 | module Coverage = MachCoverage 15 | 16 | open LoadCommand.Types 17 | 18 | let debug = false 19 | 20 | type t = { 21 | header: Header.t; 22 | load_commands: LoadCommand.t; 23 | imports: Imports.t; 24 | nimports: int; 25 | exports: Exports.t; 26 | nexports: int; 27 | nlist: SymbolTable.t; 28 | nnlist: int; 29 | name: string; 30 | is_lib: bool; 31 | libraries: string array; 32 | nlibraries: int; 33 | size: int; 34 | raw_code: bytes; 35 | entry: int64; 36 | byte_coverage: ByteCoverage.t; 37 | } 38 | 39 | let binary_to_string binary = 40 | let libstr = if (binary.is_lib) then " (LIB)" else "" in 41 | Printf.sprintf "%s%s:\nImports (%d):\n%sExports (%d):\n%s" 42 | binary.name libstr 43 | (binary.nimports) 44 | (Imports.imports_to_string binary.imports) 45 | (binary.nexports) 46 | (Exports.exports_to_string binary.exports) 47 | 48 | let print binary = 49 | Printf.printf "%s" @@ binary_to_string binary; 50 | ByteCoverage.print binary.byte_coverage 51 | 52 | let get ?coverage:(coverage=true) binary = 53 | let size = Bytes.length binary in 54 | let header = Header.get_mach_header binary in 55 | let load_commands = LoadCommand.get_load_commands binary 56 | Header.sizeof_mach_header 57 | header.Header.ncmds 58 | header.Header.sizeofcmds 59 | in 60 | let entry = LoadCommand.get_entry load_commands |> Int64.of_int in 61 | let name = LoadCommand.get_lib_name load_commands (* if "" we're not a dylib *) 62 | in 63 | let segments = LoadCommand.get_segments load_commands in 64 | let libraries = LoadCommand.get_libraries load_commands name in (* TODO: watch this, with the libs.(0) *) 65 | (* move this inside of dyld, need the nlist info to compute locals... *) 66 | let is_lib = header.Header.filetype = Header.kMH_DYLIB in 67 | let nlist = 68 | match LoadCommand.get_load_command LC_SYMTAB load_commands with 69 | | Some (LC_SYMTAB symtab) -> 70 | SymbolTable.get_symbols binary symtab 71 | | _ -> [] 72 | in 73 | let dyld_info = LoadCommand.get_dyld_info load_commands in 74 | let exports, imports = 75 | match dyld_info with 76 | | Some dyld_info -> 77 | (* TODO: add load segment boundaries, and nlists locals as a parameters *) 78 | let exports = 79 | Exports.get_exports binary dyld_info libraries 80 | in 81 | (* TODO: yea, need to fix imports like machExports; send in the libraries, 82 | do all that preprocessing there, and not in create binary *) 83 | let imports = 84 | Imports.get_imports 85 | binary dyld_info libraries segments 86 | in 87 | exports,imports 88 | | None -> 89 | [],[] 90 | in 91 | let nnlist = List.length nlist in 92 | let nimports = List.length imports in 93 | let nexports = List.length exports in 94 | let nlibraries = Array.length libraries in 95 | let raw_code = Bytes.empty in 96 | let byte_coverage = 97 | if (coverage) then 98 | Coverage.compute header load_commands size binary 99 | else 100 | ByteCoverage.null 101 | in 102 | { 103 | header; load_commands; name; nlist; nnlist; 104 | imports; nimports; exports; nexports; 105 | is_lib; libraries; nlibraries; raw_code; size; 106 | byte_coverage; 107 | entry; 108 | } 109 | -------------------------------------------------------------------------------- /lib/mach/MachBindOpcodes.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Some uses of external symbols do not need to be bound immediately. 3 | * Instead they can be lazily bound on first use. The lazy_bind 4 | * are contains a stream of BIND opcodes to bind all lazy symbols. 5 | * Normal use is that dyld ignores the lazy_bind section when 6 | * loading an image. Instead the static linker arranged for a 7 | * lazy pointer to initially point to a helper function which 8 | * pushes the offset into the lazy_bind area for the symbol 9 | * needing to be bound, then jumps to dyld which simply adds 10 | * the offset to lazy_bind_off to get the information on what 11 | * to bind. 12 | *) 13 | 14 | (* 15 | * The following are used to encode binding information 16 | *) 17 | let kBIND_TYPE_POINTER = 1 18 | let kBIND_TYPE_TEXT_ABSOLUTE32 = 2 19 | let kBIND_TYPE_TEXT_PCREL32 = 3 20 | 21 | let kBIND_SPECIAL_DYLIB_SELF = 0 22 | let kBIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = 0xf (* 15 *) 23 | let kBIND_SPECIAL_DYLIB_FLAT_LOOKUP = 0xe (* 14 *) 24 | (* 25 | let kBIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1 26 | let kBIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 27 | 28 | *) 29 | 30 | let kBIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1 31 | let kBIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8 32 | 33 | let kBIND_OPCODE_MASK = 0xF0 34 | let kBIND_IMMEDIATE_MASK = 0x0F 35 | let kBIND_OPCODE_DONE = 0x00 36 | let kBIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 37 | let kBIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 38 | let kBIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 39 | let kBIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 40 | let kBIND_OPCODE_SET_TYPE_IMM = 0x50 41 | let kBIND_OPCODE_SET_ADDEND_SLEB = 0x60 42 | let kBIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 43 | let kBIND_OPCODE_ADD_ADDR_ULEB = 0x80 44 | let kBIND_OPCODE_DO_BIND = 0x90 45 | let kBIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 46 | let kBIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 47 | let kBIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 48 | 49 | (* TODO: remove these? *) 50 | type bind_misc = BIND_TYPE_POINTER | 51 | BIND_TYPE_TEXT_ABSOLUTE32 | 52 | BIND_TYPE_TEXT_PCREL32 | 53 | BIND_SPECIAL_DYLIB_SELF | 54 | BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE | 55 | BIND_SPECIAL_DYLIB_FLAT_LOOKUP | 56 | 57 | BIND_SYMBOL_FLAGS_WEAK_IMPORT | 58 | BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION | 59 | 60 | BIND_OPCODE_MASK | 61 | BIND_IMMEDIATE_MASK 62 | 63 | (* 64 | let get_misc misc = 65 | match opcode with 66 | | 1 -> BIND_TYPE_POINTER 67 | | 2 -> BIND_TYPE_TEXT_ABSOLUTE32 68 | | 3 -> BIND_TYPE_TEXT_PCREL32 69 | | 0 -> BIND_SPECIAL_DYLIB_SELF 70 | | -1 -> BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE 71 | | -2 -> BIND_SPECIAL_DYLIB_FLAT_LOOKUP 72 | | 0x1 -> BIND_SYMBOL_FLAGS_WEAK_IMPORT 73 | | 0x8 -> BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 74 | | 0xf0 -> BIND_OPCODE_MASK 75 | | 0x0f -> BIND_IMMEDIATE_MASK 76 | *) 77 | 78 | exception Bad_opcode of int 79 | 80 | type bind_opcode = 81 | | BIND_OPCODE_DONE 82 | | BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 83 | | BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 84 | | BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 85 | | BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 86 | | BIND_OPCODE_SET_TYPE_IMM 87 | | BIND_OPCODE_SET_ADDEND_SLEB 88 | | BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 89 | | BIND_OPCODE_ADD_ADDR_ULEB 90 | | BIND_OPCODE_DO_BIND 91 | | BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 92 | | BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 93 | | BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 94 | 95 | let get_opcode opcode = 96 | match opcode with 97 | | 0x00 -> BIND_OPCODE_DONE 98 | | 0x10 -> BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 99 | | 0x20 -> BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 100 | | 0x30 -> BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 101 | | 0x40 -> BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 102 | | 0x50 -> BIND_OPCODE_SET_TYPE_IMM 103 | | 0x60 -> BIND_OPCODE_SET_ADDEND_SLEB 104 | | 0x70 -> BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 105 | | 0x80 -> BIND_OPCODE_ADD_ADDR_ULEB 106 | | 0x90 -> BIND_OPCODE_DO_BIND 107 | | 0xa0 -> BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 108 | | 0xb0 -> BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 109 | | 0xc0 -> BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 110 | | _ -> raise @@ Bad_opcode opcode 111 | 112 | let opcode_to_string opcode = 113 | match opcode with 114 | | BIND_OPCODE_DONE -> "BIND_OPCODE_DONE" 115 | | BIND_OPCODE_SET_DYLIB_ORDINAL_IMM -> "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM" 116 | | BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB -> "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB" 117 | | BIND_OPCODE_SET_DYLIB_SPECIAL_IMM -> "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM" 118 | | BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM -> "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM" 119 | | BIND_OPCODE_SET_TYPE_IMM -> "BIND_OPCODE_SET_TYPE_IMM" 120 | | BIND_OPCODE_SET_ADDEND_SLEB -> "BIND_OPCODE_SET_ADDEND_SLEB" 121 | | BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB -> "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB" 122 | | BIND_OPCODE_ADD_ADDR_ULEB -> "BIND_OPCODE_ADD_ADDR_ULEB" 123 | | BIND_OPCODE_DO_BIND -> "BIND_OPCODE_DO_BIND" 124 | | BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB -> "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB" 125 | | BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED -> "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED" 126 | | BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB -> "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB" 127 | -------------------------------------------------------------------------------- /lib/mach/MachCoverage.ml: -------------------------------------------------------------------------------- 1 | (* 2 | # TODO: 3 | * compute nlist size 4 | *) 5 | open ByteCoverage 6 | open MachLoadCommand 7 | open MachLoadCommand.Types 8 | open MachConstants 9 | 10 | let debug = false 11 | 12 | let compute_header_coverage header dataset = 13 | let mhsize = MachHeader.sizeof_mach_header in 14 | let lcsize = header.MachHeader.sizeofcmds in 15 | let range_start = 0 in 16 | let range_end = mhsize + range_start in 17 | let extra = "Mach Header" in 18 | ByteCoverage.add 19 | (create_data 20 | ~tag:Meta 21 | ~r1:range_start 22 | ~r2:range_end 23 | ~extra:extra 24 | ~understood:true 25 | ) 26 | dataset 27 | |> 28 | ByteCoverage.add 29 | (create_data 30 | ~tag:Meta 31 | ~r1:mhsize 32 | ~r2:(lcsize+mhsize) 33 | ~extra:"LoadCommands" 34 | ~understood:true 35 | ) 36 | 37 | (* granular dyldinfo/LINKEDIT coverage here *) 38 | let compute_dyldinfo_coverage load_commands dataset = 39 | match MachLoadCommand.get_dyld_info load_commands with 40 | | Some dyld_info -> 41 | let r1 = dyld_info.rebase_off in 42 | let r2 = r1 + dyld_info.rebase_size in 43 | let dyldextra = cmd_int_to_string dyld_info.cmd in 44 | let extra = "dyldinfo" in 45 | add 46 | (create_data 47 | ~tag:Rela 48 | ~r1:r1 49 | ~r2:r2 50 | ~extra:("rebase // " ^ dyldextra) 51 | ~understood:true) dataset 52 | |> add 53 | (create_data 54 | ~tag:Symbol 55 | ~r1:dyld_info.bind_off 56 | ~r2:(dyld_info.bind_off + dyld_info.bind_size) 57 | ~extra:("bind // " ^ dyldextra) 58 | ~understood:true) 59 | |> add 60 | (create_data 61 | ~tag:SymbolTable 62 | ~r1:dyld_info.weak_bind_off 63 | ~r2:(dyld_info.weak_bind_off + dyld_info.weak_bind_size) 64 | ~extra:("weak bind // " ^ dyldextra) 65 | ~understood:true) 66 | |> add 67 | (create_data 68 | ~tag:SymbolTable 69 | ~r1:dyld_info.lazy_bind_off 70 | ~r2:(dyld_info.lazy_bind_off + dyld_info.lazy_bind_size) 71 | ~extra:("lazy bind // " ^ dyldextra) 72 | ~understood:true) 73 | |> add 74 | (create_data 75 | ~tag:SymbolTable 76 | ~r1:dyld_info.export_off 77 | ~r2:(dyld_info.export_off + dyld_info.export_size) 78 | ~extra:("export // " ^ dyldextra) 79 | ~understood:true) 80 | |> add 81 | (create_data 82 | ~tag:Meta 83 | ~r1:dyld_info.rebase_off 84 | ~r2:(dyld_info.export_off + dyld_info.export_size) 85 | ~extra:extra 86 | ~understood:true) 87 | 88 | | None -> dataset 89 | 90 | let known_sections = 91 | [ 92 | (kSECT_TEXT, Code); 93 | (kSECT_DATA, Data); 94 | (kSECT_COMMON, Semantic); 95 | (kSECT_BSS, Semantic); 96 | ("__cstring", String); 97 | ("__stubs", Rela); 98 | ("__stub_helper", Rela); 99 | ("__nl_symbol_ptr", Symbol); 100 | ("__la_symbol_ptr", Symbol); 101 | ] 102 | 103 | module SectionMap = Map.Make(String) 104 | (* add the known sections to our map *) 105 | let section_map = 106 | List.fold_left (fun acc (sectname, tag) -> 107 | SectionMap.add sectname tag acc 108 | ) SectionMap.empty known_sections 109 | 110 | let compute_section_coverage dataset (section:section_64) = 111 | if (debug) then 112 | Printf.printf "SECTION called: %s\n" section.sectname; 113 | let range_start = section.offset in 114 | let range_end = range_start + section.size in 115 | let tag,understood = 116 | try (SectionMap.find section.sectname section_map),true 117 | with Not_found -> Unknown,false 118 | in 119 | let extra = 120 | section.sectname ^ " // " ^ section.segname ^ 121 | ( 122 | if (tag <> Unknown) then 123 | "" 124 | else 125 | " // Unknown" 126 | ) 127 | in 128 | add (create_data 129 | ~tag:tag 130 | ~r1:range_start 131 | ~r2:range_end 132 | ~extra:extra 133 | ~understood:understood) 134 | dataset 135 | 136 | let compute_segment_coverage tag dataset (segment:segment_command_64) = 137 | let size = segment.filesize in 138 | if (debug) then 139 | Printf.printf "SEGMENT called: %s\n" segment.segname; 140 | let range_start = segment.fileoff in 141 | let range_end = size + range_start in 142 | let extra = 143 | segment.segname ^ " // " ^ 144 | cmd_int_to_string segment.cmd 145 | in 146 | let dataset = 147 | List.fold_left compute_section_coverage 148 | dataset segment.sections 149 | in 150 | add 151 | (create_data 152 | ~tag:tag 153 | ~r1:range_start 154 | ~r2:range_end 155 | ~extra:extra 156 | ~understood:true) 157 | dataset 158 | 159 | (* todo, maybe let tags be a list, or have a single main tag... *) 160 | let known_segments = 161 | [ 162 | (* 163 | (kSEG_TEXT, [Code;Semantic]); 164 | (kSEG_DATA, [Data;Semantic]); 165 | (kSEG_OBJC, [Code;Semantic]); 166 | (kSEG_ICON, [Data;Semantic]); 167 | (kSEG_LINKEDIT, [Meta;Semantic]); 168 | *) 169 | (kSEG_TEXT, Semantic); 170 | (kSEG_DATA, Semantic); 171 | (kSEG_OBJC, Semantic); 172 | (kSEG_ICON, Semantic); 173 | (kSEG_LINKEDIT, Semantic); 174 | ] 175 | 176 | let compute_segment_coverage (segments:segment_command_64 list) dataset = 177 | if (segments = []) then 178 | dataset 179 | else 180 | let f = (fun dataset (section_name, tag) -> 181 | match get_segment_by_name section_name segments with 182 | | Some segment -> 183 | compute_segment_coverage tag dataset segment 184 | | None -> dataset 185 | (* List.fold_left (compute_segment_coverage section_type tag) dataset segments *) 186 | ) in 187 | (fun dataset -> List.fold_left f dataset known_segments) dataset 188 | 189 | let compute header (load_commands:lc list) size binary = 190 | compute_header_coverage header ByteCoverage.empty 191 | |> compute_dyldinfo_coverage load_commands 192 | |> compute_segment_coverage (get_segments load_commands) 193 | |> ByteCoverage.create size binary 194 | -------------------------------------------------------------------------------- /lib/mach/MachCpuTypes.ml: -------------------------------------------------------------------------------- 1 | (* cpu types 2 | ========== 3 | #define CPU_ARCH_MASK 0xff000000 /* mask for architecture bits */ 4 | #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ 5 | ========== 6 | *) 7 | 8 | let kCPU_ARCH_MASK = 0xff000000 9 | let kCPU_ARCH_ABI64 = 0x01000000 10 | 11 | (* 12 | #define CPU_TYPE_MC680x0 ((cpu_type_t) 6) 13 | #define CPU_TYPE_X86 ((cpu_type_t) 7) 14 | #define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ 15 | #define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) 16 | 17 | /* skip CPU_TYPE_MIPS ((cpu_type_t) 8) */ 18 | /* skip ((cpu_type_t) 9) */ 19 | #define CPU_TYPE_MC98000 ((cpu_type_t) 10) 20 | #define CPU_TYPE_HPPA ((cpu_type_t) 11) 21 | #define CPU_TYPE_ARM ((cpu_type_t) 12) 22 | #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 23 | *) 24 | 25 | let kCPU_TYPE_X86 = 7 26 | let kCPU_TYPE_ARM = 12 27 | let kCPU_TYPE_X86_64 = kCPU_TYPE_X86 lor kCPU_ARCH_ABI64 28 | let kCPU_TYPE_ARM64 = kCPU_TYPE_ARM lor kCPU_ARCH_ABI64 29 | 30 | let cpu_type_to_string cputype = 31 | if (cputype = kCPU_TYPE_ARM64) then "ARM64" 32 | else if (cputype = kCPU_TYPE_X86_64) then "x86-64" 33 | else if (cputype = kCPU_TYPE_ARM) then "ARM" 34 | else if (cputype = kCPU_TYPE_X86) then "x86" 35 | else "UNIMPLEMENTED CPUTYPE" 36 | -------------------------------------------------------------------------------- /lib/mach/MachFat.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | open Binary 3 | open MachCpuTypes 4 | 5 | (* big endian *) 6 | type fat_header = { 7 | magic: int; (* 0xCAFEBABE *) 8 | nfat_arch: int; 9 | } 10 | 11 | let kFAT_MAGIC = 0xcafebabe 12 | let sizeof_fat_header = 8 (* bytes *) 13 | 14 | (* 15 | struct fat_arch { cpu_type_t cputype; cpu_subtype_t cpusubtype; uint32_t offset; uint32_t size; uint32_t align; }; 16 | *) 17 | 18 | (* big endian *) 19 | type fat_arch = { 20 | cputype: int; 21 | cpusubtype: int; 22 | offset: int; 23 | size: int; 24 | align: int; 25 | } 26 | 27 | let sizeof_fat_arch = 20 (* bytes *) 28 | 29 | let header_to_string header = 30 | sprintf "0x%x nfat_arch: %d\n" header.magic header.nfat_arch 31 | 32 | let print_header header = 33 | printf "%s" (header_to_string header) 34 | 35 | let arch_to_string arch = 36 | sprintf "cputype: %d cpusubtype: %d offset: %d size: %d align: %d\n" arch.cputype arch.cpusubtype arch.offset arch.size arch.align 37 | 38 | let print_arch arch = 39 | printf "%s" (arch_to_string arch) 40 | 41 | let get_fat_header binary = 42 | let magic = u32be binary 0 in 43 | let nfat_arch = u32be binary 4 in 44 | {magic; nfat_arch;} 45 | 46 | let get_fat_arch binary pos = 47 | let cputype = u32be binary pos in 48 | let cpusubtype = u32be binary (pos + 4) in 49 | let offset = u32be binary (pos + 8) in 50 | let size = u32be binary (pos + 12) in 51 | let align = u32be binary (pos + 16) in 52 | {cputype; cpusubtype; offset; size; align} 53 | 54 | let is_x86_64 fat_arch = 55 | fat_arch.cputype = kCPU_TYPE_X86_64 56 | 57 | let is_arm64 fat_arch = 58 | fat_arch.cputype = kCPU_TYPE_ARM64 59 | 60 | let is_64 fat_arch = is_x86_64 fat_arch || is_arm64 fat_arch 61 | 62 | let get_fatties binary count pos = 63 | let rec loop count pos acc = 64 | if (count <= 0) then 65 | acc 66 | else 67 | let fatty = get_fat_arch binary pos in 68 | loop (count - 1) (pos + sizeof_fat_arch) (fatty::acc) 69 | in loop count pos [] 70 | 71 | let get_x86_64_binary_offset ?verbose:(verbose=false) binary nfat = 72 | let fatties = get_fatties binary nfat 0 in 73 | if (verbose) then List.iteri (fun i elem -> Printf.printf "\t(%d): " i; print_arch elem) fatties; 74 | try 75 | let fatty = List.find (fun fh -> is_64 fh) fatties in 76 | Some (fatty.offset, fatty.size) 77 | with Not_found -> None 78 | -------------------------------------------------------------------------------- /lib/mach/MachLoadCommand.ml: -------------------------------------------------------------------------------- 1 | (* 2 | # TODO: 3 | * Figure out lazy load of libraries and load weak dylibs, currently sending them to dylib_command 4 | *) 5 | 6 | module Types = MachLoadCommandTypes 7 | 8 | open MachLoadCommandTypes 9 | open MachLoadCommandMacro 10 | open MachThread64 11 | 12 | type t = lc list 13 | 14 | let sections64_to_string sections = 15 | let b = Buffer.create ((List.length sections) * 32) in 16 | let indent = "\t\t" in 17 | List.iteri (fun i section -> 18 | Printf.sprintf "\n%s(%2d) %s addr: 0x%x size: 0x%x\n%soffset: 0x%x align: %d reloff: 0x%x nreloc: 0x%x\n%sflags: 0x%x r1: 0x%x r2: 0x%x r3: 0x%x\n" 19 | indent i section.sectname section.addr section.size 20 | indent section.offset section.align section.reloff section.nreloc 21 | indent section.flags section.reserved1 section.reserved2 section.reserved3 22 | |> Buffer.add_string b 23 | ) sections; 24 | Buffer.contents b 25 | 26 | let show_segment_64 lc = 27 | Printf.sprintf "\n\t%s vmaddr: 0x%x vmsize: 0x%x\n\tfileoff: 0x%x filesize: 0x%x\n\tmaxprot: %d initprot: %d nsects: %d flags: 0x%x\n%s" 28 | lc.segname lc.vmaddr lc.vmsize 29 | lc.fileoff lc.filesize lc.maxprot 30 | lc.initprot lc.nsects lc.flags 31 | (sections64_to_string lc.sections) 32 | 33 | (* add implemented printing mechanisms here *) 34 | let load_command_to_string lc = 35 | Printf.sprintf "%s (0x%x) %d %s" (cmd_to_string lc.cmd) (cmd_to_int lc.cmd) lc.cmdsize @@ 36 | match lc.t with 37 | | LC_SEGMENT_64 lc -> 38 | show_segment_64 lc 39 | | LC_SYMTAB lc -> 40 | Printf.sprintf "\n\tsymoff: 0x%x nsyms: %u stroff: 0x%x strsize: %u" 41 | lc.symoff 42 | lc.nsyms 43 | lc.stroff 44 | lc.strsize 45 | 46 | | LC_DYSYMTAB lc -> 47 | Printf.sprintf "\n\tilocalsym: 0x%x nlocalsym: %d iextdefsym: 0x%x nextdefsym: %d\n\tiundefsym: 0x%x nundefsym: %d tocoff: 0x%x ntoc: %d\n\tmodtaboff: 0x%x nmodtab: %d extrefsymoff: 0x%x nextrefsyms: %d\n\tindirectsymoff: 0x%x nindirectsyms: %d extreloff: 0x%x nextrel: %d\n\tlocreloff: 0x%x nlocrel: %d" 48 | lc.ilocalsym 49 | lc.nlocalsym 50 | lc.iextdefsym 51 | lc.nextdefsym 52 | lc.iundefsym 53 | lc.nundefsym 54 | lc.tocoff 55 | lc.ntoc 56 | lc.modtaboff 57 | lc.nmodtab 58 | lc.extrefsymoff 59 | lc.nextrefsyms 60 | lc.indirectsymoff 61 | lc.nindirectsyms 62 | lc.extreloff 63 | lc.nextrel 64 | lc.locreloff 65 | lc.nlocrel 66 | 67 | | LC_DYLD_INFO_ONLY lc 68 | | LC_DYLD_INFO lc -> 69 | Printf.sprintf "\n\trebase_off: 0x%x rebase_size: %u \n\tbind_off: 0x%x bind_size: %u \n\tweak_bind_off: 0x%x weak_bind_size: %u \n\tlazy_bind_off: 0x%x lazy_bind_size: %u \n\texport_off: 0x%x export_size: %u" 70 | lc.rebase_off 71 | lc.rebase_size 72 | lc.bind_off 73 | lc.bind_size 74 | lc.weak_bind_off 75 | lc.weak_bind_size 76 | lc.lazy_bind_off 77 | lc.lazy_bind_size 78 | lc.export_off 79 | lc.export_size 80 | 81 | | LC_LOAD_DYLINKER lc 82 | | LC_ID_DYLINKER lc -> 83 | Printf.sprintf "\n\t%s" 84 | lc.name.str 85 | 86 | | LC_ID_DYLIB lc 87 | | LC_LOAD_UPWARD_DYLIB lc 88 | | LC_LAZY_LOAD_DYLIB lc 89 | | LC_LOAD_WEAK_DYLIB lc 90 | | LC_LOAD_DYLIB lc 91 | | LC_REEXPORT_DYLIB lc -> 92 | Printf.sprintf "\n\tname: %s\n\ttimestamp: %u current_version: %u compatibility_version: %u" 93 | lc.dylib.name.str 94 | lc.dylib.timestamp 95 | lc.dylib.current_version 96 | lc.dylib.compatibility_version 97 | 98 | | LC_VERSION_MIN_IPHONEOS lc 99 | | LC_VERSION_MIN_MACOSX lc -> 100 | Printf.sprintf "\n\tversion: %s sdk: %s" (MachVersion.version_to_string lc.version) (MachVersion.version_to_string lc.sdk) 101 | 102 | | LC_MAIN lc -> 103 | Printf.sprintf "\n\toffset: 0x%x stacksize: 0x%x" lc.entryoff lc.stacksize 104 | | LC_UNIXTHREAD lc | LC_THREAD lc -> 105 | begin 106 | match lc.thread_state with 107 | | MachThread64.Unimplemented header -> 108 | Printf.sprintf "\n\tflavor: %s count: %d" (MachThread64.show_flavor header.flavor) header.count 109 | | MachThread64.X86_64 thread -> 110 | Printf.sprintf "\n\tflavor: %s count: %d" 111 | (MachThread64.show_flavor thread.header.flavor) 112 | thread.header.count 113 | end 114 | | lc -> 115 | "" 116 | 117 | let print_segments_64 segments = 118 | List.iter (fun segment -> Printf.printf "%s\n" @@ show_segment_64 segment) segments 119 | 120 | let print_load_command lc = 121 | Printf.printf "%s\n" (load_command_to_string lc) 122 | 123 | let print_load_commands lcs = 124 | List.iteri (fun i lc -> Printf.printf "(%2d): " i; print_load_command lc) lcs; 125 | print_string "\n" 126 | 127 | let get_load_command_header binary offset = 128 | let cmd,o = Binary.u32o binary offset in 129 | let cmdsize,o = Binary.u32o binary o in 130 | cmd,cmdsize,o 131 | 132 | let get_load_command_raw binary offset size = 133 | Bytes.sub binary offset size 134 | 135 | let get_segment_by_name segname segments = 136 | try Some (List.find (fun segment -> segment.segname = segname) segments) 137 | with Not_found -> None 138 | 139 | let rec get_load_commands_it binary offset ncmds acc = 140 | if (ncmds <= 0) then 141 | List.rev acc 142 | else 143 | let cmdi,cmdsize,_ = get_load_command_header binary offset in 144 | let cmd = cmdi |> to_cmd in 145 | let bytes = binary in 146 | let t = get_t cmd bytes offset in 147 | let load_command = {cmd; cmdsize; t} in 148 | get_load_commands_it binary (offset + cmdsize) (ncmds - 1) (load_command::acc) 149 | 150 | let get_load_commands binary offset ncmds sizeofcmds : t = 151 | get_load_commands_it binary offset ncmds [] 152 | 153 | let rec get_load_command cmd (lcs:Types.lc list) :Types.lc_t option = 154 | match lcs with 155 | | [] -> None 156 | | lc::lcs -> 157 | if (lc.cmd = cmd) then 158 | Some lc.t 159 | else 160 | get_load_command cmd lcs 161 | 162 | let get_segments lcs = 163 | List.fold_left (fun acc lc -> 164 | match lc.t with 165 | | LC_SEGMENT_64 segment -> 166 | segment::acc 167 | | _ -> 168 | acc 169 | ) [] lcs |> List.rev 170 | 171 | let rec get_dyld_info lcs = 172 | match lcs with 173 | | [] -> None 174 | | lc::lcs -> 175 | match lc.t with 176 | | LC_DYLD_INFO lc 177 | | LC_DYLD_INFO_ONLY lc -> 178 | Some lc 179 | | _ -> 180 | get_dyld_info lcs 181 | 182 | let rec get_entry (lcs:Types.lc list) = 183 | match lcs with 184 | | [] -> 0x0 185 | | lc::lcs -> 186 | if (lc.cmd = LC_MAIN) then 187 | match lc.t with 188 | | LC_MAIN t -> 189 | t.entryoff 190 | | _ -> 191 | get_entry lcs 192 | else if (lc.cmd = LC_THREAD || lc.cmd = LC_UNIXTHREAD) then 193 | match lc.t with 194 | | LC_THREAD t | LC_UNIXTHREAD t -> 195 | begin 196 | match t.thread_state with 197 | | MachThread64.X86_64 thread -> 198 | MachThread64.get_entry thread 199 | | _ -> 200 | get_entry lcs 201 | end 202 | | _ -> 203 | get_entry lcs 204 | else 205 | get_entry lcs 206 | 207 | let rec get_lib_name lcs = 208 | match lcs with 209 | | [] -> "" 210 | | lc::lcs -> 211 | match lc.t with 212 | | LC_ID_DYLIB lc -> 213 | lc.dylib.name.str 214 | | _ -> get_lib_name lcs 215 | 216 | let print_libraries libs = 217 | if ((Array.length libs) <> 0) then 218 | begin 219 | Printf.printf "Libraries (%d)\n" @@ (Array.length libs - 1); 220 | Array.iteri (fun i lib -> 221 | if (i <> 0) then 222 | Printf.printf "%s\n" lib) libs 223 | end 224 | 225 | let get_libraries lcs self = 226 | List.fold_left (fun acc lc -> 227 | match lc.t with 228 | | LC_LOAD_DYLIB lc 229 | | LC_REEXPORT_DYLIB lc 230 | | LC_LOAD_UPWARD_DYLIB lc 231 | | LC_LAZY_LOAD_DYLIB lc 232 | | LC_LOAD_WEAK_DYLIB lc -> 233 | lc.dylib.name.str::acc 234 | | _ -> acc 235 | ) [self] lcs |> List.rev |> Array.of_list 236 | -------------------------------------------------------------------------------- /lib/mach/MachRebaseOpcodes.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Dyld binds an image during the loading process, if the image 3 | * requires any pointers to be initialized to symbols in other images. 4 | * The bind information is a stream of byte sized 5 | * opcodes whose symbolic names start with BIND_OPCODE_. 6 | * Conceptually the bind information is a table of tuples: 7 | * 8 | * The opcodes are a compressed way to encode the table by only 9 | * encoding when a column changes. In addition simple patterns 10 | * like for runs of pointers initialzed to the same value can be 11 | * encoded in a few bytes. 12 | *) 13 | 14 | (* 15 | * The following are used to encode rebasing information 16 | *) 17 | let kREBASE_TYPE_POINTER = 1 18 | let kREBASE_TYPE_TEXT_ABSOLUTE32 = 2 19 | let kREBASE_TYPE_TEXT_PCREL32 = 3 20 | 21 | let kREBASE_OPCODE_MASK = 0xF0 22 | let kREBASE_IMMEDIATE_MASK = 0x0F 23 | let kREBASE_OPCODE_DONE = 0x00 24 | let kREBASE_OPCODE_SET_TYPE_IMM = 0x10 25 | let kREBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 26 | let kREBASE_OPCODE_ADD_ADDR_ULEB = 0x30 27 | let kREBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 28 | let kREBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 29 | let kREBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 30 | let kREBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 31 | let kREBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 32 | -------------------------------------------------------------------------------- /lib/mach/MachSection.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * A segment is made up of zero or more sections. Non-MH_OBJECT files have 3 | * all of their segments with the proper sections in each, and padded to the 4 | * specified segment alignment when produced by the link editor. The first 5 | * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header 6 | * and load commands of the object file before its first section. The zero 7 | * fill sections are always last in their segment (in all formats). This 8 | * allows the zeroed segment padding to be mapped into memory where zero fill 9 | * sections might be. The gigabyte zero fill sections, those with the section 10 | * type S_GB_ZEROFILL, can only be in a segment with sections of this type. 11 | * These segments are then placed after all other segments. 12 | * 13 | * The MH_OBJECT format has all of its sections in one segment for 14 | * compactness. There is no padding to a specified segment boundary and the 15 | * mach_header and load commands are not part of the segment. 16 | * 17 | * Sections with the same section name, sectname, going into the same segment, 18 | * segname, are combined by the link editor. The resulting section is aligned 19 | * to the maximum alignment of the combined sections and is the new section's 20 | * alignment. The combined sections are aligned to their original alignment in 21 | * the combined section. Any padded bytes to get the specified alignment are 22 | * zeroed. 23 | * 24 | * The format of the relocation entries referenced by the reloff and nreloc 25 | * fields of the section structure for mach object files is described in the 26 | * header file . 27 | *) 28 | 29 | -------------------------------------------------------------------------------- /lib/mach/MachSymbolTable.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | 3 | open Binary 4 | open MachLoadCommand.Types 5 | 6 | (* 7 | struct nlist_64 { 8 | union { 9 | uint32_t n_strx; /* index into the string table */ 10 | } n_un; 11 | uint8_t n_type; /* type flag, see below */ 12 | uint8_t n_sect; /* section number or NO_SECT */ 13 | uint16_t n_desc; /* see */ 14 | uint64_t n_value; /* value of this symbol (or stab offset) */ 15 | }; 16 | *) 17 | 18 | type nlist = { 19 | n_strx: int; (* 4 *) 20 | n_type: int; (* 1 *) 21 | n_sect: int; (* 1 *) 22 | n_desc: int; (* 2 *) 23 | n_value: int; (* 8 *) 24 | } 25 | 26 | let kNLIST_TYPE_MASK = 0xe 27 | let kNLIST_TYPE_GLOBAL = 0x1 28 | let kNLIST_TYPE_LOCAL = 0x0 29 | 30 | type symbol = nlist * string 31 | 32 | type t = symbol list 33 | 34 | let sizeof_nlist = 16 35 | 36 | (* printing *) 37 | 38 | let n_type_to_string n_type = 39 | match n_type land kNLIST_TYPE_MASK with 40 | | 0x0 -> "LOCAL" 41 | | 0x1 -> "GLOBAL" 42 | | other -> Printf.sprintf "OTHER 0x%x" other 43 | 44 | let nlist_to_string nlist = 45 | sprintf "strx: %4u type: 0x%02x sect: %x desc: 0x%3u value: %x" 46 | nlist.n_strx 47 | nlist.n_type 48 | nlist.n_sect 49 | nlist.n_desc 50 | nlist.n_value 51 | 52 | let print_nlist nlist = 53 | printf "%s\n" @@ nlist_to_string nlist 54 | 55 | let print_nlists nlists = 56 | List.iteri (fun i nlist -> printf "(%3d): " i; print_nlist nlist) nlists; (* because 3 space formatting is _enough_ *) 57 | (* i.e., need to learn format module *) 58 | printf "\n" 59 | 60 | let print symlist = 61 | List.iteri (fun i (nlist,symname) -> 62 | printf "%-10x %s sect: %x type: %002x desc: 0x%x\n" nlist.n_value symname nlist.n_sect nlist.n_type nlist.n_desc 63 | ) symlist; 64 | printf "\n" 65 | 66 | (* build nlist, symtabs, symlist *) 67 | 68 | let rec get_nlist_it binary offset nsyms acc = 69 | if (nsyms <= 0) then 70 | List.rev acc 71 | else 72 | let n_strx,o = u32o binary offset in 73 | let n_type,o = u8o binary o in 74 | let n_sect,o = u8o binary o in 75 | let n_desc,o = u16o binary o in 76 | let n_value,o = u64o binary o in 77 | let nlist = {n_strx; n_type; n_sect; n_desc; n_value;} in 78 | get_nlist_it binary o (nsyms - 1) (nlist::acc) 79 | 80 | let rec get_symlist_it binary offset nlists acc = 81 | match nlists with 82 | [] -> List.rev acc 83 | | nlist::nlists -> 84 | let symname = Binary.string binary (offset + nlist.n_strx) in 85 | get_symlist_it binary offset nlists ((nlist,symname)::acc) 86 | 87 | let get_symlist binary symtab nlists = 88 | get_symlist_it binary symtab.stroff nlists [] 89 | 90 | let get_symbols binary symtab = 91 | let nlists = get_nlist_it binary symtab.symoff symtab.nsyms [] in 92 | let symbols = get_symlist binary symtab nlists in 93 | symbols 94 | -------------------------------------------------------------------------------- /lib/mach/MachThread64.ml: -------------------------------------------------------------------------------- 1 | type header = { 2 | flavor: int [@size 4]; 3 | count: int [@size 4]; 4 | } 5 | 6 | exception Derp 7 | 8 | let pp ppf flavor = raise Derp 9 | 10 | let show_flavor p = raise Derp 11 | 12 | type thread = { 13 | header: header; 14 | state: int list; 15 | } 16 | 17 | type t = 18 | | X86_64 of thread 19 | | Unimplemented of header 20 | 21 | 22 | (* unimplemented, see machine/thread_status.h for rest of values: 23 | uint32_t flavor flavor of thread state 24 | uint32_t count count of longs in thread state 25 | struct XXX_thread_state state thread state for this flavor 26 | ... *) 27 | 28 | let get_thread_state binary offset count = 29 | let rec loop acc current offset = 30 | if (current < count) then 31 | List.rev acc 32 | else 33 | let reg,o = Binary.u32o binary offset in 34 | loop (reg::acc) (current + 1) o 35 | in 36 | loop [] 0 offset 37 | 38 | let get_thread binary o = 39 | let flavor,o = Binary.u32o binary o in 40 | let count,o = Binary.u32o binary o in 41 | let header = {flavor; count} in 42 | let state = get_thread_state binary o count in 43 | X86_64 {header; state} 44 | 45 | let get_entry thread = 46 | 0x420 -------------------------------------------------------------------------------- /lib/mach/MachVersion.ml: -------------------------------------------------------------------------------- 1 | (* Convienence constants for return values from dyld_get_sdk_version() and friends. *) 2 | let kDYLD_MACOSX_VERSION_10_4 = 0x000A0400 3 | let kDYLD_MACOSX_VERSION_10_5 = 0x000A0500 4 | let kDYLD_MACOSX_VERSION_10_6 = 0x000A0600 5 | let kDYLD_MACOSX_VERSION_10_7 = 0x000A0700 6 | let kDYLD_MACOSX_VERSION_10_8 = 0x000A0800 7 | let kDYLD_MACOSX_VERSION_10_9 = 0x000A0900 8 | let kDYLD_MACOSX_VERSION_10_10 = 0x000A0A00 9 | let kDYLD_MACOSX_VERSION_10_11 = 0x000A0B00 10 | 11 | let kDYLD_IOS_VERSION_2_0 = 0x00020000 12 | let kDYLD_IOS_VERSION_2_1 = 0x00020100 13 | let kDYLD_IOS_VERSION_2_2 = 0x00020200 14 | let kDYLD_IOS_VERSION_3_0 = 0x00030000 15 | let kDYLD_IOS_VERSION_3_1 = 0x00030100 16 | let kDYLD_IOS_VERSION_3_2 = 0x00030200 17 | let kDYLD_IOS_VERSION_4_0 = 0x00040000 18 | let kDYLD_IOS_VERSION_4_1 = 0x00040100 19 | let kDYLD_IOS_VERSION_4_2 = 0x00040200 20 | let kDYLD_IOS_VERSION_4_3 = 0x00040300 21 | let kDYLD_IOS_VERSION_5_0 = 0x00050000 22 | let kDYLD_IOS_VERSION_5_1 = 0x00050100 23 | let kDYLD_IOS_VERSION_6_0 = 0x00060000 24 | let kDYLD_IOS_VERSION_6_1 = 0x00060100 25 | let kDYLD_IOS_VERSION_7_0 = 0x00070000 26 | let kDYLD_IOS_VERSION_7_1 = 0x00070100 27 | let kDYLD_IOS_VERSION_8_0 = 0x00080000 28 | let kDYLD_IOS_VERSION_9_0 = 0x00090000 29 | 30 | let osx_version_to_string version = 31 | match version with 32 | | 0x000A0400 -> "DYLD_MACOSX_VERSION_10_4" 33 | | 0x000A0500 -> "DYLD_MACOSX_VERSION_10_5" 34 | | 0x000A0600 -> "DYLD_MACOSX_VERSION_10_6" 35 | | 0x000A0700 -> "DYLD_MACOSX_VERSION_10_7" 36 | | 0x000A0800 -> "DYLD_MACOSX_VERSION_10_8" 37 | | 0x000A0900 -> "DYLD_MACOSX_VERSION_10_9" 38 | | 0x000A0A00 -> "DYLD_MACOSX_VERSION_10_10" 39 | | 0x000A0B00 -> "DYLD_MACOSX_VERSION_10_11" 40 | | _ -> "UNKNOWN OSX VERSION" 41 | 42 | let ios_version_to_string version = 43 | match version with 44 | | 0x00020000 -> "DYLD_IOS_VERSION_2_0" 45 | | 0x00020100 -> "DYLD_IOS_VERSION_2_1" 46 | | 0x00020200 -> "DYLD_IOS_VERSION_2_2" 47 | | 0x00030000 -> "DYLD_IOS_VERSION_3_0" 48 | | 0x00030100 -> "DYLD_IOS_VERSION_3_1" 49 | | 0x00030200 -> "DYLD_IOS_VERSION_3_2" 50 | | 0x00040000 -> "DYLD_IOS_VERSION_4_0" 51 | | 0x00040100 -> "DYLD_IOS_VERSION_4_1" 52 | | 0x00040200 -> "DYLD_IOS_VERSION_4_2" 53 | | 0x00040300 -> "DYLD_IOS_VERSION_4_3" 54 | | 0x00050000 -> "DYLD_IOS_VERSION_5_0" 55 | | 0x00050100 -> "DYLD_IOS_VERSION_5_1" 56 | | 0x00060000 -> "DYLD_IOS_VERSION_6_0" 57 | | 0x00060100 -> "DYLD_IOS_VERSION_6_1" 58 | | 0x00070000 -> "DYLD_IOS_VERSION_7_0" 59 | | 0x00070100 -> "DYLD_IOS_VERSION_7_1" 60 | | 0x00080000 -> "DYLD_IOS_VERSION_8_0" 61 | | 0x00090000 -> "DYLD_IOS_VERSION_9_0" 62 | | _ -> "UNKNOWN IOS VERSION" 63 | 64 | (* LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS *) 65 | let version_to_string version = 66 | match version with 67 | (* osx *) 68 | | 0x000A0400 -> "OSX 10.4" 69 | | 0x000A0500 -> "OSX 10.5" 70 | | 0x000A0600 -> "OSX 10.6" 71 | | 0x000A0700 -> "OSX 10.7" 72 | | 0x000A0800 -> "OSX 10.8" 73 | | 0x000A0900 -> "OSX 10.9" 74 | | 0x000A0A00 -> "OSX 10.10" 75 | | 0x000A0B00 -> "OSX 10.11" 76 | (* ios ... because numbers (smartly) don't conflict *) 77 | | 0x00020000 -> "iOS 2.0" 78 | | 0x00020100 -> "iOS 2.1" 79 | | 0x00020200 -> "iOS 2.2" 80 | | 0x00030000 -> "iOS 3.0" 81 | | 0x00030100 -> "iOS 3.1" 82 | | 0x00030200 -> "iOS 3.2" 83 | | 0x00040000 -> "iOS 4.0" 84 | | 0x00040100 -> "iOS 4.1" 85 | | 0x00040200 -> "iOS 4.2" 86 | | 0x00040300 -> "iOS 4.3" 87 | | 0x00050000 -> "iOS 5.0" 88 | | 0x00050100 -> "iOS 5.1" 89 | | 0x00060000 -> "iOS 6.0" 90 | | 0x00060100 -> "iOS 6.1" 91 | | 0x00070000 -> "iOS 7.0" 92 | | 0x00070100 -> "iOS 7.1" 93 | | 0x00080000 -> "iOS 8.0" 94 | | 0x00090000 -> "iOS 9.0" 95 | | _ -> "UNKNOWN VERSION" 96 | -------------------------------------------------------------------------------- /lib/mach/mach.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 9cacfd9a76c06e0f8ae1b2e799958402) 3 | Mach 4 | MachBindOpcodes 5 | MachCpuTypes 6 | MachFat 7 | MachThread64 8 | MachLoadCommand 9 | MachLoadCommandTypes 10 | MachConstants 11 | MachExports 12 | MachHeader 13 | MachImports 14 | MachSection 15 | MachSymbolTable 16 | MachRebaseOpcodes 17 | MachVersion 18 | MachCoverage 19 | MachLoadCommandMacro 20 | # OASIS_STOP 21 | -------------------------------------------------------------------------------- /lib/mach/mach.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 9cacfd9a76c06e0f8ae1b2e799958402) 3 | Mach 4 | MachBindOpcodes 5 | MachCpuTypes 6 | MachFat 7 | MachThread64 8 | MachLoadCommand 9 | MachLoadCommandTypes 10 | MachConstants 11 | MachExports 12 | MachHeader 13 | MachImports 14 | MachSection 15 | MachSymbolTable 16 | MachRebaseOpcodes 17 | MachVersion 18 | MachCoverage 19 | MachLoadCommandMacro 20 | # OASIS_STOP 21 | -------------------------------------------------------------------------------- /lib/pe/PE.ml: -------------------------------------------------------------------------------- 1 | let debug = true 2 | 3 | open Binary 4 | 5 | module Header = PEHeader 6 | module OptionalHeader = PEOptionalHeader 7 | module DataDirectories = PEDataDirectories 8 | module SectionTable = PESectionTable 9 | module Import = PEImport 10 | module Export = PEExport 11 | module Characteristic = PECharacteristic 12 | module MachineType = PEMachineType 13 | module Utils = PEUtils 14 | module Coverage = PEByteCoverage 15 | 16 | open Header 17 | open OptionalHeader 18 | open SectionTable 19 | 20 | type t = 21 | { 22 | header: Header.t; 23 | sections: SectionTable.t; 24 | size: int; 25 | export_data: Export.export_data option; 26 | exports: Export.t; 27 | nexports: int; 28 | import_data: Import.import_data option; 29 | imports: Import.t; 30 | nimports: int; 31 | libraries: string list; 32 | nlibraries: int; 33 | name: string; 34 | is_lib: bool; 35 | entry: int64; 36 | byte_coverage: ByteCoverage.t; 37 | } 38 | 39 | (* TODO: Add export/import data? *) 40 | let pp ppf t = 41 | Format.fprintf ppf "@["; 42 | Header.pp ppf t.header; 43 | Format.fprintf ppf "@ @ Exports(%d)@ " t.nexports; 44 | Export.pp ppf t.exports; 45 | Format.fprintf ppf "@ Imports(%d)@ " t.nimports; 46 | Import.pp ppf t.imports; 47 | Format.fprintf ppf "@]"; 48 | Format.fprintf ppf "@ "; 49 | ByteCoverage.pp ppf t.byte_coverage; 50 | (* 51 | Format.fprintf ppf "@ @[Export Data@ "; 52 | match t.export_data with 53 | | Some data -> 54 | Export.pp_export_data ppf data; 55 | | None -> 56 | Format.fprintf ppf "None"; 57 | Format.fprintf ppf "@]"; 58 | *) 59 | Format.fprintf ppf "@ IsLib: %b" t.is_lib; 60 | Format.fprintf ppf "@ Main: 0x%Lx" t.entry; 61 | Format.fprintf ppf "@ Sections@ "; 62 | PESectionTable.pp ppf t.sections; 63 | Format.fprintf ppf "@]" 64 | 65 | let show t = 66 | pp Format.str_formatter t; 67 | Format.flush_str_formatter() 68 | 69 | let print t = 70 | pp Format.std_formatter t; 71 | Format.print_newline() 72 | 73 | let print_header_stub t = 74 | Format.printf "@[PE32 %s %s@ %s@ 0x%Lx@]@." 75 | (PEMachineType.show_machine t.header.coff_header.machine) 76 | (PECharacteristic.show_type t.header.coff_header.characteristics) 77 | "@" 78 | t.entry 79 | 80 | let get ?coverage:(coverage=true) binary = 81 | let size = Bytes.length binary in 82 | let header = Header.get_header binary in 83 | (* Header.print header; flush stdout; *) 84 | let section_tables = SectionTable.get binary header in 85 | (* SectionTable.print section_tables; flush stdout; *) 86 | let is_lib = 87 | Characteristic.is_dll header.coff_header.characteristics 88 | in 89 | let export_data, name, exports, import_data, 90 | imports, libraries, entry = 91 | match header.Header.optional_header with 92 | | Some headers -> 93 | let export_data,name,exports = 94 | match 95 | PEDataDirectories.get_export_table 96 | headers.data_directories 97 | with 98 | | None -> 99 | None,"",[] 100 | | Some dd when dd.PEDataDirectories.virtual_address = 0x0 -> 101 | None,"",[] 102 | | Some dd -> 103 | let export_data = 104 | PEExport.get 105 | binary 106 | dd 107 | section_tables 108 | in 109 | Some export_data, 110 | export_data.PEExport.name, 111 | PEExport.get_exports binary export_data section_tables 112 | in 113 | let import_data, imports, libraries = 114 | match 115 | PEDataDirectories.get_import_table 116 | headers.data_directories 117 | with 118 | | None -> 119 | None,[],[] 120 | | Some dd when dd.PEDataDirectories.virtual_address = 0x0 -> 121 | None,[],[] 122 | | Some dd -> 123 | let import_data = PEImport.get 124 | binary 125 | dd 126 | section_tables 127 | in 128 | let imports = PEImport.get_imports import_data in 129 | let libs = PEImport.get_libraries imports in 130 | Some import_data, imports, libs 131 | in 132 | let main_offset = 133 | try 134 | PEUtils.find_offset 135 | headers.standard_fields.address_of_entry_point 136 | section_tables 137 | |> Int64.of_int 138 | with Not_found -> 0x0L 139 | in 140 | export_data,name,exports,import_data, 141 | imports, libraries, main_offset 142 | | None -> 143 | None,"",[],None,[],[],0x0L 144 | in 145 | (* this is a performance bottleneck 146 | probably due to the symbol additions *) 147 | let byte_coverage = 148 | if (coverage) then 149 | Coverage.compute_byte_coverage 150 | header size 151 | export_data exports 152 | import_data imports 153 | section_tables binary 154 | else 155 | ByteCoverage.null 156 | in 157 | { 158 | header; 159 | sections = section_tables; 160 | size; 161 | export_data; 162 | import_data; 163 | exports; 164 | nexports = List.length exports; 165 | imports; 166 | nimports = List.length imports; 167 | libraries; 168 | nlibraries = List.length libraries; 169 | is_lib; 170 | name; 171 | entry; 172 | byte_coverage; 173 | } 174 | -------------------------------------------------------------------------------- /lib/pe/PEByteCoverage.ml: -------------------------------------------------------------------------------- 1 | (* 2 | TODO: 3 | 4 | * verify sizes 5 | * consider postprocessing sizes with coverage information? 6 | * implement certificate and other data directories 7 | *) 8 | 9 | open ByteCoverage 10 | open PEHeader 11 | open PEDataDirectories 12 | open PESectionTable 13 | open PEExport 14 | open PEImport 15 | 16 | let debug = false 17 | 18 | let compute_import_lookup_coverage sections acc import = 19 | try 20 | match import._synthetic with 21 | | HintNameTableRVA (rva, entry) -> 22 | let r1 = PEUtils.find_offset rva sections in 23 | let r2 = r1 + (String.length entry.name) + 2 (* size black magic number *) in 24 | let extra = entry.name ^ " // Import Symbol" in 25 | ByteCoverage.add 26 | (create_data 27 | ~tag:Symbol 28 | ~r1:r1 29 | ~r2:r2 30 | ~extra:extra 31 | ~understood:true 32 | ) acc 33 | | OrdinalNumber ordinal -> 34 | acc 35 | with Not_found -> acc 36 | 37 | let compute_import_entry_coverage sections acc entry = 38 | (* TODO: fix with auto-computed offset? *) 39 | let acc = try 40 | let r1 = PEUtils.find_offset entry.import_directory_entry.name_rva sections in 41 | let extra = entry._name ^ " // Library Name" in 42 | ByteCoverage.add 43 | (create_data 44 | ~tag:String 45 | ~r1:r1 46 | ~r2:(r1 + (String.length entry._name)) 47 | ~extra:extra 48 | ~understood:true 49 | ) acc 50 | with Not_found -> acc 51 | in 52 | let r1 = PEUtils.find_offset entry.import_directory_entry.import_address_table_rva sections in 53 | let r2 = 54 | r1 + 55 | (List.length entry._import_address_table*(PEImport.sizeof_import_address_table_entry)) 56 | in 57 | let extra = entry._name ^ " // Import Address Table" in 58 | let acc = 59 | ByteCoverage.add 60 | (create_data 61 | ~tag:Meta 62 | ~r1:r1 63 | ~r2:r2 64 | ~extra:extra 65 | ~understood:true 66 | ) acc 67 | in List.fold_left (compute_import_lookup_coverage sections) acc entry._import_lookup_table 68 | 69 | let compute_import_data_coverage (dd:PEDataDirectories.data_directory) import_data imports sections data = 70 | assert (dd.virtual_address <> 0); 71 | try 72 | let size = dd.size in 73 | let r1 = PEUtils.find_offset dd.virtual_address sections in 74 | let data = 75 | ByteCoverage.add 76 | (create_data 77 | ~tag:Semantic 78 | ~r1:r1 79 | ~r2:(r1 + size) 80 | ~extra:"Import Directory Table" 81 | ~understood:true 82 | ) data 83 | in 84 | List.fold_left (compute_import_entry_coverage sections) data import_data 85 | with Not_found -> data 86 | 87 | let compute_exports_data_coverage (exports:PEExport.t) data = 88 | List.fold_left (fun acc (export:synthetic_export) -> 89 | ByteCoverage.add 90 | (create_data 91 | ~tag:Code 92 | ~r1:export.offset 93 | ~r2:(export.offset+export.size) 94 | ~extra:export.name 95 | ~understood:true 96 | ) acc 97 | ) data exports 98 | 99 | let compute_export_data_coverage (dd:PEDataDirectories.data_directory) export_data (exports:PEExport.t) sections data = 100 | assert (dd.virtual_address <> 0); 101 | let size = dd.size in 102 | try 103 | let r1 = 104 | PEUtils.find_offset dd.virtual_address sections 105 | in 106 | let r2 = r1 + size in 107 | let data = ByteCoverage.add 108 | (create_data 109 | ~tag:SymbolTable 110 | ~r1:r1 111 | ~r2:r2 112 | ~extra:"Export Table" 113 | ~understood:true 114 | ) data 115 | in 116 | let r1 = 117 | PEUtils.find_offset 118 | export_data.export_directory_table.name_pointer_rva 119 | sections 120 | in 121 | (* TODO: fold through the name pointers getting strings and their size to add a StringTable section *) 122 | let r2 = 123 | r1 124 | (* TODO: verify pointer table size is 4 *) 125 | + ((List.length export_data.export_name_pointer_table) * 4) 126 | (* bytes *) 127 | in 128 | let tag = SymbolTable in 129 | let extra = "Name Pointer Table" in 130 | let data = ByteCoverage.add 131 | (create_data 132 | ~tag:tag 133 | ~r1:r1 134 | ~r2:r2 135 | ~extra:extra 136 | ~understood:true 137 | ) data 138 | in 139 | let r1 = 140 | PEUtils.find_offset 141 | export_data.export_directory_table.ordinal_table_rva 142 | sections 143 | in 144 | let r2 = 145 | r1 146 | (* TODO: verify ordinal size is 4 *) 147 | + ((List.length export_data.export_ordinal_table) * 4) 148 | in 149 | let tag = SymbolTable in 150 | let extra = "Ordinal Table" in 151 | let data = ByteCoverage.add 152 | (create_data 153 | ~tag:tag 154 | ~r1:r1 155 | ~r2:r2 156 | ~extra:extra 157 | ~understood:true 158 | ) data 159 | in 160 | let r1 = 161 | PEUtils.find_offset 162 | export_data.export_directory_table.export_address_table_rva 163 | sections 164 | in 165 | let r2 = 166 | r1 167 | (* TODO: verify address table is 4 *) 168 | + ((List.length export_data.export_address_table) * 4) 169 | in 170 | let tag = SymbolTable in 171 | let extra = "Address Table" in 172 | ByteCoverage.add 173 | (create_data 174 | ~tag:tag 175 | ~r1:r1 176 | ~r2:r2 177 | ~extra:extra 178 | ~understood:true 179 | ) data 180 | |> compute_exports_data_coverage exports 181 | with Not_found -> data 182 | 183 | let compute_data_directory_coverage 184 | optional_header 185 | export_data (exports:PEExport.t) 186 | import_data (imports:PEImport.t) 187 | sections data = 188 | match optional_header with 189 | | None -> 190 | data 191 | | Some header -> 192 | let dds = header.PEOptionalHeader.data_directories in 193 | let data = 194 | match export_data with 195 | | Some d -> 196 | begin 197 | match PEDataDirectories.get_export_table dds with 198 | | None -> 199 | data 200 | | Some dd -> 201 | compute_export_data_coverage dd d exports sections data 202 | end 203 | | _ -> data 204 | in 205 | match import_data with 206 | | Some d -> 207 | begin 208 | match PEDataDirectories.get_import_table dds with 209 | | None -> 210 | data 211 | | Some dd -> 212 | compute_import_data_coverage dd d imports sections data 213 | end 214 | | _ -> data 215 | 216 | module SectionMap = Map.Make(String) 217 | 218 | (* TODO: use characteristics for better tag resolution *) 219 | let known_sections = 220 | List.fold_left (fun acc (name,key) -> 221 | SectionMap.add name key acc 222 | ) 223 | SectionMap.empty 224 | [(".idata", SymbolTable); 225 | (".edata", SymbolTable); 226 | (".text", Semantic); 227 | (".rdata", Data); 228 | (".data", Data); 229 | (".tls", Data); 230 | (".reloc", Rela); 231 | (".rsrc", PlatformSpecific); 232 | (".wixburn", PlatformSpecific); 233 | ] 234 | 235 | let compute_section_table_coverage sections data = 236 | List.fold_left (fun acc section -> 237 | let r1 = section.pointer_to_raw_data in 238 | let r2 = r1 + section.size_of_raw_data in 239 | let extra = "Section Table // " ^ section.name in 240 | if (SectionMap.mem section.name known_sections) then 241 | (* all semantic for now *) 242 | let tag = SectionMap.find section.name known_sections in 243 | ByteCoverage.add 244 | (create_data 245 | ~tag:tag 246 | ~r1:r1 247 | ~r2:r2 248 | ~extra:extra 249 | ~understood:true 250 | ) acc 251 | else 252 | ByteCoverage.add 253 | (create_data 254 | ~tag:Semantic 255 | ~r1:r1 256 | ~r2:r2 257 | ~extra:extra 258 | ~understood:true 259 | ) acc 260 | ) data sections 261 | 262 | let compute_byte_coverage 263 | header size 264 | export_data exports 265 | import_data imports 266 | sections binary :ByteCoverage.t = 267 | let dos_end = header.dos_header.pe_pointer in 268 | let coff_end = dos_end + PEHeader.sizeof_coff_header in 269 | let optional_end = 270 | coff_end + header.coff_header.size_of_optional_header 271 | in 272 | ByteCoverage.add 273 | (create_data 274 | ~tag:Meta 275 | ~r1:0 276 | ~r2:dos_end 277 | ~extra:"DOS Header" 278 | ~understood:true 279 | ) 280 | ByteCoverage.empty 281 | |> ByteCoverage.add 282 | (create_data 283 | ~tag:Meta 284 | ~r1:dos_end 285 | ~r2:coff_end 286 | ~extra:"COFF Header" 287 | ~understood:true 288 | ) 289 | |> ByteCoverage.add 290 | (create_data 291 | ~tag:Meta 292 | ~r1:coff_end 293 | ~r2:optional_end 294 | ~extra:"Optional Headers" 295 | ~understood:true 296 | ) 297 | |> compute_data_directory_coverage 298 | header.optional_header 299 | export_data exports 300 | import_data imports 301 | sections 302 | |> compute_section_table_coverage sections 303 | |> ByteCoverage.create size binary 304 | -------------------------------------------------------------------------------- /lib/pe/PECharacteristic.ml: -------------------------------------------------------------------------------- 1 | type characteristic = 2 | | IMAGE_FILE_RELOCS_STRIPPED 3 | | IMAGE_FILE_EXECUTABLE_IMAGE 4 | | IMAGE_FILE_LINE_NUMS_STRIPPED 5 | | IMAGE_FILE_LOCAL_SYMS_STRIPPED 6 | | IMAGE_FILE_AGGRESSIVE_WS_TRIM 7 | | IMAGE_FILE_LARGE_ADDRESS_AWARE 8 | | RESERVED 9 | | IMAGE_FILE_BYTES_REVERSED_LO 10 | | IMAGE_FILE_32BIT_MACHINE 11 | | IMAGE_FILE_DEBUG_STRIPPED 12 | | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 13 | | IMAGE_FILE_NET_RUN_FROM_SWAP 14 | | IMAGE_FILE_SYSTEM 15 | | IMAGE_FILE_DLL 16 | | IMAGE_FILE_UP_SYSTEM_ONLY 17 | | IMAGE_FILE_BYTES_REVERSED_HI 18 | | UNKNOWN of int 19 | 20 | let get_characteristic = 21 | function 22 | | 0x0001 -> IMAGE_FILE_RELOCS_STRIPPED 23 | | 0x0002 -> IMAGE_FILE_EXECUTABLE_IMAGE 24 | | 0x0004 -> IMAGE_FILE_LINE_NUMS_STRIPPED 25 | | 0x0008 -> IMAGE_FILE_LOCAL_SYMS_STRIPPED 26 | | 0x0010 -> IMAGE_FILE_AGGRESSIVE_WS_TRIM 27 | | 0x0020 -> IMAGE_FILE_LARGE_ADDRESS_AWARE 28 | | 0x0040 -> RESERVED 29 | | 0x0080 -> IMAGE_FILE_BYTES_REVERSED_LO 30 | | 0x0100 -> IMAGE_FILE_32BIT_MACHINE 31 | | 0x0200 -> IMAGE_FILE_DEBUG_STRIPPED 32 | | 0x0400 -> IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 33 | | 0x0800 -> IMAGE_FILE_NET_RUN_FROM_SWAP 34 | | 0x1000 -> IMAGE_FILE_SYSTEM 35 | | 0x2000 -> IMAGE_FILE_DLL 36 | | 0x4000 -> IMAGE_FILE_UP_SYSTEM_ONLY 37 | | 0x8000 -> IMAGE_FILE_BYTES_REVERSED_HI 38 | | x -> UNKNOWN x 39 | 40 | let characteristic_to_int = 41 | function 42 | | IMAGE_FILE_RELOCS_STRIPPED -> 0x0001 43 | | IMAGE_FILE_EXECUTABLE_IMAGE -> 0x0002 44 | | IMAGE_FILE_LINE_NUMS_STRIPPED -> 0x0004 45 | | IMAGE_FILE_LOCAL_SYMS_STRIPPED -> 0x0008 46 | | IMAGE_FILE_AGGRESSIVE_WS_TRIM -> 0x0010 47 | | IMAGE_FILE_LARGE_ADDRESS_AWARE -> 0x0020 48 | | RESERVED -> 0x0040 49 | | IMAGE_FILE_BYTES_REVERSED_LO -> 0x0080 50 | | IMAGE_FILE_32BIT_MACHINE -> 0x0100 51 | | IMAGE_FILE_DEBUG_STRIPPED -> 0x0200 52 | | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP -> 0x0400 53 | | IMAGE_FILE_NET_RUN_FROM_SWAP -> 0x0800 54 | | IMAGE_FILE_SYSTEM -> 0x1000 55 | | IMAGE_FILE_DLL -> 0x2000 56 | | IMAGE_FILE_UP_SYSTEM_ONLY -> 0x4000 57 | | IMAGE_FILE_BYTES_REVERSED_HI -> 0x8000 58 | | UNKNOWN x -> x 59 | 60 | let characteristic_to_string = 61 | function 62 | | IMAGE_FILE_RELOCS_STRIPPED -> "IMAGE_FILE_RELOCS_STRIPPED" 63 | | IMAGE_FILE_EXECUTABLE_IMAGE -> "IMAGE_FILE_EXECUTABLE_IMAGE" 64 | | IMAGE_FILE_LINE_NUMS_STRIPPED -> "IMAGE_FILE_LINE_NUMS_STRIPPED" 65 | | IMAGE_FILE_LOCAL_SYMS_STRIPPED -> "IMAGE_FILE_LOCAL_SYMS_STRIPPED" 66 | | IMAGE_FILE_AGGRESSIVE_WS_TRIM -> "IMAGE_FILE_AGGRESSIVE_WS_TRIM" 67 | | IMAGE_FILE_LARGE_ADDRESS_AWARE -> "IMAGE_FILE_LARGE_ADDRESS_AWARE" 68 | | RESERVED -> "RESERVED" 69 | | IMAGE_FILE_BYTES_REVERSED_LO -> "IMAGE_FILE_BYTES_REVERSED_LO" 70 | | IMAGE_FILE_32BIT_MACHINE -> "IMAGE_FILE_32BIT_MACHINE" 71 | | IMAGE_FILE_DEBUG_STRIPPED -> "IMAGE_FILE_DEBUG_STRIPPED" 72 | | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP -> "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP" 73 | | IMAGE_FILE_NET_RUN_FROM_SWAP -> "IMAGE_FILE_NET_RUN_FROM_SWAP" 74 | | IMAGE_FILE_SYSTEM -> "IMAGE_FILE_SYSTEM" 75 | | IMAGE_FILE_DLL -> "IMAGE_FILE_DLL" 76 | | IMAGE_FILE_UP_SYSTEM_ONLY -> "IMAGE_FILE_UP_SYSTEM_ONLY" 77 | | IMAGE_FILE_BYTES_REVERSED_HI -> "IMAGE_FILE_BYTES_REVERSED_HI" 78 | | UNKNOWN x -> Printf.sprintf "UNKNOWN_CHARACTERISTIC 0x%x" x 79 | 80 | let is_dll characteristics = 81 | let characteristic = characteristic_to_int IMAGE_FILE_DLL in 82 | characteristics land characteristic = characteristic 83 | 84 | let has characteristic characteristics = 85 | let characteristic = characteristic_to_int characteristic in 86 | characteristics land characteristic = characteristic 87 | 88 | (* TODO: this is a mad hack *) 89 | let show_type characteristics = 90 | if (has IMAGE_FILE_DLL characteristics) then "DLL" 91 | else if (has IMAGE_FILE_EXECUTABLE_IMAGE characteristics) then "EXE" 92 | else "MANY" (* print all *) 93 | -------------------------------------------------------------------------------- /lib/pe/PECharacteristic.txt: -------------------------------------------------------------------------------- 1 | characteristic int 2 | IMAGE_FILE_RELOCS_STRIPPED 0x0001 3 | IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 4 | IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 5 | IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 6 | IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 7 | IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 8 | RESERVED 0x0040 9 | IMAGE_FILE_BYTES_REVERSED_LO 0x0080 10 | IMAGE_FILE_32BIT_MACHINE 0x0100 11 | IMAGE_FILE_DEBUG_STRIPPED 0x0200 12 | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 13 | IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 14 | IMAGE_FILE_SYSTEM 0x1000 15 | IMAGE_FILE_DLL 0x2000 16 | IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 17 | IMAGE_FILE_BYTES_REVERSED_HI 0x8000 18 | -------------------------------------------------------------------------------- /lib/pe/PEDataDirectories.ml: -------------------------------------------------------------------------------- 1 | type data_directory = 2 | { 3 | virtual_address: int [@size 4]; 4 | size: int [@size 4]; 5 | } 6 | 7 | let sizeof_data_directory = 8 8 | 9 | let get_data_directory binary offset = 10 | let virtual_address,o = Binary.u32o binary offset in 11 | let size,_ = Binary.u32o binary o in 12 | {virtual_address; size} 13 | 14 | type t = data_directory list 15 | 16 | let get binary offset count = 17 | let rec loop acc offset i = 18 | if (i >= count) then 19 | List.rev acc 20 | else 21 | let entry = get_data_directory binary offset in 22 | loop (entry::acc) (offset + sizeof_data_directory) (i+1) 23 | in loop [] offset 0 24 | 25 | let get_export_table (t:t) = 26 | try Some (List.nth t 0) with Failure _ -> None 27 | 28 | let get_import_table (t:t) = 29 | try Some (List.nth t 1) with Failure _ -> None 30 | 31 | let get_resource_table (t:t) = 32 | try Some (List.nth t 2) with Failure _ -> None 33 | 34 | let get_exception_table (t:t) = 35 | try Some (List.nth t 3) with Failure _ -> None 36 | 37 | let get_certificate_table (t:t) = 38 | try Some (List.nth t 4) with Failure _ -> None 39 | 40 | let get_base_relocation_table (t:t) = 41 | try Some (List.nth t 5) with Failure _ -> None 42 | 43 | let get_debug_table (t:t) = 44 | try Some (List.nth t 6) with Failure _ -> None 45 | 46 | let get_architecture (t:t) = 47 | try Some (List.nth t 7) with Failure _ -> None 48 | 49 | let get_global_ptr (t:t) = 50 | try Some (List.nth t 8) with Failure _ -> None 51 | 52 | let get_tls_table (t:t) = 53 | try Some (List.nth t 9) with Failure _ -> None 54 | 55 | let get_load_config_table (t:t) = 56 | try Some (List.nth t 10) with Failure _ -> None 57 | 58 | let get_bound_import_table (t:t) = 59 | try Some (List.nth t 11) with Failure _ -> None 60 | 61 | let get_import_address_table (t:t) = 62 | try Some (List.nth t 12) with Failure _ -> None 63 | 64 | let get_delay_import_descriptor (t:t) = 65 | try Some (List.nth t 13) with Failure _ -> None 66 | 67 | let get_clr_runtime_header (t:t) = 68 | try Some (List.nth t 14) with Failure _ -> None 69 | 70 | let pp ppf t = 71 | Format.fprintf ppf "@[Data Directories@ "; 72 | if (t = []) then 73 | begin 74 | Format.fprintf ppf "None@]"; 75 | end 76 | else 77 | begin 78 | List.iteri (fun i dd -> 79 | match i with 80 | | i when i = 0 -> 81 | Format.fprintf ppf 82 | "ExportTable: 0x%x@ SizeOfExportTable: 0x%x" 83 | dd.virtual_address dd.size 84 | | i when i = 1 -> 85 | Format.fprintf ppf 86 | "@ ImportTable: 0x%x@ SizeOfImportTable: 0x%x" 87 | dd.virtual_address dd.size 88 | | i when i = 2 -> 89 | Format.fprintf ppf 90 | "@ ResourceTable: 0x%x@ SizeOfResourceTable: 0x%x" 91 | dd.virtual_address dd.size 92 | | i when i = 3 -> 93 | Format.fprintf ppf 94 | "@ ExceptionTable: 0x%x@ SizeOfExceptionTable: 0x%x" 95 | dd.virtual_address dd.size 96 | | i when i = 4 -> 97 | Format.fprintf ppf 98 | "@ CertificateTable: 0x%x@ SizeOfCertificateTable: 0x%x" 99 | dd.virtual_address dd.size 100 | | i when i = 5 -> 101 | Format.fprintf ppf 102 | "@ BaseRelocationTable: 0x%x@ SizeOfBaseRelocationTable: 0x%x" 103 | dd.virtual_address dd.size 104 | | i when i = 6 -> 105 | Format.fprintf ppf 106 | "@ Debug: 0x%x@ SizeOfDebugTable: 0x%x" 107 | dd.virtual_address dd.size 108 | | i when i = 7 -> 109 | Format.fprintf ppf 110 | "@ Architecture: 0x%x@ SizeOfArchitecture: 0x%x" 111 | dd.virtual_address dd.size 112 | | i when i = 8 -> 113 | Format.fprintf ppf 114 | "@ GlobalPtr: 0x%x@ SizeOfGlobalPtr: 0x%x" 115 | dd.virtual_address dd.size 116 | | i when i = 9 -> 117 | Format.fprintf ppf 118 | "@ TlsTable: 0x%x@ SizeOfTlsTable: 0x%x" 119 | dd.virtual_address dd.size 120 | | i when i = 10 -> 121 | Format.fprintf ppf 122 | "@ LoadConfigTable: 0x%x@ SizeOfLoadConfigTable: 0x%x" 123 | dd.virtual_address dd.size 124 | | i when i = 11 -> 125 | Format.fprintf ppf 126 | "@ BoundImport: 0x%x@ SizeOfBoundImport: 0x%x" 127 | dd.virtual_address dd.size 128 | | i when i = 12 -> 129 | Format.fprintf ppf 130 | "@ ImportAddressTable: 0x%x@ SizeOfImportAddressTable: 0x%x" 131 | dd.virtual_address dd.size 132 | | i when i = 13 -> 133 | Format.fprintf ppf 134 | "@ DelayImportDescriptor: 0x%x@ SizeOfDelayImportDescriptor: 0x%x" 135 | dd.virtual_address dd.size 136 | | i when i = 14 -> 137 | Format.fprintf ppf 138 | "@ ClrRuntimeHeader: 0x%x@ SizeOfClrRuntimeHeader: 0x%x" 139 | dd.virtual_address dd.size 140 | | i when i = 15 -> 141 | Format.fprintf ppf 142 | "@ Reserved: 0x%x@ SizeOfReserved: 0x%x@ " 143 | dd.virtual_address dd.size 144 | | _ -> 145 | () 146 | ) t; 147 | Format.fprintf ppf "@]" 148 | end 149 | 150 | let show t = 151 | pp Format.str_formatter t; 152 | Format.flush_str_formatter() 153 | 154 | let print t = 155 | pp Format.std_formatter t 156 | -------------------------------------------------------------------------------- /lib/pe/PEHeader.ml: -------------------------------------------------------------------------------- 1 | open Binary 2 | 3 | type dos_header = 4 | { 5 | signature: int [@size 2]; (* 5a4d *) 6 | pe_pointer: int [@size 4]; (* at offset 0x3c *) 7 | } 8 | 9 | let pp_dos_header ppf dos = 10 | Format.fprintf ppf "@[DOS@ %x@ PE -> 0x%x@]" 11 | dos.signature 12 | dos.pe_pointer 13 | 14 | let kDOS_MAGIC = 0x5a4d 15 | let kDOS_CIGAM = 0x4d5a 16 | let kPE_POINTER_OFFSET = 0x3c 17 | 18 | (* COFF Header *) 19 | type coff_header = 20 | { 21 | signature: int [@size 4, be]; (* 0x50450000 *) 22 | machine: int [@size 2]; 23 | number_of_sections: int [@size 2]; 24 | time_date_stamp: int [@size 4]; 25 | pointer_to_symbol_table: int [@size 4]; 26 | number_of_symbol_table: int [@size 4]; 27 | size_of_optional_header: int [@size 2]; 28 | characteristics: int [@size 2]; 29 | } 30 | 31 | let sizeof_coff_header = 24 (* bytes *) 32 | let kCOFF_MAGIC = 0x50450000 33 | 34 | let pp_coff_header ppf coff = 35 | Format.fprintf ppf 36 | "@[@[COFF@ 0x%x@]@ Machine: 0x%x@ NumberOfSections: %d@ TimeDateStamp: %d@ PointerToSymbolTable: 0x%x@ NumberOfSymbolTable: %d@ SizeOfOptionalHeader: 0x%x@ Characteristics: 0x%x@]" 37 | coff.signature 38 | coff.machine 39 | coff.number_of_sections 40 | coff.time_date_stamp 41 | coff.pointer_to_symbol_table 42 | coff.number_of_symbol_table 43 | coff.size_of_optional_header 44 | coff.characteristics 45 | 46 | type t = { 47 | dos_header: dos_header; 48 | coff_header: coff_header; 49 | optional_header: PEOptionalHeader.t option; 50 | } 51 | 52 | let pp ppf t = 53 | Format.fprintf ppf "@[@ "; 54 | pp_dos_header ppf t.dos_header; 55 | Format.fprintf ppf "@ "; 56 | pp_coff_header ppf t.coff_header; 57 | begin 58 | match t.optional_header with 59 | | Some header -> 60 | PEOptionalHeader.pp ppf header; 61 | | None -> 62 | Format.fprintf ppf "@ **No Optional Headers**" 63 | end; 64 | Format.fprintf ppf "@]" 65 | 66 | let show t = 67 | pp Format.str_formatter t; 68 | Format.flush_str_formatter() 69 | 70 | let print t = 71 | pp Format.std_formatter t; 72 | Format.print_newline() 73 | 74 | let get_dos_header binary offset :dos_header = 75 | let signature,o = Binary.u16o binary offset in 76 | let pe_pointer = Binary.u32 binary (offset+kPE_POINTER_OFFSET) in 77 | {signature;pe_pointer;} 78 | 79 | let get_coff_header binary offset :coff_header = 80 | let signature,o = Binary.u32o binary offset in 81 | let machine,o = Binary.u16o binary o in 82 | let number_of_sections,o = Binary.u16o binary o in 83 | let time_date_stamp,o = Binary.u32o binary o in 84 | let pointer_to_symbol_table,o = Binary.u32o binary o in 85 | let number_of_symbol_table,o = Binary.u32o binary o in 86 | let size_of_optional_header,o = Binary.u16o binary o in 87 | let characteristics = Binary.u16 binary o in 88 | {signature;machine;number_of_sections;time_date_stamp;pointer_to_symbol_table;number_of_symbol_table;size_of_optional_header;characteristics;} 89 | 90 | let get_header binary = 91 | let dos_header = get_dos_header binary 0 in 92 | let coff_header_offset = dos_header.pe_pointer in 93 | let coff_header = get_coff_header binary coff_header_offset in 94 | let optional_offset = sizeof_coff_header + coff_header_offset in 95 | let optional_header = 96 | if (coff_header.size_of_optional_header > 0) then 97 | Some (PEOptionalHeader.get binary optional_offset) 98 | else 99 | None 100 | in 101 | {dos_header; coff_header;optional_header;} 102 | 103 | let csrss_header = get_header @@ list_to_bytes [0x4d; 0x5a; 0x90; 0x00; 0x03; 0x00; 0x00; 0x00; 0x04; 0x00; 0x00; 0x00; 0xff; 0xff; 0x00; 0x00; 104 | 0xb8; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 105 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 106 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0xd0; 0x00; 0x00; 0x00; 107 | 0x0e; 0x1f; 0xba; 0x0e; 0x00; 0xb4; 0x09; 0xcd; 0x21; 0xb8; 0x01; 0x4c; 0xcd; 0x21; 0x54; 0x68; 108 | 0x69; 0x73; 0x20; 0x70; 0x72; 0x6f; 0x67; 0x72; 0x61; 0x6d; 0x20; 0x63; 0x61; 0x6e; 0x6e; 0x6f; 109 | 0x74; 0x20; 0x62; 0x65; 0x20; 0x72; 0x75; 0x6e; 0x20; 0x69; 0x6e; 0x20; 0x44; 0x4f; 0x53; 0x20; 110 | 0x6d; 0x6f; 0x64; 0x65; 0x2e; 0x0d; 0x0d; 0x0a; 0x24; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 111 | 0xaa; 0x4a; 0xc3; 0xeb; 0xee; 0x2b; 0xad; 0xb8; 0xee; 0x2b; 0xad; 0xb8; 0xee; 0x2b; 0xad; 0xb8; 112 | 0xee; 0x2b; 0xac; 0xb8; 0xfe; 0x2b; 0xad; 0xb8; 0x33; 0xd4; 0x66; 0xb8; 0xeb; 0x2b; 0xad; 0xb8; 113 | 0x33; 0xd4; 0x63; 0xb8; 0xea; 0x2b; 0xad; 0xb8; 0x33; 0xd4; 0x7a; 0xb8; 0xed; 0x2b; 0xad; 0xb8; 114 | 0x33; 0xd4; 0x64; 0xb8; 0xef; 0x2b; 0xad; 0xb8; 0x33; 0xd4; 0x61; 0xb8; 0xef; 0x2b; 0xad; 0xb8; 115 | 0x52; 0x69; 0x63; 0x68; 0xee; 0x2b; 0xad; 0xb8; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 116 | 0x50; 0x45; 0x00; 0x00; 0x4c; 0x01; 0x05; 0x00; 0xd9; 0x8f; 0x15; 0x52; 0x00; 0x00; 0x00; 0x00; 117 | 0x00; 0x00; 0x00; 0x00; 0xe0; 0x00; 0x02; 0x01; 0x0b; 0x01; 0x0b; 0x00; 0x00; 0x08; 0x00; 0x00; 118 | 0x00; 0x10; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x10; 0x11; 0x00; 0x00; 0x00; 0x10; 0x00; 0x00; 119 | 0x00; 0x20; 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x10; 0x00; 0x00; 0x00; 0x02; 0x00; 0x00; 120 | 0x06; 0x00; 0x03; 0x00; 0x06; 0x00; 0x03; 0x00; 0x06; 0x00; 0x03; 0x00; 0x00; 0x00; 0x00; 0x00; 121 | 0x00; 0x60; 0x00; 0x00; 0x00; 0x04; 0x00; 0x00; 0xe4; 0xab; 0x00; 0x00; 0x01; 0x00; 0x40; 0x05; 122 | 0x00; 0x00; 0x04; 0x00; 0x00; 0x30; 0x00; 0x00; 0x00; 0x00; 0x10; 0x00; 0x00; 0x10; 0x00; 0x00; 123 | 0x00; 0x00; 0x00; 0x00; 0x10; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 124 | 0x3c; 0x30; 0x00; 0x00; 0x3c; 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x00; 0x08; 0x00; 0x00; 125 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x1a; 0x00; 0x00; 0xb8; 0x22; 0x00; 0x00; 126 | 0x00; 0x50; 0x00; 0x00; 0x38; 0x00; 0x00; 0x00; 0x10; 0x10; 0x00; 0x00; 0x38; 0x00; 0x00; 0x00; 127 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 128 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x68; 0x10; 0x00; 0x00; 0x5c; 0x00; 0x00; 0x00; 129 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x30; 0x00; 0x00; 0x3c; 0x00; 0x00; 0x00; 130 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 131 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x2e; 0x74; 0x65; 0x78; 0x74; 0x00; 0x00; 0x00; 132 | 0x24; 0x06; 0x00; 0x00; 0x00; 0x10; 0x00; 0x00; 0x00; 0x08; 0x00; 0x00; 0x00; 0x04; 0x00; 0x00; 133 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x20; 0x00; 0x00; 0x60; 134 | 0x2e; 0x64; 0x61; 0x74; 0x61; 0x00; 0x00; 0x00; 0x3c; 0x03; 0x00; 0x00; 0x00; 0x20; 0x00; 0x00; 135 | 0x00; 0x02; 0x00; 0x00; 0x00; 0x0c; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 136 | 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0xc0; 0x2e; 0x69; 0x64; 0x61; 0x74; 0x61; 0x00; 0x00; 137 | 0xf8; 0x01; 0x00; 0x00; 0x00; 0x30; 0x00; 0x00; 0x00; 0x02; 0x00; 0x00; 0x00; 0x0e; 0x00; 0x00; 138 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x40; 139 | 0x2e; 0x72; 0x73; 0x72; 0x63; 0x00; 0x00; 0x00; 0x00; 0x08; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 140 | 0x00; 0x08; 0x00; 0x00; 0x00; 0x10; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 141 | 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x42; 0x2e; 0x72; 0x65; 0x6c; 0x6f; 0x63; 0x00; 0x00; 142 | 0x86; 0x01; 0x00; 0x00; 0x00; 0x50; 0x00; 0x00; 0x00; 0x02; 0x00; 0x00; 0x00; 0x18; 0x00; 0x00; 143 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x40; 0x00; 0x00; 0x42; 144 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 145 | 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;] 146 | 147 | let to_hex hex = Printf.printf "0x%x\n" hex 148 | -------------------------------------------------------------------------------- /lib/pe/PEMachineType.ml: -------------------------------------------------------------------------------- 1 | type machine_type = 2 | | IMAGE_FILE_MACHINE_UNKNOWN 3 | | IMAGE_FILE_MACHINE_AM33 4 | | IMAGE_FILE_MACHINE_AMD64 5 | | IMAGE_FILE_MACHINE_ARM 6 | | IMAGE_FILE_MACHINE_ARMNT 7 | | IMAGE_FILE_MACHINE_ARM64 8 | | IMAGE_FILE_MACHINE_EBC 9 | | IMAGE_FILE_MACHINE_I386 10 | | IMAGE_FILE_MACHINE_IA64 11 | | IMAGE_FILE_MACHINE_M32R 12 | | IMAGE_FILE_MACHINE_MIPS16 13 | | IMAGE_FILE_MACHINE_MIPSFPU 14 | | IMAGE_FILE_MACHINE_MIPSFPU16 15 | | IMAGE_FILE_MACHINE_POWERPC 16 | | IMAGE_FILE_MACHINE_POWERPCFP 17 | | IMAGE_FILE_MACHINE_R4000 18 | | IMAGE_FILE_MACHINE_SH3 19 | | IMAGE_FILE_MACHINE_SH3DSP 20 | | IMAGE_FILE_MACHINE_SH4 21 | | IMAGE_FILE_MACHINE_SH5 22 | | IMAGE_FILE_MACHINE_THUMB 23 | | IMAGE_FILE_MACHINE_WCEMIPSV2 24 | | UNKNOWN of int 25 | 26 | let get_machine_type = 27 | function 28 | | 0x0 -> IMAGE_FILE_MACHINE_UNKNOWN 29 | | 0x1d3 -> IMAGE_FILE_MACHINE_AM33 30 | | 0x8664 -> IMAGE_FILE_MACHINE_AMD64 31 | | 0x1c0 -> IMAGE_FILE_MACHINE_ARM 32 | | 0x1c4 -> IMAGE_FILE_MACHINE_ARMNT 33 | | 0xaa64 -> IMAGE_FILE_MACHINE_ARM64 34 | | 0xebc -> IMAGE_FILE_MACHINE_EBC 35 | | 0x14c -> IMAGE_FILE_MACHINE_I386 36 | | 0x200 -> IMAGE_FILE_MACHINE_IA64 37 | | 0x9041 -> IMAGE_FILE_MACHINE_M32R 38 | | 0x266 -> IMAGE_FILE_MACHINE_MIPS16 39 | | 0x366 -> IMAGE_FILE_MACHINE_MIPSFPU 40 | | 0x466 -> IMAGE_FILE_MACHINE_MIPSFPU16 41 | | 0x1f0 -> IMAGE_FILE_MACHINE_POWERPC 42 | | 0x1f1 -> IMAGE_FILE_MACHINE_POWERPCFP 43 | | 0x166 -> IMAGE_FILE_MACHINE_R4000 44 | | 0x1a2 -> IMAGE_FILE_MACHINE_SH3 45 | | 0x1a3 -> IMAGE_FILE_MACHINE_SH3DSP 46 | | 0x1a6 -> IMAGE_FILE_MACHINE_SH4 47 | | 0x1a8 -> IMAGE_FILE_MACHINE_SH5 48 | | 0x1c2 -> IMAGE_FILE_MACHINE_THUMB 49 | | 0x169 -> IMAGE_FILE_MACHINE_WCEMIPSV2 50 | | x -> UNKNOWN x 51 | 52 | let machine_type_to_int = 53 | function 54 | | IMAGE_FILE_MACHINE_UNKNOWN -> 0x0 55 | | IMAGE_FILE_MACHINE_AM33 -> 0x1d3 56 | | IMAGE_FILE_MACHINE_AMD64 -> 0x8664 57 | | IMAGE_FILE_MACHINE_ARM -> 0x1c0 58 | | IMAGE_FILE_MACHINE_ARMNT -> 0x1c4 59 | | IMAGE_FILE_MACHINE_ARM64 -> 0xaa64 60 | | IMAGE_FILE_MACHINE_EBC -> 0xebc 61 | | IMAGE_FILE_MACHINE_I386 -> 0x14c 62 | | IMAGE_FILE_MACHINE_IA64 -> 0x200 63 | | IMAGE_FILE_MACHINE_M32R -> 0x9041 64 | | IMAGE_FILE_MACHINE_MIPS16 -> 0x266 65 | | IMAGE_FILE_MACHINE_MIPSFPU -> 0x366 66 | | IMAGE_FILE_MACHINE_MIPSFPU16 -> 0x466 67 | | IMAGE_FILE_MACHINE_POWERPC -> 0x1f0 68 | | IMAGE_FILE_MACHINE_POWERPCFP -> 0x1f1 69 | | IMAGE_FILE_MACHINE_R4000 -> 0x166 70 | | IMAGE_FILE_MACHINE_SH3 -> 0x1a2 71 | | IMAGE_FILE_MACHINE_SH3DSP -> 0x1a3 72 | | IMAGE_FILE_MACHINE_SH4 -> 0x1a6 73 | | IMAGE_FILE_MACHINE_SH5 -> 0x1a8 74 | | IMAGE_FILE_MACHINE_THUMB -> 0x1c2 75 | | IMAGE_FILE_MACHINE_WCEMIPSV2 -> 0x169 76 | | UNKNOWN x -> x 77 | 78 | let machine_type_to_string = 79 | function 80 | | IMAGE_FILE_MACHINE_UNKNOWN -> "IMAGE_FILE_MACHINE_UNKNOWN" 81 | | IMAGE_FILE_MACHINE_AM33 -> "IMAGE_FILE_MACHINE_AM33" 82 | | IMAGE_FILE_MACHINE_AMD64 -> "IMAGE_FILE_MACHINE_AMD64" 83 | | IMAGE_FILE_MACHINE_ARM -> "IMAGE_FILE_MACHINE_ARM" 84 | | IMAGE_FILE_MACHINE_ARMNT -> "IMAGE_FILE_MACHINE_ARMNT" 85 | | IMAGE_FILE_MACHINE_ARM64 -> "IMAGE_FILE_MACHINE_ARM64" 86 | | IMAGE_FILE_MACHINE_EBC -> "IMAGE_FILE_MACHINE_EBC" 87 | | IMAGE_FILE_MACHINE_I386 -> "IMAGE_FILE_MACHINE_I386" 88 | | IMAGE_FILE_MACHINE_IA64 -> "IMAGE_FILE_MACHINE_IA64" 89 | | IMAGE_FILE_MACHINE_M32R -> "IMAGE_FILE_MACHINE_M32R" 90 | | IMAGE_FILE_MACHINE_MIPS16 -> "IMAGE_FILE_MACHINE_MIPS16" 91 | | IMAGE_FILE_MACHINE_MIPSFPU -> "IMAGE_FILE_MACHINE_MIPSFPU" 92 | | IMAGE_FILE_MACHINE_MIPSFPU16 -> "IMAGE_FILE_MACHINE_MIPSFPU16" 93 | | IMAGE_FILE_MACHINE_POWERPC -> "IMAGE_FILE_MACHINE_POWERPC" 94 | | IMAGE_FILE_MACHINE_POWERPCFP -> "IMAGE_FILE_MACHINE_POWERPCFP" 95 | | IMAGE_FILE_MACHINE_R4000 -> "IMAGE_FILE_MACHINE_R4000" 96 | | IMAGE_FILE_MACHINE_SH3 -> "IMAGE_FILE_MACHINE_SH3" 97 | | IMAGE_FILE_MACHINE_SH3DSP -> "IMAGE_FILE_MACHINE_SH3DSP" 98 | | IMAGE_FILE_MACHINE_SH4 -> "IMAGE_FILE_MACHINE_SH4" 99 | | IMAGE_FILE_MACHINE_SH5 -> "IMAGE_FILE_MACHINE_SH5" 100 | | IMAGE_FILE_MACHINE_THUMB -> "IMAGE_FILE_MACHINE_THUMB" 101 | | IMAGE_FILE_MACHINE_WCEMIPSV2 -> "IMAGE_FILE_MACHINE_WCEMIPSV2" 102 | | UNKNOWN x -> Printf.sprintf "UNKNOWN_MACHINE_TYPE 0x%x" x 103 | 104 | let show_machine machine = 105 | match get_machine_type machine with 106 | | IMAGE_FILE_MACHINE_AMD64 -> "x86-64" 107 | | IMAGE_FILE_MACHINE_I386 -> "i386" 108 | | IMAGE_FILE_MACHINE_IA64 -> "IA64" 109 | | IMAGE_FILE_MACHINE_ARM64 -> "ARM64" 110 | | IMAGE_FILE_MACHINE_ARM -> "ARM" 111 | | other -> machine_type_to_string other 112 | -------------------------------------------------------------------------------- /lib/pe/PEMachineType.txt: -------------------------------------------------------------------------------- 1 | machine_type int 2 | IMAGE_FILE_MACHINE_UNKNOWN 0x0 3 | IMAGE_FILE_MACHINE_AM33 0x1d3 4 | IMAGE_FILE_MACHINE_AMD64 0x8664 5 | IMAGE_FILE_MACHINE_ARM 0x1c0 6 | IMAGE_FILE_MACHINE_ARMNT 0x1c4 7 | IMAGE_FILE_MACHINE_ARM64 0xaa64 8 | IMAGE_FILE_MACHINE_EBC 0xebc 9 | IMAGE_FILE_MACHINE_I386 0x14c 10 | IMAGE_FILE_MACHINE_IA64 0x200 11 | IMAGE_FILE_MACHINE_M32R 0x9041 12 | IMAGE_FILE_MACHINE_MIPS16 0x266 13 | IMAGE_FILE_MACHINE_MIPSFPU 0x366 14 | IMAGE_FILE_MACHINE_MIPSFPU16 0x466 15 | IMAGE_FILE_MACHINE_POWERPC 0x1f0 16 | IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 17 | IMAGE_FILE_MACHINE_R4000 0x166 18 | IMAGE_FILE_MACHINE_SH3 0x1a2 19 | IMAGE_FILE_MACHINE_SH3DSP 0x1a3 20 | IMAGE_FILE_MACHINE_SH4 0x1a6 21 | IMAGE_FILE_MACHINE_SH5 0x1a8 22 | IMAGE_FILE_MACHINE_THUMB 0x1c2 23 | IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 24 | -------------------------------------------------------------------------------- /lib/pe/PEOptionalHeader.ml: -------------------------------------------------------------------------------- 1 | (* standard COFF fields *) 2 | type standard_fields = 3 | { 4 | magic: int [@size 2]; 5 | major_linker_version: int [@size 1]; 6 | minor_linker_version: int [@size 1]; 7 | size_of_code: int [@size 4]; 8 | size_of_initialized_data: int [@size 4]; 9 | size_of_uninitialized_data: int [@size 4]; 10 | address_of_entry_point: int [@size 4]; 11 | base_of_code: int [@size 4]; 12 | base_of_data: int [@size 4]; (* absent in 64-bit PE32+ *) 13 | } 14 | 15 | let sizeof_standard_fields = (3 * 8) + 4 16 | 17 | let pp_standard_fields ppf standard = 18 | Format.fprintf ppf 19 | "@[@[Standard@ 0x%x@]@ MajorLinkerVersion: %d@ MinorLinkerVersion: %d@ SizeOfCode: 0x%x@ SizeOfInitializedData: 0x%x@ SizeOfUninitializedData: 0x%x@ AddressOfEntryPoint: 0x%x@ BaseOfCode: 0x%x@ BaseOfData: 0x%x@]" 20 | standard.magic 21 | standard.major_linker_version 22 | standard.minor_linker_version 23 | standard.size_of_code 24 | standard.size_of_initialized_data 25 | standard.size_of_uninitialized_data 26 | standard.address_of_entry_point 27 | standard.base_of_code 28 | standard.base_of_data 29 | 30 | (* windows specific fields *) 31 | type windows_fields = 32 | { 33 | image_base: int [@size 4]; (* 8 in 64-bit *) 34 | section_alignment: int [@size 4]; 35 | file_alignment: int [@size 4]; 36 | major_operating_system_version: int [@size 2]; 37 | minor_operating_system_version: int [@size 2]; 38 | major_image_version: int [@size 2]; 39 | minor_image_version: int [@size 2]; 40 | major_subsystem_version: int [@size 2]; 41 | minor_subsystem_version: int [@size 2]; 42 | win32_version_value: int [@size 4]; 43 | size_of_image: int [@size 4]; 44 | size_of_headers: int [@size 4]; 45 | check_sum: int [@size 4]; 46 | subsystem: int [@size 2]; 47 | dll_characteristics: int [@size 2]; 48 | size_of_stack_reserve: int [@size 4]; (* 8 64-bit *) 49 | size_of_stack_commit: int [@size 4]; (* 8 *) 50 | size_of_heap_reserve: int [@size 4]; (* 8 *) 51 | size_of_heap_commit: int [@size 4]; (* 8 *) 52 | loader_flags: int [@size 4]; 53 | number_of_rva_and_sizes: int [@size 4]; 54 | } 55 | 56 | let sizeof_windows_fields = (8 * 8) + 4 57 | 58 | let pp_windows_fields ppf windows = 59 | Format.fprintf ppf 60 | "@[@[Windows@]@ ImageBase: 0x%x@ SectionAlignment: 0x%x@ FileAlignment: 0x%x@ MajorOperatingSystemVersion: %d@ MinorOperatingSystemVersion: %d@ MajorImageVersion: %d@ MinorImageVersion: %d@ MajorSubsystemVersion: %d@ MinorSubsystemVersion: %d@ Win32VersionValue: %d@ SizeOfImage: 0x%x@ SizeOfHeaders: 0x%x@ CheckSum: 0x%x@ Subsystem: 0x%x@ DLLCharacteristics: 0x%x@ SizeOfStackReserve: 0x%x@ SizeOfStackCommit: 0x%x@ SizeOfHeapReserve: 0x%x@ SizeOfHeapCommit: 0x%x@ LoaderFlags: 0x%x@ NumberOfRvaAndSizes: %d@]" 61 | windows.image_base 62 | windows.section_alignment 63 | windows.file_alignment 64 | windows.major_operating_system_version 65 | windows.minor_operating_system_version 66 | windows.major_image_version 67 | windows.minor_image_version 68 | windows.major_subsystem_version 69 | windows.minor_subsystem_version 70 | windows.win32_version_value 71 | windows.size_of_image 72 | windows.size_of_headers 73 | windows.check_sum 74 | windows.subsystem 75 | windows.dll_characteristics 76 | windows.size_of_stack_reserve 77 | windows.size_of_stack_commit 78 | windows.size_of_heap_reserve 79 | windows.size_of_heap_commit 80 | windows.loader_flags 81 | windows.number_of_rva_and_sizes 82 | 83 | type t = 84 | { 85 | standard_fields: standard_fields; 86 | windows_fields: windows_fields; 87 | data_directories: PEDataDirectories.t; 88 | } 89 | 90 | let pp ppf header = 91 | Format.fprintf ppf "@ "; 92 | pp_standard_fields ppf header.standard_fields; 93 | Format.fprintf ppf "@ "; 94 | pp_windows_fields ppf header.windows_fields; 95 | Format.fprintf ppf "@ "; 96 | PEDataDirectories.pp ppf header.data_directories 97 | 98 | let get_standard_fields binary offset :standard_fields = 99 | let magic,o = Binary.u16o binary offset in 100 | let major_linker_version,o = Binary.u8o binary o in 101 | let minor_linker_version,o = Binary.u8o binary o in 102 | let size_of_code,o = Binary.u32o binary o in 103 | let size_of_initialized_data,o = Binary.u32o binary o in 104 | let size_of_uninitialized_data,o = Binary.u32o binary o in 105 | let address_of_entry_point,o = Binary.u32o binary o in 106 | let base_of_code,o = Binary.u32o binary o in 107 | let base_of_data = Binary.u32 binary o in 108 | {magic;major_linker_version;minor_linker_version;size_of_code;size_of_initialized_data;size_of_uninitialized_data;address_of_entry_point;base_of_code;base_of_data;} 109 | 110 | let get_windows_fields binary offset :windows_fields = 111 | let image_base,o = Binary.u32o binary offset in 112 | let section_alignment,o = Binary.u32o binary o in 113 | let file_alignment,o = Binary.u32o binary o in 114 | let major_operating_system_version,o = Binary.u16o binary o in 115 | let minor_operating_system_version,o = Binary.u16o binary o in 116 | let major_image_version,o = Binary.u16o binary o in 117 | let minor_image_version,o = Binary.u16o binary o in 118 | let major_subsystem_version,o = Binary.u16o binary o in 119 | let minor_subsystem_version,o = Binary.u16o binary o in 120 | let win32_version_value,o = Binary.u32o binary o in 121 | let size_of_image,o = Binary.u32o binary o in 122 | let size_of_headers,o = Binary.u32o binary o in 123 | let check_sum,o = Binary.u32o binary o in 124 | let subsystem,o = Binary.u16o binary o in 125 | let dll_characteristics,o = Binary.u16o binary o in 126 | let size_of_stack_reserve,o = Binary.u32o binary o in 127 | let size_of_stack_commit,o = Binary.u32o binary o in 128 | let size_of_heap_reserve,o = Binary.u32o binary o in 129 | let size_of_heap_commit,o = Binary.u32o binary o in 130 | let loader_flags,o = Binary.u32o binary o in 131 | let number_of_rva_and_sizes = Binary.u32 binary o in 132 | {image_base;section_alignment;file_alignment;major_operating_system_version;minor_operating_system_version;major_image_version;minor_image_version;major_subsystem_version;minor_subsystem_version;win32_version_value;size_of_image;size_of_headers;check_sum;subsystem;dll_characteristics;size_of_stack_reserve;size_of_stack_commit;size_of_heap_reserve;size_of_heap_commit;loader_flags;number_of_rva_and_sizes;} 133 | 134 | let get binary offset = 135 | let standard_fields = 136 | get_standard_fields binary offset 137 | in 138 | let wf_offset = offset + sizeof_standard_fields in 139 | let windows_fields = get_windows_fields binary wf_offset in 140 | let dd_offset = wf_offset + sizeof_windows_fields in 141 | let data_directories = 142 | PEDataDirectories.get 143 | binary 144 | dd_offset 145 | windows_fields.number_of_rva_and_sizes 146 | in 147 | {standard_fields; windows_fields; data_directories;} 148 | 149 | -------------------------------------------------------------------------------- /lib/pe/PESectionTable.ml: -------------------------------------------------------------------------------- 1 | open PEHeader 2 | 3 | type section_table = { 4 | name: string [@size 8]; 5 | virtual_size: int [@size 4]; 6 | virtual_address: int [@size 4]; 7 | size_of_raw_data: int [@size 4]; 8 | pointer_to_raw_data: int [@size 4]; 9 | pointer_to_relocations: int [@size 4]; 10 | pointer_to_linenumbers: int [@size 4]; 11 | number_of_relocations: int [@size 2]; 12 | number_of_linenumbers: int [@size 2]; 13 | characteristics: int [@size 4]; 14 | } 15 | 16 | let sizeof_section_table = 8 * 5 17 | 18 | let pp_section_table ppf section = 19 | Format.fprintf ppf 20 | "@[%s@ VirtualSize: 0x%x@ VirtualAddress: 0x%x@ SizeOofRawData: 0x%x@ PointerToRawData: 0x%x@ PointerToRelocations: 0x%x@ PointerToLinenumbers: 0x%x@ NumberOfRelocations: 0x%x@ NumberOfLinenumbers: 0x%x@ Characteristics: 0x%x@]" 21 | section.name 22 | section.virtual_size 23 | section.virtual_address 24 | section.size_of_raw_data 25 | section.pointer_to_raw_data 26 | section.pointer_to_relocations 27 | section.pointer_to_linenumbers 28 | section.number_of_relocations 29 | section.number_of_linenumbers 30 | section.characteristics 31 | 32 | type t = section_table list 33 | 34 | let pp ppf t = 35 | Format.fprintf ppf "@ @[Section Tables@ "; 36 | RdrPrinter.pp_seq ppf pp_section_table t 37 | 38 | let show t = 39 | pp Format.str_formatter t; 40 | Format.flush_str_formatter() 41 | 42 | let print t = 43 | pp Format.std_formatter t 44 | 45 | let rec get_section name sections = 46 | match sections with 47 | | [] -> raise Not_found 48 | | section::sections -> 49 | if (section.name = name) then 50 | section 51 | else 52 | get_section name sections 53 | 54 | let get_section_table binary offset :section_table = 55 | let name,o = Binary.stringo binary offset ~num_bytes:8 in 56 | let virtual_size,o = Binary.u32o binary o in 57 | let virtual_address,o = Binary.u32o binary o in 58 | let size_of_raw_data,o = Binary.u32o binary o in 59 | let pointer_to_raw_data,o = Binary.u32o binary o in 60 | let pointer_to_relocations,o = Binary.u32o binary o in 61 | let pointer_to_linenumbers,o = Binary.u32o binary o in 62 | let number_of_relocations,o = Binary.u16o binary o in 63 | let number_of_linenumbers,o = Binary.u16o binary o in 64 | let characteristics = Binary.u32 binary o in 65 | {name;virtual_size;virtual_address;size_of_raw_data;pointer_to_raw_data;pointer_to_relocations;pointer_to_linenumbers;number_of_relocations;number_of_linenumbers;characteristics;} 66 | 67 | let get binary (header:PEHeader.t) = 68 | let offset = 69 | header.dos_header.pe_pointer + PEHeader.sizeof_coff_header 70 | + header.coff_header.size_of_optional_header 71 | in 72 | let nsections = header.coff_header.number_of_sections in 73 | let rec loop acc count = 74 | if (count >= nsections) then 75 | List.rev acc 76 | else 77 | let o = offset + (count * sizeof_section_table) in 78 | let st = get_section_table binary o in 79 | loop (st::acc) (count+1) 80 | in loop [] 0 81 | -------------------------------------------------------------------------------- /lib/pe/PEUtils.ml: -------------------------------------------------------------------------------- 1 | open PESectionTable 2 | 3 | let is_in_range rva r1 r2 = 4 | r1 <= rva 5 | && rva < r2 6 | 7 | let rva2offset rva (section:section_table) = 8 | (rva - section.virtual_address) + section.pointer_to_raw_data 9 | 10 | let is_in_section rva section = 11 | section.virtual_address <= rva 12 | && rva < (section.virtual_address + section.virtual_size) 13 | 14 | let rec find_offset rva (sections:PESectionTable.t) = 15 | match sections with 16 | | [] -> raise Not_found 17 | | section::sections -> 18 | if (is_in_section rva section) then 19 | rva2offset rva section 20 | else 21 | find_offset rva sections 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/pe/pe.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 7ff4b22dd16587f4d0656ea5b8f2021a) 3 | PE 4 | PEHeader 5 | PEOptionalHeader 6 | PESectionTable 7 | PEDataDirectories 8 | PEImport 9 | PEExport 10 | PEMachineType 11 | PECharacteristic 12 | PEByteCoverage 13 | PEUtils 14 | # OASIS_STOP 15 | -------------------------------------------------------------------------------- /lib/pe/pe.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 7ff4b22dd16587f4d0656ea5b8f2021a) 3 | PE 4 | PEHeader 5 | PEOptionalHeader 6 | PESectionTable 7 | PEDataDirectories 8 | PEImport 9 | PEExport 10 | PEMachineType 11 | PECharacteristic 12 | PEByteCoverage 13 | PEUtils 14 | # OASIS_STOP 15 | -------------------------------------------------------------------------------- /lib/rdr.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 6c6005368c46496f7179297b41c075a2) 3 | Rdr 4 | RdrObject 5 | RdrGraph 6 | SymbolMap 7 | TransitiveClosure 8 | # OASIS_STOP 9 | -------------------------------------------------------------------------------- /lib/rdr.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 6c6005368c46496f7179297b41c075a2) 3 | Rdr 4 | RdrObject 5 | RdrGraph 6 | SymbolMap 7 | TransitiveClosure 8 | # OASIS_STOP 9 | -------------------------------------------------------------------------------- /lib/utils/Generics.ml: -------------------------------------------------------------------------------- 1 | let list_to_string ?omit_singleton_braces:(osb=false) list = 2 | let len = List.length list in 3 | if (len = 0) then "[]" 4 | else if (len = 1 && osb) then List.hd list 5 | else 6 | let b = Buffer.create ((List.hd list |> String.length) * len) in 7 | Buffer.add_string b "["; 8 | let rec loop ss = 9 | match ss with 10 | | [] -> 11 | Buffer.contents b 12 | | s::[] -> 13 | Buffer.add_string b s; 14 | Buffer.add_string b "]"; 15 | Buffer.contents b 16 | | s::ss -> 17 | Buffer.add_string b s; 18 | Buffer.add_string b ", "; 19 | loop ss 20 | in loop list 21 | 22 | (* TODO: this name is really not helpful; change *) 23 | let list_with_stringer ?newline:(newline=false) ?omit_singleton_braces:(osb=false) stringer list = 24 | let len = List.length list in 25 | if (len = 0) then "[]" 26 | else if (len = 1 && osb) then List.hd list |> stringer 27 | else 28 | let b = Buffer.create ((List.hd list |> stringer |> String.length) * len) in 29 | (if (newline) then "\n\t\t" else "[") |> Buffer.add_string b; 30 | let rec loop ss = 31 | match ss with 32 | | [] -> 33 | Buffer.contents b 34 | | s::[] -> 35 | Buffer.add_string b (stringer s); 36 | (if (newline) then "" else "]") |> Buffer.add_string b; 37 | Buffer.contents b 38 | | s::ss -> 39 | Buffer.add_string b (stringer s); 40 | (if (newline) then "\n\t\t" else ", ") |> Buffer.add_string b; 41 | loop ss 42 | in loop list 43 | 44 | let string_tuple_list_to_string list = 45 | let len = List.length list in 46 | if (len = 0) then "[]" 47 | else if (len = 1) then 48 | let lib,data = List.hd list in 49 | Printf.sprintf "%s @ %s" lib data 50 | else 51 | let b = Buffer.create ((List.hd list |> fst |> String.length) * len) in 52 | Buffer.add_string b "["; 53 | let rec loop ss = 54 | match ss with 55 | | [] -> 56 | Buffer.contents b 57 | | (lib,data)::[] -> 58 | Buffer.add_string b lib; 59 | Buffer.add_string b " @ "; 60 | Buffer.add_string b data; 61 | Buffer.add_string b "]"; 62 | Buffer.contents b 63 | | (lib, data)::ss -> 64 | Buffer.add_string b lib; 65 | Buffer.add_string b " @ "; 66 | Buffer.add_string b data; 67 | Buffer.add_string b ", "; 68 | loop ss 69 | in loop list 70 | 71 | 72 | let find pred arr = 73 | let size = Array.length arr in 74 | let rec loop i = 75 | if (i >= size) then raise Not_found 76 | else 77 | if (pred arr.(i)) then 78 | arr.(i) 79 | else 80 | loop (i + 1) 81 | in loop 0 82 | 83 | 84 | module StringSet = Set.Make (struct 85 | type t = string 86 | let compare = Pervasives.compare 87 | end);; 88 | 89 | let string_set_of_list = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty;; 90 | -------------------------------------------------------------------------------- /lib/utils/Input.ml: -------------------------------------------------------------------------------- 1 | open Printf 2 | 3 | let readAll fileName = 4 | let ic = open_in_bin fileName in 5 | let byte = ref 0 in 6 | printf "size: %d\n" (in_channel_length ic); 7 | try 8 | while (true) do 9 | begin 10 | byte := input_byte ic; 11 | printf "%x " (!byte) 12 | end 13 | done 14 | with End_of_file -> 15 | begin 16 | printf "\n"; 17 | exit 0 18 | end 19 | 20 | let input_i32 ic = 21 | let one = input_byte ic in 22 | let two = (input_byte ic) lsl 8 in 23 | let three = (input_byte ic) lsl 16 in 24 | let four = (input_byte ic) lsl 24 in 25 | one lor two lor three lor four 26 | 27 | (* big endian *) 28 | let input_i32be ic = 29 | let one = (input_byte ic) lsl 24 in 30 | let two = (input_byte ic) lsl 16 in 31 | let three = (input_byte ic) lsl 8 in 32 | let four = input_byte ic in 33 | one lor two lor three lor four 34 | 35 | let discard_n_bytes n ic = 36 | seek_in ic ((pos_in ic) + n) 37 | 38 | let input_n_bytes n ic = 39 | let res = ref (input_byte ic) in 40 | for i = 1 to (n - 1) do 41 | let counter = 8 * i in 42 | let byte = input_byte ic in 43 | res := !res lor (byte lsl counter) 44 | done; 45 | !res 46 | -------------------------------------------------------------------------------- /lib/utils/Json.ml: -------------------------------------------------------------------------------- 1 | module Obj = Map.Make(String) 2 | 3 | exception JSON_exception of string 4 | 5 | type json = 6 | | JSONObject of json Obj.t 7 | | JSONArray of json array 8 | | Float of float 9 | | Int of int 10 | | Str of string 11 | | NULL 12 | 13 | let rec json_to_string_it json b identation = 14 | match json with 15 | | JSONObject jobj -> 16 | if (Obj.is_empty jobj) then 17 | Buffer.add_string b "{}" 18 | else 19 | let identation' = identation + 4 in 20 | let spaces = Bytes.make identation' ' ' |> Bytes.to_string in 21 | let max,v = Obj.max_binding jobj in 22 | Buffer.add_string b "{\n"; 23 | Obj.iter (fun key elem -> 24 | Buffer.add_string b spaces; 25 | Buffer.add_string b @@ Printf.sprintf "\"%s\"" key; 26 | Buffer.add_string b " : "; 27 | json_to_string_it elem b identation'; 28 | if (key <> max) then 29 | Buffer.add_string b ",\n" 30 | else 31 | Buffer.add_string b "\n" 32 | ) jobj; 33 | Buffer.add_string b spaces; 34 | Buffer.add_string b "}"; 35 | | JSONArray jarray -> 36 | let len = Array.length jarray in 37 | Buffer.add_string b "["; 38 | Array.iteri (fun i elem -> 39 | json_to_string_it elem b identation; 40 | if (i < len - 1) then 41 | Buffer.add_string b ", "; 42 | ) jarray; 43 | Buffer.add_string b "]"; 44 | | Int i -> string_of_int i |> Buffer.add_string b 45 | | Float f -> string_of_float f |> Buffer.add_string b 46 | | Str str -> Buffer.add_string b @@ Printf.sprintf "\"%s\"" str; 47 | | NULL -> Buffer.add_string b "null" 48 | 49 | let json_to_string json = 50 | let b = Buffer.create 0 in 51 | json_to_string_it json b (-4); 52 | Buffer.contents b 53 | 54 | let print_json json = 55 | Printf.printf "%s\n" @@ json_to_string json 56 | 57 | let emp = Obj.empty 58 | let ( $: ) key value = (Obj.add key value) 59 | let e = ( $: ) 60 | 61 | let empty = JSONObject Obj.empty 62 | 63 | let add key value json = 64 | match json with 65 | | JSONObject obj -> 66 | JSONObject (Obj.add key value obj) 67 | | _ -> raise @@ JSON_exception "cannot add key/value pair to a non JSONObject" 68 | 69 | let find key json = 70 | match json with 71 | | JSONObject obj -> 72 | Obj.find key obj 73 | | _ -> raise @@ JSON_exception "cannot get a value from a non JSONObject" 74 | 75 | let set json i elem = 76 | match json with 77 | | JSONArray arr -> 78 | arr.(i) <- elem 79 | | _ -> raise @@ JSON_exception "cannot set a value in a non JSONArray" 80 | 81 | let get json i = 82 | match json with 83 | | JSONArray arr -> 84 | arr.(i) 85 | | _ -> raise @@ JSON_exception "cannot get a value from a non JSONArray" 86 | 87 | let unit0 = JSONObject (emp |> e "test" (Float 0.5) |> e "hiya" (Int 20)) 88 | 89 | let unit1 = JSONObject (Obj.empty |> Obj.add "test" (Float 0.5) |> Obj.add "hiya" (Int 20)) 90 | 91 | let unit2 = JSONObject (Obj.empty |> e "test" (Float 0.5) |> e "hiya" (Int 20) |> e "array" (JSONArray [| Int 40; Float 60.0; |])) 92 | 93 | let unit3 = JSONObject (emp |> e "inside" unit2 |> e "sample" NULL) 94 | 95 | let unit4 = JSONObject (emp |> e "inside" unit2 |> e "empty" (JSONObject emp)) 96 | 97 | let unit5 = empty |> add "inside" unit2 |> add "empty" empty |> add "sample" (JSONArray [|unit0|]) 98 | let unit6 = add "inside" unit2 empty 99 | 100 | let unit7 = Int 1 101 | let unit8 = JSONArray ([|unit7; Int 2; Int 3; Int 4; Int 5; Int 6;|]) 102 | 103 | let x = print_json unit2 104 | let y = print_json unit0;; 105 | 106 | #install_printer print_json;; 107 | -------------------------------------------------------------------------------- /lib/utils/Leb128.ml: -------------------------------------------------------------------------------- 1 | open Binary 2 | 3 | let decode_uleb128 byte_stream = 4 | let rec loop pos shift acc = 5 | let byte = Binary.i8 byte_stream pos in 6 | let high_order_bit_is_zero = (byte land 0x80) = 0x0 in 7 | let acc' = acc lor ((byte land 0x7f) lsl shift) in 8 | if (high_order_bit_is_zero) then 9 | acc' 10 | else 11 | loop (pos + 1) (shift + 7) acc' in 12 | loop 0 0 0 13 | 14 | let num_bytes integer = 15 | let rec loop integer acc = 16 | let integer' = integer lsr 7 in 17 | if integer' == 0 then 18 | acc 19 | else 20 | loop integer' (acc + 1) in 21 | loop integer 1 22 | 23 | (* hasn't been tested extensively at all *) 24 | let encode_uleb128 integer = 25 | let size = num_bytes integer in 26 | let byte_stream = Bytes.create size in 27 | (* shift integer to the right by mod 7s, 28 | then bitmask with 0111 1111 (0x7f) 29 | then set the highest bit (lor 0x80), repeat 30 | *) 31 | for i = 0 to size - 2 do 32 | let shift = i * 7 in 33 | let byte = ((integer lsr shift) land 0x7f) lor 0x80 in 34 | Bytes.set byte_stream i (Char.chr byte); 35 | done; 36 | (* perform the final byte encoding with 0 for msb *) 37 | let byte = (integer lsr (7 * (size - 1))) land 0x7f in 38 | Bytes.set byte_stream (size - 1) (Char.chr byte); 39 | byte_stream 40 | 41 | (* 624485 = 0xE5 0x8E 0x26 = "\229\142&" *) 42 | 43 | (* returns decoded uleb128 and next position in stream *) 44 | let get_uleb128 byte_stream pos = 45 | let rec loop pos shift acc = 46 | let byte = Binary.i8 byte_stream pos in 47 | let high_order_bit_is_zero = (byte land 0x80) = 0x0 in 48 | let acc' = acc lor ((byte land 0x7f) lsl shift) in 49 | if (high_order_bit_is_zero) then 50 | (acc',pos+1) 51 | else 52 | loop (pos + 1) (shift + 7) acc' in 53 | loop pos 0 0 54 | 55 | let print_uleb128 bytes = 56 | Bytes.iter (fun byte -> Printf.printf "%x " @@ Char.code byte) bytes; 57 | Printf.printf "\n" 58 | 59 | (* result = 0; 60 | shift = 0; 61 | size = number of bits in signed integer; 62 | while(true) { 63 | byte = next byte in input; 64 | result |= (low order 7 bits of byte << shift); 65 | shift += 7; 66 | if (high order bit of byte == 0) 67 | break; 68 | } 69 | 70 | /* sign bit of byte is second high order bit (0x40) */ 71 | if ((shift Char.chr @@ List.nth bytes i) in 113 | get_sleb128 b 0 114 | 115 | let gu bytes = 116 | let b = Bytes.init (List.length bytes) (fun i -> Char.chr @@ List.nth bytes i) in 117 | get_uleb128 b 0 118 | 119 | let hext i = 120 | Printf.printf "0x%x\n" @@ fst i 121 | 122 | let hex i = 123 | Printf.printf "0x%x\n" i 124 | 125 | let p i = Printf.printf "%d\n" @@ fst i 126 | 127 | let unita = List.map (fun x -> Char.chr x) [0xca; 0xfe; 0xba; 0xbe;] 128 | let unit0 = [0x10] 129 | let unit1 = [0x45] 130 | let unit2 = [0x8e; 0x32] 131 | let unit3 = [0xc1; 0x57] 132 | let unit4 = [0x80; 0x80; 0x80; 0x3f] (*+0x7e00000*) (*+0x7e00000*) 133 | let unit5 = [0x80; 0x80; 0x80; 0x4f] (* +0x9e00000 -0x6200000 *) 134 | 135 | let res = -624485 136 | (* -624485 *) 137 | let u1 = [0x9b; 0xf1; 0x59]; 138 | let e1 = Bytes.init (List.length unit1) (fun i -> List.nth unit1 i) 139 | printf = "b0d2 10" @ 0x9cc92 in libsystem_c.dylib 140 | *) 141 | -------------------------------------------------------------------------------- /lib/utils/Leb128.mli: -------------------------------------------------------------------------------- 1 | val get_sleb128 : bytes -> int -> int * int 2 | val get_uleb128 : bytes -> int -> int * int 3 | val encode_uleb128 : int -> bytes 4 | -------------------------------------------------------------------------------- /lib/utils/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: e8a47bb4a2b8113d81482c5b08352e15) 3 | version = "1.1" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library with no dependencies" 6 | archive(byte) = "utils.cma" 7 | archive(byte, plugin) = "utils.cma" 8 | archive(native) = "utils.cmxa" 9 | archive(native, plugin) = "utils.cmxs" 10 | exists_if = "utils.cma" 11 | # OASIS_STOP 12 | 13 | -------------------------------------------------------------------------------- /lib/utils/RdrCommand.ml: -------------------------------------------------------------------------------- 1 | let program_in_path program_name = 2 | (* nuuuuu hacks *) 3 | let ret = Sys.command 4 | @@ Printf.sprintf 5 | "which %s 2>&1 > /dev/null" program_name 6 | in 7 | ret = 0 8 | 9 | let is_linux () = 10 | let ic = Unix.open_process_in "uname" in 11 | let uname = input_line ic in 12 | let () = close_in ic in 13 | uname = "Linux" 14 | 15 | let is_osx () = 16 | let ic = Unix.open_process_in "uname" in 17 | let uname = input_line ic in 18 | let () = close_in ic in 19 | uname = "Darwin" 20 | 21 | let disassemble file offset size = 22 | let ic = open_in_bin file in 23 | seek_in ic offset; 24 | let code = really_input_string ic size |> Binary.to_hex_string in 25 | close_in ic; 26 | flush stdout; 27 | Printf.printf "\t\n"; 28 | (* NOW FOR THE HACKS *) 29 | if (program_in_path "llvm-mc") then 30 | Sys.command 31 | @@ Printf.sprintf 32 | "echo \"%s\" | llvm-mc --disassemble" code 33 | |> ignore 34 | else 35 | Printf.eprintf "Error: llvm-mc not installed or not in ${PATH}\n"; 36 | -------------------------------------------------------------------------------- /lib/utils/RdrPrinter.ml: -------------------------------------------------------------------------------- 1 | let pp_strings iter ppf list = 2 | iter (fun s -> 3 | Format.fprintf ppf "%s@ " s) list 4 | 5 | let pp_slist ppf list = 6 | List.iter (fun s -> 7 | Format.fprintf ppf "%s@ " s) list 8 | 9 | let pp_seq ?brackets:(brackets=false) ppf pp list = 10 | if (brackets) then 11 | (* "@[[]@]","@[[%a]@]","@[[@]@[%a@ ","%a@ ","@]@[]@]" *) 12 | match list with 13 | | [] -> Format.fprintf ppf "@[[]@]" 14 | | x::[] -> Format.fprintf ppf "@[[%a]@]" pp x 15 | | x::xs -> 16 | Format.fprintf ppf "@[[@]@[%a@ " pp x; 17 | List.iter (fun x -> Format.fprintf ppf "%a@ " pp x) xs; 18 | Format.fprintf ppf "@]@[]@]" 19 | else 20 | match list with 21 | (* "@[@]","@[%a@]","@[%a@ ","%a@ ","@]" *) 22 | | [] -> Format.fprintf ppf "@[@]" 23 | | x::[] -> Format.fprintf ppf "@[%a@]" pp x 24 | | x::xs -> 25 | Format.fprintf ppf "@[%a@ " pp x; 26 | List.iter (fun x -> Format.fprintf ppf "%a@ " pp x) xs; 27 | Format.fprintf ppf "@]" 28 | 29 | let pp_h ?brackets:(brackets=false) ppf pp list = 30 | if (brackets) then 31 | match list with 32 | | [] -> Format.fprintf ppf "@[[]@]" 33 | | x::[] -> Format.fprintf ppf "@[[%a]@]" pp x 34 | | x::xs -> 35 | Format.fprintf ppf "@[[@]@[%a@ " pp x; 36 | List.iter (fun x -> Format.fprintf ppf "%a@ " pp x) xs; 37 | Format.fprintf ppf "@]@[]@]" 38 | else 39 | match list with 40 | | [] -> Format.fprintf ppf "@[@]" 41 | | x::[] -> Format.fprintf ppf "@[%a@]" pp x 42 | | x::xs -> 43 | Format.fprintf ppf "@[%a@ " pp x; 44 | List.iter (fun x -> Format.fprintf ppf "%a@ " pp x) xs; 45 | Format.fprintf ppf "@]" 46 | 47 | (* let pp_header ppf *) 48 | 49 | let pp_option ppf pp optional = 50 | match optional with 51 | | Some data -> 52 | pp ppf data 53 | | None -> 54 | Format.fprintf ppf "None" 55 | 56 | let pp_string ppf s = 57 | Format.fprintf ppf "%s" s 58 | 59 | let pp_hex ppf x = 60 | Format.fprintf ppf "0x%x" x 61 | 62 | (* 63 | match list with 64 | | [] -> Format.fprintf ppf "@[@]" 65 | | x::[] -> Format.fprintf ppf "@[%a@]" pp x 66 | | x::xs -> 67 | Format.fprintf ppf "@[%a@ " pp x; 68 | List.iter (fun x -> Format.fprintf ppf "%a@ " pp x) xs; 69 | Format.fprintf ppf "@]" 70 | *) 71 | -------------------------------------------------------------------------------- /lib/utils/RdrStorage.ml: -------------------------------------------------------------------------------- 1 | let dot_directory_name = "rdr" ^ Filename.dir_sep 2 | 3 | let get_dot_directory () = 4 | (Sys.getenv "HOME") ^ Filename.dir_sep ^ "." ^ dot_directory_name 5 | 6 | let create_dot_directory () = 7 | if (not @@ Sys.file_exists @@ get_dot_directory ()) then 8 | Unix.mkdir (get_dot_directory ()) 0o740 9 | 10 | let get_path filename = 11 | (get_dot_directory ()) ^ filename 12 | 13 | let graph_name = "lib_dependency_graph.gv" 14 | 15 | let get_graph_path ?graph_name:(graph_name=graph_name) 16 | ~use_dot_storage:use_dot_storage = 17 | if (use_dot_storage) then 18 | get_path graph_name 19 | else 20 | (Unix.getcwd()) ^ Filename.dir_sep ^ graph_name 21 | 22 | -------------------------------------------------------------------------------- /lib/utils/RdrUtils.ml: -------------------------------------------------------------------------------- 1 | module Binary = Binary 2 | module Leb128 = Leb128 3 | module Input = Input 4 | module ByteCoverage = ByteCoverage 5 | module Generics = Generics 6 | module Printer = RdrPrinter 7 | module Command = RdrCommand 8 | module Storage = RdrStorage 9 | -------------------------------------------------------------------------------- /lib/utils/utils.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: c77cf2298105fcea4da190994dc74aba) 3 | RdrUtils 4 | Binary 5 | Input 6 | Leb128 7 | ByteCoverage 8 | Generics 9 | RdrPrinter 10 | RdrCommand 11 | RdrStorage 12 | # OASIS_STOP 13 | -------------------------------------------------------------------------------- /lib/utils/utils.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: c77cf2298105fcea4da190994dc74aba) 3 | RdrUtils 4 | Binary 5 | Input 6 | Leb128 7 | ByteCoverage 8 | Generics 9 | RdrPrinter 10 | RdrCommand 11 | RdrStorage 12 | # OASIS_STOP 13 | -------------------------------------------------------------------------------- /opam/descr: -------------------------------------------------------------------------------- 1 | Rdr is a cross-platform binary analysis and reverse engineering library, 2 | utilizing a unique symbol map for global analysis. 3 | 4 | `rdr` is an OCaml tool/library for doing cross-platform analysis of binaries, 5 | by printing headers, locating entry points, showing import and export 6 | symbols, their binary offsets and size, etc. 7 | 8 | It also features a symbol map which allows fast lookups for arbitrary 9 | symbols, and their associated data, on your system 10 | (the default search location are binaries in /usr/lib). 11 | 12 | The latest release also makes `rdr` a package which you can link against 13 | and use in your own projects. 14 | 15 | See the README at http://github.com/m4b/rdr for more details. 16 | 17 | Features: 18 | 19 | * 64-bit Linux and Mach-o binary analysis 20 | * Searchable symbol-map of all the symbols on your system, including binary 21 | offset, size, and exporting library 22 | * Print imports and exports of binaries 23 | * Make pretty graphs, at the binary or symbol map level 24 | * Byte Coverage algorithm which marks byte sequences as understood (or not) 25 | and provides other meta-data -------------------------------------------------------------------------------- /opam/files/rdr.install: -------------------------------------------------------------------------------- 1 | bin: [ 2 | "?_build/src/Rdr.byte" {"rdr"} 3 | "?_build/src/Rdr.native" {"rdr"} 4 | ] 5 | -------------------------------------------------------------------------------- /opam/findlib: -------------------------------------------------------------------------------- 1 | rdr 2 | -------------------------------------------------------------------------------- /opam/opam: -------------------------------------------------------------------------------- 1 | opam-version: "1.2" 2 | name: "rdr" 3 | version: "2.0.1" 4 | maintainer: "" 5 | authors: [ "m4b" ] 6 | license: "BSD-3-clause" 7 | homepage: "http://github.com/m4b/rdr" 8 | build: [ 9 | ["ocaml" "setup.ml" "-configure" "--prefix" prefix] 10 | ["ocaml" "setup.ml" "-build"] 11 | ] 12 | install: ["ocaml" "setup.ml" "-install"] 13 | remove: [ 14 | ["ocamlfind" "remove" "rdr"] 15 | ] 16 | depends: [ 17 | "ocamlfind" 18 | ] 19 | depopts: [ 20 | "base-unix" 21 | ] 22 | -------------------------------------------------------------------------------- /project_deps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/project_deps.png -------------------------------------------------------------------------------- /scripts/consts_to_pattern.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This takes a file of form: 4 | # 5 | # 6 | # .... ..... 7 | # where is the name of type you want generated 8 | # is the type, i.e., int 9 | # is the singleton constructor to create (must be capitalized obviously 10 | # and is the int that you want the constructor to match with 11 | 12 | import re 13 | import sys 14 | 15 | constre = re.compile("(?P\w+)\s+(?P\w+)") 16 | 17 | f = open (sys.argv[1]) 18 | content = f.read() 19 | f.close() 20 | 21 | def getTypeDef(content): 22 | name = content[0][0] 23 | typ = content[0][1] 24 | res = "type "+name.lower()+" =\n" 25 | for constructor, const in content[1:]: 26 | res+= " | "+constructor+"\n" 27 | res+=" | UNKNOWN of "+typ+"\n" 28 | return res 29 | 30 | def getConstToName(content): 31 | name = content[0][0] 32 | typ = content[0][1] 33 | res = "let get_"+name+" =\n function\n" 34 | for constructor,const in content[1:]: 35 | res+= " | "+const+" -> "+constructor+"\n" 36 | res+=" | x -> UNKNOWN x\n" 37 | return res 38 | 39 | def getNameToConst(content): 40 | name = content[0][0] 41 | typ = content[0][1] 42 | res = "let "+name+"_to_int =\n function\n" 43 | for constructor,const in content[1:]: 44 | res+= " | "+constructor+" -> "+const+"\n" 45 | res+=" | UNKNOWN x -> x\n" 46 | return res 47 | 48 | def getPrinter(content): 49 | name = content[0][0] 50 | typ = content[0][1] 51 | res = "let "+name+"_to_string =\n function\n" 52 | for constructor,const in content[1:]: 53 | res+= " | "+constructor+" -> \""+constructor+"\"\n" 54 | res+=" | UNKNOWN x -> Printf.sprintf \"UNKNOWN_"+name.upper()+" 0x%x\" x\n" 55 | return res 56 | 57 | def get(program): 58 | nodes = [] 59 | tokens = constre.findall(program) 60 | nodes.append(getTypeDef(tokens)) 61 | nodes.append(getConstToName(tokens)) 62 | nodes.append(getNameToConst(tokens)) 63 | nodes.append(getPrinter(tokens)) 64 | return nodes 65 | 66 | def printOcamlModule (nodes): 67 | for node in nodes: 68 | print (node) 69 | 70 | nodes = get(content) 71 | 72 | printOcamlModule(nodes) 73 | -------------------------------------------------------------------------------- /setup.ml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4b/rdr/2bf1f73fc317cd74f8c7cacd542889df729bd003/setup.ml -------------------------------------------------------------------------------- /src/Config.ml: -------------------------------------------------------------------------------- 1 | type t = 2 | { 3 | (* internal *) 4 | analyze: bool; 5 | search: bool; 6 | silent: bool; 7 | consume_bytes: bool; 8 | use_tol: bool; 9 | name: string; 10 | install_name: string; 11 | resolve_imports: bool; 12 | (* analysis *) 13 | verbose: bool; (* -v *) 14 | print_headers: bool; (* -h *) 15 | print_nlist: bool; (* -s *) 16 | print_libraries: bool; (* -l *) 17 | print_exports: bool; (* -e *) 18 | print_imports: bool; (* -i *) 19 | print_coverage: bool; (* -c *) 20 | print_sections: bool; (* --sections *) 21 | scan_bytes: string; (* --scan *) 22 | disassemble: bool; (* -D *) 23 | (* building *) 24 | use_map: bool; (* -m *) 25 | recursive: bool; (* -r *) 26 | write_symbols: bool; (* -w *) 27 | marshal_symbols: bool; (* -b *) 28 | base_symbol_map_directories: string list; (* -d *) 29 | framework_directories: string list; (* -F *) 30 | (* general *) 31 | graph: bool; (* -g *) 32 | filename: string; (* anonarg *) 33 | search_term: string; (* -f *) 34 | print_goblin: bool; (* -G *) 35 | transitive_closure_filter: string list; (* -t *) 36 | compute_transitive_closure: bool; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 0f40f2daeab7f1483f33797198e013b4) 3 | version = "3.0.0" 4 | description = 5 | "Lightweight, cross platform binary parsing and analysis library" 6 | requires = "rdr.pe rdr.elf rdr.mach rdr.goblin rdr.utils rdr str unix" 7 | archive(byte) = "rdrutils.cma" 8 | archive(byte, plugin) = "rdrutils.cma" 9 | archive(native) = "rdrutils.cmxa" 10 | archive(native, plugin) = "rdrutils.cmxs" 11 | exists_if = "rdrutils.cma" 12 | # OASIS_STOP 13 | 14 | -------------------------------------------------------------------------------- /src/ReadElf.ml: -------------------------------------------------------------------------------- 1 | open Config 2 | 3 | let debug = false 4 | 5 | let analyze config binary = 6 | let elf = Elf.get ~meta_only:true binary in 7 | (* for consistency and display, goblin makes everything have names *) 8 | let goblin = 9 | Goblin.Elf.from 10 | ~use_tree:config.resolve_imports 11 | config.install_name 12 | elf 13 | in 14 | (* print switches *) 15 | if (not config.silent) then 16 | begin 17 | if (not config.search) then 18 | Elf.Header.print_elf_header64 elf.Elf.header; 19 | if (config.verbose || config.print_headers) then 20 | begin 21 | Elf.Header.print_elf_header64 22 | ~verbose:(config.verbose || config.print_headers) 23 | elf.Elf.header; 24 | Elf.ProgramHeader.print_program_headers elf.Elf.program_headers; 25 | Elf.Dynamic.print_dynamic elf.Elf._dynamic; 26 | if (elf.Elf.interpreter <> "") then 27 | Printf.printf "Interpreter: %s\n" elf.Elf.interpreter 28 | end; 29 | if (config.verbose || config.print_sections) then 30 | Elf.SectionHeader.print_section_headers elf.Elf.section_headers; 31 | if (config.verbose || config.print_nlist) then 32 | Elf.SymbolTable.print elf.Elf.symbol_table; 33 | if (config.verbose || config.print_libraries) then 34 | begin 35 | if (elf.Elf.is_lib) then 36 | Printf.printf "Soname: %s\n" elf.Elf.soname; 37 | Printf.printf 38 | "Libraries (%d)\n" 39 | (List.length elf.Elf.libraries); 40 | List.iter (Printf.printf "\t%s\n") elf.Elf.libraries 41 | end; 42 | if (config.verbose || config.print_exports) then 43 | begin 44 | Printf.printf 45 | "Exports (%d)\n" 46 | (Array.length goblin.Goblin.exports); 47 | Goblin.print_exports goblin.Goblin.exports 48 | end; 49 | if (config.verbose || config.print_imports) then 50 | begin 51 | Printf.printf 52 | "Imports (%d)\n" 53 | (Array.length goblin.Goblin.imports); 54 | Goblin.print_imports goblin.Goblin.imports 55 | end; 56 | if (config.verbose || config.print_coverage) then 57 | ByteCoverage.print elf.Elf.byte_coverage 58 | end; 59 | goblin 60 | -------------------------------------------------------------------------------- /src/ReadMach.ml: -------------------------------------------------------------------------------- 1 | (* 2 | #TODO 3 | * add load segment boundaries, and nlists locals as a parameters to the compute size 4 | * compute final sizes after imports, locals, and exports are glommed into a goblin symbol soup, using all the information available 5 | *) 6 | 7 | open Printf 8 | 9 | open Mach.LoadCommand 10 | open Goblin.Export 11 | open Goblin.Import 12 | open Config 13 | 14 | let analyze config binary = 15 | let mach = Mach.get binary in 16 | let goblin = Goblin.Mach.from config.install_name mach in 17 | if (not config.silent) then 18 | begin 19 | if (not config.search) then 20 | Mach.Header.print_long_header mach.Mach.header; 21 | if (config.verbose || config.print_headers) then 22 | Mach.LoadCommand.print_load_commands mach.Mach.load_commands; 23 | if (config.verbose || config.print_sections) then 24 | Mach.LoadCommand.print_segments_64 25 | @@ Mach.LoadCommand.get_segments mach.Mach.load_commands; 26 | if (config.verbose || config.print_libraries) then 27 | Mach.LoadCommand.print_libraries mach.Mach.libraries; 28 | if (config.verbose || config.print_exports) then 29 | Goblin.print_exports goblin.Goblin.exports; 30 | if (config.verbose || config.print_imports) then 31 | Goblin.print_imports goblin.Goblin.imports; 32 | if (config.print_nlist) then Mach.SymbolTable.print mach.Mach.nlist; 33 | if (config.print_coverage) then 34 | ByteCoverage.print mach.Mach.byte_coverage 35 | end; 36 | goblin 37 | 38 | -------------------------------------------------------------------------------- /src/ReadPE.ml: -------------------------------------------------------------------------------- 1 | open Config 2 | open PE.Import 3 | open PE.Export 4 | open PE.Header 5 | open Goblin.Import 6 | open Goblin.Export 7 | 8 | let analyze (config:Config.t) binary = 9 | let pe = PE.get ~coverage:config.print_coverage binary in 10 | (* print switches *) 11 | if (not config.silent) then 12 | begin 13 | if (not config.search) then 14 | PE.print_header_stub pe; 15 | if (config.verbose || config.print_headers) then 16 | begin 17 | PE.Header.print pe.PE.header; 18 | let ppf = Format.std_formatter in 19 | Format.fprintf ppf "@[Export Data@ "; 20 | Rdr.Utils.Printer.pp_option 21 | ppf PE.Export.pp_export_data pe.PE.export_data; 22 | Format.fprintf ppf "@]"; 23 | Format.fprintf ppf "@ @[Import Data@ "; 24 | Rdr.Utils.Printer.pp_option 25 | ppf PE.Import.pp_import_data pe.PE.import_data; 26 | Format.fprintf ppf "@]@."; 27 | end; 28 | if (config.verbose || config.print_sections) then 29 | PE.SectionTable.print pe.PE.sections; 30 | if (config.verbose || config.print_nlist) then 31 | (* NO-OP no debug symbols implemented yet *) 32 | (); 33 | if (config.verbose || config.print_libraries) then 34 | PE.Import.print_libraries pe.PE.libraries; 35 | if (config.verbose || config.print_exports) then 36 | begin 37 | PE.Export.print pe.PE.exports; 38 | end; 39 | if (config.verbose || config.print_imports) then 40 | begin 41 | PE.Import.print pe.PE.imports; 42 | end; 43 | if (config.verbose || config.print_coverage) then 44 | ByteCoverage.print pe.PE.byte_coverage 45 | end; 46 | Goblin.PE.from config.install_name pe 47 | 48 | -------------------------------------------------------------------------------- /src/rdrutils.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 849cf3813a76b9dfe586623cf979f15f) 3 | Config 4 | ReadMach 5 | ReadElf 6 | ReadPE 7 | # OASIS_STOP 8 | -------------------------------------------------------------------------------- /src/rdrutils.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 849cf3813a76b9dfe586623cf979f15f) 3 | Config 4 | ReadMach 5 | ReadElf 6 | ReadPE 7 | # OASIS_STOP 8 | -------------------------------------------------------------------------------- /src/x86/X86Assembly.ml: -------------------------------------------------------------------------------- 1 | type size = int 2 | 3 | type operand = 4 | | Register of int (* int because index into registers 0-16 *) 5 | | Literal of int64 (* >= 0x8000000000000000 are negative *) 6 | | Memory of int (* which register *) 7 | | MemoryDisp of int * int64 (* which register and the displacement *) 8 | (* SIB? | MemorySib of int * int *) 9 | 10 | type asm = 11 | | Add of size * operand * operand 12 | -------------------------------------------------------------------------------- /src/x86/X86_64.ml: -------------------------------------------------------------------------------- 1 | (* TODO: 2 | (1) not sure this approach is going to work; 3 | for raw_inst, I can't know _not_ to read a modRM or sib or displacement without first consulting the opcode; which means I need to perform a lookup using the opcode to tell me whether to eat a modRM or a sib or a displacement or an immediate; but then, if I'm doing a table lookup, I probably should just do all the work in the table lookup, including generating the disassembled instruction, etc? 4 | *) 5 | 6 | (* prefix all these with x86 ? *) 7 | 8 | (* register contents [register] [register] + disp [register * sib] *) 9 | (* type operand = [ `Register of int | `Memory of int | `MemoryDisp of int * int] (* SIB? | MemorySib of int * int *) *) 10 | 11 | type operand = Register of int | Memory of int | MemoryDisp of int * int (* SIB? | MemorySib of int * int *) 12 | 13 | type opcode = | OneByte of int | TwoByte of int * int | ThreeByte of int * int * int 14 | type raw_inst = 15 | { 16 | legacy_prefix : int list option; 17 | mandatory_prefix : int option; 18 | rex_or_vex : int option; (* rex with vex is #UD *) 19 | opcode : opcode; 20 | modRM : int option; 21 | sib : int option; 22 | displacement : bytes option; 23 | immediate : bytes option; 24 | } 25 | (* 26 | type group1 = [ `LOCK | `REPNE | `REP] 27 | type legacy_prefix = gro 28 | *) 29 | let is_legacy_prefix prefix = 30 | match prefix with 31 | | 0xf0 | 0xf2 | 0xf3 (* group1 *) 32 | | 0x2e | 0x36 | 0x3e | 0x26 | 0x64 | 0x65 (* group2 *) 33 | | 0x66 (* group 3 *) 34 | | 0x67 (* group 4 *) 35 | -> true 36 | | _ -> false 37 | 38 | let leg1 = "\xf0\xf2\x2e\xff\x25\xd8" 39 | 40 | let is_rex prefix = prefix >= 0x40 && prefix <= 0x4f 41 | 42 | (* this index out of bounds if "\xf0" or "", for example *) 43 | let get_legacy_prefixes bytes offset = 44 | let rec loop acc offset = 45 | let byte = Char.code @@ Bytes.get bytes offset in 46 | if (is_legacy_prefix byte) then 47 | loop (byte::acc) (offset + 1) 48 | else 49 | Some (List.rev acc), offset 50 | in 51 | let byte = Char.code @@ Bytes.get bytes offset in 52 | if (is_legacy_prefix byte) then 53 | loop [byte] (offset + 1) 54 | else 55 | None, offset 56 | 57 | let unit1 = (get_legacy_prefixes leg1 0) = (Some [240; 242; 46],3) 58 | 59 | (* mandatory prefix always before a rex or vex so sort of need a lookahead? *) 60 | let get_rex_or_vex bytes offset = 61 | let byte = Binary.u8 bytes offset in 62 | if (is_rex byte) then 63 | Some byte 64 | else 65 | None 66 | 67 | exception Unknown_instruction 68 | 69 | (* i should probably just decode here, instead of turning it into a raw, then decoding it again... *) 70 | let get_opcode bytes offset legacy_prefix mandatory_prefix rex_or_vex = 71 | let opcode = Binary.u8 bytes offset in 72 | match opcode with 73 | | 0x00 | 0x01 74 | | 0x02 | 0x03 -> 75 | (* 76 | let opcode = OneByte opcode in 77 | let modRM = None in 78 | *) 79 | raise Unknown_instruction 80 | | 0x04 | 0x05 -> 81 | raise Unknown_instruction 82 | | 0x50 | 0x51 83 | | 0x52 | 0x53 84 | | 0x54 | 0x55 85 | | 0x56 | 0x57 -> 86 | let opcode = OneByte opcode in 87 | let modRM = None in 88 | let sib = None in 89 | let displacement = None in 90 | let immediate = None in 91 | {legacy_prefix; mandatory_prefix; rex_or_vex; opcode; modRM; sib; displacement; immediate} 92 | | 0x58 | 0x59 93 | | 0x5a | 0x5b 94 | | 0x5c | 0x5d 95 | | 0x5e | 0x5f -> 96 | let opcode = OneByte opcode in 97 | let modRM = None in 98 | let sib = None in 99 | let displacement = None in 100 | let immediate = None in 101 | {legacy_prefix; mandatory_prefix; rex_or_vex; opcode; modRM; sib; displacement; immediate} 102 | | 0x0f -> 103 | (* two byte opcode *) 104 | raise Unknown_instruction 105 | | _ -> raise Unknown_instruction 106 | 107 | let get_raw_inst bytes = raise Not_found 108 | 109 | (* addl 1(%rip), %eax *) 110 | let unit1 = "0x03 0x05 0x1 0x0 0x0 0x0" 111 | --------------------------------------------------------------------------------