├── .gitignore ├── LICENSE ├── README.md ├── bindiff ├── core ├── __init__.py ├── arch.py ├── binary.py ├── compiler.py ├── context.py ├── func.py ├── linker.py └── patcher.py ├── deps.sh ├── explore ├── ida ├── allfuncs.py └── funcs.idc ├── patch ├── run ├── samples ├── arm │ └── movt_combine.py ├── cgc │ ├── backdoor │ │ └── 01_nacl.py │ ├── create │ │ ├── heap.py │ │ ├── hello.py │ │ └── knock.py │ └── obfuscate │ │ ├── 01_jit_xor.py │ │ ├── 02_rc4.py │ │ ├── 03_xor_patches.py │ │ ├── 04_xor_prog.py │ │ ├── 05_xor_entry.py │ │ └── 06_xor_magic.py ├── common │ └── reflow │ │ ├── 01_replace_syscalls.py │ │ ├── 02_static_aslr.py │ │ └── 03_dynamic_aslr.py └── x86 │ ├── fuzzing │ └── 01_cmp_split.py │ ├── harden │ ├── 02_ropshift.py │ ├── 03_spadjust.py │ ├── 05_io_filter.py │ └── 06_stack_cookies.py │ ├── hello │ ├── hello32.py │ └── hello64.py │ └── optimize │ ├── 01_coalesce.py │ ├── 02_useless_stash.py │ └── 03_remove_ebp.py └── util ├── __init__.py ├── autolink.py ├── backdoor ├── Makefile ├── backdoor_poc.py ├── ecc.c ├── keygen ├── keygen.c ├── posixrand.c ├── privkey.h ├── pubkey.h ├── sign ├── sign.c ├── tweetnacl.c └── tweetnacl.h ├── cfg.py ├── crypto ├── __init__.py ├── rc4.c ├── rc4.py └── xor.py ├── elffile.py ├── emu.py ├── heap ├── __init__.py ├── malloc.c ├── malloc.h └── test_binary │ ├── Makefile │ └── src │ ├── libc.c │ ├── libc.h │ ├── malloc.c │ ├── malloc.h │ └── test.c ├── patch ├── __init__.py ├── aslr.py ├── dis.py └── syscall.py └── stdlib ├── __init__.py ├── chk.c ├── ctype.c ├── defines.h ├── io.c ├── libc.c ├── num.c ├── string.c ├── syscall.h ├── syscalls.c └── types.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Ryan Hileman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | ---- 22 | 23 | `util/elffile.py` has the following license: 24 | 25 | Copyright 2010 - 2011 K. Richard Pixley 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining 28 | a copy of this software and associated documentation files (the 29 | "Software"), to deal in the Software without restriction, including 30 | without limitation the rights to use, copy, modify, merge, publish, 31 | distribute, sublicense, and/or sell copies of the Software, and to 32 | permit persons to whom the Software is furnished to do so, subject to 33 | the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be 36 | included in all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 39 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 40 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 41 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 42 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 43 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 44 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | patchkit 2 | ---- 3 | Patches an ELF binary using one or more simple Python scripts. 4 | 5 | Usage: 6 | 7 | patch [patchdir|file...] 8 | 9 | 10 | patchdir 11 | ---- 12 | Contains one or more Python patch files, which will be executed in alphabetical order against a binary. 13 | 14 | 15 | Patch Examples 16 | ---- 17 | 18 | Nopping an address, injecting an assembly function, and hooking the entry point: 19 | 20 | def simple_patch(pt): 21 | # nop out a jump at the entry point 22 | pt.patch(pt.entry, hex='90' * 5) 23 | 24 | # inject assembly into the binary and return the address 25 | addr = pt.inject(asm='mov eax, 1; ret') 26 | 27 | # hook the entry point to make it call addr (ret will run the original entry point) 28 | pt.hook(pt.entry, addr) 29 | 30 | Replacing a C function: 31 | 32 | def replace_free(pt): 33 | # pretend free() is at this address: 34 | old_free = 0x804fc4 35 | 36 | # inject a function to replace free() 37 | new_free = pt.inject(c=r''' 38 | void free_stub(void *addr) { 39 | printf("stubbed free(%p)\n", addr); 40 | } 41 | ''') 42 | 43 | # patch the beginning of free() with a jump to our new function 44 | pt.patch(old_free, jmp=new_free) 45 | 46 | 47 | API 48 | ---- 49 | addr = search(data) 50 | hook(addr, new_addr) 51 | patch(addr, *compile arg*) 52 | addr = inject(*compile arg*) 53 | 54 | *compile arg* is any of the following: 55 | raw='data' 56 | hex='0bfe' 57 | asm='nop' 58 | jmp=0xaddr 59 | c='void func() { int a; a = 1; }' (only supported on inject, not patch) 60 | 61 | 62 | IDA scripts 63 | ---- 64 | Some scripts live in the ida/ path. Run them like this: 65 | 66 | /Applications/IDA\ Pro\ 6.8/idaq.app/Contents/MacOS/idaq64 -A -Sida/allfuncs.py a.out 67 | 68 | When invoked like this, allfuncs.py will generate `a.out.funcs` which is used by hardening scripts. 69 | 70 | 71 | Tools 72 | ---- 73 | These are somewhat CGC and x86-specific right now, but will be ported for general use in the future. 74 | 75 | - explore: uses a Python CFG and recursive backtracking emulator to find basic blocks in an executable 76 | - bindiff: uses the block boundaries from an explore run, as well as additional analysis to find and output basic block diffs between two binaries 77 | 78 | 79 | Dependencies 80 | ---- 81 | - Run `./deps.sh` to automatically install these. 82 | - Capstone Engine - https://github.com/aquynh/capstone.git 83 | - Keystone Engine - https://github.com/keystone-engine/keystone.git 84 | - Unicorn Engine - https://github.com/unicorn-engine/unicorn.git 85 | -------------------------------------------------------------------------------- /bindiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import binascii 4 | import difflib 5 | import itertools 6 | import sys 7 | 8 | from core.binary import Binary 9 | from core.func import Func 10 | from util import cfg 11 | 12 | def hexdump(s, addr): 13 | lines = [] 14 | for i in xrange(0, len(s), 16): 15 | chunks = [s[i+j:i+j+4] for j in xrange(0, 16, 4)] 16 | chunks += (4 - len(chunks)) * [' ' * 8] 17 | data = ' '.join(binascii.hexlify(chunk) for chunk in chunks) 18 | lines.append('0x%x: %s' % (addr, data)) 19 | return '\n'.join(lines) 20 | 21 | def dishex(pt, data, addr): 22 | dis = pt.arch.dis(data, addr=addr) 23 | spill = sum([len(i.bytes) for i in dis]) 24 | extra = data[spill:] 25 | 26 | good = pt.pdis(dis) 27 | bad = hexdump(extra, addr=addr + spill) 28 | return filter(None, good.strip().split('\n') + bad.strip().split('\n')) 29 | 30 | def diff(pt, a, b, addr): 31 | da = dishex(pt, a, addr) 32 | db = dishex(pt, b, addr) 33 | for line in itertools.islice(difflib.unified_diff(da, db), 3, None): 34 | pt.debug(line.strip()) 35 | 36 | if __name__ == '__main__': 37 | backtrack = False 38 | if len(sys.argv) >= 2 and sys.argv[1] == '--backtrack': 39 | sys.argv.pop(1) 40 | backtrack = True 41 | 42 | if len(sys.argv) != 3: 43 | print 'Usage: %s [--backtrack] ' % sys.argv[0] 44 | sys.exit(1) 45 | 46 | bina, binb = sys.argv[1:3] 47 | a = Binary(bina) 48 | b = Binary(binb) 49 | a.verbose = True 50 | b.verbose = True 51 | 52 | with a.collect() as pt, b.collect() as ptb: 53 | if pt.entry != ptb.entry: 54 | pt.debug('[-======== New Entry Point ========-]') 55 | pt.debug('[ENTRY] Moved 0x%x -> 0x%x' % (pt.entry, ptb.entry)) 56 | ptb.debug(dis=ptb.dis(ptb.entry)) 57 | 58 | pt.debug('') 59 | pt.debug('[-======== Function Diff ========-]') 60 | for func in pt.funcs(): 61 | funcb = Func(ptb, func.addr, func.size) 62 | if func.read() == funcb.read(): 63 | continue 64 | diff(pt, func.read(), funcb.read(), func.addr) 65 | 66 | pt.debug('') 67 | pt.debug('[-======== Exploring Branches ========-]') 68 | known = list(pt.funcs()) 69 | new_funcs, selfmod = cfg.explore(ptb, known, backtrack=backtrack) 70 | if new_funcs: 71 | tmp = [] 72 | for f in new_funcs: 73 | try: 74 | da = pt.elf.read(f.addr, f.size) 75 | except Exception: 76 | tmp.append(f) 77 | continue 78 | db = ptb.elf.read(f.addr, f.size) 79 | if da != db: 80 | tmp.append(f) 81 | new_funcs = tmp 82 | 83 | pt.debug('[-======== New Functions ========-]') 84 | for f in new_funcs: 85 | pt.debug(' - 0x%x +%d' % (f.addr, f.size)) 86 | pt.debug('') 87 | 88 | for f in new_funcs: 89 | if f.addr == ptb.entry: 90 | pt.debug('[FUNC] @0x%x (ENTRY POINT)' % f.addr) 91 | else: 92 | pt.debug('[FUNC] @0x%x' % f.addr) 93 | 94 | fmt = lambda addrs: ', '.join(['0x%x' % x for x in addrs]) 95 | if len(f.stacks) == 1: 96 | pt.debug(' [call stack]: ' + fmt(list(f.stacks)[0])) 97 | elif f.stacks: 98 | pt.debug(' [call stacks]:') 99 | for stack in f.stacks: 100 | pt.debug(' | %s' % fmt(stack)) 101 | 102 | if f.xrefs: 103 | pt.debug(' [xrefs]: %s' % fmt(f.xrefs)) 104 | 105 | pt.debug(dis=f.dis()) 106 | 107 | for (addr, size), data in selfmod: 108 | if addr in f: 109 | pt.warn('SELF-MODIFYING CODE') 110 | ref = ptb.elf.read(addr, size) 111 | diff(pt, ref, data, addr) 112 | 113 | pt.debug('') 114 | 115 | if selfmod: 116 | pt.debug('[-======== Old Functions (self-modifying) ========-]') 117 | for f in pt.funcs(): 118 | for (addr, size), data in selfmod: 119 | if addr in f: 120 | pt.warn('SELF-MODIFYING CODE') 121 | ref = ptb.elf.read(addr, size) 122 | diff(pt, ref, data, addr) 123 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from patcher import Patcher 2 | 3 | __all__ = [ 4 | 'Patcher', 5 | ] 6 | -------------------------------------------------------------------------------- /core/arch.py: -------------------------------------------------------------------------------- 1 | import re 2 | from capstone import * 3 | from keystone import * 4 | 5 | class Arch: 6 | def __init__(self): 7 | self.cs = Cs(*self._cs) 8 | self.cs.detail = True 9 | self.ks = Ks(*self._ks) 10 | 11 | def asm(self, asm, addr=0, att_syntax=False): 12 | if not asm: 13 | return '' 14 | # asm start label for use with relative offsets 15 | asm = '_PKST_:;' + asm 16 | 17 | saved = self.ks.syntax 18 | if att_syntax: 19 | self.ks.syntax = KS_OPT_SYNTAX_ATT 20 | tmp, _ = self.ks.asm(asm, addr=addr) 21 | self.ks.syntax = saved 22 | return ''.join(map(chr, tmp)) 23 | 24 | def dis(self, raw, addr=0): 25 | return list(self.cs.disasm(str(raw), addr)) 26 | 27 | def jmp(self, dst): 28 | raise NotImplementedError 29 | 30 | def call(self, dst): 31 | raise NotImplementedError 32 | 33 | def ret(self): 34 | raise NotImplementedError 35 | 36 | def nop(self): 37 | raise NotImplementedError 38 | 39 | class x86(Arch): 40 | _cs = CS_ARCH_X86, CS_MODE_32 41 | _ks = KS_ARCH_X86, KS_MODE_32 42 | 43 | def call(self, dst): return 'call 0x%x;' % dst 44 | def jmp(self, dst): return 'jmp 0x%x;' % dst 45 | 46 | def ret(self): return 'ret;' 47 | def nop(self): return 'nop;' 48 | 49 | # memcpy should be pc-relative 50 | # dst and src are offsets from the _PKST_ label 51 | def memcpy(self, dst, src, size): 52 | return ''' 53 | push edi 54 | push esi 55 | push ecx 56 | 57 | call ref 58 | ref: pop edi 59 | sub edi, ref - _PKST_ 60 | mov esi, edi 61 | 62 | add edi, %d 63 | add esi, %d 64 | mov ecx, %d 65 | 66 | rep movsb 67 | 68 | pop ecx 69 | pop esi 70 | pop edi 71 | ''' % (dst, src, size) 72 | 73 | class x86_64(x86): 74 | _cs = CS_ARCH_X86, CS_MODE_64 75 | _ks = KS_ARCH_X86, KS_MODE_64 76 | 77 | def memcpy(self, dst, src, size): 78 | return ''' 79 | push rdi 80 | push rsi 81 | push rcx 82 | 83 | lea rdi, [rip - _PKST_ + %d] 84 | lea rsi, [rip - _PKST_ + %d] 85 | mov rcx, %d 86 | 87 | rep movsb 88 | 89 | pop rcx 90 | pop rsi 91 | pop rdi 92 | ''' % (dst, src, size) 93 | 94 | class arm(Arch): 95 | _cs = CS_ARCH_ARM, CS_MODE_ARM 96 | _ks = KS_ARCH_ARM, KS_MODE_ARM 97 | -------------------------------------------------------------------------------- /core/binary.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import os 3 | 4 | from util import autolink 5 | from util import elffile 6 | from util.elffile import PT 7 | 8 | from context import Context 9 | from linker import Linker 10 | 11 | class Binary: 12 | def __init__(self, path): 13 | self.path = path 14 | self.fileobj = open(path, 'rb') 15 | self.elf = elffile.open(fileobj=self.fileobj) 16 | self.linker = Linker(self) 17 | 18 | self.final_hook = [] 19 | self.asm_hook = [] 20 | self.c_hook = [] 21 | 22 | self.verbose = False 23 | autolink.declare(self.linker) 24 | 25 | start = 0xFFFFFFFFFFFFFFFF 26 | end = 0 27 | # TODO: doesn't handle new mem being mapped or unmapped 28 | for ph in reversed(self.elf.progs): 29 | if ph.isload: 30 | start = min(start, ph.vaddr) 31 | end = max(ph.vaddr + ph.vsize, end) 32 | 33 | # add patch segment 34 | def new_segment(addr): 35 | align = 0x1000 36 | ph = self.elf.programHeaderClass() 37 | ph.data = bytearray() 38 | ph.type = PT['PT_LOAD'].code 39 | ph.vaddr = (addr + align - 1) & ~(align - 1) 40 | ph.paddr = ph.vaddr 41 | # TODO: default is RWX?! 42 | ph.flags = 7 43 | ph.align = align 44 | ph.memsz = 0 45 | ph.filesz = 0 46 | self.elf.progs.append(ph) 47 | return ph 48 | 49 | self.patch = new_segment(end) 50 | self.nxpatch = new_segment(end + 0x800000) 51 | self.nxpatch.flags = 6 52 | self.linkpatch = new_segment(end + 0x1600000) 53 | self.jitpatch = new_segment(end + 0x2400000) 54 | 55 | self.entry_hooks = [] 56 | 57 | def _seg(self, name): 58 | return { 59 | 'patch': self.patch, 60 | 'nx': self.nxpatch, 61 | 'link': self.linkpatch, 62 | 'jit': self.jitpatch, 63 | }.get(name, 'patch') 64 | 65 | @contextlib.contextmanager 66 | def collect(self): 67 | p = Context(self, verbose=self.verbose) 68 | yield p 69 | 70 | def next_alloc(self, target='patch'): 71 | return self._seg(target).vend 72 | 73 | def alloc(self, size, target='patch'): 74 | ph = self._seg(target) 75 | tmp = self.next_alloc(target) 76 | ph.data += '\0' * size 77 | ph.memsz += size 78 | ph.filesz += size 79 | return tmp 80 | 81 | def onfinal(self, cb): 82 | self.final_hook.append(cb) 83 | 84 | def onasm(self, cb): 85 | self.asm_hook.append(cb) 86 | 87 | def save(self, path): 88 | self.nxpatch.flags &= ~1 89 | 90 | print '[+] Saving binary to: %s' % path 91 | # hooking the entry point is a special case that generates a more efficient call table 92 | if self.entry_hooks: 93 | with self.collect() as pt: 94 | # call each hook addr then jump to original entry point 95 | calls = map(pt.arch.call, self.entry_hooks) + [pt.arch.jmp(pt.entry)] 96 | addr = pt.inject(asm=';'.join(calls), internal=True) 97 | pt.entry = addr 98 | 99 | for cb in self.final_hook: 100 | with self.collect() as pt: 101 | cb(pt) 102 | 103 | for prog in (self.patch, self.nxpatch, self.linkpatch, self.jitpatch): 104 | if not prog.filesz and prog in self.elf.progs: 105 | self.elf.progs.remove(prog) 106 | 107 | self.elf.save(path) 108 | os.chmod(path, 0755) 109 | -------------------------------------------------------------------------------- /core/compiler.py: -------------------------------------------------------------------------------- 1 | import re 2 | import subprocess 3 | 4 | # TODO: last few args are optional, but clang emits them so I'll deal with it if it breaks 5 | zerofill_re = re.compile(r'^.zerofill\s+' + '(?P[^,]+),\s*' + '(?P
[^,]+),\s*' + '(?P[^,]+),\s*' + '(?P[^,]+),\s*' + '(?P.+)') 6 | zero_re = re.compile(r'^.zero\s+(?P\d+)') 7 | local_re = re.compile(r'^.local\s+(?P[^ ]+)$') 8 | comm_re = re.compile(r'^.comm\s+(?P[^,]+),(?P[^,]+),(?P[^,]+)') 9 | section_re = re.compile(r'^.section\s+(?P.+)$') 10 | 11 | class BuildError(Exception): pass 12 | 13 | def clean(asm): 14 | # work around directives that crash Keystone 15 | strip = ( 16 | '.macosx_version_min', 17 | '.subsections_via_symbols', 18 | '.align', 19 | '.globl', 20 | '.weak_definition', 21 | '.p2align', 22 | '.cfi', 23 | '.file', 24 | '#', 25 | ) 26 | text = [] 27 | data = [] 28 | cur = text 29 | for line in asm.split('\n'): 30 | line = line.strip() 31 | if line.startswith(strip): 32 | continue 33 | nocom = line.split('#', 1)[0] 34 | 35 | match = section_re.match(nocom) 36 | if match: 37 | section = match.group('name') 38 | if section.startswith(('.rodata', '.note', '__DATA')): 39 | cur = data 40 | elif section.startswith(('.text', '__TEXT')): 41 | cur = text 42 | else: 43 | print 'unknown section', section 44 | continue 45 | 46 | if line.startswith('.text'): 47 | cur = text 48 | continue 49 | elif line.startswith('.data'): 50 | cur = data 51 | continue 52 | 53 | match = zerofill_re.match(nocom) 54 | if match: 55 | seg, sec, sym, size, align = match.groups() 56 | size, align = int(size), int(align) 57 | cur.append('%s:' % sym) 58 | cur.append('.byte %s' % (', '.join(['0'] * size))) 59 | continue 60 | 61 | match = zero_re.match(nocom) 62 | if match: 63 | size = int(match.group('size')) 64 | cur.append('.byte %s' % (', '.join(['0'] * size))) 65 | continue 66 | 67 | match = local_re.match(nocom) 68 | if match: 69 | cur.append('%s:' % match.group('name')) 70 | continue 71 | 72 | match = comm_re.match(nocom) 73 | if match: 74 | size = int(match.group('size')) 75 | cur.append('.byte %s' % (', '.join(['0'] * size))) 76 | continue 77 | 78 | ''' 79 | if line.startswith('.') and not line.endswith(':'): 80 | if not line.startswith(('.long', '.byte')): 81 | print line 82 | ''' 83 | 84 | cur.append(line) 85 | return '\n'.join(text + data) 86 | 87 | compiler_version = None 88 | def compile(code, linker, syms=()): 89 | global compiler_version 90 | cflags = ['-mno-sse', '-Os', '-std=c99', '-fno-pic', '-ffreestanding', '-fno-stack-protector'] 91 | 92 | if compiler_version is None: 93 | compiler_version = subprocess.check_output(['gcc', '--version']) 94 | 95 | if 'gcc' in compiler_version and not 'clang' in compiler_version: 96 | cflags += ['-fleading-underscore', '-fno-toplevel-reorder'] 97 | 98 | cflags += linker.cflags 99 | code = linker.pre(code, syms=syms) 100 | p = subprocess.Popen(['gcc', '-xc', '-S', '-o-', '-'] + cflags, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 101 | asm, err = p.communicate(code) 102 | if 'error:' in err.lower(): 103 | raise BuildError(err) 104 | elif err: 105 | print err 106 | 107 | asm = linker.post(asm, syms=syms) 108 | asm = clean(asm) 109 | return asm 110 | -------------------------------------------------------------------------------- /core/context.py: -------------------------------------------------------------------------------- 1 | import capstone 2 | import binascii 3 | 4 | import arch 5 | import compiler 6 | from func import Func 7 | from util import stdlib 8 | from util.elffile import EM 9 | from util.patch.dis import irdis, IR, IRStream 10 | 11 | def pfcol(s): 12 | return '[\033[1m\033[32m%s\033[0m] ' % s 13 | 14 | class Context(object): 15 | def __init__(self, binary, verbose=False): 16 | self.binary = binary 17 | self.verbose = verbose 18 | machine = EM[binary.elf.header.machine] 19 | cflags = binary.linker.cflags 20 | if machine == EM['EM_386']: 21 | self.arch = arch.x86() 22 | cflags.append('-m32') 23 | elif machine == EM['EM_X86_64']: 24 | self.arch = arch.x86_64() 25 | cflags.append('-m64') 26 | elif machine == EM['EM_ARM']: 27 | self.arch = arch.arm() 28 | else: 29 | raise NotImplementedError("Unknown machine: %s" % machine) 30 | 31 | self.current_func = None 32 | self.func_printed = None 33 | self.marked_funcs = [] 34 | 35 | def relopen(self, name, *args, **kwargs): 36 | return open(self.binary.path + '.' + name, *args, **kwargs) 37 | 38 | @property 39 | def elf(self): 40 | return self.binary.elf 41 | 42 | @property 43 | def entry(self): 44 | return self.elf.entry 45 | 46 | @entry.setter 47 | def entry(self, val): 48 | self.info(pfcol('MOVE ENTRY POINT') + '-> 0x%x' % val) 49 | self.elf.entry = val 50 | 51 | def funcs(self, marked=False): 52 | addrs = [] 53 | self.func_printed = None 54 | try: 55 | funcs = self.relopen('funcs') 56 | except IOError: 57 | return 58 | 59 | for line in funcs: 60 | s, e = line.strip().split(' ') 61 | start, end = int(s, 16), int(e, 16) 62 | func = Func(self, start, end - start) 63 | self.current_func = func 64 | yield func 65 | self.current_func = None 66 | 67 | if marked: 68 | tmp = self.marked_funcs[:] 69 | for func in tmp: 70 | yield func 71 | 72 | def info(self, *args, **kwargs): 73 | for arg in args: 74 | for line in arg.split('\n'): 75 | indent = ' ' 76 | if self.current_func: 77 | if self.func_printed != self.current_func: 78 | if self.func_printed is not None: 79 | print 80 | func = self.current_func 81 | print indent + '[FUNC] @0x%x-0x%x' % (func.addr, func.addr + func.size) 82 | self.func_printed = self.current_func 83 | indent += ' ' 84 | if kwargs.get('prefix'): 85 | indent += kwargs['prefix'] + ' ' 86 | print indent + line 87 | 88 | dis = kwargs.get('dis', None) 89 | if dis: 90 | self.info(self.pdis(dis), prefix=kwargs.get('prefix')) 91 | 92 | # TODO: show warn/error at the end, and colorize 93 | def warn(self, *args, **kwargs): 94 | kwargs['prefix'] = '\033[1m\033[33m[WARN]\033[0m' 95 | self.info(*args, **kwargs) 96 | 97 | def error(self, *args, **kwargs): 98 | kwargs['prefix'] = '\033[1m\033[31m[ERR]\033[0m' 99 | self.info(*args, **kwargs) 100 | 101 | def debug(self, *args, **kwargs): 102 | if self.verbose: 103 | self.info(*args, **kwargs) 104 | 105 | def pdis(self, dis): 106 | if not dis: return '' 107 | if isinstance(dis, capstone.CsInsn): 108 | dis = [dis] 109 | 110 | out = [] 111 | nop_start = 0 112 | nop_bytes = '' 113 | nops = 0 114 | just = max(len(i.bytes) for i in dis) 115 | 116 | pnop = lambda: ('0x%x: %s nop (x%d)' % (nop_start, binascii.hexlify(nop_bytes).ljust(just * 2), nops)) 117 | 118 | for i in dis: 119 | if i.mnemonic == 'nop': 120 | if not nops: 121 | nop_start = i.address 122 | nop_bytes += str(i.bytes) 123 | nops += 1 124 | else: 125 | if nops: 126 | out.append(pnop()) 127 | nops = 0 128 | nop_bytes = '' 129 | data = binascii.hexlify(i.bytes).ljust(just * 2) 130 | out.append('0x%x: %s %s %s' % (i.address, data, i.mnemonic, i.op_str)) 131 | if nops: 132 | out.append(pnop()) 133 | return '\n'.join(out) 134 | 135 | # patch API below 136 | 137 | def asm(self, asm, addr=0, att_syntax=False): 138 | return self.arch.asm(asm, addr=addr, att_syntax=att_syntax) 139 | 140 | def dis(self, addr, size=64): 141 | return self.arch.dis(self.elf.read(addr, size), addr) 142 | 143 | def disiter(self, addr): 144 | # TODO: handle reading past the end 145 | dis = self.dis(addr, 128) 146 | while True: 147 | if not dis: 148 | break 149 | for ins in dis: 150 | yield ins 151 | ins = dis[-1] 152 | addr = ins.address + len(ins.bytes) 153 | dis = self.dis(addr, 128) 154 | 155 | def irdis(self, addr, size=64): 156 | return irdis(self.dis(addr, size)) 157 | 158 | def irstream(self, addr): 159 | return IRStream(self.disiter(addr)) 160 | 161 | def ir(self, asm, **kwargs): 162 | return irdis(self.arch.dis(self.asm(asm, **kwargs), addr=kwargs.get('addr', 0))) 163 | 164 | def make_writable(self, addr): 165 | for prog in self.elf.progs: 166 | if prog.isload: 167 | if addr in prog and prog.flags & 2 == 0: 168 | self.debug('[!] Segment made writable: 0x%x-0x%x' % (prog.vaddr, prog.vaddr + prog.memsz)) 169 | prog.flags |= 2 170 | 171 | def search(self, data): 172 | tmp = data 173 | if len(data) > 10: 174 | tmp = data[:8] + '..' 175 | 176 | for segment in self.binary.mem.segments: 177 | try: 178 | idx = segment.data.index(data) 179 | if idx >= 0: 180 | addr = segment.addr + idx 181 | self.debug(pfcol('SEARCH') + '"%s" found at 0x%x' % (tmp, addr)) 182 | return addr 183 | except ValueError: 184 | pass 185 | self.error(pfcol('SEARCH') + '"%s" not found.' % tmp) 186 | 187 | def hook(self, src, dst, first=False, noentry=False): 188 | # hooking the entry point is a special, more efficient case 189 | if src == self.entry and not noentry: 190 | if first: 191 | self.binary.entry_hooks.insert(0, dst) 192 | else: 193 | self.binary.entry_hooks.append(dst) 194 | self.debug(pfcol('HOOK') + 'ENTRY -> 0x%x' % dst) 195 | return 196 | self.debug(pfcol('HOOK') + '@0x%x -> 0x%x' % (src, dst)) 197 | self.make_writable(src) 198 | 199 | alloc = self.binary.next_alloc() 200 | # TODO: what if call(0) is smaller than the call to our hook? 201 | call = self.asm(self.arch.call(alloc), addr=alloc) 202 | 203 | # our injected code is guaranteed to be sequential and unaligned 204 | # so we can inject twice and call the first one 205 | evicted = '' 206 | # eh we'll just trust that a call won't be anywhere near 64 bytes 207 | ins = self.dis(src) 208 | for ins in ins: 209 | evicted += ins.bytes 210 | if len(evicted) >= len(call): 211 | break 212 | 213 | evicted = evicted.strip(self.asm(self.arch.nop())) # your loss 214 | if len(evicted) == 0 and False: 215 | self.patch(src, asm=self.arch.call(dst)) 216 | return 217 | 218 | # augh I don't like this, need to double-check how MS works 219 | # at least recursion works? 220 | # 1. replace patch-site with call to us 221 | # 2. call hook addr 222 | # 3. overwrite patch-site with saved data, then a jmp to us 223 | # 4. jmp to patch site 224 | # 5. patch site executes first few instructions, then jmps back to us 225 | # 6. re-hook the patch site (and remove the jmp) 226 | # 7. jmp to where the tmp jmp was 227 | 228 | emptyjmp = self.asm(self.arch.jmp(self.binary.next_alloc()), addr=src) 229 | jmpoff = src + len(evicted) 230 | jmpevict = str(self.elf.read(jmpoff, len(emptyjmp))) 231 | 232 | stage0 = evicted + jmpevict 233 | # TODO: self.alloc()? 234 | stage1_addr = self.binary.alloc(len(stage0), target='patch') 235 | stage2_addr = self.binary.alloc(len(stage0), target='patch') 236 | 237 | # memcpy needs to be pc-relative 238 | base = self.binary.next_alloc() 239 | hook1 = self.inject(asm=';'.join(( 240 | self.arch.call(dst), 241 | self.arch.memcpy(src - base, stage2_addr - base, len(stage0)), 242 | self.arch.jmp(src), 243 | )), internal=True) 244 | base = self.binary.next_alloc() 245 | hook2 = self.inject(asm=';'.join(( 246 | self.arch.memcpy(src - base, stage1_addr - base, len(stage0)), 247 | self.arch.jmp(jmpoff), 248 | )), internal=True) 249 | 250 | # we need to overwrite both stages because we didn't know the hook addrs at the time 251 | stage1 = self.asm(';'.join( 252 | (self.arch.jmp(hook1),) + (self.arch.nop(),) * (len(evicted) - len(emptyjmp)), 253 | ), addr=src) + jmpevict 254 | self.patch(stage1_addr, raw=stage1, is_asm=True, internal=True, desc='hook stage 1') 255 | stage2 = evicted + self.asm(self.arch.jmp(hook2), addr=jmpoff) 256 | self.patch(stage2_addr, raw=stage2, is_asm=True, internal=True, desc='hook stage 2') 257 | 258 | # TODO: act more like mobile substrate wrt orig calling? 259 | # that is, make calling orig optional 260 | self.patch(src, raw=stage1, is_asm=True, internal=True, desc='hook entry point') 261 | 262 | def _lint(self, addr, raw, typ, is_asm=False): 263 | if typ == 'asm' or is_asm: 264 | dis = self.arch.dis(raw, addr=addr) 265 | for ins in dis: 266 | if ins.bytes == 'ebfe'.decode('hex'): 267 | self.warn('JMP 0 emitted!') 268 | 269 | def _compile(self, addr, **kwargs): 270 | asm, jmp, sym, c, hex, raw = map(kwargs.get, ('asm', 'jmp', 'sym', 'c', 'hex', 'raw')) 271 | if sym is not None: 272 | jmp = self.resolve(sym) 273 | if jmp is not None: 274 | asm = self.arch.jmp(jmp) 275 | 276 | if asm is not None: 277 | raw = self.asm(asm, addr=addr) 278 | typ = 'asm' 279 | elif c is not None: 280 | raise NotImplementedError 281 | typ = 'c' 282 | elif hex is not None: 283 | raw = binascii.unhexlify(hex) 284 | typ = 'raw' 285 | elif raw is not None: 286 | typ = 'raw' 287 | else: 288 | raise Exception('inject/patch parameter missing: need one of (asm, c, hex, raw)') 289 | return raw, typ 290 | 291 | def inject(self, **kwargs): 292 | internal = kwargs.get('internal', False) 293 | is_asm = kwargs.get('is_asm', False) 294 | mark_func = kwargs.get('mark_func', False) 295 | return_size = kwargs.get('size', False) 296 | target = kwargs.get('target', 'patch') 297 | desc = kwargs.get('desc', '') 298 | if desc: 299 | desc = ' | "%s"' % desc 300 | 301 | addr = self.binary.next_alloc(target) 302 | c = kwargs.get('c') 303 | if c: 304 | asm = compiler.compile(c, self.binary.linker) 305 | raw = self.asm(asm, addr=addr, att_syntax=True) 306 | typ = 'c' 307 | is_asm = True 308 | else: 309 | raw, typ = self._compile(addr, **kwargs) 310 | 311 | self._lint(addr, raw, typ, is_asm=kwargs.get('is_asm')) 312 | if typ == 'asm': 313 | ret = self.asm(self.arch.ret()) 314 | if raw[-len(ret):] != ret and not internal: 315 | self.warn('Injected asm does not return!') 316 | 317 | self.info(pfcol('INJECT') + '@0x%x-0x%x%s' % (addr, addr + len(raw), desc)) 318 | if not kwargs.get('silent'): 319 | if typ == 'asm' or is_asm: 320 | self.debug(dis=self.arch.dis(raw, addr=addr)) 321 | else: 322 | self.debug(binascii.hexlify(raw)) 323 | 324 | addr = self.binary.alloc(len(raw), target=target) 325 | if mark_func: 326 | self.marked_funcs.append(Func(self, addr, len(raw))) 327 | self.elf.write(addr, raw) 328 | if return_size: 329 | return addr, len(raw) 330 | else: 331 | return addr 332 | 333 | def patch(self, addr, **kwargs): 334 | raw, typ = self._compile(addr, **kwargs) 335 | desc = kwargs.get('desc', '') 336 | if desc: 337 | desc = ' | "%s"' % desc 338 | 339 | self.info(pfcol('PATCH') + '@0x%x-0x%x%s' % (addr, addr + len(raw), desc)) 340 | if len(raw) == 0: 341 | self.warn('Empty patch.') 342 | return 343 | 344 | if typ == 'asm' or kwargs.get('is_asm'): 345 | size = len(''.join([str(i.bytes) for i in self.dis(addr, len(raw))])) 346 | if size != len(raw) and not kwargs.get('internal'): 347 | self.warn('Assembly patch is not aligned with underlying instructions.') 348 | 349 | self._lint(addr, raw, typ, is_asm=kwargs.get('is_asm')) 350 | if not kwargs.get('silent'): 351 | if typ == 'asm' or kwargs.get('is_asm'): 352 | # collapse nulls 353 | old = self.elf.read(addr, len(raw)) 354 | if old == '\0' * len(raw): 355 | self.debug('- %s' % ('00' * len(raw))) 356 | else: 357 | for line in self.pdis(self.dis(addr, len(raw))).split('\n'): 358 | self.debug('- %s' % line) 359 | for line in self.pdis(self.arch.dis(raw, addr=addr)).split('\n'): 360 | self.debug('+ %s' % line) 361 | else: 362 | self.debug('- %s' % binascii.hexlify(self.elf.read(addr, len(raw)))) 363 | self.debug('+ %s' % binascii.hexlify(raw)) 364 | self.elf.write(addr, raw) 365 | 366 | def resolve(self, sym): 367 | return self.binary.linker.resolve(sym) 368 | 369 | def declare(self, symbols=None, headers='', source=''): 370 | self.binary.linker.declare(symbols, headers, source) 371 | 372 | def final(self, cb): 373 | self.binary.final(cb) 374 | -------------------------------------------------------------------------------- /core/func.py: -------------------------------------------------------------------------------- 1 | class Func: 2 | def __init__(self, pt, addr, size): 3 | self.pt = pt 4 | self.addr = addr 5 | self.size = size 6 | 7 | def dis(self): 8 | return self.pt.dis(self.addr, self.size) 9 | 10 | def read(self): 11 | return self.pt.elf.read(self.addr, self.size) 12 | 13 | def nop(self): 14 | self.pt.patch(self.addr, asm=self.pt.arch.nop() * self.size) 15 | 16 | def __contains__(self, addr): 17 | if not self.size: 18 | return False 19 | return addr >= self.addr and addr < self.addr + self.size 20 | 21 | def __gt__(self, func): 22 | return self.addr <= func.addr and (not func.size or self.size > func.size) 23 | -------------------------------------------------------------------------------- /core/linker.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | 3 | import compiler 4 | import re 5 | 6 | STUB_PRE = '__attribute__((noinline,weak)) ' 7 | STUB_POST = r' { __asm__ __volatile__ (".ascii \"patchkit-skip\""); }' 8 | STUB_PRAGMA = r''' 9 | #pragma GCC diagnostic push 10 | #pragma GCC diagnostic ignored "-Wreturn-type" 11 | ''' 12 | STUB_PRAGMA_POP = r''' 13 | #pragma GCC diagnostic pop 14 | ''' 15 | 16 | func_re_1 = r'^(?P(?P[^\s].+?(?P%s)(?P\(.*?\)))\s*{(?P(.|\n)+?)^})$' 17 | 18 | class Decl: 19 | def __init__(self, syms, source, headers): 20 | self.syms = syms or {} 21 | self.source = source 22 | self._headers = headers 23 | self.cflags = [] 24 | 25 | @property 26 | def headers(self): 27 | descs = '\n'.join([desc + ';' for desc in self.syms.values()]) 28 | return '\n'.join([self._headers, descs]) 29 | 30 | def inject(self, linker, sym): 31 | # TODO: alloc pos will shift between inject() 32 | # could use a separate segment for linker. 33 | addrs = {} 34 | with linker.binary.collect() as pt: 35 | if len(self.syms) > 1: 36 | pt.info('[LINK] %s (includes [%s])' % (sym, ', '.join(self.syms.keys()))) 37 | else: 38 | pt.info('[LINK] %s' % sym) 39 | asm = compiler.compile(self.source, linker, syms=self.syms.keys()) 40 | 41 | table = '\n'.join([pt.arch.jmp('_' + sym) for sym in self.syms.keys()]) 42 | sep = 'PATCHKITJMPTABLE' 43 | asm += ('\n.ascii "%s"\n__JMPTABLE__:\n' % sep) + table 44 | addr = pt.binary.next_alloc('link') 45 | raw = pt.asm(asm, addr=addr, att_syntax=True) 46 | raw, jmps = raw.rsplit(sep, 1) 47 | for sym, ins in zip(self.syms.keys(), pt.arch.dis(jmps, addr=addr + len(sep) + len(raw))): 48 | addrs[sym] = ins.operands[0].imm 49 | 50 | pt.inject(raw=raw, is_asm=True, target='link') 51 | return addrs 52 | 53 | class Linker: 54 | def __init__(self, binary): 55 | self.binary = binary 56 | self.decls = [] 57 | self.syms = {} 58 | self.addrs = {} 59 | 60 | self.pre_hooks = [] 61 | self.post_hooks = [] 62 | 63 | def __contains__(self, sym): 64 | return sym in self.syms 65 | 66 | def onpre(self, cb): 67 | self.pre_hooks.append(cb) 68 | 69 | def onpost(self, cb): 70 | self.post_hooks.append(cb) 71 | 72 | # symbol declaration helpers 73 | def declare(self, symbols=None, source='', headers=''): 74 | decl = Decl(symbols, source, headers) 75 | self.decls.append(decl) 76 | if symbols: 77 | for sym, desc in symbols.items(): 78 | if sym in self.syms: 79 | print 'Warning: duplicate symbol (%s)' % sym 80 | self.syms[sym] = (desc, decl) 81 | 82 | @staticmethod 83 | def getfunc(src, name): 84 | match = re.search(func_re_1 % re.escape(name), src, re.MULTILINE) 85 | return match.groupdict() 86 | 87 | def declarefuncs(self, src, names): 88 | for name in names: 89 | func = self.getfunc(src, name) 90 | self.declare(symbols={name: func['desc']}, source=func['all']) 91 | 92 | def autodecl(self, src): 93 | syms = [m[2] for m in re.findall(func_re_1 % '\w+', src, re.MULTILINE)] 94 | 95 | for name in syms: 96 | func = self.getfunc(src, name) 97 | self.declare(symbols={name: func['desc']}, source=func['all']) 98 | 99 | # link-time logic 100 | def inject(self, sym): 101 | self.addrs.update(self.syms[sym][1].inject(self, sym)) 102 | 103 | def resolve(self, sym): 104 | if not sym in self.addrs: 105 | if sym in self.syms: 106 | self.inject(sym) 107 | else: 108 | raise NameError(sym) 109 | return self.addrs[sym] 110 | 111 | # TODO: need a pt context so I can print stuff 112 | # TODO: should debug the "after" code? 113 | def pre(self, code, syms=()): 114 | for cb in self.pre_hooks: 115 | tmp = cb(code, syms) 116 | if tmp: 117 | code = tmp 118 | 119 | headers = '\n'.join([decl.headers for decl in self.decls]) 120 | stubs = [] 121 | for name, (desc, _) in self.syms.items(): 122 | if name in syms: 123 | continue 124 | stubs.append(STUB_PRE + desc + STUB_POST) 125 | stubs = STUB_PRAGMA + '\n'.join(stubs) + STUB_PRAGMA_POP 126 | code = '\n'.join([headers, code, stubs]) 127 | return code 128 | # TODO: when does "source" get compiled here? 129 | # I think it'll get injected in post if a symbol is used 130 | 131 | def post(self, asm, syms=()): 132 | for cb in self.post_hooks: 133 | tmp = cb(asm, syms) 134 | if tmp: 135 | asm = tmp 136 | 137 | # strip stubs 138 | stubs = set(self.syms.keys()) - set(syms) 139 | refs = set() 140 | out = [] 141 | buf = [] 142 | skip = False 143 | valid_skip = False 144 | end_heuristic = re.compile(r'^([^.]\w+:|\s*)$') 145 | for line in asm.split('\n'): 146 | line = line.strip() 147 | if line.startswith(('.globl', '.weak_definition', '.weak', '.type', '.size')): 148 | continue 149 | if skip and (end_heuristic.match(line) or line.startswith('.cfi_endproc')): 150 | if not valid_skip: 151 | out += buf 152 | buf = [] 153 | skip = False 154 | if line.startswith(('.cfi_startproc', '.cfi_endproc')): 155 | continue 156 | for stub in stubs: 157 | if line.startswith('_%s:' % stub): 158 | refs.add(stub) 159 | skip = True 160 | break 161 | 162 | if skip and 'patchkit-skip' in line: 163 | valid_skip = True 164 | if not skip: 165 | out.append(line) 166 | else: 167 | buf.append(line) 168 | 169 | asm = '\n'.join(out) 170 | while '\n\n\n' in asm: 171 | asm = asm.replace('\n\n\n', '\n\n') 172 | # resolve referenced addresses 173 | for ref in refs: 174 | # TODO: clean asm first? 175 | find_ref = r'\b_%s\b' % (re.escape(ref)) 176 | if re.search(find_ref, asm): 177 | asm = re.sub(find_ref, '0x%x' % self.resolve(ref), asm) 178 | 179 | return asm 180 | -------------------------------------------------------------------------------- /core/patcher.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import inspect 3 | import os 4 | import sys 5 | import traceback 6 | 7 | from binary import Binary 8 | 9 | class Patcher: 10 | def __init__(self, binary, verbose=False, cflags=None, silent=False): 11 | self.bin = Binary(binary) 12 | self.bin.verbose = verbose 13 | self.bin.linker.cflags = cflags or [] 14 | self.patches = [] 15 | self.patchfiles = [] 16 | self.verbose = verbose 17 | self.cflags = cflags 18 | self.silent = silent 19 | 20 | def add(self, path): 21 | if path.endswith('.py'): 22 | self.patchfiles.append((path, os.path.basename(path))) 23 | else: 24 | base = os.path.basename(path.rstrip(os.path.sep)) 25 | for name in glob.glob(path + '/*.py'): 26 | if os.path.basename(name).startswith('_'): 27 | continue 28 | self.patchfiles.append((name, os.path.join(base, os.path.basename(name)))) 29 | 30 | def debug(self, *args): 31 | if not self.silent: 32 | print >>sys.stderr, ' '.join(map(str, args)) 33 | 34 | def patch(self): 35 | cwd = os.getcwd() 36 | try: 37 | for path, pathname in self.patchfiles: 38 | sys.path.insert(0, os.path.dirname(path)) 39 | self.debug('[*]', pathname) 40 | patchfile = os.path.basename(path).rsplit('.', 1)[0] 41 | patch = __import__(patchfile) 42 | sys.path.pop(0) 43 | 44 | # preserve function order 45 | try: 46 | source, _ = inspect.getsourcelines(patch) 47 | order = [] 48 | for line in source: 49 | if line.startswith('def'): 50 | name = line.split(' ', 1)[1].split('(', 1)[0] 51 | try: 52 | order.append(getattr(patch, name)) 53 | except AttributeError: 54 | pass 55 | except Exception: 56 | self.debug('Warning: could not preserve patch function order') 57 | self.debug(traceback.format_exc()) 58 | order = vars(patch).values() 59 | 60 | for func in order: 61 | if func.__name__.startswith('_'): 62 | # skip "private" functions 63 | continue 64 | 65 | if hasattr(func, '__call__'): 66 | self.debug(' [+] %s()' % func.__name__) 67 | with self.bin.collect() as patchset: 68 | try: 69 | func(patchset) 70 | except Exception as e: 71 | self.debug('Exception thrown by patch:', path, func.__name__) 72 | traceback.print_exc() 73 | self.debug('Memory maps:') 74 | for prog in self.bin.elf.progs: 75 | if prog.isload: 76 | self.debug('0x%x-0x%x' % (prog.vaddr, prog.vaddr + prog.vsize)) 77 | sys.exit(1) 78 | self.debug() 79 | finally: 80 | os.chdir(cwd) 81 | 82 | def save(self, path): 83 | self.bin.save(path) 84 | -------------------------------------------------------------------------------- /deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | echo 4 | echo "Take a look here if Unicorn fails to build:" 5 | echo " https://github.com/unicorn-engine/unicorn/blob/master/docs/COMPILE-NIX.md" 6 | echo 7 | echo "If you're on Ubuntu, you want to do this first:" 8 | echo " sudo apt-get update" 9 | echo " sudo apt-get install python-pip build-essential git cmake python-dev libglib2.0-dev" 10 | echo 11 | echo "If you're on a Mac, do this first:" 12 | echo " brew install pkg-config glib cmake" 13 | echo 14 | echo "Using ./build as a tmp dir. ^C if that's a bad idea." 15 | echo 16 | echo -n "[press enter to continue]" 17 | read 18 | echo 19 | 20 | cwd=$(pwd) 21 | build="$cwd/build" 22 | 23 | mkdir build &>/dev/null 24 | set -e 25 | 26 | echo "[*] Building Keystone" 27 | cd "$build" 28 | git clone https://github.com/keystone-engine/keystone.git 29 | cd keystone && mkdir build && cd build 30 | cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DLLVM_TARGETS_TO_BUILD="all" -G "Unix Makefiles" .. && make -j2 31 | echo 32 | 33 | echo "[*] Building Capstone" 34 | cd "$build" 35 | git clone https://github.com/aquynh/capstone.git 36 | cd capstone && make -j2 37 | echo 38 | 39 | echo "[*] Building Unicorn" 40 | cd "$build" 41 | git clone https://github.com/unicorn-engine/unicorn.git 42 | cd unicorn && ./make.sh 43 | 44 | echo 45 | echo "[*] Installing projects and Python bindings (using sudo)" 46 | cd "$build/keystone/build" && sudo make install 47 | cd "$build/keystone/bindings/python" && sudo make install 48 | 49 | cd "$build/capstone" && sudo make install 50 | cd "$build/capstone/bindings/python" && sudo make install 51 | 52 | cd "$build/unicorn" && sudo ./make.sh install 53 | cd "$build/unicorn/bindings/python" && sudo make install 54 | 55 | which ldconfig &>/dev/null && sudo ldconfig 56 | 57 | echo 58 | echo "All done!" 59 | echo 60 | echo -n "Testing Python import: " 61 | python -c "import capstone, keystone, unicorn; capstone.CS_ARCH_X86, unicorn.UC_ARCH_X86, keystone.KS_ARCH_X86; print 'works.'" 62 | -------------------------------------------------------------------------------- /explore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import binascii 4 | import difflib 5 | import itertools 6 | import sys 7 | 8 | from core.binary import Binary 9 | from util import cfg 10 | 11 | if __name__ == '__main__': 12 | backtrack = False 13 | if len(sys.argv) >= 2 and sys.argv[1] == '--backtrack': 14 | sys.argv.pop(1) 15 | backtrack = True 16 | 17 | if len(sys.argv) != 2: 18 | print 'Usage: %s [--backtrack] ' % sys.argv[0] 19 | sys.exit(1) 20 | 21 | bina = sys.argv[1] 22 | a = Binary(bina) 23 | a.verbose = True 24 | 25 | with a.collect() as pt: 26 | pt.debug('[-======== Exploring Branches ========-]') 27 | known = list(pt.funcs()) 28 | new_funcs, selfmod = cfg.explore(pt, known, backtrack=backtrack) 29 | pt.debug('[*] Functions') 30 | funcs = sorted(known + new_funcs, key=lambda x: x.addr) 31 | for func in funcs: 32 | pt.debug(' - 0x%x 0x%x' % (func.addr, func.addr + func.size)) 33 | 34 | pt.debug('') 35 | for func in funcs: 36 | pt.debug('[FUNC] 0x%x-0x%x' % (func.addr, func.addr + func.size)) 37 | pt.debug(dis=func.dis()) 38 | pt.debug('') 39 | 40 | with pt.relopen('funcs', 'w') as f: 41 | for func in funcs: 42 | f.write('%08x %08x\n' % (func.addr, func.addr + func.size)) 43 | -------------------------------------------------------------------------------- /ida/allfuncs.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idaapi import * 3 | import idc 4 | autoWait() 5 | start = NextFunction(SegStart(BeginEA())) 6 | filename = '%s.funcs' % get_root_filename() 7 | fp = open(filename, 'w') 8 | while start != BADADDR: 9 | end = FindFuncEnd(start) 10 | l = '%08x %08x\n' % (start,end) 11 | fp.write(l) 12 | print(l) 13 | start = NextFunction(start) 14 | idc.Exit(0) 15 | -------------------------------------------------------------------------------- /ida/funcs.idc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static main(void) { 4 | Wait(); 5 | 6 | auto bin = GetInputFile(); 7 | auto start = NextFunction(SegStart(BeginEA())); 8 | 9 | auto fd = fopen(bin + ".funcs", "w"); 10 | while (start != BADADDR) { 11 | auto end = FindFuncEnd(start); 12 | fprintf(fd, "0x%08x 0x%08x\n", start, end); 13 | start = NextFunction(start); 14 | } 15 | fclose(fd); 16 | Exit(0); 17 | } 18 | -------------------------------------------------------------------------------- /patch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | from core import Patcher 7 | from optparse import OptionParser 8 | 9 | if __name__ == '__main__': 10 | parser = OptionParser(usage='Usage: patcher [options] [patchdir...]') 11 | parser.add_option('-o', '--out', dest="out", help="output filename (default = .patched)") 12 | parser.add_option('-v', '--verbose', dest="verbose", action="store_true", help="verbose output") 13 | parser.add_option('-n', '--new', dest='new', help="create new binary from template/") 14 | parser.add_option('--cflags', dest='cflags', help="add compiler flags to injected C") 15 | 16 | options, args = parser.parse_args() 17 | 18 | if len(args) < 2: 19 | parser.print_help() 20 | sys.exit(1) 21 | 22 | args = map(os.path.abspath, args) 23 | patchdirs = args[1:] 24 | 25 | if options.new: 26 | binary = os.path.join(os.path.dirname(__file__), 'template', options.new) 27 | defout = args[0] 28 | else: 29 | binary = args[0] 30 | defout = (binary + '.patched') 31 | 32 | out = os.path.abspath(options.out or defout) 33 | 34 | patch = Patcher(binary, verbose=options.verbose, cflags=options.cflags) 35 | for d in patchdirs: 36 | patch.add(d) 37 | 38 | patch.patch() 39 | patch.save(out) 40 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | from core import Patcher 7 | from optparse import OptionParser 8 | 9 | if __name__ == '__main__': 10 | parser = OptionParser(usage='Usage: run [options] [patchdir...]') 11 | parser.add_option('-v', '--verbose', dest="verbose", action="store_true", help="verbose output") 12 | 13 | options, args = parser.parse_args() 14 | 15 | if len(args) < 2: 16 | parser.print_help() 17 | sys.exit(1) 18 | 19 | args = map(os.path.abspath, args) 20 | patchdirs = args[1:] 21 | 22 | patch = Patcher(args[0], verbose=options.verbose, silent=not options.verbose) 23 | for d in patchdirs: 24 | patch.add(d) 25 | patch.patch() 26 | -------------------------------------------------------------------------------- /samples/arm/movt_combine.py: -------------------------------------------------------------------------------- 1 | from capstone.arm_const import * 2 | 3 | def patch(pt): 4 | for func in pt.funcs(): 5 | dis = func.dis() 6 | movs = {} 7 | 8 | out = list(dis) 9 | for i, ins in enumerate(dis): 10 | # remember the last `mov dst, #imm` 11 | if ins.id in (ARM_INS_MOV, ARM_INS_MOVS, ARM_INS_MOVW): 12 | # ignore barrel shifter :( 13 | if len(ins.operands) == 3: 14 | continue 15 | dst, src = ins.operands 16 | if dst.type == ARM_OP_REG: 17 | movs[dst.reg] = (i, ins) 18 | 19 | # possibly coalesce this movt to the previous mov 20 | elif ins.id == ARM_INS_MOVT: 21 | dst, src = ins.operands 22 | if dst.type == ARM_OP_REG and src.type == ARM_OP_IMM: 23 | ppos, prev = movs.get(dst.reg, [0, None]) 24 | if not prev or ins.address == prev.address + len(prev.bytes): 25 | continue 26 | if ins.address - prev.address > 16: 27 | continue 28 | 29 | out.pop(i) 30 | out.insert(ppos + 1, ins) 31 | 32 | # pop reg's mov if another instruction might use reg as dst 33 | elif len(ins.operands) > 0: 34 | op = ins.operands[0] 35 | if op.type == ARM_OP_REG: 36 | movs.pop(op.reg, None) 37 | 38 | if out != dis: 39 | pt.patch(dis[0].address, raw=''.join(str(ins.bytes) for ins in out), is_asm=True) 40 | -------------------------------------------------------------------------------- /samples/cgc/backdoor/01_nacl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from util import read 5 | from util.crypto.rc4 import rc4 6 | from util.patch.syscall import find_syscall_funcs 7 | 8 | def backdoor(pt): 9 | c = read('backdoor/tweetnacl.c') 10 | c = c.replace('#include "tweetnacl.h"', '') 11 | h = read('backdoor/tweetnacl.h') 12 | pubkey = read('backdoor/pubkey.h') 13 | randombytes = r''' 14 | void randombytes(uint8_t *msg, uint64_t len) { 15 | syscall3(SYS_random, (uint32_t)msg, len, 0); 16 | } 17 | ''' 18 | 19 | # TODO: transmit_all / receive_all? 20 | code = r''' 21 | #define NONCE_LEN 8 22 | #define SM_LEN 0x48 23 | void handshake() { 24 | uint8_t nonce[NONCE_LEN]; 25 | syscall3(SYS_random, (uint32_t)nonce, NONCE_LEN, 0); 26 | syscall4(SYS_transmit, 1, (uint32_t)nonce, NONCE_LEN, 0); 27 | 28 | uint32_t smlen = SM_LEN; 29 | uint8_t *sm, *m; 30 | if (syscall3(SYS_allocate, smlen, 0, (uint32_t)&sm) || 31 | (syscall3(SYS_allocate, smlen, 0, (uint32_t)&m))) { 32 | _terminate(1); 33 | } 34 | syscall4(SYS_receive, 0, (uint32_t)sm, smlen, 0); 35 | 36 | uint64_t mlen; 37 | int valid = crypto_sign_open(m, &mlen, sm, smlen, pubkey); 38 | if (valid == 0 && memcmp(m, nonce, NONCE_LEN) == 0) { 39 | syscall4(SYS_transmit, 1, 0x4347c000, 4, 0); 40 | } 41 | syscall1(SYS__terminate, 2); 42 | } 43 | ''' 44 | 45 | code = h + pubkey + code + randombytes + c 46 | backdoor_addr, size = pt.inject(c=code, size=True) 47 | 48 | # rc4-encrypt the backdoor so you can't ROP directly into the type2 pov 49 | # this block also intercepts the receive() syscall function 50 | rc4_key = os.urandom(16) 51 | 52 | # as part of rc4-encrypting, we relocate the backdoor to the NX page so it doesn't add 1000+ ROP gadgets 53 | xor = rc4(rc4_key) 54 | data = pt.elf.read(backdoor_addr, size) 55 | for i in xrange(len(data)): 56 | data[i] ^= xor.next() 57 | shadow_addr = pt.inject(raw=data, target='nx', silent=True) 58 | pt.patch(backdoor_addr, raw=size * '\x00', silent=True) 59 | 60 | 61 | # xor key so they can't just pull it out of memory 62 | key_otp = os.urandom(len(rc4_key)) 63 | key = ''.join([chr(ord(c) ^ ord(key_otp[i])) for i, c in enumerate(rc4_key)]) 64 | str2c = lambda x: ', '.join(map(str, map(ord, x))) 65 | 66 | call_backdoor = r''' 67 | void call_backdoor() { 68 | void (*backdoor)() = (void (*)())%d; 69 | char *shadow_addr = (char *)%d; 70 | size_t bd_size = %d; 71 | memcpy(backdoor, shadow_addr, bd_size); 72 | 73 | uint8_t state[256]; 74 | uint8_t rc4_key[] = {%s}; 75 | uint8_t rc4_otp[] = {%s}; 76 | int keylen = %d; 77 | for (int i = 0; i < keylen; i++) { 78 | rc4_key[i] ^= rc4_otp[i]; 79 | } 80 | ksa(state, rc4_key, keylen); 81 | rc4(state, (uint8_t *)backdoor, bd_size); 82 | 83 | backdoor(); 84 | // always exit after backdoor so there's not a decrypted type 2 POV in memory you can ROP into 85 | _terminate(0); 86 | } 87 | ''' % (backdoor_addr, shadow_addr, size, str2c(key), str2c(key_otp), len(key)) 88 | 89 | receive_hook_head = r''' 90 | void call_backdoor(); 91 | void check_init(char *buf, uint32_t size); 92 | int _receive(int fd, void *buf, uint32_t size, uint32_t *count); 93 | #define WINDOW 9 94 | char bufsave[WINDOW] = {1}; 95 | uint32_t first = 1, saved = 1, pos = 1; 96 | int receive(int fd, void *_buf, uint32_t size, uint32_t *count) { 97 | char *buf = (char *)_buf; 98 | // shortcut if we've run and buffer is empty, or on fd > 0 99 | if (fd != 0 || saved == 0) { 100 | return _receive(fd, buf, size, count); 101 | } else if (first) { 102 | first = 0; 103 | if (size < WINDOW) { 104 | int ret = _receive(fd, bufsave, WINDOW, &saved); 105 | if (ret) return ret; 106 | check_init(bufsave, saved); 107 | pos = 0; 108 | } else { 109 | int ret = _receive(fd, buf, size, &saved); 110 | if (ret) return ret; 111 | if (count) *count = saved; 112 | check_init(buf, saved); 113 | saved = 0; 114 | return ret; 115 | } 116 | } 117 | // flush buffer 118 | if (saved > 0 && saved < size) { 119 | memcpy(buf, bufsave + pos, saved); 120 | if (count) *count = saved; 121 | uint32_t tmp; 122 | int ret = _receive(fd, buf + saved, size - saved, &tmp); 123 | saved = 0; 124 | if (ret) return ret; 125 | if (count) *count += tmp; 126 | return ret; 127 | } else if (saved >= size) { 128 | memcpy(buf, bufsave + pos, size); 129 | if (count) *count = size; 130 | saved -= size; 131 | pos += size; 132 | } 133 | return 0; 134 | }''' 135 | receive_hook_tail = r''' 136 | void check_init(char *buf, uint32_t size) { 137 | if (size < 5) return; 138 | char hash[4]; 139 | for (int i = 0; i < 4; i++) hash[i] = buf[i]; 140 | char *key = "ECAF"; 141 | for (int i = 4; i < size; i++) { 142 | for (int j = 0; j < 4; j++) { 143 | hash[j] ^= buf[i]; 144 | } 145 | if (*(uint32_t *)hash == *(uint32_t *)key) { 146 | size = i + 1; 147 | // respond with inverted key so POV can seek ahead to handshake 148 | for (int j = 0; j < size; j++) { 149 | buf[j] ^= 0xff; 150 | } 151 | transmit(1, buf, size, 0); 152 | call_backdoor(); 153 | } 154 | } 155 | } 156 | ''' + call_backdoor 157 | 158 | def patch_receive(code, syms): 159 | if syms == ['receive']: 160 | out = [] 161 | # TODO: this is really gross text parsing 162 | out.append(receive_hook_head) 163 | for line in code.split('\n'): 164 | if line.startswith('int receive(') and line.endswith('{'): 165 | out.append(line.replace('int receive(', 'int _receive(', 1)) 166 | else: 167 | out.append(line) 168 | out.append(receive_hook_tail) 169 | return '\n'.join(out) 170 | 171 | pt.binary.linker.onpre(patch_receive) 172 | -------------------------------------------------------------------------------- /samples/cgc/create/heap.py: -------------------------------------------------------------------------------- 1 | def patch(pt): 2 | pt.entry = pt.inject(c=r''' 3 | void main() { 4 | void *test = dlmalloc(5); 5 | memcpy(test, "hi\n", 3); 6 | transmit(1, test, 3, 0); 7 | _terminate(0); 8 | } 9 | ''') 10 | -------------------------------------------------------------------------------- /samples/cgc/create/hello.py: -------------------------------------------------------------------------------- 1 | def create(pt): 2 | pt.entry = pt.inject(c=r''' 3 | void _start() { 4 | transmit(1, "hello\n", 6, 0); 5 | _terminate(0); 6 | } 7 | ''') 8 | -------------------------------------------------------------------------------- /samples/cgc/create/knock.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from util import read 4 | 5 | def create(pt): 6 | c = read('backdoor/tweetnacl.c') 7 | c = c.replace('#include "tweetnacl.h"', '') 8 | h = read('backdoor/tweetnacl.h') 9 | privkey = read('backdoor/privkey.h') 10 | randombytes = r''' 11 | void randombytes(uint8_t *msg, uint64_t len) { 12 | random(msg, (uint32_t)len, 0); 13 | } 14 | ''' 15 | 16 | code = r''' 17 | void _start() { 18 | uint32_t negotiate = 2; 19 | transmit(3, &negotiate, 4, 0); 20 | for (int i = 0; i < 3; i++) { 21 | receive(3, &negotiate, 4, 0); 22 | } 23 | 24 | // generate handshake key 25 | char key[9] = {'E', 'C', 'A', 'F'}; 26 | // random length from 5-8 27 | uint8_t b = 0; 28 | random(&b, 1, 0); 29 | int length = 5 + (b % 4); 30 | 31 | for (int i = 4; i < length; i++) { 32 | uint8_t b = 0; 33 | while (!b) random(&b, 1, 0); 34 | key[i] = b; 35 | for (int j = 0; j < 4; j++) { 36 | key[j] ^= b; 37 | } 38 | } 39 | transmit(1, key, length, 0); 40 | 41 | // wait for key response 42 | for (int i = 0; i < length; i++) { 43 | key[i] ^= 0xff; 44 | } 45 | char ring[8] = {0}; 46 | int p = 0; 47 | while (1) { 48 | uint32_t count = 0; 49 | if (receive(0, ring + p, 1, &count) || count == 0) _terminate(1); 50 | int match = 0; 51 | for (int i = 0; i < length; i++) { 52 | int off = (p - length + i + 1) % 8; 53 | if (off < 0) off += 8; 54 | if (ring[off] != key[i]) break; 55 | match++; 56 | } 57 | if (match == length) break; 58 | 59 | p = (p + 1) % 8; 60 | } 61 | 62 | uint8_t nonce[8]; 63 | if (receive(0, nonce, 8, 0)) _terminate(1); 64 | 65 | uint8_t *sm; 66 | uint64_t smlen = 8 + crypto_sign_BYTES; 67 | if (allocate(smlen, 0, &sm)) _terminate(1); 68 | crypto_sign(sm, &smlen, nonce, 8, privkey); 69 | 70 | size_t size = smlen; 71 | transmit(1, &size, 4, 0); 72 | transmit(1, sm, size, 0); 73 | 74 | char flag[4]; 75 | receive(0, flag, 4, 0); 76 | transmit(3, flag, 4, 0); 77 | 78 | _terminate(0); 79 | } 80 | ''' 81 | 82 | code = h + privkey + code + randombytes + c 83 | pt.entry = pt.inject(c=code) 84 | -------------------------------------------------------------------------------- /samples/cgc/obfuscate/01_jit_xor.py: -------------------------------------------------------------------------------- 1 | # TODO: block receive() from working on program segments (because they'll be writable) 2 | 3 | # encrypt all functions found in program body in-place 4 | # replace the start of each encrypted function with a `jmp xor_tramp`, 5 | # xor_tramp calls xor_func with the right arguments, then jumps to the original function address 6 | 7 | import os 8 | 9 | # what if each function is xor'd with its low address byte and a random one-byte key? it's still pretty annoying but way faster to "decrypt" 10 | xor_key = ord(os.urandom(1)) 11 | 12 | def patch(pt): 13 | xor_func = r''' 14 | void xor_func(unsigned char *addr, unsigned long size, unsigned char *restore, unsigned long restore_size) { 15 | unsigned char xor_key = %d ^ ((unsigned long)addr & 0xff); 16 | for (unsigned int i = 0; i < restore_size; i++) { 17 | addr[i] = restore[i]; 18 | } 19 | for (unsigned int i = 0; i < size; i++) { 20 | addr[i] ^= xor_key; 21 | } 22 | } 23 | ''' % xor_key 24 | xor_func_addr = pt.inject(c=xor_func) 25 | 26 | for func in pt.funcs(): 27 | jmp_size = len(pt.arch.asm(pt.arch.jmp(pt.binary.next_alloc()), addr=func.addr)) 28 | if func.size < jmp_size: 29 | pt.warning('Skipping, function too small.') 30 | continue 31 | 32 | pt.info('Encoding function.') 33 | pt.make_writable(func.addr) 34 | data = func.read() 35 | local_key = xor_key ^ (func.addr & 0xff) 36 | for i in xrange(len(data)): 37 | data[i] ^= local_key 38 | 39 | save_data = data[:jmp_size] 40 | save_addr = pt.inject(raw=save_data) 41 | 42 | xor_tramp = r''' 43 | push ebp 44 | mov ebp, esp 45 | push %(save_size)d 46 | push %(save)d 47 | push %(size)d 48 | push %(addr)d 49 | call %(xor_func)d 50 | leave 51 | jmp %(addr)d 52 | ''' % { 53 | 'addr': func.addr, 54 | 'size': func.size, 55 | 'save': save_addr, 56 | 'save_size': len(save_data), 57 | 'xor_func': xor_func_addr, 58 | } 59 | xor_tramp_addr = pt.inject(asm=xor_tramp, internal=True) 60 | pt.patch(func.addr, jmp=xor_tramp_addr) 61 | pt.patch(func.addr + jmp_size, raw=data[jmp_size:]) 62 | -------------------------------------------------------------------------------- /samples/cgc/obfuscate/02_rc4.py: -------------------------------------------------------------------------------- 1 | import os 2 | from util.crypto.rc4 import rc4, rc4_decrypt_template 3 | 4 | rc4_key = os.urandom(16) 5 | 6 | def rc4_encrypt(pt): 7 | xor = rc4(rc4_key) 8 | 9 | seg = pt.binary.patch 10 | data = pt.elf.read(seg.vaddr, seg.memsz) 11 | for i in xrange(len(data)): 12 | data[i] ^= xor.next() 13 | pt.patch(seg.vaddr, raw=data) 14 | 15 | def inject_rc4_decrypt(pt): 16 | seg = pt.binary.patch 17 | if seg.memsz == 0: 18 | pt.error('Skipping inject: patch segment is empty.') 19 | return 20 | 21 | pt.make_writable(seg.vaddr) 22 | code = rc4_decrypt_template(rc4_key, seg.vaddr, seg.memsz) 23 | addr = pt.inject(c=code) 24 | pt.hook(pt.entry, addr, first=True) 25 | -------------------------------------------------------------------------------- /samples/cgc/obfuscate/03_xor_patches.py: -------------------------------------------------------------------------------- 1 | import os 2 | from util.crypto.xor import xor_mem 3 | 4 | def xor_patches(pt): 5 | seg = pt.binary.patch 6 | xor_mem(pt, seg.vaddr, seg.memsz, ord(os.urandom(1))) 7 | -------------------------------------------------------------------------------- /samples/cgc/obfuscate/04_xor_prog.py: -------------------------------------------------------------------------------- 1 | import os 2 | from util.crypto.xor import xor_mem 3 | 4 | def xor_patches(pt): 5 | for prog in pt.elf.progs: 6 | if prog.isload and prog.offset == 0: 7 | xor_mem(pt, prog.vaddr, prog.memsz, ord(os.urandom(1))) 8 | break 9 | -------------------------------------------------------------------------------- /samples/cgc/obfuscate/05_xor_entry.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | def _final(pt): 4 | secret = 0x4347c000 5 | addr = pt.binary.next_alloc() 6 | raw = pt.arch.asm(r''' 7 | call label 8 | add eax, 11 9 | xor [eax], ecx 10 | jmp label2 11 | label: mov eax, [esp] 12 | ret 13 | label2: xor eax, eax 14 | jmp 0x%x 15 | ''' % pt.entry, addr=addr) 16 | pt.info('[*] Breaking entry point.') 17 | pt.debug(dis=pt.arch.dis(raw, addr=addr)) 18 | off = len(raw) - 4 19 | ruin = struct.pack('entries[choice]; 45 | uint8_t *target = entry->jmp; 46 | memcpy(reloc->addr, target, 5); 47 | // nop out the other function 48 | for (int j = 0; j < %(choices)d; j++) { 49 | if (j != choice) { 50 | reloc_entry *entry = &relocs[i].entries[j]; 51 | memset(entry->addr, 0x0f, entry->size); 52 | } 53 | } 54 | } 55 | // TODO: these might not actually help anything and just be slow 56 | // memset(choices, 0, len); 57 | // memset(relocs, 0, sizeof(relocs)); 58 | } 59 | ''' % {'funcs': len(funcs), 'choices': COUNT, 'relocs': relocs})) 60 | -------------------------------------------------------------------------------- /samples/x86/fuzzing/01_cmp_split.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from capstone.x86_const import * 3 | 4 | def _decompose(ins, jmps): 5 | invert = { 6 | 'jne': 'je', 7 | 'je': 'jne', 8 | } 9 | a, b = ins.operands 10 | name = ins.reg_name(a.mem.base) 11 | asm = [] 12 | # TODO: this assumes dword 13 | # This might not generate instrumentable branches for qemu-user. I haven't tested if these properly count as separate basic blocks in QEMU. 14 | for i, byte in enumerate(struct.pack(' 0xff: 33 | cmps.append((ins, [])) 34 | cur = cmps[-1] 35 | elif cur is not None: 36 | if X86_REG_EFLAGS in ins.regs_write: 37 | cur = None 38 | elif ins.id in (X86_INS_JE, X86_INS_JNE): 39 | cur[-1].append(ins) 40 | 41 | for cmp, jmps in cmps: 42 | if len(jmps) != 1: 43 | continue 44 | pt.debug('[*] Splitting CMP') 45 | pt.debug(dis=[cmp] + jmps) 46 | addr = pt.inject(asm=_decompose(cmp, jmps)) 47 | 48 | call = pt.asm(pt.arch.call(addr), addr=cmp.address) 49 | call += pt.asm(pt.arch.nop() * (len(cmp.bytes) - len(call))) 50 | pt.patch(cmp.address, raw=call, is_asm=True) 51 | -------------------------------------------------------------------------------- /samples/x86/harden/02_ropshift.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | from collections import OrderedDict 3 | import random 4 | 5 | def patch(pt): 6 | for func in pt.funcs(): 7 | dis = func.dis() 8 | pops = OrderedDict() 9 | first = True 10 | for ins in reversed(dis): 11 | if ins.id in (X86_INS_RET, X86_INS_LEAVE): 12 | continue 13 | elif ins.id == X86_INS_POP: 14 | reg = ins.operands[0].reg 15 | # ignore a leading ebp pop so we don't screw up the frame restore 16 | if not (first and reg == X86_REG_EBP): 17 | pops[reg] = ins 18 | 19 | first = False 20 | else: 21 | break 22 | 23 | if len(pops) == 1: 24 | ins = pops.values()[0] 25 | name = ins.reg_name(ins.operands[0].reg) 26 | pt.warn('Only one POP, function not hardened (reg %s)' % (name)) 27 | elif len(pops) > 1: 28 | # TODO: bail on pc-relative loads? probably not a problem for CGC as it's not PIE 29 | pt.info('[*] Hardening (%d) pops.' % (len(pops))) 30 | remain = set(pops.keys()) 31 | pushes = OrderedDict() 32 | extra = [] 33 | for ins in dis: 34 | if ins.id == X86_INS_PUSH: 35 | reg = ins.operands[0].reg 36 | if reg in remain: 37 | remain.remove(reg) 38 | pushes[reg] = ins 39 | continue 40 | if not remain: 41 | break 42 | extra.append(ins) 43 | 44 | regs = list(pops.keys()) 45 | new = list(regs) 46 | while new[-1] == regs[-1]: 47 | random.shuffle(new) 48 | 49 | head = [pushes[reg] for reg in new] + extra 50 | head_addr = min(ins.address for ins in head) 51 | head_data = ''.join(str(ins.bytes) for ins in head) 52 | 53 | tail = [pops[reg] for reg in reversed(new)] 54 | tail_addr = min(ins.address for ins in tail) 55 | tail_data = ''.join(str(ins.bytes) for ins in tail) 56 | 57 | pt.patch(head_addr, raw=head_data, is_asm=True) 58 | pt.patch(tail_addr, raw=tail_data, is_asm=True) 59 | -------------------------------------------------------------------------------- /samples/x86/harden/03_spadjust.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | import random 3 | 4 | def patch(pt): 5 | def is_sp_arith(ins): 6 | dst, src = ins.operands 7 | return dst.type == X86_OP_REG and src.type == X86_OP_IMM and dst.reg in (X86_REG_RSP, X86_REG_ESP) 8 | 9 | for func in pt.funcs(): 10 | # initial adjustment, may be reduced so instruction size stays unchanged 11 | # remains unchanged 12 | adj = random.randrange(4, 128, 4) 13 | # total amount adjusted 14 | total = 0 15 | dis = func.dis() 16 | 17 | # sanity: check function fingerprint (1+ SUBs, ends with either a single ADD or a LEAVE) 18 | sub_total = 0 19 | add_total = 0 20 | 21 | subs = 0 22 | adds = 0 23 | leave = False 24 | good = True 25 | for ins in dis: 26 | if ins.id == X86_INS_SUB and is_sp_arith(ins): 27 | subs += 1 28 | sub_total += ins.operands[1].imm 29 | if adds: 30 | good = False 31 | break 32 | 33 | if ins.id == X86_INS_ADD and is_sp_arith(ins): 34 | adds += 1 35 | add_total += ins.operands[1].imm 36 | 37 | if ins.id == X86_INS_LEAVE: 38 | leave = True 39 | 40 | if not (good and subs and (adds == 1 or leave) and sub_total == add_total): 41 | if subs: 42 | pt.warn('Fingerprint failed.') 43 | continue 44 | 45 | for ins in dis: 46 | if ins.id == X86_INS_SUB: 47 | dst, src = ins.operands 48 | if dst.type == X86_OP_REG and src.type == X86_OP_IMM: 49 | if dst.reg in (X86_REG_RSP, X86_REG_ESP): 50 | # ensure our adjustment fits inside current instr 51 | tmp = adj 52 | while tmp > 0: 53 | asm = 'sub %s, 0x%x' % (ins.reg_name(dst.reg), src.imm+tmp) 54 | patch = pt.asm(asm, ins.address) 55 | if len(patch) > ins.size: 56 | tmp -= 4 57 | else: 58 | break 59 | if tmp < 0: 60 | pt.warn('[0x%x] failed to adjust' % ins.address) 61 | continue 62 | 63 | total += tmp 64 | patched_sub = True 65 | pt.patch(ins.address, asm=asm) 66 | 67 | if patched_sub: 68 | for ins in dis: 69 | # use previously adjusted size to readjust 70 | if ins.id == X86_INS_ADD and total > 0: 71 | dst, src = ins.operands 72 | if dst.type == X86_OP_REG and src.type == X86_OP_IMM: 73 | if dst.reg in (X86_REG_RSP, X86_REG_ESP): 74 | asm = 'add %s, 0x%x' % (ins.reg_name(dst.reg), src.imm+total) 75 | pt.patch(ins.address, asm=asm) 76 | -------------------------------------------------------------------------------- /samples/x86/harden/05_io_filter.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | 3 | # TODO: there's no pt context in here 4 | def _c_pre(code, syms): 5 | out = [] 6 | if syms == ['transmit']: 7 | # TODO: this is really gross text parsing 8 | for line in code.split('\n'): 9 | out.append(line) 10 | if line.startswith('int transmit(') and line.endswith('{'): 11 | out.append(r''' 12 | if (((uint32_t)buf < 0x4347c000 && (uint32_t)buf + size > 0x4347c000) || 13 | ((uint32_t)buf >= 0x4347c000 && (uint32_t)buf < 0x4347c000 + 0x1000)) { 14 | transmit(1, "Try a type 3.\n", 14, 0); 15 | _terminate(0); 16 | } 17 | ''') 18 | return '\n'.join(out) 19 | elif syms == ['receive']: 20 | return 21 | # TODO: receive hook can't currently happen at a good time 22 | # it needs to happen last, and the page list needs to be injected into NX page? 23 | 24 | def queue(pt): 25 | pt.binary.linker.onpre(_c_pre) 26 | -------------------------------------------------------------------------------- /samples/x86/harden/06_stack_cookies.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | 4 | from util.patch.dis import IR, Ins, Imm, Reg, Mem 5 | 6 | op_reg = re.compile(r'^(?P\w+) (?P\w+), (?P0x[0-9a-f]+|\d+)') 7 | 8 | def _int(val): 9 | if val.startswith('0x'): 10 | return int(val, 16) 11 | return int(val) 12 | 13 | def _search(ir, ins, skip=(), reverse=False): 14 | if reverse: 15 | space = reversed(list(enumerate(ir))) 16 | else: 17 | space = enumerate(ir) 18 | for i, cmp in space: 19 | if ins in skip: 20 | continue 21 | if ins == cmp: 22 | return i, cmp 23 | raise ValueError('%s not found in IR' % ins) 24 | 25 | def _scan(ir, window): 26 | for i in xrange(len(ir)): 27 | if ir[i:i+len(window)] == window: 28 | return i 29 | return None 30 | 31 | def _asm(pt, ir): 32 | stack_chk_fail = pt.resolve('stack_chk_fail') 33 | if len(ir) >= 4: 34 | # head_match = [Ins('push', Reg('ebp')), Ins('mov', Reg('ebp'), Reg('esp'))] 35 | head_match = pt.ir('push ebp; mov ebp, esp') 36 | tail_match = pt.ir('pop ebp; ret') 37 | tail_pos = _scan(ir, tail_match) 38 | if ir[:2] == head_match and tail_pos is not None: 39 | try: 40 | subi, sub = _search(ir[2:], Ins('sub', Reg('esp'), Imm(any=True)), skip=[Ins('push', any=True)]) 41 | subi += 2 42 | addi, add = _search(ir[:tail_pos], Ins('add', Reg('esp'), Imm(any=True)), skip=[Ins('pop', any=True)], reverse=True) 43 | except ValueError: 44 | return 45 | if sub.ops[1] > add.ops[1]: 46 | return 47 | 48 | for ins in ir: 49 | if not isinstance(ins, Ins): 50 | continue 51 | for op in ins.ops: 52 | if isinstance(op, Mem) and op.base == 'ebp' and op.off < 0: 53 | op.off -= 4 54 | 55 | before = ir[:subi] 56 | mid = ir[subi:addi] 57 | after = ir[addi:] 58 | addr = pt.inject(raw="ABCD", target='nx') 59 | 60 | pt.hook(pt.entry, pt.inject(c=r""" 61 | void gen() { 62 | random((void*) %d, 4, 0); 63 | } 64 | """ % addr)) 65 | 66 | # TODO: runtime dynamic cookies 67 | cookie = random.randint(1, 0xffffffff) 68 | ir = before + pt.ir(''' 69 | mov eax, dword ptr [%d] 70 | mov [ebp - 4], eax 71 | ''' % addr) + mid + pt.ir(''' 72 | mov ebp, [ebp - 4] 73 | cmp ebp, dword ptr [%d] 74 | jne %d 75 | ''' % (addr, stack_chk_fail)) + after 76 | 77 | return ir 78 | 79 | def patch(pt): 80 | pt.warn('This patch depends on the ASLR patch.') 81 | pt.binary.onasm(_asm) 82 | -------------------------------------------------------------------------------- /samples/x86/hello/hello32.py: -------------------------------------------------------------------------------- 1 | def patch(pt): 2 | hello, size = pt.inject(raw='hello world\n', size=True) 3 | 4 | addr = pt.inject(asm=r''' 5 | push eax 6 | push ebx 7 | push ecx 8 | push edx 9 | 10 | mov eax, 4 # SYS_write 11 | mov ebx, 1 # fd 12 | mov ecx, %d # buf 13 | mov edx, %d # size 14 | int 0x80 15 | 16 | pop edx 17 | pop ecx 18 | pop ebx 19 | pop eax 20 | ret 21 | ''' % (hello, size)) 22 | pt.hook(pt.entry, addr) 23 | -------------------------------------------------------------------------------- /samples/x86/hello/hello64.py: -------------------------------------------------------------------------------- 1 | def patch(pt): 2 | hello, size = pt.inject(raw='hello world\n', size=True) 3 | 4 | addr = pt.inject(asm=r''' 5 | push rax 6 | push rdi 7 | push rsi 8 | push rdx 9 | 10 | mov rax, 1 # SYS_write 11 | mov rdi, 1 # fd 12 | mov rsi, %d # buf 13 | mov rdx, %d # size 14 | syscall 15 | 16 | pop rdx 17 | pop rsi 18 | pop rdi 19 | pop rax 20 | ret 21 | ''' % (hello, size)) 22 | pt.hook(pt.entry, addr) 23 | -------------------------------------------------------------------------------- /samples/x86/optimize/01_coalesce.py: -------------------------------------------------------------------------------- 1 | # combine useless adjacent instructions 2 | # like: 3 | # sub esp, 8 4 | # sub esp, 8 5 | # into: 6 | # sub esp, 16 7 | 8 | from util.patch.dis import Label, Ins, Imm, Reg, Mem 9 | 10 | def _optimize(pt, ir): 11 | i = 0 12 | add = Ins('add', Reg(any=True), Imm(any=True)) 13 | sub = Ins('sub', Reg(any=True), Imm(any=True)) 14 | total = 0 15 | while i < len(ir) - 1: 16 | ins, nins = ir[i:i+2] 17 | if (ins, nins) == (add, add) or (ins, nins) == (sub, sub): 18 | if ins.ops[0] == nins.ops[0]: 19 | pt.debug('Combining: %s <- %s' % (ins, nins)) 20 | total += 1 21 | ins.ops[1].val += nins.ops[1].val 22 | ir.pop(i + 1) 23 | continue 24 | i += 1 25 | if total: 26 | pt.info('Combined %d instructions.' % total) 27 | return ir 28 | 29 | def defer(pt): 30 | pt.binary.onasm(_optimize) 31 | -------------------------------------------------------------------------------- /samples/x86/optimize/02_useless_stash.py: -------------------------------------------------------------------------------- 1 | # remove useless register stashes 2 | # like: 3 | # call _printf 4 | # mov [ebp - 8], eax 5 | # call _printf 6 | # mov [ebp - 12], eax 7 | # ret 8 | ## [ebp - 8] and [ebp - 12] are never used. 9 | ## NOTE: If a lower offset is ever loaded with LEA, then all offsets above must be considered tainted (in case of arrays/structs) 10 | ## NOTE 2: If the EBP offset is used twice by any instruction in the function, we consider it tainted. 11 | 12 | from collections import defaultdict 13 | 14 | from util.patch.dis import LabelOp, Label, Ins, Imm, Reg, Mem 15 | 16 | def _optimize(pt, ir): 17 | lea = Ins('lea', Reg(any=True), Mem(any=True)) 18 | 19 | counts = defaultdict(int) 20 | # record used ebp offsets 21 | for ins in ir: 22 | for i, op in enumerate(ins.ops): 23 | if isinstance(op, Mem) and op.base == 'ebp' and op.off < 0: 24 | # we mix in i so loads will always disqualify an ebp offset 25 | # also align the offset here and when considering, so byte/word overlap isn't a problem 26 | counts[op.off & ~3] += 1 + i 27 | 28 | taint = 0 29 | # LEA blocks any greater EBP offsets 30 | for i, ins in enumerate(ir): 31 | if ins == lea and ins.src.base == 'ebp' and ins.src.off < 0: 32 | taint = min(taint, ins.src.off) 33 | 34 | stash = Ins('mov', Mem(any=True), Reg(any=True)) 35 | i = 0 36 | total = 0 37 | while i < len(ir): 38 | ins = ir[i] 39 | if ins == stash and ins.dst.base == 'ebp': 40 | off = ins.dst.off 41 | if off < taint and counts[off & ~3] <= 1: 42 | pt.debug('Removing %d: %s' % (i, ins)) 43 | total += 1 44 | ir.pop(i) 45 | continue 46 | i += 1 47 | 48 | if total: 49 | pt.info('Removed %d stashes.' % total) 50 | 51 | def defer(pt): 52 | pt.binary.onasm(_optimize) 53 | -------------------------------------------------------------------------------- /samples/x86/optimize/03_remove_ebp.py: -------------------------------------------------------------------------------- 1 | # optimize EBP out of a function 2 | # 1. fingerprint if a function has the standard header/footer 3 | # 2. decide if a function otherwise looks "safe" 4 | # 3. remove all references to EBP 5 | # 4. calculate stack offsets at every instruction and replace any ebp lookups with the stack offset 6 | 7 | from util.patch.dis import IR, Label, Ins, Mem, Reg, Imm 8 | 9 | def _scan(ir, window): 10 | for i in xrange(len(ir)): 11 | if ir[i:i+len(window)] == window: 12 | return i 13 | return None 14 | 15 | def _optimize(pt, ir): 16 | # TODO: considering functions with internal jumps too scary for now (because I don't have a helper to follow control flow yet) 17 | for ins in ir: 18 | if isinstance(ins, Label): 19 | return 20 | 21 | add_esp = Ins('add', Reg('esp'), Imm(any=True)) 22 | sub_esp = Ins('sub', Reg('esp'), Imm(any=True)) 23 | 24 | # make sure ESP is only directly used for ADD (esp, imm); SUB; MOV reg, esp 25 | for ins in ir: 26 | if ins == add_esp or ins == sub_esp: 27 | continue 28 | if ins == Ins('mov', Reg(any=True), Reg('esp')): 29 | continue 30 | for op in ins.ops: 31 | if op == 'esp': 32 | return 33 | 34 | # TODO: put "standard header/footer" fingerprint helper in util.patch? 35 | head_match = pt.ir('push ebp; mov ebp, esp') 36 | tail_match = pt.ir('pop ebp; ret') 37 | tail_pos = _scan(ir, tail_match) 38 | if ir[:2] != head_match or tail_pos is None: 39 | return 40 | 41 | # make sure EBP is never modified outside the header/footer 42 | for i, ins in enumerate(ir): 43 | for op in ins.ops: 44 | if op == 'ebp' and i != tail_pos and i > 2: 45 | pt.debug('COWARDLY ABORTING') 46 | pt.debug(ins) 47 | return 48 | 49 | push = Ins('push', any=True) 50 | pop = Ins('pop', any=True) 51 | 52 | pt.info('Removing EBP.') 53 | ir.pop(tail_pos) 54 | ir = ir[2:] 55 | esp = 4 56 | for ins in ir: 57 | if ins == add_esp: 58 | esp += ins.src.val 59 | elif ins == sub_esp: 60 | esp -= ins.src.val 61 | elif ins == push: 62 | esp -= ins.ins.operands[0].size 63 | elif ins == pop: 64 | esp += ins.ins.operands[0].size 65 | else: 66 | for op in ins.ops: 67 | if isinstance(op, Mem) and op.base == 'ebp': 68 | op.base = 'esp' 69 | op.off -= esp 70 | return ir 71 | 72 | def defer(pt): 73 | pt.binary.onasm(_optimize) 74 | -------------------------------------------------------------------------------- /util/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def read(name): 4 | path = os.path.join(os.path.dirname(__file__), name) 5 | return open(path, 'rb').read() 6 | -------------------------------------------------------------------------------- /util/autolink.py: -------------------------------------------------------------------------------- 1 | from util import stdlib 2 | from util import heap 3 | from util import crypto 4 | 5 | __all__ = ['declare'] 6 | 7 | def declare(linker): 8 | stdlib.declare(linker) 9 | heap.declare(linker) 10 | crypto.declare(linker) 11 | -------------------------------------------------------------------------------- /util/backdoor/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: keygen sign 2 | .DEFAULT_GOAL := all 3 | 4 | keygen: 5 | gcc -std=c99 -o keygen keygen.c posixrand.c tweetnacl.c 6 | 7 | sign: 8 | gcc -std=c99 -o sign sign.c posixrand.c tweetnacl.c 9 | 10 | all: keygen sign 11 | -------------------------------------------------------------------------------- /util/backdoor/backdoor_poc.py: -------------------------------------------------------------------------------- 1 | import random 2 | import subprocess 3 | from pwn import * 4 | p = process(sys.argv[1:]) 5 | 6 | key = list('ECAF') 7 | for i in xrange(random.randint(1, 4)): 8 | c = os.urandom(1) 9 | key.append(c) 10 | for j in xrange(4): 11 | key[j] = chr(ord(key[j]) ^ ord(c)) 12 | key = ''.join(key) 13 | print 'sending key', repr(key) 14 | p.send(key) 15 | 16 | # wait for key response 17 | ref = ''.join([chr(ord(c) ^ 0xff) for c in key]) 18 | buf = '' 19 | while True: 20 | buf += p.recv(1) 21 | if buf[-len(ref):] == ref: 22 | print 'got key response' 23 | break 24 | 25 | nonce = p.recv(8).encode('hex') 26 | sign = subprocess.Popen(['util/backdoor/sign', nonce], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 27 | sig = sign.communicate()[0].strip().decode('hex') 28 | 29 | p.send(p32(len(sig))) 30 | p.send(sig) 31 | flag = p.recv(4) 32 | print 'flag', repr(flag) 33 | -------------------------------------------------------------------------------- /util/backdoor/keygen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/patchkit/95dc699b7f95fcca9a190cf1be6cb3ae20e09c4f/util/backdoor/keygen -------------------------------------------------------------------------------- /util/backdoor/keygen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "tweetnacl.h" 4 | 5 | void _cdump(uint8_t *msg, uint64_t len) { 6 | printf("{"); 7 | for (int i = 0; i < len; i++) { 8 | printf("%d", msg[i]); 9 | if (i < len - 1) printf(", "); 10 | } 11 | printf("};\n"); 12 | } 13 | #define cdump(c) _cdump(c, sizeof(c)) 14 | 15 | int main() { 16 | uint8_t pk[crypto_sign_PUBLICKEYBYTES]; 17 | uint8_t sk[crypto_sign_SECRETKEYBYTES]; 18 | 19 | crypto_sign_keypair(pk, sk); 20 | 21 | printf("uint8_t pubkey[] = "); 22 | cdump(pk); 23 | printf("uint8_t privkey[] = "); 24 | cdump(sk); 25 | } 26 | -------------------------------------------------------------------------------- /util/backdoor/posixrand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void randombytes(uint8_t *buf, uint64_t len) { 5 | FILE *f = fopen("/dev/urandom", "r"); 6 | fread(buf, len, 1, f); 7 | fclose(f); 8 | } 9 | -------------------------------------------------------------------------------- /util/backdoor/privkey.h: -------------------------------------------------------------------------------- 1 | uint8_t pubkey[] = {61, 161, 123, 5, 84, 244, 54, 178, 195, 255, 20, 91, 115, 248, 188, 167, 242, 137, 49, 137, 92, 57, 202, 164, 45, 211, 20, 234, 221, 27, 113, 165}; 2 | uint8_t privkey[] = {185, 232, 226, 146, 176, 155, 226, 196, 254, 53, 239, 4, 208, 206, 230, 21, 54, 38, 137, 1, 223, 165, 141, 36, 123, 76, 199, 104, 32, 219, 175, 201, 61, 161, 123, 5, 84, 244, 54, 178, 195, 255, 20, 91, 115, 248, 188, 167, 242, 137, 49, 137, 92, 57, 202, 164, 45, 211, 20, 234, 221, 27, 113, 165}; 3 | -------------------------------------------------------------------------------- /util/backdoor/pubkey.h: -------------------------------------------------------------------------------- 1 | uint8_t pubkey[] = {61, 161, 123, 5, 84, 244, 54, 178, 195, 255, 20, 91, 115, 248, 188, 167, 242, 137, 49, 137, 92, 57, 202, 164, 45, 211, 20, 234, 221, 27, 113, 165}; 2 | -------------------------------------------------------------------------------- /util/backdoor/sign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/patchkit/95dc699b7f95fcca9a190cf1be6cb3ae20e09c4f/util/backdoor/sign -------------------------------------------------------------------------------- /util/backdoor/sign.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tweetnacl.h" 7 | #include "privkey.h" 8 | 9 | extern void randombytes(uint8_t *buf, uint64_t len); 10 | #define MLEN 8 11 | 12 | int verify(uint8_t *sm, uint64_t smlen) { 13 | uint8_t *m = malloc(smlen); 14 | uint64_t mlen; 15 | int ret = crypto_sign_open(m, &mlen, sm, smlen, pubkey); 16 | free(m); 17 | return ret; 18 | } 19 | 20 | void sign(uint8_t *msg, uint64_t mlen, uint8_t **sm, uint64_t *smlen) { 21 | *sm = malloc(mlen + crypto_sign_BYTES); 22 | crypto_sign(*sm, smlen, msg, mlen, privkey); 23 | } 24 | 25 | int main(int argc, char **argv) { 26 | uint8_t *sm; 27 | uint64_t smlen; 28 | if (argc == 2) { 29 | char *hex = argv[1]; 30 | int hexlen = strlen(hex); 31 | if (hexlen % 2) { 32 | printf("odd hex?\n"); 33 | return 1; 34 | } 35 | uint64_t mlen = hexlen / 2; 36 | uint8_t *msg = malloc(mlen); 37 | 38 | for (uint64_t i = 0; i < mlen; i++) { 39 | sscanf(hex, "%2hhx", &msg[i]); 40 | hex += 2; 41 | } 42 | sign(msg, mlen, &sm, &smlen); 43 | 44 | for (int i = 0; i < smlen; i++) { 45 | printf("%02x", sm[i]); 46 | } 47 | printf("\n"); 48 | } else { 49 | uint8_t msg[MLEN]; 50 | randombytes(msg, MLEN); 51 | 52 | sign(msg, MLEN, &sm, &smlen); 53 | 54 | printf("msg = "); 55 | for (int i = 0; i < MLEN; i++) { 56 | printf("%02x", msg[i]); 57 | } 58 | printf("\n"); 59 | 60 | printf("sig = "); 61 | for (int i = 0; i < smlen; i++) { 62 | printf("%02x", sm[i]); 63 | } 64 | printf("\n"); 65 | 66 | printf("verify = %d\n", verify(sm, smlen)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /util/backdoor/tweetnacl.c: -------------------------------------------------------------------------------- 1 | #include "tweetnacl.h" 2 | #define FOR(i,n) for (i = 0;i < n;++i) 3 | #define sv static void 4 | 5 | typedef unsigned char u8; 6 | typedef unsigned long u32; 7 | typedef unsigned long long u64; 8 | typedef long long i64; 9 | typedef i64 gf[16]; 10 | extern void randombytes(u8 *,u64); 11 | 12 | static const u8 13 | _0[16], 14 | _9[32] = {9}; 15 | static const gf 16 | gf0, 17 | gf1 = {1}, 18 | _121665 = {0xDB41,1}, 19 | D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, 20 | D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, 21 | X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, 22 | Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}, 23 | I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; 24 | 25 | static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); } 26 | 27 | static u32 ld32(const u8 *x) 28 | { 29 | u32 u = x[3]; 30 | u = (u<<8)|x[2]; 31 | u = (u<<8)|x[1]; 32 | return (u<<8)|x[0]; 33 | } 34 | 35 | static u64 dl64(const u8 *x) 36 | { 37 | u64 i,u=0; 38 | FOR(i,8) u=(u<<8)|x[i]; 39 | return u; 40 | } 41 | 42 | sv st32(u8 *x,u32 u) 43 | { 44 | int i; 45 | FOR(i,4) { x[i] = u; u >>= 8; } 46 | } 47 | 48 | sv ts64(u8 *x,u64 u) 49 | { 50 | int i; 51 | for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; } 52 | } 53 | 54 | static int vn(const u8 *x,const u8 *y,int n) 55 | { 56 | u32 i,d = 0; 57 | FOR(i,n) d |= x[i]^y[i]; 58 | return (1 & ((d - 1) >> 8)) - 1; 59 | } 60 | 61 | int crypto_verify_16(const u8 *x,const u8 *y) 62 | { 63 | return vn(x,y,16); 64 | } 65 | 66 | int crypto_verify_32(const u8 *x,const u8 *y) 67 | { 68 | return vn(x,y,32); 69 | } 70 | 71 | sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h) 72 | { 73 | u32 w[16],x[16],y[16],t[4]; 74 | int i,j,m; 75 | 76 | FOR(i,4) { 77 | x[5*i] = ld32(c+4*i); 78 | x[1+i] = ld32(k+4*i); 79 | x[6+i] = ld32(in+4*i); 80 | x[11+i] = ld32(k+16+4*i); 81 | } 82 | 83 | FOR(i,16) y[i] = x[i]; 84 | 85 | FOR(i,20) { 86 | FOR(j,4) { 87 | FOR(m,4) t[m] = x[(5*j+4*m)%16]; 88 | t[1] ^= L32(t[0]+t[3], 7); 89 | t[2] ^= L32(t[1]+t[0], 9); 90 | t[3] ^= L32(t[2]+t[1],13); 91 | t[0] ^= L32(t[3]+t[2],18); 92 | FOR(m,4) w[4*j+(j+m)%4] = t[m]; 93 | } 94 | FOR(m,16) x[m] = w[m]; 95 | } 96 | 97 | if (h) { 98 | FOR(i,16) x[i] += y[i]; 99 | FOR(i,4) { 100 | x[5*i] -= ld32(c+4*i); 101 | x[6+i] -= ld32(in+4*i); 102 | } 103 | FOR(i,4) { 104 | st32(out+4*i,x[5*i]); 105 | st32(out+16+4*i,x[6+i]); 106 | } 107 | } else 108 | FOR(i,16) st32(out + 4 * i,x[i] + y[i]); 109 | } 110 | 111 | int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) 112 | { 113 | core(out,in,k,c,0); 114 | return 0; 115 | } 116 | 117 | int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) 118 | { 119 | core(out,in,k,c,1); 120 | return 0; 121 | } 122 | 123 | static const u8 sigma[16] = "expand 32-byte k"; 124 | 125 | int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k) 126 | { 127 | u8 z[16],x[64]; 128 | u32 u,i; 129 | if (!b) return 0; 130 | FOR(i,16) z[i] = 0; 131 | FOR(i,8) z[i] = n[i]; 132 | while (b >= 64) { 133 | crypto_core_salsa20(x,z,k,sigma); 134 | FOR(i,64) c[i] = (m?m[i]:0) ^ x[i]; 135 | u = 1; 136 | for (i = 8;i < 16;++i) { 137 | u += (u32) z[i]; 138 | z[i] = u; 139 | u >>= 8; 140 | } 141 | b -= 64; 142 | c += 64; 143 | if (m) m += 64; 144 | } 145 | if (b) { 146 | crypto_core_salsa20(x,z,k,sigma); 147 | FOR(i,b) c[i] = (m?m[i]:0) ^ x[i]; 148 | } 149 | return 0; 150 | } 151 | 152 | int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k) 153 | { 154 | return crypto_stream_salsa20_xor(c,0,d,n,k); 155 | } 156 | 157 | int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k) 158 | { 159 | u8 s[32]; 160 | crypto_core_hsalsa20(s,n,k,sigma); 161 | return crypto_stream_salsa20(c,d,n+16,s); 162 | } 163 | 164 | int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) 165 | { 166 | u8 s[32]; 167 | crypto_core_hsalsa20(s,n,k,sigma); 168 | return crypto_stream_salsa20_xor(c,m,d,n+16,s); 169 | } 170 | 171 | sv add1305(u32 *h,const u32 *c) 172 | { 173 | u32 j,u = 0; 174 | FOR(j,17) { 175 | u += h[j] + c[j]; 176 | h[j] = u & 255; 177 | u >>= 8; 178 | } 179 | } 180 | 181 | static const u32 minusp[17] = { 182 | 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 183 | } ; 184 | 185 | int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k) 186 | { 187 | u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17]; 188 | 189 | FOR(j,17) r[j]=h[j]=0; 190 | FOR(j,16) r[j]=k[j]; 191 | r[3]&=15; 192 | r[4]&=252; 193 | r[7]&=15; 194 | r[8]&=252; 195 | r[11]&=15; 196 | r[12]&=252; 197 | r[15]&=15; 198 | 199 | while (n > 0) { 200 | FOR(j,17) c[j] = 0; 201 | for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j]; 202 | c[j] = 1; 203 | m += j; n -= j; 204 | add1305(h,c); 205 | FOR(i,17) { 206 | x[i] = 0; 207 | FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]); 208 | } 209 | FOR(i,17) h[i] = x[i]; 210 | u = 0; 211 | FOR(j,16) { 212 | u += h[j]; 213 | h[j] = u & 255; 214 | u >>= 8; 215 | } 216 | u += h[16]; h[16] = u & 3; 217 | u = 5 * (u >> 2); 218 | FOR(j,16) { 219 | u += h[j]; 220 | h[j] = u & 255; 221 | u >>= 8; 222 | } 223 | u += h[16]; h[16] = u; 224 | } 225 | 226 | FOR(j,17) g[j] = h[j]; 227 | add1305(h,minusp); 228 | s = -(h[16] >> 7); 229 | FOR(j,17) h[j] ^= s & (g[j] ^ h[j]); 230 | 231 | FOR(j,16) c[j] = k[j + 16]; 232 | c[16] = 0; 233 | add1305(h,c); 234 | FOR(j,16) out[j] = h[j]; 235 | return 0; 236 | } 237 | 238 | int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k) 239 | { 240 | u8 x[16]; 241 | crypto_onetimeauth(x,m,n,k); 242 | return crypto_verify_16(h,x); 243 | } 244 | 245 | int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) 246 | { 247 | int i; 248 | if (d < 32) return -1; 249 | crypto_stream_xor(c,m,d,n,k); 250 | crypto_onetimeauth(c + 16,c + 32,d - 32,c); 251 | FOR(i,16) c[i] = 0; 252 | return 0; 253 | } 254 | 255 | int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) 256 | { 257 | int i; 258 | u8 x[32]; 259 | if (d < 32) return -1; 260 | crypto_stream(x,32,n,k); 261 | if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1; 262 | crypto_stream_xor(m,c,d,n,k); 263 | FOR(i,32) m[i] = 0; 264 | return 0; 265 | } 266 | 267 | sv set25519(gf r, const gf a) 268 | { 269 | int i; 270 | FOR(i,16) r[i]=a[i]; 271 | } 272 | 273 | sv car25519(gf o) 274 | { 275 | int i; 276 | i64 c; 277 | FOR(i,16) { 278 | o[i]+=(1LL<<16); 279 | c=o[i]>>16; 280 | o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); 281 | o[i]-=c<<16; 282 | } 283 | } 284 | 285 | sv sel25519(gf p,gf q,int b) 286 | { 287 | i64 t,i,c=~(b-1); 288 | FOR(i,16) { 289 | t= c&(p[i]^q[i]); 290 | p[i]^=t; 291 | q[i]^=t; 292 | } 293 | } 294 | 295 | sv pack25519(u8 *o,const gf n) 296 | { 297 | int i,j,b; 298 | gf m,t; 299 | FOR(i,16) t[i]=n[i]; 300 | car25519(t); 301 | car25519(t); 302 | car25519(t); 303 | FOR(j,2) { 304 | m[0]=t[0]-0xffed; 305 | for(i=1;i<15;i++) { 306 | m[i]=t[i]-0xffff-((m[i-1]>>16)&1); 307 | m[i-1]&=0xffff; 308 | } 309 | m[15]=t[15]-0x7fff-((m[14]>>16)&1); 310 | b=(m[15]>>16)&1; 311 | m[14]&=0xffff; 312 | sel25519(t,m,1-b); 313 | } 314 | FOR(i,16) { 315 | o[2*i]=t[i]&0xff; 316 | o[2*i+1]=t[i]>>8; 317 | } 318 | } 319 | 320 | static int neq25519(const gf a, const gf b) 321 | { 322 | u8 c[32],d[32]; 323 | pack25519(c,a); 324 | pack25519(d,b); 325 | return crypto_verify_32(c,d); 326 | } 327 | 328 | static u8 par25519(const gf a) 329 | { 330 | u8 d[32]; 331 | pack25519(d,a); 332 | return d[0]&1; 333 | } 334 | 335 | sv unpack25519(gf o, const u8 *n) 336 | { 337 | int i; 338 | FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); 339 | o[15]&=0x7fff; 340 | } 341 | 342 | sv A(gf o,const gf a,const gf b) 343 | { 344 | int i; 345 | FOR(i,16) o[i]=a[i]+b[i]; 346 | } 347 | 348 | sv Z(gf o,const gf a,const gf b) 349 | { 350 | int i; 351 | FOR(i,16) o[i]=a[i]-b[i]; 352 | } 353 | 354 | sv M(gf o,const gf a,const gf b) 355 | { 356 | i64 i,j,t[31]; 357 | FOR(i,31) t[i]=0; 358 | FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; 359 | FOR(i,15) t[i]+=38*t[i+16]; 360 | FOR(i,16) o[i]=t[i]; 361 | car25519(o); 362 | car25519(o); 363 | } 364 | 365 | sv S(gf o,const gf a) 366 | { 367 | M(o,a,a); 368 | } 369 | 370 | sv inv25519(gf o,const gf i) 371 | { 372 | gf c; 373 | int a; 374 | FOR(a,16) c[a]=i[a]; 375 | for(a=253;a>=0;a--) { 376 | S(c,c); 377 | if(a!=2&&a!=4) M(c,c,i); 378 | } 379 | FOR(a,16) o[a]=c[a]; 380 | } 381 | 382 | sv pow2523(gf o,const gf i) 383 | { 384 | gf c; 385 | int a; 386 | FOR(a,16) c[a]=i[a]; 387 | for(a=250;a>=0;a--) { 388 | S(c,c); 389 | if(a!=1) M(c,c,i); 390 | } 391 | FOR(a,16) o[a]=c[a]; 392 | } 393 | 394 | int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p) 395 | { 396 | u8 z[32]; 397 | i64 x[80],r,i; 398 | gf a,b,c,d,e,f; 399 | FOR(i,31) z[i]=n[i]; 400 | z[31]=(n[31]&127)|64; 401 | z[0]&=248; 402 | unpack25519(x,p); 403 | FOR(i,16) { 404 | b[i]=x[i]; 405 | d[i]=a[i]=c[i]=0; 406 | } 407 | a[0]=d[0]=1; 408 | for(i=254;i>=0;--i) { 409 | r=(z[i>>3]>>(i&7))&1; 410 | sel25519(a,b,r); 411 | sel25519(c,d,r); 412 | A(e,a,c); 413 | Z(a,a,c); 414 | A(c,b,d); 415 | Z(b,b,d); 416 | S(d,e); 417 | S(f,a); 418 | M(a,c,a); 419 | M(c,b,e); 420 | A(e,a,c); 421 | Z(a,a,c); 422 | S(b,a); 423 | Z(c,d,f); 424 | M(a,c,_121665); 425 | A(a,a,d); 426 | M(c,c,a); 427 | M(a,d,f); 428 | M(d,b,x); 429 | S(b,e); 430 | sel25519(a,b,r); 431 | sel25519(c,d,r); 432 | } 433 | FOR(i,16) { 434 | x[i+16]=a[i]; 435 | x[i+32]=c[i]; 436 | x[i+48]=b[i]; 437 | x[i+64]=d[i]; 438 | } 439 | inv25519(x+32,x+32); 440 | M(x+16,x+16,x+32); 441 | pack25519(q,x+16); 442 | return 0; 443 | } 444 | 445 | int crypto_scalarmult_base(u8 *q,const u8 *n) 446 | { 447 | return crypto_scalarmult(q,n,_9); 448 | } 449 | 450 | int crypto_box_keypair(u8 *y,u8 *x) 451 | { 452 | randombytes(x,32); 453 | return crypto_scalarmult_base(y,x); 454 | } 455 | 456 | int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x) 457 | { 458 | u8 s[32]; 459 | crypto_scalarmult(s,x,y); 460 | return crypto_core_hsalsa20(k,_0,s,sigma); 461 | } 462 | 463 | int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) 464 | { 465 | return crypto_secretbox(c,m,d,n,k); 466 | } 467 | 468 | int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) 469 | { 470 | return crypto_secretbox_open(m,c,d,n,k); 471 | } 472 | 473 | int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x) 474 | { 475 | u8 k[32]; 476 | crypto_box_beforenm(k,y,x); 477 | return crypto_box_afternm(c,m,d,n,k); 478 | } 479 | 480 | int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x) 481 | { 482 | u8 k[32]; 483 | crypto_box_beforenm(k,y,x); 484 | return crypto_box_open_afternm(m,c,d,n,k); 485 | } 486 | 487 | static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); } 488 | static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); } 489 | static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); } 490 | static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); } 491 | static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); } 492 | static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); } 493 | static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); } 494 | 495 | static const u64 K[80] = 496 | { 497 | 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 498 | 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 499 | 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 500 | 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 501 | 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 502 | 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 503 | 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 504 | 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 505 | 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 506 | 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 507 | 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 508 | 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 509 | 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 510 | 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 511 | 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 512 | 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 513 | 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 514 | 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 515 | 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 516 | 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL 517 | }; 518 | 519 | int crypto_hashblocks(u8 *x,const u8 *m,u64 n) 520 | { 521 | u64 z[8],b[8],a[8],w[16],t; 522 | int i,j; 523 | 524 | FOR(i,8) z[i] = a[i] = dl64(x + 8 * i); 525 | 526 | while (n >= 128) { 527 | FOR(i,16) w[i] = dl64(m + 8 * i); 528 | 529 | FOR(i,80) { 530 | FOR(j,8) b[j] = a[j]; 531 | t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; 532 | b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); 533 | b[3] += t; 534 | FOR(j,8) a[(j+1)%8] = b[j]; 535 | if (i%16 == 15) 536 | FOR(j,16) 537 | w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); 538 | } 539 | 540 | FOR(i,8) { a[i] += z[i]; z[i] = a[i]; } 541 | 542 | m += 128; 543 | n -= 128; 544 | } 545 | 546 | FOR(i,8) ts64(x+8*i,z[i]); 547 | 548 | return n; 549 | } 550 | 551 | static const u8 iv[64] = { 552 | 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, 553 | 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, 554 | 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, 555 | 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, 556 | 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, 557 | 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, 558 | 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, 559 | 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 560 | } ; 561 | 562 | int crypto_hash(u8 *out,const u8 *m,u64 n) 563 | { 564 | u8 h[64],x[256]; 565 | u64 i,b = n; 566 | 567 | FOR(i,64) h[i] = iv[i]; 568 | 569 | crypto_hashblocks(h,m,n); 570 | m += n; 571 | n &= 127; 572 | m -= n; 573 | 574 | FOR(i,256) x[i] = 0; 575 | FOR(i,n) x[i] = m[i]; 576 | x[n] = 128; 577 | 578 | n = 256-128*(n<112); 579 | x[n-9] = b >> 61; 580 | ts64(x+n-8,b<<3); 581 | crypto_hashblocks(h,x,n); 582 | 583 | FOR(i,64) out[i] = h[i]; 584 | 585 | return 0; 586 | } 587 | 588 | sv add(gf p[4],gf q[4]) 589 | { 590 | gf a,b,c,d,t,e,f,g,h; 591 | 592 | Z(a, p[1], p[0]); 593 | Z(t, q[1], q[0]); 594 | M(a, a, t); 595 | A(b, p[0], p[1]); 596 | A(t, q[0], q[1]); 597 | M(b, b, t); 598 | M(c, p[3], q[3]); 599 | M(c, c, D2); 600 | M(d, p[2], q[2]); 601 | A(d, d, d); 602 | Z(e, b, a); 603 | Z(f, d, c); 604 | A(g, d, c); 605 | A(h, b, a); 606 | 607 | M(p[0], e, f); 608 | M(p[1], h, g); 609 | M(p[2], g, f); 610 | M(p[3], e, h); 611 | } 612 | 613 | sv cswap(gf p[4],gf q[4],u8 b) 614 | { 615 | int i; 616 | FOR(i,4) 617 | sel25519(p[i],q[i],b); 618 | } 619 | 620 | sv pack(u8 *r,gf p[4]) 621 | { 622 | gf tx, ty, zi; 623 | inv25519(zi, p[2]); 624 | M(tx, p[0], zi); 625 | M(ty, p[1], zi); 626 | pack25519(r, ty); 627 | r[31] ^= par25519(tx) << 7; 628 | } 629 | 630 | sv scalarmult(gf p[4],gf q[4],const u8 *s) 631 | { 632 | int i; 633 | set25519(p[0],gf0); 634 | set25519(p[1],gf1); 635 | set25519(p[2],gf1); 636 | set25519(p[3],gf0); 637 | for (i = 255;i >= 0;--i) { 638 | u8 b = (s[i/8]>>(i&7))&1; 639 | cswap(p,q,b); 640 | add(q,p); 641 | add(p,p); 642 | cswap(p,q,b); 643 | } 644 | } 645 | 646 | sv scalarbase(gf p[4],const u8 *s) 647 | { 648 | gf q[4]; 649 | set25519(q[0],X); 650 | set25519(q[1],Y); 651 | set25519(q[2],gf1); 652 | M(q[3],X,Y); 653 | scalarmult(p,q,s); 654 | } 655 | 656 | int crypto_sign_keypair(u8 *pk, u8 *sk) 657 | { 658 | u8 d[64]; 659 | gf p[4]; 660 | int i; 661 | 662 | randombytes(sk, 32); 663 | crypto_hash(d, sk, 32); 664 | d[0] &= 248; 665 | d[31] &= 127; 666 | d[31] |= 64; 667 | 668 | scalarbase(p,d); 669 | pack(pk,p); 670 | 671 | FOR(i,32) sk[32 + i] = pk[i]; 672 | return 0; 673 | } 674 | 675 | static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; 676 | 677 | sv modL(u8 *r,i64 x[64]) 678 | { 679 | i64 carry,i,j; 680 | for (i = 63;i >= 32;--i) { 681 | carry = 0; 682 | for (j = i - 32;j < i - 12;++j) { 683 | x[j] += carry - 16 * x[i] * L[j - (i - 32)]; 684 | carry = (x[j] + 128) >> 8; 685 | x[j] -= carry << 8; 686 | } 687 | x[j] += carry; 688 | x[i] = 0; 689 | } 690 | carry = 0; 691 | FOR(j,32) { 692 | x[j] += carry - (x[31] >> 4) * L[j]; 693 | carry = x[j] >> 8; 694 | x[j] &= 255; 695 | } 696 | FOR(j,32) x[j] -= carry * L[j]; 697 | FOR(i,32) { 698 | x[i+1] += x[i] >> 8; 699 | r[i] = x[i] & 255; 700 | } 701 | } 702 | 703 | sv reduce(u8 *r) 704 | { 705 | i64 x[64],i; 706 | FOR(i,64) x[i] = (u64) r[i]; 707 | FOR(i,64) r[i] = 0; 708 | modL(r,x); 709 | } 710 | 711 | int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk) 712 | { 713 | u8 d[64],h[64],r[64]; 714 | i64 i,j,x[64]; 715 | gf p[4]; 716 | 717 | crypto_hash(d, sk, 32); 718 | d[0] &= 248; 719 | d[31] &= 127; 720 | d[31] |= 64; 721 | 722 | *smlen = n+64; 723 | FOR(i,n) sm[64 + i] = m[i]; 724 | FOR(i,32) sm[32 + i] = d[32 + i]; 725 | 726 | crypto_hash(r, sm+32, n+32); 727 | reduce(r); 728 | scalarbase(p,r); 729 | pack(sm,p); 730 | 731 | FOR(i,32) sm[i+32] = sk[i+32]; 732 | crypto_hash(h,sm,n + 64); 733 | reduce(h); 734 | 735 | FOR(i,64) x[i] = 0; 736 | FOR(i,32) x[i] = (u64) r[i]; 737 | FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; 738 | modL(sm + 32,x); 739 | 740 | return 0; 741 | } 742 | 743 | static int unpackneg(gf r[4],const u8 p[32]) 744 | { 745 | gf t, chk, num, den, den2, den4, den6; 746 | set25519(r[2],gf1); 747 | unpack25519(r[1],p); 748 | S(num,r[1]); 749 | M(den,num,D); 750 | Z(num,num,r[2]); 751 | A(den,r[2],den); 752 | 753 | S(den2,den); 754 | S(den4,den2); 755 | M(den6,den4,den2); 756 | M(t,den6,num); 757 | M(t,t,den); 758 | 759 | pow2523(t,t); 760 | M(t,t,num); 761 | M(t,t,den); 762 | M(t,t,den); 763 | M(r[0],t,den); 764 | 765 | S(chk,r[0]); 766 | M(chk,chk,den); 767 | if (neq25519(chk, num)) M(r[0],r[0],I); 768 | 769 | S(chk,r[0]); 770 | M(chk,chk,den); 771 | if (neq25519(chk, num)) return -1; 772 | 773 | if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); 774 | 775 | M(r[3],r[0],r[1]); 776 | return 0; 777 | } 778 | 779 | int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk) 780 | { 781 | int i; 782 | u8 t[32],h[64]; 783 | gf p[4],q[4]; 784 | 785 | *mlen = -1; 786 | if (n < 64) return -1; 787 | 788 | if (unpackneg(q,pk)) return -1; 789 | 790 | FOR(i,n) m[i] = sm[i]; 791 | FOR(i,32) m[i+32] = pk[i]; 792 | crypto_hash(h,m,n); 793 | reduce(h); 794 | scalarmult(p,q,h); 795 | 796 | scalarbase(q,sm + 32); 797 | add(p,q); 798 | pack(t,p); 799 | 800 | n -= 64; 801 | if (crypto_verify_32(sm, t)) { 802 | FOR(i,n) m[i] = 0; 803 | return -1; 804 | } 805 | 806 | FOR(i,n) m[i] = sm[i + 64]; 807 | *mlen = n; 808 | return 0; 809 | } 810 | -------------------------------------------------------------------------------- /util/backdoor/tweetnacl.h: -------------------------------------------------------------------------------- 1 | #ifndef TWEETNACL_H 2 | #define TWEETNACL_H 3 | #define crypto_auth_PRIMITIVE "hmacsha512256" 4 | #define crypto_auth crypto_auth_hmacsha512256 5 | #define crypto_auth_verify crypto_auth_hmacsha512256_verify 6 | #define crypto_auth_BYTES crypto_auth_hmacsha512256_BYTES 7 | #define crypto_auth_KEYBYTES crypto_auth_hmacsha512256_KEYBYTES 8 | #define crypto_auth_IMPLEMENTATION crypto_auth_hmacsha512256_IMPLEMENTATION 9 | #define crypto_auth_VERSION crypto_auth_hmacsha512256_VERSION 10 | #define crypto_auth_hmacsha512256_tweet_BYTES 32 11 | #define crypto_auth_hmacsha512256_tweet_KEYBYTES 32 12 | extern int crypto_auth_hmacsha512256_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); 13 | extern int crypto_auth_hmacsha512256_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); 14 | #define crypto_auth_hmacsha512256_tweet_VERSION "-" 15 | #define crypto_auth_hmacsha512256 crypto_auth_hmacsha512256_tweet 16 | #define crypto_auth_hmacsha512256_verify crypto_auth_hmacsha512256_tweet_verify 17 | #define crypto_auth_hmacsha512256_BYTES crypto_auth_hmacsha512256_tweet_BYTES 18 | #define crypto_auth_hmacsha512256_KEYBYTES crypto_auth_hmacsha512256_tweet_KEYBYTES 19 | #define crypto_auth_hmacsha512256_VERSION crypto_auth_hmacsha512256_tweet_VERSION 20 | #define crypto_auth_hmacsha512256_IMPLEMENTATION "crypto_auth/hmacsha512256/tweet" 21 | #define crypto_box_PRIMITIVE "curve25519xsalsa20poly1305" 22 | #define crypto_box crypto_box_curve25519xsalsa20poly1305 23 | #define crypto_box_open crypto_box_curve25519xsalsa20poly1305_open 24 | #define crypto_box_keypair crypto_box_curve25519xsalsa20poly1305_keypair 25 | #define crypto_box_beforenm crypto_box_curve25519xsalsa20poly1305_beforenm 26 | #define crypto_box_afternm crypto_box_curve25519xsalsa20poly1305_afternm 27 | #define crypto_box_open_afternm crypto_box_curve25519xsalsa20poly1305_open_afternm 28 | #define crypto_box_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES 29 | #define crypto_box_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES 30 | #define crypto_box_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES 31 | #define crypto_box_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_NONCEBYTES 32 | #define crypto_box_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_ZEROBYTES 33 | #define crypto_box_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES 34 | #define crypto_box_IMPLEMENTATION crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION 35 | #define crypto_box_VERSION crypto_box_curve25519xsalsa20poly1305_VERSION 36 | #define crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 32 37 | #define crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 32 38 | #define crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 32 39 | #define crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 24 40 | #define crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 32 41 | #define crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 16 42 | extern int crypto_box_curve25519xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); 43 | extern int crypto_box_curve25519xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); 44 | extern int crypto_box_curve25519xsalsa20poly1305_tweet_keypair(unsigned char *,unsigned char *); 45 | extern int crypto_box_curve25519xsalsa20poly1305_tweet_beforenm(unsigned char *,const unsigned char *,const unsigned char *); 46 | extern int crypto_box_curve25519xsalsa20poly1305_tweet_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 47 | extern int crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 48 | #define crypto_box_curve25519xsalsa20poly1305_tweet_VERSION "-" 49 | #define crypto_box_curve25519xsalsa20poly1305 crypto_box_curve25519xsalsa20poly1305_tweet 50 | #define crypto_box_curve25519xsalsa20poly1305_open crypto_box_curve25519xsalsa20poly1305_tweet_open 51 | #define crypto_box_curve25519xsalsa20poly1305_keypair crypto_box_curve25519xsalsa20poly1305_tweet_keypair 52 | #define crypto_box_curve25519xsalsa20poly1305_beforenm crypto_box_curve25519xsalsa20poly1305_tweet_beforenm 53 | #define crypto_box_curve25519xsalsa20poly1305_afternm crypto_box_curve25519xsalsa20poly1305_tweet_afternm 54 | #define crypto_box_curve25519xsalsa20poly1305_open_afternm crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm 55 | #define crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 56 | #define crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 57 | #define crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 58 | #define crypto_box_curve25519xsalsa20poly1305_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 59 | #define crypto_box_curve25519xsalsa20poly1305_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 60 | #define crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 61 | #define crypto_box_curve25519xsalsa20poly1305_VERSION crypto_box_curve25519xsalsa20poly1305_tweet_VERSION 62 | #define crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION "crypto_box/curve25519xsalsa20poly1305/tweet" 63 | #define crypto_core_PRIMITIVE "salsa20" 64 | #define crypto_core crypto_core_salsa20 65 | #define crypto_core_OUTPUTBYTES crypto_core_salsa20_OUTPUTBYTES 66 | #define crypto_core_INPUTBYTES crypto_core_salsa20_INPUTBYTES 67 | #define crypto_core_KEYBYTES crypto_core_salsa20_KEYBYTES 68 | #define crypto_core_CONSTBYTES crypto_core_salsa20_CONSTBYTES 69 | #define crypto_core_IMPLEMENTATION crypto_core_salsa20_IMPLEMENTATION 70 | #define crypto_core_VERSION crypto_core_salsa20_VERSION 71 | #define crypto_core_salsa20_tweet_OUTPUTBYTES 64 72 | #define crypto_core_salsa20_tweet_INPUTBYTES 16 73 | #define crypto_core_salsa20_tweet_KEYBYTES 32 74 | #define crypto_core_salsa20_tweet_CONSTBYTES 16 75 | extern int crypto_core_salsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); 76 | #define crypto_core_salsa20_tweet_VERSION "-" 77 | #define crypto_core_salsa20 crypto_core_salsa20_tweet 78 | #define crypto_core_salsa20_OUTPUTBYTES crypto_core_salsa20_tweet_OUTPUTBYTES 79 | #define crypto_core_salsa20_INPUTBYTES crypto_core_salsa20_tweet_INPUTBYTES 80 | #define crypto_core_salsa20_KEYBYTES crypto_core_salsa20_tweet_KEYBYTES 81 | #define crypto_core_salsa20_CONSTBYTES crypto_core_salsa20_tweet_CONSTBYTES 82 | #define crypto_core_salsa20_VERSION crypto_core_salsa20_tweet_VERSION 83 | #define crypto_core_salsa20_IMPLEMENTATION "crypto_core/salsa20/tweet" 84 | #define crypto_core_hsalsa20_tweet_OUTPUTBYTES 32 85 | #define crypto_core_hsalsa20_tweet_INPUTBYTES 16 86 | #define crypto_core_hsalsa20_tweet_KEYBYTES 32 87 | #define crypto_core_hsalsa20_tweet_CONSTBYTES 16 88 | extern int crypto_core_hsalsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); 89 | #define crypto_core_hsalsa20_tweet_VERSION "-" 90 | #define crypto_core_hsalsa20 crypto_core_hsalsa20_tweet 91 | #define crypto_core_hsalsa20_OUTPUTBYTES crypto_core_hsalsa20_tweet_OUTPUTBYTES 92 | #define crypto_core_hsalsa20_INPUTBYTES crypto_core_hsalsa20_tweet_INPUTBYTES 93 | #define crypto_core_hsalsa20_KEYBYTES crypto_core_hsalsa20_tweet_KEYBYTES 94 | #define crypto_core_hsalsa20_CONSTBYTES crypto_core_hsalsa20_tweet_CONSTBYTES 95 | #define crypto_core_hsalsa20_VERSION crypto_core_hsalsa20_tweet_VERSION 96 | #define crypto_core_hsalsa20_IMPLEMENTATION "crypto_core/hsalsa20/tweet" 97 | #define crypto_hashblocks_PRIMITIVE "sha512" 98 | #define crypto_hashblocks crypto_hashblocks_sha512 99 | #define crypto_hashblocks_STATEBYTES crypto_hashblocks_sha512_STATEBYTES 100 | #define crypto_hashblocks_BLOCKBYTES crypto_hashblocks_sha512_BLOCKBYTES 101 | #define crypto_hashblocks_IMPLEMENTATION crypto_hashblocks_sha512_IMPLEMENTATION 102 | #define crypto_hashblocks_VERSION crypto_hashblocks_sha512_VERSION 103 | #define crypto_hashblocks_sha512_tweet_STATEBYTES 64 104 | #define crypto_hashblocks_sha512_tweet_BLOCKBYTES 128 105 | extern int crypto_hashblocks_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); 106 | #define crypto_hashblocks_sha512_tweet_VERSION "-" 107 | #define crypto_hashblocks_sha512 crypto_hashblocks_sha512_tweet 108 | #define crypto_hashblocks_sha512_STATEBYTES crypto_hashblocks_sha512_tweet_STATEBYTES 109 | #define crypto_hashblocks_sha512_BLOCKBYTES crypto_hashblocks_sha512_tweet_BLOCKBYTES 110 | #define crypto_hashblocks_sha512_VERSION crypto_hashblocks_sha512_tweet_VERSION 111 | #define crypto_hashblocks_sha512_IMPLEMENTATION "crypto_hashblocks/sha512/tweet" 112 | #define crypto_hashblocks_sha256_tweet_STATEBYTES 32 113 | #define crypto_hashblocks_sha256_tweet_BLOCKBYTES 64 114 | extern int crypto_hashblocks_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); 115 | #define crypto_hashblocks_sha256_tweet_VERSION "-" 116 | #define crypto_hashblocks_sha256 crypto_hashblocks_sha256_tweet 117 | #define crypto_hashblocks_sha256_STATEBYTES crypto_hashblocks_sha256_tweet_STATEBYTES 118 | #define crypto_hashblocks_sha256_BLOCKBYTES crypto_hashblocks_sha256_tweet_BLOCKBYTES 119 | #define crypto_hashblocks_sha256_VERSION crypto_hashblocks_sha256_tweet_VERSION 120 | #define crypto_hashblocks_sha256_IMPLEMENTATION "crypto_hashblocks/sha256/tweet" 121 | #define crypto_hash_PRIMITIVE "sha512" 122 | #define crypto_hash crypto_hash_sha512 123 | #define crypto_hash_BYTES crypto_hash_sha512_BYTES 124 | #define crypto_hash_IMPLEMENTATION crypto_hash_sha512_IMPLEMENTATION 125 | #define crypto_hash_VERSION crypto_hash_sha512_VERSION 126 | #define crypto_hash_sha512_tweet_BYTES 64 127 | extern int crypto_hash_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); 128 | #define crypto_hash_sha512_tweet_VERSION "-" 129 | #define crypto_hash_sha512 crypto_hash_sha512_tweet 130 | #define crypto_hash_sha512_BYTES crypto_hash_sha512_tweet_BYTES 131 | #define crypto_hash_sha512_VERSION crypto_hash_sha512_tweet_VERSION 132 | #define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/tweet" 133 | #define crypto_hash_sha256_tweet_BYTES 32 134 | extern int crypto_hash_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); 135 | #define crypto_hash_sha256_tweet_VERSION "-" 136 | #define crypto_hash_sha256 crypto_hash_sha256_tweet 137 | #define crypto_hash_sha256_BYTES crypto_hash_sha256_tweet_BYTES 138 | #define crypto_hash_sha256_VERSION crypto_hash_sha256_tweet_VERSION 139 | #define crypto_hash_sha256_IMPLEMENTATION "crypto_hash/sha256/tweet" 140 | #define crypto_onetimeauth_PRIMITIVE "poly1305" 141 | #define crypto_onetimeauth crypto_onetimeauth_poly1305 142 | #define crypto_onetimeauth_verify crypto_onetimeauth_poly1305_verify 143 | #define crypto_onetimeauth_BYTES crypto_onetimeauth_poly1305_BYTES 144 | #define crypto_onetimeauth_KEYBYTES crypto_onetimeauth_poly1305_KEYBYTES 145 | #define crypto_onetimeauth_IMPLEMENTATION crypto_onetimeauth_poly1305_IMPLEMENTATION 146 | #define crypto_onetimeauth_VERSION crypto_onetimeauth_poly1305_VERSION 147 | #define crypto_onetimeauth_poly1305_tweet_BYTES 16 148 | #define crypto_onetimeauth_poly1305_tweet_KEYBYTES 32 149 | extern int crypto_onetimeauth_poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); 150 | extern int crypto_onetimeauth_poly1305_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); 151 | #define crypto_onetimeauth_poly1305_tweet_VERSION "-" 152 | #define crypto_onetimeauth_poly1305 crypto_onetimeauth_poly1305_tweet 153 | #define crypto_onetimeauth_poly1305_verify crypto_onetimeauth_poly1305_tweet_verify 154 | #define crypto_onetimeauth_poly1305_BYTES crypto_onetimeauth_poly1305_tweet_BYTES 155 | #define crypto_onetimeauth_poly1305_KEYBYTES crypto_onetimeauth_poly1305_tweet_KEYBYTES 156 | #define crypto_onetimeauth_poly1305_VERSION crypto_onetimeauth_poly1305_tweet_VERSION 157 | #define crypto_onetimeauth_poly1305_IMPLEMENTATION "crypto_onetimeauth/poly1305/tweet" 158 | #define crypto_scalarmult_PRIMITIVE "curve25519" 159 | #define crypto_scalarmult crypto_scalarmult_curve25519 160 | #define crypto_scalarmult_base crypto_scalarmult_curve25519_base 161 | #define crypto_scalarmult_BYTES crypto_scalarmult_curve25519_BYTES 162 | #define crypto_scalarmult_SCALARBYTES crypto_scalarmult_curve25519_SCALARBYTES 163 | #define crypto_scalarmult_IMPLEMENTATION crypto_scalarmult_curve25519_IMPLEMENTATION 164 | #define crypto_scalarmult_VERSION crypto_scalarmult_curve25519_VERSION 165 | #define crypto_scalarmult_curve25519_tweet_BYTES 32 166 | #define crypto_scalarmult_curve25519_tweet_SCALARBYTES 32 167 | extern int crypto_scalarmult_curve25519_tweet(unsigned char *,const unsigned char *,const unsigned char *); 168 | extern int crypto_scalarmult_curve25519_tweet_base(unsigned char *,const unsigned char *); 169 | #define crypto_scalarmult_curve25519_tweet_VERSION "-" 170 | #define crypto_scalarmult_curve25519 crypto_scalarmult_curve25519_tweet 171 | #define crypto_scalarmult_curve25519_base crypto_scalarmult_curve25519_tweet_base 172 | #define crypto_scalarmult_curve25519_BYTES crypto_scalarmult_curve25519_tweet_BYTES 173 | #define crypto_scalarmult_curve25519_SCALARBYTES crypto_scalarmult_curve25519_tweet_SCALARBYTES 174 | #define crypto_scalarmult_curve25519_VERSION crypto_scalarmult_curve25519_tweet_VERSION 175 | #define crypto_scalarmult_curve25519_IMPLEMENTATION "crypto_scalarmult/curve25519/tweet" 176 | #define crypto_secretbox_PRIMITIVE "xsalsa20poly1305" 177 | #define crypto_secretbox crypto_secretbox_xsalsa20poly1305 178 | #define crypto_secretbox_open crypto_secretbox_xsalsa20poly1305_open 179 | #define crypto_secretbox_KEYBYTES crypto_secretbox_xsalsa20poly1305_KEYBYTES 180 | #define crypto_secretbox_NONCEBYTES crypto_secretbox_xsalsa20poly1305_NONCEBYTES 181 | #define crypto_secretbox_ZEROBYTES crypto_secretbox_xsalsa20poly1305_ZEROBYTES 182 | #define crypto_secretbox_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES 183 | #define crypto_secretbox_IMPLEMENTATION crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION 184 | #define crypto_secretbox_VERSION crypto_secretbox_xsalsa20poly1305_VERSION 185 | #define crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 32 186 | #define crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 24 187 | #define crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 32 188 | #define crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 16 189 | extern int crypto_secretbox_xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 190 | extern int crypto_secretbox_xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 191 | #define crypto_secretbox_xsalsa20poly1305_tweet_VERSION "-" 192 | #define crypto_secretbox_xsalsa20poly1305 crypto_secretbox_xsalsa20poly1305_tweet 193 | #define crypto_secretbox_xsalsa20poly1305_open crypto_secretbox_xsalsa20poly1305_tweet_open 194 | #define crypto_secretbox_xsalsa20poly1305_KEYBYTES crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 195 | #define crypto_secretbox_xsalsa20poly1305_NONCEBYTES crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 196 | #define crypto_secretbox_xsalsa20poly1305_ZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 197 | #define crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 198 | #define crypto_secretbox_xsalsa20poly1305_VERSION crypto_secretbox_xsalsa20poly1305_tweet_VERSION 199 | #define crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION "crypto_secretbox/xsalsa20poly1305/tweet" 200 | #define crypto_sign_PRIMITIVE "ed25519" 201 | #define crypto_sign crypto_sign_ed25519 202 | #define crypto_sign_open crypto_sign_ed25519_open 203 | #define crypto_sign_keypair crypto_sign_ed25519_keypair 204 | #define crypto_sign_BYTES crypto_sign_ed25519_BYTES 205 | #define crypto_sign_PUBLICKEYBYTES crypto_sign_ed25519_PUBLICKEYBYTES 206 | #define crypto_sign_SECRETKEYBYTES crypto_sign_ed25519_SECRETKEYBYTES 207 | #define crypto_sign_IMPLEMENTATION crypto_sign_ed25519_IMPLEMENTATION 208 | #define crypto_sign_VERSION crypto_sign_ed25519_VERSION 209 | #define crypto_sign_ed25519_tweet_BYTES 64 210 | #define crypto_sign_ed25519_tweet_PUBLICKEYBYTES 32 211 | #define crypto_sign_ed25519_tweet_SECRETKEYBYTES 64 212 | extern int crypto_sign_ed25519_tweet(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); 213 | extern int crypto_sign_ed25519_tweet_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); 214 | extern int crypto_sign_ed25519_tweet_keypair(unsigned char *,unsigned char *); 215 | #define crypto_sign_ed25519_tweet_VERSION "-" 216 | #define crypto_sign_ed25519 crypto_sign_ed25519_tweet 217 | #define crypto_sign_ed25519_open crypto_sign_ed25519_tweet_open 218 | #define crypto_sign_ed25519_keypair crypto_sign_ed25519_tweet_keypair 219 | #define crypto_sign_ed25519_BYTES crypto_sign_ed25519_tweet_BYTES 220 | #define crypto_sign_ed25519_PUBLICKEYBYTES crypto_sign_ed25519_tweet_PUBLICKEYBYTES 221 | #define crypto_sign_ed25519_SECRETKEYBYTES crypto_sign_ed25519_tweet_SECRETKEYBYTES 222 | #define crypto_sign_ed25519_VERSION crypto_sign_ed25519_tweet_VERSION 223 | #define crypto_sign_ed25519_IMPLEMENTATION "crypto_sign/ed25519/tweet" 224 | #define crypto_stream_PRIMITIVE "xsalsa20" 225 | #define crypto_stream crypto_stream_xsalsa20 226 | #define crypto_stream_xor crypto_stream_xsalsa20_xor 227 | #define crypto_stream_KEYBYTES crypto_stream_xsalsa20_KEYBYTES 228 | #define crypto_stream_NONCEBYTES crypto_stream_xsalsa20_NONCEBYTES 229 | #define crypto_stream_IMPLEMENTATION crypto_stream_xsalsa20_IMPLEMENTATION 230 | #define crypto_stream_VERSION crypto_stream_xsalsa20_VERSION 231 | #define crypto_stream_xsalsa20_tweet_KEYBYTES 32 232 | #define crypto_stream_xsalsa20_tweet_NONCEBYTES 24 233 | extern int crypto_stream_xsalsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 234 | extern int crypto_stream_xsalsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 235 | #define crypto_stream_xsalsa20_tweet_VERSION "-" 236 | #define crypto_stream_xsalsa20 crypto_stream_xsalsa20_tweet 237 | #define crypto_stream_xsalsa20_xor crypto_stream_xsalsa20_tweet_xor 238 | #define crypto_stream_xsalsa20_KEYBYTES crypto_stream_xsalsa20_tweet_KEYBYTES 239 | #define crypto_stream_xsalsa20_NONCEBYTES crypto_stream_xsalsa20_tweet_NONCEBYTES 240 | #define crypto_stream_xsalsa20_VERSION crypto_stream_xsalsa20_tweet_VERSION 241 | #define crypto_stream_xsalsa20_IMPLEMENTATION "crypto_stream/xsalsa20/tweet" 242 | #define crypto_stream_salsa20_tweet_KEYBYTES 32 243 | #define crypto_stream_salsa20_tweet_NONCEBYTES 8 244 | extern int crypto_stream_salsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 245 | extern int crypto_stream_salsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); 246 | #define crypto_stream_salsa20_tweet_VERSION "-" 247 | #define crypto_stream_salsa20 crypto_stream_salsa20_tweet 248 | #define crypto_stream_salsa20_xor crypto_stream_salsa20_tweet_xor 249 | #define crypto_stream_salsa20_KEYBYTES crypto_stream_salsa20_tweet_KEYBYTES 250 | #define crypto_stream_salsa20_NONCEBYTES crypto_stream_salsa20_tweet_NONCEBYTES 251 | #define crypto_stream_salsa20_VERSION crypto_stream_salsa20_tweet_VERSION 252 | #define crypto_stream_salsa20_IMPLEMENTATION "crypto_stream/salsa20/tweet" 253 | #define crypto_verify_PRIMITIVE "16" 254 | #define crypto_verify crypto_verify_16 255 | #define crypto_verify_BYTES crypto_verify_16_BYTES 256 | #define crypto_verify_IMPLEMENTATION crypto_verify_16_IMPLEMENTATION 257 | #define crypto_verify_VERSION crypto_verify_16_VERSION 258 | #define crypto_verify_16_tweet_BYTES 16 259 | extern int crypto_verify_16_tweet(const unsigned char *,const unsigned char *); 260 | #define crypto_verify_16_tweet_VERSION "-" 261 | #define crypto_verify_16 crypto_verify_16_tweet 262 | #define crypto_verify_16_BYTES crypto_verify_16_tweet_BYTES 263 | #define crypto_verify_16_VERSION crypto_verify_16_tweet_VERSION 264 | #define crypto_verify_16_IMPLEMENTATION "crypto_verify/16/tweet" 265 | #define crypto_verify_32_tweet_BYTES 32 266 | extern int crypto_verify_32_tweet(const unsigned char *,const unsigned char *); 267 | #define crypto_verify_32_tweet_VERSION "-" 268 | #define crypto_verify_32 crypto_verify_32_tweet 269 | #define crypto_verify_32_BYTES crypto_verify_32_tweet_BYTES 270 | #define crypto_verify_32_VERSION crypto_verify_32_tweet_VERSION 271 | #define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/tweet" 272 | #endif 273 | -------------------------------------------------------------------------------- /util/cfg.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | from collections import defaultdict 3 | 4 | from core import func 5 | from util.emu import Emu 6 | 7 | SYS_terminate = 1 8 | SYS_transmit = 2 9 | SYS_receive = 3 10 | SYS_fdwait = 4 11 | SYS_allocate = 5 12 | SYS_deallocate = 6 13 | SYS_random = 7 14 | 15 | CONDJMPS = [ 16 | X86_INS_JA, 17 | X86_INS_JAE, 18 | X86_INS_JB, 19 | X86_INS_JBE, 20 | X86_INS_JCXZ, 21 | X86_INS_JE, 22 | X86_INS_JECXZ, 23 | X86_INS_JG, 24 | X86_INS_JGE, 25 | X86_INS_JL, 26 | X86_INS_JLE, 27 | X86_INS_JNE, 28 | X86_INS_JNO, 29 | X86_INS_JNP, 30 | X86_INS_JNS, 31 | X86_INS_JO, 32 | X86_INS_JP, 33 | X86_INS_JRCXZ, 34 | X86_INS_JS, 35 | ] 36 | 37 | JMPS = CONDJMPS + [ 38 | X86_INS_JMP, 39 | 40 | X86_INS_LOOP, 41 | X86_INS_LOOPE, 42 | X86_INS_LOOPNE, 43 | ] 44 | CALL = X86_INS_CALL 45 | RET = X86_INS_RET 46 | 47 | class Func(func.Func): 48 | def __init__(self, pt, addr, size=None): 49 | func.Func.__init__(self, pt, addr, size) 50 | self.syscall = None 51 | self.stacks = set() 52 | self.xrefs = set() 53 | self.jit = False 54 | 55 | def explore(pt, known_funcs=[], backtrack=False): 56 | seen = set() 57 | funcs = {} 58 | regs = defaultdict(int) 59 | 60 | def do_bb(addr, stack=None, func=None, xref=None): 61 | for prog in pt.elf.progs: 62 | if addr in prog: 63 | break 64 | else: 65 | return 66 | 67 | if addr in seen: 68 | if func and xref and xref != func.addr: 69 | func.xrefs.add(xref) 70 | return 71 | seen.add(addr) 72 | if func is None: 73 | func = funcs[addr] = Func(pt, addr) 74 | if xref and xref != func.addr: 75 | func.xrefs.add(xref) 76 | if stack is None: 77 | stack = [] 78 | elif stack: 79 | func.stacks.add(tuple(f.addr for f in stack)) 80 | 81 | pad = ((len(stack) + 3) * ' ') 82 | pos = addr 83 | while True: 84 | dis = pt.dis(pos) 85 | if not dis: 86 | break 87 | 88 | for ins in dis: 89 | pos = ins.address + len(ins.bytes) 90 | cur_size = pos - func.addr 91 | 92 | pad = ((len(stack) + 3) * ' ') 93 | if ins.id == X86_INS_MOV: 94 | dst, src = ins.operands 95 | if dst.type == X86_OP_REG: 96 | if src.type == X86_OP_REG: 97 | regs[dst.reg] = regs[src.reg] 98 | elif src.type == X86_OP_IMM: 99 | regs[dst.reg] = src.imm 100 | 101 | func.size = max(func.size, cur_size) 102 | if ins.id == X86_INS_INT and ins.operands[0].imm == 0x80: 103 | sys = regs[X86_REG_EAX] 104 | # don't trust the top-level function to be a syscall (it's just a raw _terminate) 105 | if len(stack) > 0: 106 | func.syscall = sys 107 | 108 | if ins.id in JMPS or ins.id == CALL: 109 | tmp = list(stack) 110 | branch = ins.operands[0].imm 111 | dist = abs(branch - ins.address) 112 | longjmp = False 113 | if ins.id != CALL and dist > 0x10000: 114 | longjmp = True 115 | try: 116 | # TODO: trampoline jumps aren't handled well 117 | # assume no funcs over 10KB 118 | if ins.id == CALL or longjmp: 119 | stack.append(func) 120 | do_bb(branch, stack, xref=addr) 121 | if longjmp: 122 | return 123 | # call to next instruction makes a function split 124 | if ins.id == CALL and ins.operands[0].imm == ins.address + ins.size: 125 | return 126 | else: 127 | do_bb(branch, stack, func=func, xref=addr) 128 | except Exception: 129 | # import traceback 130 | # traceback.print_exc() 131 | pt.warn('Failed to follow branch: %s' % pt.pdis(ins)) 132 | finally: 133 | stack = tmp 134 | elif ins.id == RET: 135 | try: 136 | stack.pop() 137 | except Exception: 138 | pass # print 'Warning: ret on empty call stack' 139 | return 140 | 141 | emu = Emu(pt.binary) 142 | blocks, modified = emu.explore(backtrack=backtrack) 143 | for addr, size in blocks: 144 | do_bb(addr) 145 | 146 | do_bb(pt.entry) 147 | for f in known_funcs: 148 | do_bb(f.addr) 149 | 150 | funcs = funcs.values() 151 | # make sure entry gets its own func 152 | for f in funcs: 153 | if f.addr != pt.entry and pt.entry in f: 154 | f.size = pt.entry - f.addr 155 | 156 | out = [] 157 | for f in funcs: 158 | if f.addr in [x.addr for x in known_funcs]: 159 | continue 160 | 161 | # filter out invalid addrs 162 | for prog in pt.elf.progs: 163 | if f.addr in prog: 164 | break 165 | else: 166 | if (f.addr, f.size) in blocks: 167 | func.jit = True 168 | pt.warn('JIT function detected (will likely crash diff)') 169 | break 170 | else: 171 | continue 172 | 173 | for fb in funcs: 174 | if f != fb and f.addr in fb and fb > f and not f.addr == pt.entry: 175 | break 176 | else: 177 | if not f.size: 178 | f.size = 16 179 | out.append(f) 180 | 181 | out.sort(key=lambda x: x.addr) 182 | return out, modified 183 | -------------------------------------------------------------------------------- /util/crypto/__init__.py: -------------------------------------------------------------------------------- 1 | from util import read 2 | 3 | def declare(linker): 4 | linker.autodecl(read('crypto/rc4.c')) 5 | -------------------------------------------------------------------------------- /util/crypto/rc4.c: -------------------------------------------------------------------------------- 1 | void ksa(unsigned char *state, unsigned char *key, int keylen) { 2 | int i, j = 0, t; 3 | for (i = 0; i < 256; ++i) 4 | state[i] = i; 5 | for (i = 0; i < 256; ++i) { 6 | j = (j + state[i] + key[i % keylen]) % 256; 7 | t = state[i]; 8 | state[i] = state[j]; 9 | state[j] = t; 10 | } 11 | } 12 | 13 | void rc4(unsigned char *state, unsigned char *data, int len) { 14 | int i = 0, j = 0, x, t; 15 | for (x = 0; x < len; ++x) { 16 | i = (i + 1) % 256; 17 | j = (j + state[i]) % 256; 18 | t = state[i]; 19 | state[i] = state[j]; 20 | state[j] = t; 21 | data[x] ^= state[(state[i] + state[j]) % 256]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /util/crypto/rc4.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def rc4(key): 4 | key = map(ord, key) 5 | # ksa 6 | S = range(256) 7 | j = 0 8 | for i in range(256): 9 | j = (j + S[i] + key[i % len(key)]) % 256 10 | S[i], S[j] = S[j], S[i] 11 | 12 | # prga 13 | i = j = 0 14 | while True: 15 | i = (i + 1) % 256 16 | j = (j + S[i]) % 256 17 | S[i], S[j] = S[j], S[i] 18 | t = (S[i] + S[j]) % 256 19 | yield S[t] 20 | 21 | def rc4_decrypt_template(key, addr, size): 22 | # xor key so they can't just pull it out of memory 23 | key_otp = os.urandom(len(key)) 24 | key = ''.join([chr(ord(c) ^ ord(key_otp[i])) for i, c in enumerate(key)]) 25 | str2c = lambda x: ', '.join(map(str, map(ord, x))) 26 | 27 | code = r''' 28 | void _start() { 29 | uint8_t state[256]; 30 | uint8_t rc4_key[] = {%s}; 31 | uint8_t rc4_otp[] = {%s}; 32 | int keylen = %d; 33 | for (int i = 0; i < keylen; i++) { 34 | rc4_key[i] ^= rc4_otp[i]; 35 | } 36 | ksa(state, rc4_key, keylen); 37 | rc4(state, (unsigned char *)%d, %d); 38 | } 39 | ''' % (str2c(key), str2c(key_otp), len(key), addr, size) 40 | return code 41 | -------------------------------------------------------------------------------- /util/crypto/xor.py: -------------------------------------------------------------------------------- 1 | def xor_mem(pt, addr, size, key): 2 | if size == 0: 3 | pt.debug('Skipping XOR: patch size is 0.') 4 | return 5 | 6 | data = pt.elf.read(addr, size) 7 | for i in xrange(len(data)): 8 | data[i] ^= key 9 | pt.debug('[XOR] 0x%x +0x%x ^ 0x%x' % (addr, size, key)) 10 | pt.patch(addr, raw=data) 11 | 12 | pt.make_writable(addr) 13 | addr = pt.inject(c=r''' 14 | void _start() { 15 | char *p = (char *)%d; 16 | for (unsigned int i = 0; i < %d; i++) { 17 | *p++ ^= %s; 18 | } 19 | } 20 | ''' % (addr, size, key)) 21 | pt.hook(pt.entry, addr, first=True) 22 | -------------------------------------------------------------------------------- /util/emu.py: -------------------------------------------------------------------------------- 1 | import os 2 | import struct 3 | import time 4 | import traceback 5 | from contextlib import contextmanager 6 | from collections import defaultdict, OrderedDict 7 | from unicorn import * 8 | from unicorn.x86_const import * 9 | from capstone import * 10 | from capstone.x86_const import * 11 | 12 | def align(addr, size): 13 | start = addr 14 | end = addr + size 15 | end = (end + 0xfff) & ~0xfff 16 | start = start & ~0xfff 17 | return start, end - start 18 | 19 | X86_REGS = [ 20 | UC_X86_REG_EAX, 21 | UC_X86_REG_EBX, 22 | UC_X86_REG_ECX, 23 | UC_X86_REG_EDX, 24 | UC_X86_REG_ESI, 25 | UC_X86_REG_EDI, 26 | UC_X86_REG_ESP, 27 | ] 28 | 29 | class Block: 30 | def __init__(self, addr, size, data): 31 | self.addr = addr 32 | self.size = size 33 | self.data = str(data) 34 | 35 | def __hash__(self): 36 | return hash((self.addr, self.size, self.data)) 37 | 38 | def __lt__(self, other): 39 | return self.addr < other.addr 40 | 41 | def __contains__(self, addr): 42 | return addr >= self.addr and addr < self.addr + self.size 43 | 44 | # other should have a higher addr 45 | def merge(self, other): 46 | end = self.addr + self.size 47 | self.addr = min(self.addr, other.addr) 48 | self.size = (other.addr + other.size) - self.addr 49 | if end < other.addr: 50 | gap = (other.addr - end) * '\0' 51 | self.data += gap + other.data 52 | 53 | class Transaction: 54 | def __init__(self, uc): 55 | self.uc = uc 56 | # save all registers 57 | self.saved_regs = {} 58 | for enum in X86_REGS: 59 | val = uc.reg_read(enum) 60 | self.saved_regs[enum] = val 61 | 62 | # save memory undo deltas 63 | self.saved_mem = {} 64 | def hook_mem(uc, access, addr, size, value, user): 65 | try: 66 | data = str(uc.mem_read(addr, size)) 67 | except UcError: 68 | return 69 | for i in xrange(size): 70 | waddr = addr + i 71 | if not waddr in self.saved_mem: 72 | self.saved_mem[waddr] = data[i] 73 | 74 | self.hh = uc.hook_add(UC_HOOK_MEM_WRITE, hook_mem) 75 | 76 | def rewind(self): 77 | self.uc.hook_del(self.hh) 78 | 79 | # rollback register changes 80 | for enum in X86_REGS: 81 | self.uc.reg_write(enum, self.saved_regs[enum]) 82 | 83 | # rollback memory writes 84 | for addr, b in self.saved_mem.items(): 85 | self.uc.mem_write(addr, b) 86 | 87 | def discard(self): 88 | self.uc.hook_del(self.hh) 89 | 90 | class Backtrack: 91 | def __init__(self, emu, uc): 92 | self.uc = uc 93 | self.emu = emu 94 | self.cs = Cs(CS_ARCH_X86, CS_MODE_32) 95 | self.cs.detail = True 96 | 97 | uc.hook_add(UC_HOOK_CODE, self.jmp_hook) 98 | 99 | # recursive import :( 100 | from util.cfg import CONDJMPS 101 | self.CONDJMPS = CONDJMPS 102 | 103 | self.visited = set() 104 | self.pending = set() 105 | self.targets = [] 106 | self.last_addr = 0 107 | 108 | def add(self, prev, addr): 109 | key = (prev, addr) 110 | if not key in self.visited and not key in self.pending: 111 | self.pending.add(key) 112 | self.targets.append((addr, Transaction(self.uc))) 113 | 114 | def jmp_hook(self, uc, addr, size, user): 115 | # TODO: caching/skipping here will be huge 116 | # this is gonna be slow 117 | data = uc.mem_read(addr, size) 118 | ins = tuple(self.cs.disasm(str(data), addr))[0] 119 | if ins.id in self.CONDJMPS: 120 | dst = ins.operands[0] 121 | if dst.type == X86_OP_IMM: 122 | dst = dst.imm 123 | self.add(addr, addr + len(ins.bytes)) 124 | self.add(addr, dst) 125 | 126 | self.visited.add((self.last_addr, addr)) 127 | self.last_addr = addr 128 | 129 | def run(self, entry): 130 | uc = self.uc 131 | self.last_addr = entry 132 | 133 | print ' [BACKTRACK] Running once:' 134 | self.recv_hist = [] 135 | # stop emulator on receive() spam 136 | def hook_intr(uc, intno, user): 137 | if intno == 0x80: 138 | if self.emu.sysnum == 3: # SYS_receive 139 | self.recv_hist.append(time.time()) 140 | if len(self.recv_hist) > 10: 141 | if (self.recv_hist[-1] - self.recv_hist[-11]) < 1: 142 | uc.emu_stop() 143 | 144 | hh = uc.hook_add(UC_HOOK_INTR, hook_intr) 145 | 146 | self.emu.verbose = True 147 | uc.emu_start(entry, 0) 148 | self.emu.verbose = False 149 | self.emu.block_timeout = 1 150 | 151 | print ' [BACKTRACK] Backtracking...' 152 | last = time.time() 153 | finished = 0 154 | while self.targets: 155 | now = time.time() 156 | if now - last > 1: 157 | last = now 158 | print ' [BACKTRACK] %d/%d' % (finished, finished + len(self.targets)) 159 | 160 | addr, transaction = self.targets.pop(-1) 161 | transaction.rewind() 162 | self.emu.alive = time.time() 163 | try: 164 | self.last_addr = addr 165 | self.recv_hist = [] 166 | uc.emu_start(addr, 0) 167 | except Exception as e: 168 | pass 169 | finished += 1 170 | print ' [BACKTRACK] Finished (%d/%d)' % (finished, finished + len(self.targets)) 171 | uc.hook_del(hh) 172 | 173 | class Emu: 174 | def __init__(self, binary): 175 | self.binary = binary 176 | self.blocks = {} 177 | self.modified = [] 178 | self.seen_receive = False 179 | self.alive = 0 180 | self.block_timeout = 10 181 | self.verbose = True 182 | self.sysnum = 0 183 | 184 | @contextmanager 185 | def emu(self): 186 | uc = Uc(UC_ARCH_X86, UC_MODE_32) 187 | 188 | # map memory 189 | todo = [] 190 | for prog in self.binary.elf.progs: 191 | if prog.isload and prog.memsz: 192 | addr, size = align(prog.vaddr, prog.memsz) 193 | uc.mem_map(addr, size) 194 | todo.append(prog) 195 | 196 | for prog in todo: 197 | uc.mem_write(prog.vaddr, str(prog.data)) 198 | 199 | # map stack 200 | stack_base = 0xbaaab000 - 0x800000 201 | uc.mem_map(stack_base, 0x800000) 202 | uc.reg_write(UC_X86_REG_ESP, 0xbaaaaffc) 203 | 204 | # map secret page 205 | secret_addr = 0x4347c000 206 | uc.mem_map(secret_addr, 0x1000) 207 | uc.mem_write(secret_addr, os.urandom(0x1000)) 208 | 209 | # initial register(s) 210 | uc.reg_write(UC_X86_REG_ECX, secret_addr) 211 | 212 | yield uc 213 | # TODO: cleanup? there's no uc.close() yet 214 | 215 | def explore(self, backtrack=False): 216 | with self.emu() as uc, self.binary.collect() as pt: 217 | self.alive = time.time() 218 | self.alloc_pos = 0x10000 219 | 220 | def hook_block(uc, addr, size, user): 221 | # pt.debug('[BLOCK] 0x%x - 0x%x' % (addr, size)) 222 | # TODO: xrefs? 223 | 224 | now = time.time() 225 | key = (addr, size) 226 | data = str(uc.mem_read(addr, size)) 227 | if data != pt.elf.read(addr, size): 228 | tmp = (key, data) 229 | if not tmp in self.modified: 230 | self.modified.append(tmp) 231 | 232 | if not key in self.blocks: 233 | self.alive = now 234 | self.blocks[key] = Block(addr, size, data) 235 | elif now - self.alive > self.block_timeout and self.seen_receive: 236 | if self.verbose: 237 | pt.debug('[EMU] No recent new blocks, stopping emulation.') 238 | uc.emu_stop() 239 | 240 | def hook_code(uc, addr, size, user): 241 | data = uc.mem_read(addr, size) 242 | pt.debug(dis=pt.arch.dis(data, addr=addr)) 243 | 244 | def handle_syscall(): 245 | num = uc.reg_read(UC_X86_REG_EAX) 246 | self.sysnum = num 247 | arg1 = uc.reg_read(UC_X86_REG_EBX) 248 | arg2 = uc.reg_read(UC_X86_REG_ECX) 249 | arg3 = uc.reg_read(UC_X86_REG_EDX) 250 | arg4 = uc.reg_read(UC_X86_REG_ESI) 251 | arg5 = uc.reg_read(UC_X86_REG_EDI) 252 | arg6 = uc.reg_read(UC_X86_REG_EBP) 253 | 254 | if num == 1: # _terminate 255 | if self.verbose: 256 | pt.debug('[EMU] _terminate(%d)' % arg1) 257 | uc.emu_stop() 258 | return 259 | 260 | if num == 2: # transmit 261 | data = str(uc.mem_read(arg2, arg3)) 262 | if arg4: 263 | uc.mem_write(arg4, struct.pack('. This is needed only if 15 | your system defines a struct mallinfo that is incompatible with the 16 | standard one declared here. Otherwise, you can include this file 17 | INSTEAD of your system system . At least on ANSI, all 18 | declarations should be compatible with system versions 19 | 20 | * If MSPACES is defined, declarations for mspace versions are included. 21 | */ 22 | 23 | #ifndef MALLOC_280_H 24 | #define MALLOC_280_H 25 | #define USE_DL_PREFIX 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #ifndef ONLY_MSPACES 31 | #define ONLY_MSPACES 0 /* define to a value */ 32 | #elif ONLY_MSPACES != 0 33 | #define ONLY_MSPACES 1 34 | #endif /* ONLY_MSPACES */ 35 | #ifndef NO_MALLINFO 36 | #define NO_MALLINFO 0 37 | #endif /* NO_MALLINFO */ 38 | 39 | #ifndef MSPACES 40 | #if ONLY_MSPACES 41 | #define MSPACES 1 42 | #else /* ONLY_MSPACES */ 43 | #define MSPACES 0 44 | #endif /* ONLY_MSPACES */ 45 | #endif /* MSPACES */ 46 | 47 | #if !ONLY_MSPACES 48 | 49 | #ifndef USE_DL_PREFIX 50 | #define dlcalloc calloc 51 | #define dlfree free 52 | #define dlmalloc malloc 53 | #define dlmemalign memalign 54 | #define dlposix_memalign posix_memalign 55 | #define dlrealloc realloc 56 | #define dlvalloc valloc 57 | #define dlpvalloc pvalloc 58 | #define dlmallinfo mallinfo 59 | #define dlmallopt mallopt 60 | #define dlmalloc_trim malloc_trim 61 | #define dlmalloc_stats malloc_stats 62 | #define dlmalloc_usable_size malloc_usable_size 63 | #define dlmalloc_footprint malloc_footprint 64 | #define dlmalloc_max_footprint malloc_max_footprint 65 | #define dlmalloc_footprint_limit malloc_footprint_limit 66 | #define dlmalloc_set_footprint_limit malloc_set_footprint_limit 67 | #define dlmalloc_inspect_all malloc_inspect_all 68 | #define dlindependent_calloc independent_calloc 69 | #define dlindependent_comalloc independent_comalloc 70 | #define dlbulk_free bulk_free 71 | #endif /* USE_DL_PREFIX */ 72 | 73 | #if !NO_MALLINFO 74 | #ifndef HAVE_USR_INCLUDE_MALLOC_H 75 | #ifndef _MALLOC_H 76 | #ifndef MALLINFO_FIELD_TYPE 77 | #define MALLINFO_FIELD_TYPE size_t 78 | #endif /* MALLINFO_FIELD_TYPE */ 79 | #ifndef STRUCT_MALLINFO_DECLARED 80 | #define STRUCT_MALLINFO_DECLARED 1 81 | struct mallinfo { 82 | MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ 83 | MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ 84 | MALLINFO_FIELD_TYPE smblks; /* always 0 */ 85 | MALLINFO_FIELD_TYPE hblks; /* always 0 */ 86 | MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ 87 | MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ 88 | MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ 89 | MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ 90 | MALLINFO_FIELD_TYPE fordblks; /* total free space */ 91 | MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ 92 | }; 93 | #endif /* STRUCT_MALLINFO_DECLARED */ 94 | #endif /* _MALLOC_H */ 95 | #endif /* HAVE_USR_INCLUDE_MALLOC_H */ 96 | #endif /* !NO_MALLINFO */ 97 | 98 | /* 99 | malloc(size_t n) 100 | Returns a pointer to a newly allocated chunk of at least n bytes, or 101 | null if no space is available, in which case errno is set to ENOMEM 102 | on ANSI C systems. 103 | 104 | If n is zero, malloc returns a minimum-sized chunk. (The minimum 105 | size is 16 bytes on most 32bit systems, and 32 bytes on 64bit 106 | systems.) Note that size_t is an unsigned type, so calls with 107 | arguments that would be negative if signed are interpreted as 108 | requests for huge amounts of space, which will often fail. The 109 | maximum supported value of n differs across systems, but is in all 110 | cases less than the maximum representable value of a size_t. 111 | */ 112 | void* dlmalloc(size_t); 113 | 114 | /* 115 | free(void* p) 116 | Releases the chunk of memory pointed to by p, that had been previously 117 | allocated using malloc or a related routine such as realloc. 118 | It has no effect if p is null. If p was not malloced or already 119 | freed, free(p) will by default cuase the current program to abort. 120 | */ 121 | void dlfree(void*); 122 | 123 | /* 124 | calloc(size_t n_elements, size_t element_size); 125 | Returns a pointer to n_elements * element_size bytes, with all locations 126 | set to zero. 127 | */ 128 | void* dlcalloc(size_t, size_t); 129 | 130 | /* 131 | realloc(void* p, size_t n) 132 | Returns a pointer to a chunk of size n that contains the same data 133 | as does chunk p up to the minimum of (n, p's size) bytes, or null 134 | if no space is available. 135 | 136 | The returned pointer may or may not be the same as p. The algorithm 137 | prefers extending p in most cases when possible, otherwise it 138 | employs the equivalent of a malloc-copy-free sequence. 139 | 140 | If p is null, realloc is equivalent to malloc. 141 | 142 | If space is not available, realloc returns null, errno is set (if on 143 | ANSI) and p is NOT freed. 144 | 145 | if n is for fewer bytes than already held by p, the newly unused 146 | space is lopped off and freed if possible. realloc with a size 147 | argument of zero (re)allocates a minimum-sized chunk. 148 | 149 | The old unix realloc convention of allowing the last-free'd chunk 150 | to be used as an argument to realloc is not supported. 151 | */ 152 | void* dlrealloc(void*, size_t); 153 | 154 | /* 155 | realloc_in_place(void* p, size_t n) 156 | Resizes the space allocated for p to size n, only if this can be 157 | done without moving p (i.e., only if there is adjacent space 158 | available if n is greater than p's current allocated size, or n is 159 | less than or equal to p's size). This may be used instead of plain 160 | realloc if an alternative allocation strategy is needed upon failure 161 | to expand space; for example, reallocation of a buffer that must be 162 | memory-aligned or cleared. You can use realloc_in_place to trigger 163 | these alternatives only when needed. 164 | 165 | Returns p if successful; otherwise null. 166 | */ 167 | void* dlrealloc_in_place(void*, size_t); 168 | 169 | /* 170 | memalign(size_t alignment, size_t n); 171 | Returns a pointer to a newly allocated chunk of n bytes, aligned 172 | in accord with the alignment argument. 173 | 174 | The alignment argument should be a power of two. If the argument is 175 | not a power of two, the nearest greater power is used. 176 | 8-byte alignment is guaranteed by normal malloc calls, so don't 177 | bother calling memalign with an argument of 8 or less. 178 | 179 | Overreliance on memalign is a sure way to fragment space. 180 | */ 181 | void* dlmemalign(size_t, size_t); 182 | 183 | /* 184 | int posix_memalign(void** pp, size_t alignment, size_t n); 185 | Allocates a chunk of n bytes, aligned in accord with the alignment 186 | argument. Differs from memalign only in that it (1) assigns the 187 | allocated memory to *pp rather than returning it, (2) fails and 188 | returns EINVAL if the alignment is not a power of two (3) fails and 189 | returns ENOMEM if memory cannot be allocated. 190 | */ 191 | int dlposix_memalign(void**, size_t, size_t); 192 | 193 | /* 194 | valloc(size_t n); 195 | Equivalent to memalign(pagesize, n), where pagesize is the page 196 | size of the system. If the pagesize is unknown, 4096 is used. 197 | */ 198 | void* dlvalloc(size_t); 199 | 200 | /* 201 | mallopt(int parameter_number, int parameter_value) 202 | Sets tunable parameters The format is to provide a 203 | (parameter-number, parameter-value) pair. mallopt then sets the 204 | corresponding parameter to the argument value if it can (i.e., so 205 | long as the value is meaningful), and returns 1 if successful else 206 | 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, 207 | normally defined in malloc.h. None of these are use in this malloc, 208 | so setting them has no effect. But this malloc also supports other 209 | options in mallopt: 210 | 211 | Symbol param # default allowed param values 212 | M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) 213 | M_GRANULARITY -2 page size any power of 2 >= page size 214 | M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) 215 | */ 216 | int dlmallopt(int, int); 217 | 218 | #define M_TRIM_THRESHOLD (-1) 219 | #define M_GRANULARITY (-2) 220 | #define M_MMAP_THRESHOLD (-3) 221 | 222 | 223 | /* 224 | malloc_footprint(); 225 | Returns the number of bytes obtained from the system. The total 226 | number of bytes allocated by malloc, realloc etc., is less than this 227 | value. Unlike mallinfo, this function returns only a precomputed 228 | result, so can be called frequently to monitor memory consumption. 229 | Even if locks are otherwise defined, this function does not use them, 230 | so results might not be up to date. 231 | */ 232 | size_t dlmalloc_footprint(void); 233 | 234 | /* 235 | malloc_max_footprint(); 236 | Returns the maximum number of bytes obtained from the system. This 237 | value will be greater than current footprint if deallocated space 238 | has been reclaimed by the system. The peak number of bytes allocated 239 | by malloc, realloc etc., is less than this value. Unlike mallinfo, 240 | this function returns only a precomputed result, so can be called 241 | frequently to monitor memory consumption. Even if locks are 242 | otherwise defined, this function does not use them, so results might 243 | not be up to date. 244 | */ 245 | size_t dlmalloc_max_footprint(void); 246 | 247 | /* 248 | malloc_footprint_limit(); 249 | Returns the number of bytes that the heap is allowed to obtain from 250 | the system, returning the last value returned by 251 | malloc_set_footprint_limit, or the maximum size_t value if 252 | never set. The returned value reflects a permission. There is no 253 | guarantee that this number of bytes can actually be obtained from 254 | the system. 255 | */ 256 | size_t dlmalloc_footprint_limit(void); 257 | 258 | /* 259 | malloc_set_footprint_limit(); 260 | Sets the maximum number of bytes to obtain from the system, causing 261 | failure returns from malloc and related functions upon attempts to 262 | exceed this value. The argument value may be subject to page 263 | rounding to an enforceable limit; this actual value is returned. 264 | Using an argument of the maximum possible size_t effectively 265 | disables checks. If the argument is less than or equal to the 266 | current malloc_footprint, then all future allocations that require 267 | additional system memory will fail. However, invocation cannot 268 | retroactively deallocate existing used memory. 269 | */ 270 | size_t dlmalloc_set_footprint_limit(size_t bytes); 271 | 272 | /* 273 | malloc_inspect_all(void(*handler)(void *start, 274 | void *end, 275 | size_t used_bytes, 276 | void* callback_arg), 277 | void* arg); 278 | Traverses the heap and calls the given handler for each managed 279 | region, skipping all bytes that are (or may be) used for bookkeeping 280 | purposes. Traversal does not include include chunks that have been 281 | directly memory mapped. Each reported region begins at the start 282 | address, and continues up to but not including the end address. The 283 | first used_bytes of the region contain allocated data. If 284 | used_bytes is zero, the region is unallocated. The handler is 285 | invoked with the given callback argument. If locks are defined, they 286 | are held during the entire traversal. It is a bad idea to invoke 287 | other malloc functions from within the handler. 288 | 289 | For example, to count the number of in-use chunks with size greater 290 | than 1000, you could write: 291 | static int count = 0; 292 | void count_chunks(void* start, void* end, size_t used, void* arg) { 293 | if (used >= 1000) ++count; 294 | } 295 | then: 296 | malloc_inspect_all(count_chunks, NULL); 297 | 298 | malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. 299 | */ 300 | void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), 301 | void* arg); 302 | 303 | #if !NO_MALLINFO 304 | /* 305 | mallinfo() 306 | Returns (by copy) a struct containing various summary statistics: 307 | 308 | arena: current total non-mmapped bytes allocated from system 309 | ordblks: the number of free chunks 310 | smblks: always zero. 311 | hblks: current number of mmapped regions 312 | hblkhd: total bytes held in mmapped regions 313 | usmblks: the maximum total allocated space. This will be greater 314 | than current total if trimming has occurred. 315 | fsmblks: always zero 316 | uordblks: current total allocated space (normal or mmapped) 317 | fordblks: total free space 318 | keepcost: the maximum number of bytes that could ideally be released 319 | back to system via malloc_trim. ("ideally" means that 320 | it ignores page restrictions etc.) 321 | 322 | Because these fields are ints, but internal bookkeeping may 323 | be kept as longs, the reported values may wrap around zero and 324 | thus be inaccurate. 325 | */ 326 | 327 | struct mallinfo dlmallinfo(void); 328 | #endif /* NO_MALLINFO */ 329 | 330 | /* 331 | independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); 332 | 333 | independent_calloc is similar to calloc, but instead of returning a 334 | single cleared space, it returns an array of pointers to n_elements 335 | independent elements that can hold contents of size elem_size, each 336 | of which starts out cleared, and can be independently freed, 337 | realloc'ed etc. The elements are guaranteed to be adjacently 338 | allocated (this is not guaranteed to occur with multiple callocs or 339 | mallocs), which may also improve cache locality in some 340 | applications. 341 | 342 | The "chunks" argument is optional (i.e., may be null, which is 343 | probably the most typical usage). If it is null, the returned array 344 | is itself dynamically allocated and should also be freed when it is 345 | no longer needed. Otherwise, the chunks array must be of at least 346 | n_elements in length. It is filled in with the pointers to the 347 | chunks. 348 | 349 | In either case, independent_calloc returns this pointer array, or 350 | null if the allocation failed. If n_elements is zero and "chunks" 351 | is null, it returns a chunk representing an array with zero elements 352 | (which should be freed if not wanted). 353 | 354 | Each element must be freed when it is no longer needed. This can be 355 | done all at once using bulk_free. 356 | 357 | independent_calloc simplifies and speeds up implementations of many 358 | kinds of pools. It may also be useful when constructing large data 359 | structures that initially have a fixed number of fixed-sized nodes, 360 | but the number is not known at compile time, and some of the nodes 361 | may later need to be freed. For example: 362 | 363 | struct Node { int item; struct Node* next; }; 364 | 365 | struct Node* build_list() { 366 | struct Node** pool; 367 | int n = read_number_of_nodes_needed(); 368 | if (n <= 0) return 0; 369 | pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); 370 | if (pool == 0) die(); 371 | // organize into a linked list... 372 | struct Node* first = pool[0]; 373 | for (i = 0; i < n-1; ++i) 374 | pool[i]->next = pool[i+1]; 375 | free(pool); // Can now free the array (or not, if it is needed later) 376 | return first; 377 | } 378 | */ 379 | void** dlindependent_calloc(size_t, size_t, void**); 380 | 381 | /* 382 | independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); 383 | 384 | independent_comalloc allocates, all at once, a set of n_elements 385 | chunks with sizes indicated in the "sizes" array. It returns 386 | an array of pointers to these elements, each of which can be 387 | independently freed, realloc'ed etc. The elements are guaranteed to 388 | be adjacently allocated (this is not guaranteed to occur with 389 | multiple callocs or mallocs), which may also improve cache locality 390 | in some applications. 391 | 392 | The "chunks" argument is optional (i.e., may be null). If it is null 393 | the returned array is itself dynamically allocated and should also 394 | be freed when it is no longer needed. Otherwise, the chunks array 395 | must be of at least n_elements in length. It is filled in with the 396 | pointers to the chunks. 397 | 398 | In either case, independent_comalloc returns this pointer array, or 399 | null if the allocation failed. If n_elements is zero and chunks is 400 | null, it returns a chunk representing an array with zero elements 401 | (which should be freed if not wanted). 402 | 403 | Each element must be freed when it is no longer needed. This can be 404 | done all at once using bulk_free. 405 | 406 | independent_comallac differs from independent_calloc in that each 407 | element may have a different size, and also that it does not 408 | automatically clear elements. 409 | 410 | independent_comalloc can be used to speed up allocation in cases 411 | where several structs or objects must always be allocated at the 412 | same time. For example: 413 | 414 | struct Head { ... } 415 | struct Foot { ... } 416 | 417 | void send_message(char* msg) { 418 | int msglen = strlen(msg); 419 | size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; 420 | void* chunks[3]; 421 | if (independent_comalloc(3, sizes, chunks) == 0) 422 | die(); 423 | struct Head* head = (struct Head*)(chunks[0]); 424 | char* body = (char*)(chunks[1]); 425 | struct Foot* foot = (struct Foot*)(chunks[2]); 426 | // ... 427 | } 428 | 429 | In general though, independent_comalloc is worth using only for 430 | larger values of n_elements. For small values, you probably won't 431 | detect enough difference from series of malloc calls to bother. 432 | 433 | Overuse of independent_comalloc can increase overall memory usage, 434 | since it cannot reuse existing noncontiguous small chunks that 435 | might be available for some of the elements. 436 | */ 437 | void** dlindependent_comalloc(size_t, size_t*, void**); 438 | 439 | /* 440 | bulk_free(void* array[], size_t n_elements) 441 | Frees and clears (sets to null) each non-null pointer in the given 442 | array. This is likely to be faster than freeing them one-by-one. 443 | If footers are used, pointers that have been allocated in different 444 | mspaces are not freed or cleared, and the count of all such pointers 445 | is returned. For large arrays of pointers with poor locality, it 446 | may be worthwhile to sort this array before calling bulk_free. 447 | */ 448 | size_t dlbulk_free(void**, size_t n_elements); 449 | 450 | /* 451 | pvalloc(size_t n); 452 | Equivalent to valloc(minimum-page-that-holds(n)), that is, 453 | round up n to nearest pagesize. 454 | */ 455 | void* dlpvalloc(size_t); 456 | 457 | /* 458 | malloc_trim(size_t pad); 459 | 460 | If possible, gives memory back to the system (via negative arguments 461 | to sbrk) if there is unused memory at the `high' end of the malloc 462 | pool or in unused MMAP segments. You can call this after freeing 463 | large blocks of memory to potentially reduce the system-level memory 464 | requirements of a program. However, it cannot guarantee to reduce 465 | memory. Under some allocation patterns, some large free blocks of 466 | memory will be locked between two used chunks, so they cannot be 467 | given back to the system. 468 | 469 | The `pad' argument to malloc_trim represents the amount of free 470 | trailing space to leave untrimmed. If this argument is zero, only 471 | the minimum amount of memory to maintain internal data structures 472 | will be left. Non-zero arguments can be supplied to maintain enough 473 | trailing space to service future expected allocations without having 474 | to re-obtain memory from the system. 475 | 476 | Malloc_trim returns 1 if it actually released any memory, else 0. 477 | */ 478 | int dlmalloc_trim(size_t); 479 | 480 | /* 481 | malloc_stats(); 482 | Prints on stderr the amount of space obtained from the system (both 483 | via sbrk and mmap), the maximum amount (which may be more than 484 | current if malloc_trim and/or munmap got called), and the current 485 | number of bytes allocated via malloc (or realloc, etc) but not yet 486 | freed. Note that this is the number of bytes allocated, not the 487 | number requested. It will be larger than the number requested 488 | because of alignment and bookkeeping overhead. Because it includes 489 | alignment wastage as being in use, this figure may be greater than 490 | zero even when no user-level chunks are allocated. 491 | 492 | The reported current and maximum system memory can be inaccurate if 493 | a program makes other calls to system memory allocation functions 494 | (normally sbrk) outside of malloc. 495 | 496 | malloc_stats prints only the most commonly interesting statistics. 497 | More information can be obtained by calling mallinfo. 498 | 499 | malloc_stats is not compiled if NO_MALLOC_STATS is defined. 500 | */ 501 | void dlmalloc_stats(void); 502 | 503 | #endif /* !ONLY_MSPACES */ 504 | 505 | /* 506 | malloc_usable_size(void* p); 507 | 508 | Returns the number of bytes you can actually use in 509 | an allocated chunk, which may be more than you requested (although 510 | often not) due to alignment and minimum size constraints. 511 | You can use this many bytes without worrying about 512 | overwriting other allocated objects. This is not a particularly great 513 | programming practice. malloc_usable_size can be more useful in 514 | debugging and assertions, for example: 515 | 516 | p = malloc(n); 517 | assert(malloc_usable_size(p) >= 256); 518 | */ 519 | size_t dlmalloc_usable_size(const void*); 520 | 521 | #if MSPACES 522 | 523 | /* 524 | mspace is an opaque type representing an independent 525 | region of space that supports mspace_malloc, etc. 526 | */ 527 | typedef void* mspace; 528 | 529 | /* 530 | create_mspace creates and returns a new independent space with the 531 | given initial capacity, or, if 0, the default granularity size. It 532 | returns null if there is no system memory available to create the 533 | space. If argument locked is non-zero, the space uses a separate 534 | lock to control access. The capacity of the space will grow 535 | dynamically as needed to service mspace_malloc requests. You can 536 | control the sizes of incremental increases of this space by 537 | compiling with a different DEFAULT_GRANULARITY or dynamically 538 | setting with mallopt(M_GRANULARITY, value). 539 | */ 540 | mspace create_mspace(size_t capacity, int locked); 541 | 542 | /* 543 | destroy_mspace destroys the given space, and attempts to return all 544 | of its memory back to the system, returning the total number of 545 | bytes freed. After destruction, the results of access to all memory 546 | used by the space become undefined. 547 | */ 548 | size_t destroy_mspace(mspace msp); 549 | 550 | /* 551 | create_mspace_with_base uses the memory supplied as the initial base 552 | of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this 553 | space is used for bookkeeping, so the capacity must be at least this 554 | large. (Otherwise 0 is returned.) When this initial space is 555 | exhausted, additional memory will be obtained from the system. 556 | Destroying this space will deallocate all additionally allocated 557 | space (if possible) but not the initial base. 558 | */ 559 | mspace create_mspace_with_base(void* base, size_t capacity, int locked); 560 | 561 | /* 562 | mspace_track_large_chunks controls whether requests for large chunks 563 | are allocated in their own untracked mmapped regions, separate from 564 | others in this mspace. By default large chunks are not tracked, 565 | which reduces fragmentation. However, such chunks are not 566 | necessarily released to the system upon destroy_mspace. Enabling 567 | tracking by setting to true may increase fragmentation, but avoids 568 | leakage when relying on destroy_mspace to release all memory 569 | allocated using this space. The function returns the previous 570 | setting. 571 | */ 572 | int mspace_track_large_chunks(mspace msp, int enable); 573 | 574 | #if !NO_MALLINFO 575 | /* 576 | mspace_mallinfo behaves as mallinfo, but reports properties of 577 | the given space. 578 | */ 579 | struct mallinfo mspace_mallinfo(mspace msp); 580 | #endif /* NO_MALLINFO */ 581 | 582 | /* 583 | An alias for mallopt. 584 | */ 585 | int mspace_mallopt(int, int); 586 | 587 | /* 588 | The following operate identically to their malloc counterparts 589 | but operate only for the given mspace argument 590 | */ 591 | void* mspace_malloc(mspace msp, size_t bytes); 592 | void mspace_free(mspace msp, void* mem); 593 | void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); 594 | void* mspace_realloc(mspace msp, void* mem, size_t newsize); 595 | void* mspace_realloc_in_place(mspace msp, void* mem, size_t newsize); 596 | void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); 597 | void** mspace_independent_calloc(mspace msp, size_t n_elements, 598 | size_t elem_size, void* chunks[]); 599 | void** mspace_independent_comalloc(mspace msp, size_t n_elements, 600 | size_t sizes[], void* chunks[]); 601 | size_t mspace_bulk_free(mspace msp, void**, size_t n_elements); 602 | size_t mspace_usable_size(const void* mem); 603 | void mspace_malloc_stats(mspace msp); 604 | int mspace_trim(mspace msp, size_t pad); 605 | size_t mspace_footprint(mspace msp); 606 | size_t mspace_max_footprint(mspace msp); 607 | size_t mspace_footprint_limit(mspace msp); 608 | size_t mspace_set_footprint_limit(mspace msp, size_t bytes); 609 | void mspace_inspect_all(mspace msp, 610 | void(*handler)(void *, void *, size_t, void*), 611 | void* arg); 612 | #endif /* MSPACES */ 613 | 614 | #ifdef __cplusplus 615 | }; /* end of extern "C" */ 616 | #endif 617 | 618 | #endif /* MALLOC_280_H */ 619 | -------------------------------------------------------------------------------- /util/heap/test_binary/Makefile: -------------------------------------------------------------------------------- 1 | POLLS_RELEASE_SEED= 1496047114 2 | POLLS_TESTING_SEED= 596837685 3 | AUTHOR_ID = R3DEY 4 | SERVICE_ID = 00001 5 | CFLAGS = -Os -g -Werror -Wno-overlength-strings -Wno-packed 6 | include /usr/share/cb-testing/cgc-cb.mk 7 | -------------------------------------------------------------------------------- /util/heap/test_binary/src/libc.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBC_H 2 | #define LIBC_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef int cmp_t(const void *, const void *); 11 | void qsort(void *a, size_t n, size_t es, cmp_t *cmp); 12 | 13 | #define HUGE_VAL (__builtin_huge_val()) 14 | 15 | #ifndef LONG_MAX 16 | #define LONG_MAX SSIZE_MAX 17 | #endif 18 | #ifndef LONG_MIN 19 | #define LONG_MIN (-SSIZE_MAX - 1) 20 | #endif 21 | 22 | #define EMAX EPIPE 23 | #define ERANGE (EMAX+1) 24 | 25 | typedef __builtin_va_list va_list; 26 | 27 | #define va_arg(a, type) __builtin_va_arg(a,type) 28 | 29 | #define va_end(args) __builtin_va_end(args) 30 | 31 | #define va_start(ap, last) \ 32 | __builtin_va_start((ap), (last)) 33 | 34 | extern int errno; 35 | 36 | unsigned int htonl(unsigned int hostlong); 37 | 38 | size_t strlen(const char *str); 39 | char *strcpy(char *dst, const char *src); 40 | int strcmp(const char *s1, const char *s2); 41 | char *strchr(const char *s, int c); 42 | long strtol(const char *str, char **endptr, int base); 43 | char *strsep(char **stringp, const char *delim); 44 | char *strdup(const char *str); 45 | 46 | double atof(const char *nptr); 47 | 48 | void *memset(void *dst, int c, unsigned int n); 49 | int memcmp(const void *b1, const void *b2, size_t n); 50 | void *memcpy(void *b1, const void *b2, size_t n); 51 | void *memmove(void *b1, const void *b2, size_t n); 52 | 53 | int toupper(int c); 54 | int tolower(int c); 55 | 56 | int transmit_all(int fd, const void *buf, const size_t size); 57 | unsigned int recieve_all(int fd, char *buf, unsigned int size); 58 | int read_until_delim(int fd, char *buf, unsigned int size, char endchar); 59 | 60 | #define EOF -1 61 | 62 | #ifndef NULL 63 | #define NULL ((void*)0) 64 | #endif 65 | 66 | #define _FILE_STATE_OPEN 1 67 | #define _FILE_STATE_ERROR 2 68 | #define _FILE_STATE_EOF 4 69 | #define _FILE_HAVE_LAST 8 70 | 71 | struct _FILE; 72 | typedef struct _FILE FILE; 73 | 74 | extern FILE *stdin; 75 | extern FILE *stdout; 76 | extern FILE *stderr; 77 | extern FILE *stdneg; 78 | 79 | int fgetc(FILE *); 80 | int getc(FILE *); 81 | int getchar(void); 82 | int putchar(int c); 83 | int fputc(int c, FILE *stream); 84 | 85 | char *fgets(char *, int, FILE *); 86 | int fdread(void *, size_t, size_t, int); 87 | int fread(void *, size_t, size_t, FILE *); 88 | int fwrite(const void *buf, size_t size, size_t nmemb, FILE *f); 89 | 90 | int ferror(FILE *stream); 91 | int feof(FILE *stream); 92 | 93 | int printf(const char *format, ...); 94 | int dprintf(int fd, const char *format, ...); 95 | int fprintf(FILE * stream, const char *format, ...); 96 | int snprintf(char *str, size_t size, const char *format, ...); 97 | int sprintf(char *str, const char *format, ...); 98 | int vsprintf(char *str, const char *format, va_list ap); 99 | int vprintf(const char *format, va_list ap); 100 | int vfprintf(FILE *stream, const char *format, va_list ap); 101 | int vdprintf(int fd, const char *format, va_list ap); 102 | int vsnprintf(char *str, size_t size, const char *format, va_list ap); 103 | 104 | int fflush(FILE *stream); 105 | 106 | ssize_t getline(char **lineptr, size_t *n, FILE *stream); 107 | ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream); 108 | 109 | ssize_t fd_getline(char **lineptr, size_t *n, int fd); 110 | ssize_t fd_getdelim(char **lineptr, size_t *n, int delim, int fd); 111 | 112 | void *malloc(size_t size); 113 | void *calloc(size_t number, size_t size); 114 | void *realloc(void *ptr, size_t size); 115 | void free(void *ptr); 116 | 117 | #ifdef __cplusplus 118 | } 119 | #endif 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /util/heap/test_binary/src/test.c: -------------------------------------------------------------------------------- 1 | #define USE_DL_PREFIX 2 | #include "malloc.h" 3 | #include 4 | #include "libc.h" 5 | /* 6 | void *malloc(size_t size); 7 | void free(void *buf); 8 | void *realloc(void *buf, size_t size); 9 | void *calloc(size_t n_elements, size_t elem_size); 10 | 11 | #define MAGIC 0xdeadbeef 12 | 13 | //typedef struct MAGIC 14 | void *malloc(size_t size) { 15 | return dlmalloc(size); 16 | } 17 | 18 | void free(void *buf) { 19 | dlfree(buf); 20 | } 21 | 22 | void *realloc(void *buf, size_t size) { 23 | return dlrealloc(buf, size); 24 | } 25 | void *calloc(size_t n_elements, size_t elem_size) { 26 | size_t req = 0; 27 | if (n_elements != 0) { 28 | req = n_elements * elem_size; 29 | if (((n_elements | elem_size) & ~(size_t)0xffff) && 30 | (req / n_elements != elem_size)) 31 | //Just fail 32 | return NULL; 33 | } 34 | return dlcalloc(req, 1); 35 | } 36 | 37 | */ 38 | int count = 25; 39 | 40 | void check(char *b, int s, int v) { 41 | int i; 42 | for (i = 0; i < s; i++) { 43 | if (b[i] != v) { 44 | printf("FAILED exp %d != actual %d, s=%d\n", v, b[i], s); 45 | break; 46 | } 47 | } 48 | } 49 | int main() { 50 | char *buf[50]; 51 | int i; 52 | for (i = 2; i < count; i++) { 53 | buf[i] = malloc(1<= size] 41 | if tmp: 42 | addr, hsize = tmp[0] 43 | holes.remove(tmp[0]) 44 | raw = pt.asm(txt, addr=addr) 45 | if len(raw) <= hsize: 46 | pt.patch(addr, raw=raw, is_asm=True) 47 | funcs[func].append((addr, len(raw))) 48 | continue 49 | 50 | addr, isize = pt.inject(asm=txt, size=True, is_asm=True) 51 | funcs[func].append((addr, isize)) 52 | 53 | return funcs 54 | -------------------------------------------------------------------------------- /util/patch/dis.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | import re as _re 3 | 4 | # TODO: this is all x86_specific :( 5 | JMPS = [ 6 | X86_INS_JA, 7 | X86_INS_JAE, 8 | X86_INS_JB, 9 | X86_INS_JBE, 10 | X86_INS_JCXZ, 11 | X86_INS_JE, 12 | X86_INS_JECXZ, 13 | X86_INS_JG, 14 | X86_INS_JGE, 15 | X86_INS_JL, 16 | X86_INS_JLE, 17 | X86_INS_JMP, 18 | X86_INS_JNE, 19 | X86_INS_JNO, 20 | X86_INS_JNP, 21 | X86_INS_JNS, 22 | X86_INS_JO, 23 | X86_INS_JP, 24 | X86_INS_JRCXZ, 25 | X86_INS_JS, 26 | 27 | X86_INS_CALL, 28 | X86_INS_LOOP, 29 | X86_INS_LOOPE, 30 | X86_INS_LOOPNE, 31 | ] 32 | 33 | class Base(object): 34 | def __repr__(self): 35 | return '<%s %s>' % (self.__class__.__name__, repr(str(self))) 36 | 37 | def re_match(maybe_re, s): 38 | pass 39 | 40 | class Op(Base): 41 | ins = None 42 | op = None 43 | 44 | @classmethod 45 | def fromop(cls, ins, op): 46 | i = cls() 47 | i.any = False 48 | i.ins = ins 49 | i.op = op 50 | i.parse() 51 | return i 52 | 53 | def __ne__(self, other): 54 | return not(self == other) 55 | 56 | class LabelOp(Op): 57 | def __init__(self, name=None, any=False): 58 | if name is None: 59 | any = True 60 | self.name = name 61 | self.any = any 62 | 63 | def __eq__(self, other): 64 | if other == LabelOp: 65 | return True 66 | return isinstance(other, LabelOp) and (other.name == self.name or self.any or other.any) 67 | 68 | def __str__(self): 69 | return self.name 70 | 71 | class Imm(Op): 72 | def __init__(self, val=None, any=False): 73 | if val is None: 74 | val = 0 75 | any = True 76 | self.val = val 77 | self.any = any 78 | 79 | def parse(self): 80 | self.val = self.op.imm 81 | 82 | def __eq__(self, other): 83 | if isinstance(other, (int, long)) and (self.val == other): 84 | return True 85 | if other == Imm: 86 | return True 87 | return isinstance(other, Imm) and (other.val == self.val or self.any or other.any) 88 | 89 | def __cmp__(self, other): 90 | if not isinstance(other, Imm): 91 | raise TypeError 92 | return cmp(self.val, other.val) 93 | 94 | def __str__(self): 95 | if self.any: 96 | return '' 97 | if self.val >= 0: 98 | return '0x%x' % self.val 99 | else: 100 | return '-0x%x' % abs(self.val) 101 | 102 | class Reg(Op): 103 | def __init__(self, reg=None, any=False, re=None): 104 | self.re = None 105 | self.reg = reg or '' 106 | self.any = any 107 | if re is not None: 108 | self.re = _re.compile(re) 109 | elif reg is None: 110 | self.any = True 111 | 112 | def parse(self): 113 | self.reg = self.ins.reg_name(self.op.reg) 114 | 115 | def __eq__(self, other): 116 | if isinstance(other, basestring) and (self.reg == other or self.re and self.re.match(other)): 117 | return True 118 | return isinstance(other, Reg) and ( 119 | other.reg == self.reg or 120 | self.any or other.any or 121 | bool(self.re and self.re.match(other.reg)) or 122 | bool(other.re and other.re.match(self.reg))) 123 | 124 | def __str__(self): 125 | if self.any: 126 | return '' 127 | if self.re and not self.reg: 128 | return '/%s/' % self.re.pattern 129 | return self.reg 130 | 131 | class Mem(Op): 132 | MEM_SIZE = { 133 | 1: 'byte ptr', 134 | 2: 'word ptr', 135 | 4: 'dword ptr', 136 | 8: 'qword ptr', 137 | 10: 'xword ptr', 138 | } 139 | 140 | def __init__(self, size=0, base=None, index=None, segment=None, scale=1, off=0, any=False): 141 | self.size = size 142 | self.base = base 143 | self.index = index 144 | self.segment = segment 145 | self.scale = scale 146 | self.off = off 147 | self.any = any 148 | 149 | def parse(self): 150 | ins = self.ins 151 | op = self.op.mem 152 | 153 | self.size = self.op.size 154 | # TODO: op.size = dword ptr? 155 | if op.base: 156 | self.base = ins.reg_name(op.base) 157 | if op.index: 158 | self.index = ins.reg_name(op.index) 159 | if op.segment: 160 | # segment looks like es:[%s] 161 | self.segment = ins.reg_name(op.segment) 162 | self.scale = op.scale 163 | self.off = op.disp 164 | 165 | def __eq__(self, other): 166 | if other == Mem: 167 | return True 168 | return isinstance(other, Mem) and (( 169 | self.size, self.base, self.index, self.segment, self.scale, self.off, 170 | ) == (other.size, other.base, other.index, other.segment, other.scale, other.off) or self.any or other.any) 171 | 172 | def __str__(self): 173 | if self.any: 174 | return '' 175 | tmp = [] 176 | if self.base: 177 | tmp.append(self.base) 178 | if self.index: 179 | if tmp: tmp.append('+') 180 | tmp.append(self.index) 181 | if self.scale != 1: 182 | # you'd better have an index to multiply! 183 | assert(self.index) 184 | tmp += ['*', '%d' % self.scale] 185 | if self.off: 186 | if tmp: 187 | if self.off > 0: tmp.append('+') 188 | else: tmp.append('-') 189 | tmp.append('%d' % abs(self.off)) 190 | 191 | bracket = '[%s]' % (' '.join(tmp)) 192 | if self.segment: 193 | bracket = '%s:%s' % (self.segment, bracket) 194 | final = '%s %s' % (self.MEM_SIZE[self.size], bracket) 195 | return final 196 | 197 | OPS = { 198 | X86_OP_IMM: Imm, 199 | X86_OP_REG: Reg, 200 | X86_OP_MEM: Mem, 201 | } 202 | 203 | class Ins(Base): 204 | addr = None 205 | ins = None 206 | 207 | @classmethod 208 | def fromins(cls, ins): 209 | ops = [] 210 | for op in ins.operands: 211 | opcls = OPS.get(op.type) 212 | if opcls: 213 | ops.append(opcls.fromop(ins, op)) 214 | else: 215 | print 'UNSUPPORTED OP', op, ins.op_str 216 | assert(False) 217 | 218 | c = cls(ins.mnemonic, *ops) 219 | c.ins = ins 220 | c.addr = ins.address 221 | return c 222 | 223 | @property 224 | def dst(self): return self.ops[0] 225 | 226 | @property 227 | def src(self): return self.ops[1] 228 | 229 | @property 230 | def a(self): return self.ops[0] 231 | 232 | @property 233 | def b(self): return self.ops[1] 234 | 235 | @property 236 | def c(self): return self.ops[2] 237 | 238 | @property 239 | def d(self): return self.ops[3] 240 | 241 | @property 242 | def e(self): return self.ops[4] 243 | 244 | @property 245 | def f(self): return self.ops[5] 246 | 247 | def __init__(self, mne, *ops, **kwargs): 248 | self.mne = mne 249 | self.ops = ops 250 | self.label = None 251 | self.any = kwargs.get('any', False) 252 | re = kwargs.pop('re', None) 253 | self.re = re 254 | if re is not None: 255 | self.re = re.compile(re) 256 | 257 | def op_str(self): 258 | return ', '.join(map(str, self.ops)) 259 | 260 | def __eq__(self, other): 261 | if isinstance(other, basestring) and other == self.mne: 262 | return True 263 | elif isinstance(other, Ins): 264 | return isinstance(other, Ins) and ( 265 | self.any or other.any or 266 | (self.mne == other.mne or 267 | bool(self.re and self.re.match(other.mne)) or 268 | bool(other.re and other.re.match(self.mne))) and 269 | len(other.ops) == len(self.ops) and all(other.ops[i] == op for i, op in enumerate(self.ops))) 270 | return False 271 | 272 | def __str__(self): 273 | out = '%s %s' % (self.mne, self.op_str()) 274 | if self.re and not self.mne: 275 | out = '/%s/ %s' % (self.re.pattern, self.op_str) 276 | if self.label: 277 | out = '%s: %s' % self.label 278 | return out 279 | 280 | class Label(Base): 281 | mne = None 282 | ops = () 283 | 284 | def __init__(self, name): 285 | self.name = name 286 | 287 | def __str__(self): 288 | return '%s:' % self.name 289 | 290 | class IR(list): 291 | def cs(self): 292 | 'converts IR back to capstone assembly' 293 | return [ins.ins for ins in self] 294 | 295 | def asm(self): 296 | 'converts IR to assembly source' 297 | return '\n'.join(map(str, self)) 298 | 299 | def findall(self, query, stop=None): 300 | return IR(IRStream(self).filter(query, stop)) 301 | 302 | class IRStream: 303 | def __init__(self, gen): 304 | self.gen = gen 305 | 306 | def __iter__(self): 307 | for ins in self.gen: 308 | if isinstance(ins, Base): 309 | yield ins 310 | else: 311 | yield Ins.fromins(ins) 312 | 313 | def filter(self, query=None, stop=None): 314 | def fuzzy(a, match): 315 | if match is None: 316 | return True 317 | if isinstance(match, (list, tuple)): 318 | for other in match: 319 | if a == other: 320 | return True 321 | else: 322 | return a == match 323 | 324 | for ins in self: 325 | if fuzzy(ins, query): 326 | yield ins 327 | if stop and fuzzy(ins, stop): 328 | break 329 | 330 | def irdis(dis): 331 | if not dis: 332 | return IR([]) 333 | dis_addr = dis[0].address 334 | size = dis[-1].address + dis[-1].size - dis_addr 335 | 336 | tmp = [] 337 | next_label = 1 338 | labels = {} 339 | # TODO: make more portable (maybe allow arch to do this step) 340 | # find jumps 341 | for ins in dis: 342 | if ins.id in JMPS: 343 | dst = ins.operands[0] 344 | if dst.type == X86_OP_IMM: 345 | addr = dst.imm 346 | if addr >= dis_addr and addr < dis_addr + size: 347 | if addr not in labels: 348 | labels[addr] = Label('L%d' % next_label) 349 | next_label += 1 350 | 351 | x = Ins(ins.mnemonic, LabelOp(labels[addr].name)) 352 | x.addr = ins.address 353 | x.ins = ins 354 | tmp.append(x) 355 | continue 356 | tmp.append(Ins.fromins(ins)) 357 | 358 | out = [] 359 | for i, ins in enumerate(tmp): 360 | label = labels.get(ins.addr) 361 | if label: 362 | out.append(label) 363 | out.append(ins) 364 | 365 | ir = IR(out) 366 | return ir 367 | -------------------------------------------------------------------------------- /util/patch/syscall.py: -------------------------------------------------------------------------------- 1 | from capstone.x86_const import * 2 | 3 | from dis import irdis, IR, Ins, Imm, Reg, Mem 4 | 5 | syscall_table = { 6 | 1: '_terminate', 7 | 2: 'transmit', 8 | 3: 'receive', 9 | 4: 'fdwait', 10 | 5: 'allocate', 11 | 6: 'deallocate', 12 | 7: 'random', 13 | } 14 | 15 | def find_syscall_funcs(pt): 16 | for func in pt.funcs(): 17 | ir = irdis(func.dis()) 18 | sysnum = None 19 | sysname = None 20 | int80 = None 21 | bad = [] 22 | for ins in ir: 23 | if ins == Ins('mov', Reg('eax'), Imm(any=True)) and not sysnum: 24 | # imm to reg is safe 25 | sysnum = ins.src.val 26 | sysname = syscall_table.get(sysnum) 27 | elif ins == pt.ir('mov ebp, esp'): 28 | # some syscall functions do this 29 | pass 30 | elif ins == Ins('push', Reg(any=True)) or ins == Ins('pop', Reg(any=True)) or ins == 'ret': 31 | # push/pop/ret is safe 32 | pass 33 | elif ins == Ins('int', Imm(0x80)) and not int80: 34 | # of course it'll have an int 0x80 35 | int80 = ins 36 | elif ins == Ins('mov', Reg(any=True), Mem(any=True)) and ins.src.base == 'esp': 37 | # reg = mem is safe if it's stack-relative 38 | pass 39 | else: 40 | bad.append(ins) # assume unsafe 41 | 42 | if int80 is not None: 43 | if sysname is None or sysnum is None: 44 | pt.debug('[*] Syscall function skipped (unknown num: %s)' % sysnum) 45 | if bad: 46 | pt.debug('[*] Syscall function was marked as bad:') 47 | pt.debug(ir.asm()) 48 | pt.debug('[*] Offending instructions:') 49 | # TODO: make this pt.debug(dis=bad)? 50 | pt.debug(IR(bad).asm()) 51 | continue 52 | 53 | yield (func, int80, sysname, sysnum) 54 | -------------------------------------------------------------------------------- /util/stdlib/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | from util import read 5 | 6 | __all__ = ['declare'] 7 | 8 | header_names = ['stdlib/types.h', 'stdlib/defines.h', 'stdlib/syscall.h'] 9 | headers = '\n'.join(map(read, header_names)) 10 | 11 | def declare(linker): 12 | if not '_terminate' in linker: 13 | linker.declare(headers=headers) 14 | linker.autodecl(read('stdlib/libc.c')) 15 | linker.autodecl(read('stdlib/syscalls.c')) 16 | linker.autodecl(read('stdlib/ctype.c')) 17 | linker.autodecl(read('stdlib/string.c')) 18 | linker.autodecl(read('stdlib/chk.c')) 19 | linker.declare(headers='#include ') 20 | linker.autodecl(read('stdlib/io.c')) 21 | linker.declare(symbols={ 22 | 'itoa': 'char *itoa(unsigned int i, int base)', 23 | 'atoi': 'int atoi(char *str)', 24 | }, source=read('stdlib/num.c')) 25 | -------------------------------------------------------------------------------- /util/stdlib/chk.c: -------------------------------------------------------------------------------- 1 | void stack_chk_fail() { 2 | printf("**** stack smashing detected ****\n"); 3 | _terminate(1); 4 | } 5 | -------------------------------------------------------------------------------- /util/stdlib/ctype.c: -------------------------------------------------------------------------------- 1 | int digittoint(int c) { 2 | if (isxdigit(c)) { 3 | if (isdigit(c)) return c - 0x30; 4 | else return toupper(c) - 0x37; 5 | } else { 6 | return 0; 7 | } 8 | } 9 | 10 | int isalnum(int c) { 11 | return isalpha(c) || isdigit(c); 12 | } 13 | 14 | int isalpha(int c) { 15 | return islower(c) || isupper(c); 16 | } 17 | 18 | int isascii(int c) { 19 | return c >= 0 && c <= 127; 20 | } 21 | 22 | int iscntrl(int c) { 23 | return (c >= 0 && c <= 0x1f) || 24 | c == 0x7f; 25 | } 26 | 27 | int isdigit(int c) { 28 | return c >= '0' && c <= '9'; 29 | } 30 | 31 | int isgraph(int c) { 32 | return c >= 0x21 && c <= 0x7e; 33 | } 34 | 35 | int ishexnumber(int c) { 36 | return isxdigit(c); 37 | } 38 | 39 | int islower(int c) { 40 | return c >= 'a' && c <= 'z'; 41 | } 42 | 43 | int isnumber(int c) { 44 | return isdigit(c); 45 | } 46 | 47 | int isprint(int c) { 48 | return c >= 0x20 && c <= 0x7e; 49 | } 50 | 51 | int ispunct(int c) { 52 | return (c >= 0x21 && c <= 0x2f) || 53 | (c >= 0x3a && c <= 0x3f) || 54 | c == '@' || 55 | (c >= 0x5b && c <= 0x60) || 56 | (c >= 0x7b && c <= 0x7e); 57 | } 58 | 59 | int isrune(int c) { 60 | return isascii(c); 61 | } 62 | 63 | int isspace(int c) { 64 | return c == '\t' || 65 | c == '\n' || 66 | c == '\v' || 67 | c == '\f' || 68 | c == '\r' || 69 | c == ' '; 70 | } 71 | 72 | int isupper(int c) { 73 | return c >= 'A' && c <= 'Z'; 74 | } 75 | 76 | int isxdigit(int c) { 77 | return isdigit(c) || 78 | (c >= 'a' && c <= 'f') || 79 | (c >= 'A' && c <= 'F'); 80 | } 81 | 82 | int toascii(int c) { 83 | return c & 0x7f; 84 | } 85 | 86 | int tolower(int c) { 87 | return isupper(c) ? (c + 0x20) : c; 88 | } 89 | 90 | int toupper(int c) { 91 | return islower(c) ? (c - 0x20) : c; 92 | } 93 | -------------------------------------------------------------------------------- /util/stdlib/defines.h: -------------------------------------------------------------------------------- 1 | #define true 1 2 | #define false 0 3 | 4 | #define STDIN 0 5 | #define STDOUT 1 6 | #define STDERR 2 7 | 8 | #define NULL ((void *)0) 9 | 10 | #define SSIZE_MAX 2147483647 11 | #define SIZE_MAX 4294967295 12 | #define FD_SETSIZE 1024 13 | 14 | #define EBADF 1 15 | #define EFAULT 2 16 | #define EINVAL 3 17 | #define ENOMEM 4 18 | #define ENOSYS 5 19 | #define EPIPE 6 20 | -------------------------------------------------------------------------------- /util/stdlib/io.c: -------------------------------------------------------------------------------- 1 | int gets(char *buf, size_t size) { 2 | for (int i = 0; i < size - 1; i++) { 3 | int err = receive(0, buf, 1, 0); 4 | if (err) return err; 5 | if (*buf == '\n') break; 6 | buf++; 7 | } 8 | *buf = '\0'; 9 | return 0; 10 | } 11 | 12 | void puts(char *s) { 13 | transmit(1, s, strlen(s), 0); 14 | } 15 | 16 | void putc(int c) { 17 | transmit(1, &c, 1, 0); 18 | } 19 | 20 | int printf(const char *fmt, ...) { 21 | #define arg(type) va_arg(params, type) 22 | const char *pos = fmt, *flush = fmt; 23 | char c; 24 | int control = false; 25 | 26 | va_list params; 27 | va_start(params, fmt); 28 | while ((c = *pos++) != 0) { 29 | if (control) { 30 | if (flush < pos - 2) { 31 | transmit(1, flush, pos - flush - 2, 0); 32 | flush = pos; 33 | } 34 | control = false; 35 | // TODO: implement padding controls 36 | if (c >= '0' && c <= '9') { 37 | control = true; 38 | continue; 39 | } 40 | switch (c) { 41 | case '%': 42 | putc(c); 43 | break; 44 | case 'c': 45 | putc(arg(int)); 46 | break; 47 | case 's': { 48 | char *s = arg(char *); 49 | if (s == NULL) { 50 | puts("(null)"); 51 | } else { 52 | puts(s); 53 | } 54 | break; 55 | } 56 | case 'd': 57 | case 'i': { 58 | int i = arg(int); 59 | if (i < 0) { 60 | i = (i ^ -1) + 1; 61 | puts("-"); 62 | } 63 | puts(itoa(i, 10)); 64 | break; 65 | } 66 | case 'u': 67 | puts(itoa(arg(int), 10)); 68 | break; 69 | case 'p': 70 | case 'x': 71 | puts(itoa(arg(int), 16)); 72 | break; 73 | case 'X': 74 | puts(strupr(itoa(arg(int), 16))); 75 | break; 76 | } 77 | } else if (c == '%') { 78 | control = true; 79 | } 80 | } 81 | if (flush < pos - 1) { 82 | transmit(1, flush, pos - flush - 1, 0); 83 | flush = pos; 84 | } 85 | va_end(params); 86 | #undef arg 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /util/stdlib/libc.c: -------------------------------------------------------------------------------- 1 | int memcmp(const void *p1, const void *p2, size_t len) { 2 | unsigned char *c1 = (unsigned char *)p1, *c2 = (unsigned char *)p2; 3 | for (size_t i = 0; i < len; i++) { 4 | if (c1[i] < c2[i]) { 5 | return -1; 6 | } else if (c1[i] > c2[i]) { 7 | return 1; 8 | } 9 | } 10 | return 0; 11 | } 12 | 13 | void *memchr(const void *ptr, int c, size_t n) { 14 | unsigned const char *cptr = ptr; 15 | for (size_t i = 0; i < n; i++) { 16 | if (cptr[i] == c) { 17 | return (void *)cptr+i; 18 | } 19 | } 20 | return 0; 21 | } 22 | 23 | void *memcpy(void *dst, const void *src, size_t len) { 24 | char *cdst = dst; 25 | const char *csrc = src; 26 | for (size_t i = 0; i < len; i++) { 27 | *cdst++ = *csrc++; 28 | } 29 | return dst; 30 | } 31 | 32 | void *memmove(void *dst, const void *src, size_t len) { 33 | // TODO: handle overlapping memory 34 | return memcpy(dst, src, len); 35 | } 36 | 37 | void *memset(void *ptr, int val, size_t len) { 38 | unsigned char *cptr = ptr, c = val; 39 | for (size_t i = 0; i < len; i++) { 40 | *cptr++ = c; 41 | } 42 | return ptr; 43 | } 44 | -------------------------------------------------------------------------------- /util/stdlib/num.c: -------------------------------------------------------------------------------- 1 | static const char *digits = "0123456789abcdef"; 2 | 3 | char *itoa(unsigned int i, int base) { 4 | if (base < 0 || base > 16) { 5 | return "(invalid base)"; 6 | } 7 | static char buf[11] = {0}; 8 | char *pos = &buf[10]; 9 | do { 10 | *--pos = (char)digits[i % base]; 11 | i /= base; 12 | } while (i > 0); 13 | return pos; 14 | } 15 | 16 | int atoi(char *str) { 17 | int i = 0; 18 | char c; 19 | while ((c = *str++) != '\0') { 20 | if (c >= '0' && c <= '9') { 21 | i *= 10; 22 | i += (c - 48); 23 | } 24 | } 25 | return i; 26 | } 27 | -------------------------------------------------------------------------------- /util/stdlib/string.c: -------------------------------------------------------------------------------- 1 | size_t strlen(const char *s1) { 2 | size_t i = 0; 3 | do i++; while (s1[i] != '\0'); 4 | return i; 5 | } 6 | 7 | void *strdup(const char *s1) { 8 | void *out = malloc(strlen(s1)); 9 | strcpy(out, s1); 10 | return out; 11 | } 12 | 13 | int strcmp(const char *s1, const char *s2) { 14 | return strncmp(s1, s2, SIZE_MAX); 15 | } 16 | 17 | int strncmp(const char *s1, const char *s2, size_t n) { 18 | for (size_t pos = 0; pos < n; pos++) { 19 | char c1 = *s1++; 20 | char c2 = *s2++; 21 | if (c1 < c2) return -1; 22 | if (c1 > c2) return 1; 23 | if (c1 == 0 || c2 == 0) break; 24 | } 25 | return 0; 26 | } 27 | 28 | char *strcpy(char *dst, const char *src) { 29 | return strncpy(dst, src, SIZE_MAX); 30 | } 31 | 32 | char *strncpy(char *dst, const char *src, size_t n) { 33 | size_t i = 0; 34 | do dst[i] = *src; while (*src++ != '\0' && i++ < n); 35 | return dst; 36 | } 37 | 38 | char *strcat(char *s1, const char *s2) { 39 | return strncat(s1, s2, SIZE_MAX); 40 | } 41 | 42 | char *strncat(char *s1, const char *s2, size_t n) { 43 | strncpy(s1 + strlen(s1), s2, n); 44 | return s1; 45 | } 46 | 47 | char *strupr(char *s1) { 48 | do *s1 = toupper(*s1); while (*s1++ != '\0'); 49 | return s1; 50 | } 51 | -------------------------------------------------------------------------------- /util/stdlib/syscall.h: -------------------------------------------------------------------------------- 1 | #define SYS__terminate 1 2 | #define SYS_transmit 2 3 | #define SYS_receive 3 4 | #define SYS_fdwait 4 5 | #define SYS_allocate 5 6 | #define SYS_deallocate 6 7 | #define SYS_random 7 8 | 9 | #define __reg(name) register uint32_t name __asm__(#name) 10 | 11 | inline int syscall1(uint32_t num, uint32_t arg1) { 12 | __reg(eax) = num; 13 | __asm__ volatile ("int $0x80" :"+a"(eax) :: "memory"); 14 | return eax; 15 | } 16 | 17 | inline int syscall2(uint32_t num, uint32_t arg1, uint32_t arg2) { 18 | __reg(eax) = num; __reg(ebx) = arg1; __reg(ecx) = arg2; 19 | __asm__ volatile ("int $0x80" :"+a"(eax) :"r"(ebx), "r"(ecx) : "memory"); 20 | return eax; 21 | } 22 | 23 | inline int syscall3(uint32_t num, uint32_t arg1, uint32_t arg2, uint32_t arg3) { 24 | __reg(eax) = num; __reg(ebx) = arg1; __reg(ecx) = arg2; __reg(edx) = arg3; 25 | __asm__ volatile ("int $0x80" :"+a"(eax) :"r"(ebx), "r"(ecx), "r"(edx) : "memory"); 26 | return eax; 27 | } 28 | 29 | inline int syscall4(uint32_t num, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) { 30 | __reg(eax) = num; __reg(ebx) = arg1; __reg(ecx) = arg2; __reg(edx) = arg3; __reg(esi) = arg4; 31 | __asm__ volatile ("int $0x80" :"+a"(eax) :"r"(ebx), "r"(ecx), "r"(edx), "r"(esi) : "memory"); 32 | return eax; 33 | } 34 | 35 | #undef __reg 36 | -------------------------------------------------------------------------------- /util/stdlib/syscalls.c: -------------------------------------------------------------------------------- 1 | void _terminate(int code) { 2 | syscall1(SYS__terminate, code); 3 | } 4 | 5 | int transmit(int fd, const void *buf, uint32_t size, uint32_t *count) { 6 | return syscall4(SYS_transmit, fd, (uint32_t)buf, size, (uint32_t)count); 7 | } 8 | 9 | int receive(int fd, void *buf, uint32_t size, uint32_t *count) { 10 | return syscall4(SYS_receive, fd, (uint32_t)buf, size, (uint32_t)count); 11 | } 12 | 13 | // TODO: not implemented 14 | int fdwait() { 15 | return 0; 16 | } 17 | 18 | int allocate(uint32_t size, int is_x, void *addr) { 19 | return syscall3(SYS_allocate, size, is_x, (uint32_t)addr); 20 | } 21 | 22 | int deallocate(void *addr, uint32_t size) { 23 | return syscall2(SYS_deallocate, (uint32_t)addr, size); 24 | } 25 | 26 | int random(void *buf, uint32_t size, uint32_t *count) { 27 | return syscall3(SYS_random, (uint32_t)buf, size, (uint32_t)count); 28 | } 29 | -------------------------------------------------------------------------------- /util/stdlib/types.h: -------------------------------------------------------------------------------- 1 | typedef unsigned char uint8_t; 2 | typedef unsigned short uint16_t; 3 | typedef unsigned long uint32_t; 4 | typedef unsigned long long uint64_t; 5 | typedef char int8_t; 6 | typedef short int16_t; 7 | typedef long int32_t; 8 | typedef long long int64_t; 9 | typedef uint32_t size_t; 10 | typedef int32_t ssize_t; 11 | --------------------------------------------------------------------------------