├── .gitignore ├── README ├── __init__.py ├── atrophy.py ├── bench.py ├── defines ├── AtrophyCompleter.py ├── ElfEater.py ├── ProcMap.py ├── __init__.py ├── ascii_art.py ├── asm_utils.py ├── elf_defines.py ├── emu_utils.py ├── ptrace_defines.py ├── syscalls.py └── utility.py ├── external ├── capstone-3.0.4.tar.gz ├── install_stones.sh └── keystone-0.9.1.tar.gz ├── test.py └── testprogs ├── .gdb_history ├── Makefile ├── basic.c ├── func ├── function_tests.c ├── heap_gen.c ├── loop ├── looper.c └── peda-session-a.out.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | defines/*.pyc 3 | *.swp 4 | *.swo 5 | external/capstone-3.0.4 6 | external/keystone-0.9.1 7 | .atrophy-history 8 | .gdb_history 9 | *peda* 10 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A x86/x64 Linux debugger utilizing PTRACE. 2 | 3 | Written in Python stdlib (<3 portability), with extensions Keystone/Capstone/Unicorn Engine. 4 | 5 | ***WARNING*** there's alot of features not working (Snapshots,signal handling,multithreading). 6 | 7 | [^_^] 8 | 9 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thiefyface/Atrophy/ede63b95890bb19058622e644db66a6cddc791bd/__init__.py -------------------------------------------------------------------------------- /atrophy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Atrophy Debugger 3 | # Created/Copyright Lilith Wyatt <(^.^)> 4 | # BSD Licensing 5 | import re 6 | import os 7 | import sys 8 | import time 9 | import glob 10 | import copy 11 | import struct 12 | import socket 13 | import subprocess 14 | 15 | from signal import * 16 | from ctypes import * 17 | from ctypes.util import find_library 18 | 19 | from defines.utility import * 20 | from defines.ProcMap import * 21 | from defines.syscalls import * 22 | from defines.ElfEater import * 23 | from defines.ptrace_defines import * 24 | from defines.AtrophyCompleter import * 25 | 26 | try: 27 | from defines.asm_utils import * 28 | except Exception as e: 29 | print e 30 | print WARN("Assembly utils disabled") 31 | 32 | try: 33 | from defines.emu_utils import * 34 | except Exception as e: 35 | print e 36 | print WARN("Emulation utils disabled") 37 | 38 | ### TODO: 39 | # - Complete Threading Support (oh god why) 40 | # - Make savepoints more reliable ( lol, Executing loop: { ) 41 | # - memory isn't getting written or perhaps can't? 42 | # - resolving dynamic symbols 43 | # Syscalls 44 | # - keep on listing x64 syscall arg types 45 | # Signals 46 | # Breakpoints 47 | # - how to make persistant ones without 48 | # messing up backtraces... 49 | # 64-Bit issues 50 | # - x64 backtrace 51 | # Feature Wishlist 52 | # - Hardware/Guard breaks 53 | # - Heap dumping 54 | # - Rop gadgets 55 | # - Auto-attach/monitor via process name (e.g. telnetd) 56 | # - start thinking about hooking certain calls 57 | # - - Syscalls especially 58 | # -- 'dt' from windbg.... 59 | # Command Wishlist 60 | # - "copy"/"paste" command - save X bytes at addr Y (clipboard essentially) 61 | # - "restart" command - reload prog/etc 62 | # Emulator Issues: 63 | # - Restoring memory along with register context 64 | # - Make the emulator commands print useful info 65 | ### 66 | 67 | class Atrophy(object): 68 | 69 | def __init__(self,rhost="",rport=-1): 70 | 71 | self.verbose = False 72 | 73 | self.last_cmd = "" 74 | self.debugger_pid = os.getpid() 75 | self.debug_pid = 0 76 | self.proc_map = None 77 | self.traceme_flag = False 78 | self.elf = None 79 | self.completer = None 80 | 81 | 82 | ## Comms variables 83 | self.comms_sock = None 84 | if rhost and rport > -1: 85 | self.rhost=rhost 86 | self.rport=rport 87 | 88 | try: 89 | self.comms_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 90 | self.comms_sock.connect((rhost,rport)) 91 | except: 92 | self.output(ERROR("Unable to connect to %s:%d"%(rhost,rport))) 93 | sys.exit(1) 94 | 95 | # since this relies on capstone/keystone/unicorn 96 | self.AsmUtil = False 97 | self.EmuUtil = False 98 | 99 | try: 100 | self.AsmUtil = AsmUtil() 101 | self.AsmUtil.share_functions(self.getString,self.getMem,self.getRegs) 102 | 103 | except Exception as e: 104 | self.output(str(e)) 105 | self.output("Could not load Capstone/Keystone, Certain features disabled") 106 | 107 | try: 108 | self.EmuUtil = EmuUtil() 109 | # so disasm can be commented from emu (jump taken/rets/etc) 110 | self.EmuUtil.init_commenting_function(self.AsmUtil.emu_append_comment) 111 | 112 | except Exception as e: 113 | self.output(str(e)) 114 | self.output("Could not load Unicorn Engine, Emulation features disabled") 115 | 116 | 117 | if maxsize < 2**32: 118 | self.instr_reg = "eip" 119 | self.stack_reg = "esp" 120 | self.bot_reg = "ebp" 121 | else: 122 | self.instr_reg = "rip" 123 | self.stack_reg = "rsp" 124 | self.bot_reg = "rbp" 125 | 126 | self.next_flag = False 127 | 128 | self.sig_handlers = {} 129 | self.sig_actions = { 130 | "pass2prog":self.pass2prog, 131 | "ignore":self.ignoreSig, 132 | "dump":self.shitSelf, 133 | } 134 | self.realtime = False 135 | self.stack = None 136 | 137 | # for relocating symbols for ASLR 138 | self.virtual_offset = 0x0 139 | 140 | # Populated with the last dis/assembled bytes 141 | # and also anything the user wants to save 142 | self.byte_dict = {} 143 | self.cmd_count = 0 144 | 145 | 146 | ## savepoint stuff 147 | self.memory_slots = [] 148 | self.savepoint_limit = 3 149 | 150 | 151 | # saves the results of last findBytes 152 | self.query_addr_list = [] 153 | 154 | ## For setting up or PTRACE_OPTIONS 155 | self.options = {} 156 | self.options["strace"] = False 157 | self.options["exit"] = True 158 | self.options["fork"] = False 159 | self.options["vfork"] = False 160 | self.options["clone"] = False 161 | self.options["vfork_done"] = False 162 | self.options["exec"] = False 163 | 164 | # map address=>instruction 165 | self.break_dict = {} 166 | self.break_restore_loc = None 167 | 168 | self.thread_count = 0 169 | self.current_thread = -1 170 | self.threads = [] 171 | self.status = c_int(0) 172 | 173 | # Assorted options 174 | self.MAX_STR = 512 175 | 176 | self.cmd_dict = { "run":self.run, 177 | "attach":self.attach, 178 | "detach":self.detach, 179 | "gr":self.printRegs, 180 | "print":self.getMem, 181 | "sd":self.setMem, 182 | "x":self.getMem, 183 | "gd":self.getMem, 184 | "sr":self._set_reg, 185 | "bt":self.backtrace, 186 | "die":self.die, 187 | "c":self.cont, 188 | "stop":self.stop, 189 | "b":self.setBreak, 190 | "lb":self.listBreak, 191 | "db":self.delBreak, 192 | "sw":self.switchThread, # not implemented yet 193 | "clear":self.clear, 194 | "s":self.stepInstruction, 195 | "n":self.nextInstruction, 196 | "dis":self.pp_disassemble, 197 | "disb":self.pp_disassemble_str, 198 | "asm":self.assemble, 199 | "sys":self.syscall, 200 | "gs":self.getString, 201 | "map":self.getProcMap, 202 | "sec":self.getSections, 203 | "sym":self.getSymbols, 204 | "fb":self.findBytes, 205 | # under development/experimental # 206 | "sb":self.saveBytes, 207 | "shb":self.showSavedBytes, 208 | "handle":self.addSigHandler, 209 | "lsig":self.listSigAction, 210 | "sig":self.sendSig, 211 | "desync":self.toggleDesync, # 212 | ################################## 213 | "stack":self.printStack, 214 | # Snapshot stuff. (doesn't really work...) 215 | "save":self.save, 216 | "load":self.load, 217 | "listSaves":self.listSavepoints, 218 | "info":self.summaryInfo, 219 | # emulator commands 220 | "emu":self.emulate, 221 | "remu":self.restart_emu, 222 | "lemu":self.load_emu_context, 223 | "semu":self.save_emu_context, 224 | "bb":self.bb_dump, 225 | # emulator control (regs/mem) 226 | "gde":self.get_emu_mem, 227 | "sde":self.set_emu_mem, 228 | "gre":self.get_emu_reg, 229 | "sre":self.set_emu_reg, 230 | # 231 | "hist":self.print_history, 232 | "#":self.add_comment, 233 | "//":self.add_comment, 234 | "dc":self.del_comment, 235 | "sp":self.save_project, 236 | "lp":self.load_project, 237 | "#!":self.ignore_line, 238 | } 239 | 240 | self.completer = AtrophyCompleter(self.cmd_dict.keys()) 241 | self.sysflag = False 242 | 243 | 244 | def clear(self): 245 | self.output("\33[H\33[J") 246 | 247 | # print or send over socket 248 | def output(self,msg,newline=True): 249 | msg = str(msg) 250 | if newline: 251 | msg+="\n" 252 | if self.comms_sock: 253 | self.comms_sock.send(msg) 254 | else: 255 | sys.stdout.write(msg) 256 | sys.stdout.flush() 257 | 258 | ### Atrophy Commands 259 | # Run binary from commandline/attach 260 | def run(self,targetfile,args=None): 261 | full_path = os.path.join(os.getcwd(),targetfile) 262 | # test to see if file exists first: 263 | if not os.path.isfile(full_path): 264 | self.output(ERROR("File %s does not exist!" % full_path)) 265 | return 266 | if not os.access(full_path,os.X_OK): 267 | self.output(ERROR("Cannot execute file %s, exiting!" % full_path)) 268 | 269 | 270 | pid = libc.fork() 271 | 272 | #child thread 273 | if pid == 0: 274 | argv = self._parse_args(args,full_path) 275 | libc.ptrace(PTRACE_TRACEME,0,NULL,NULL) 276 | self.output(PURP("Child executing: %s" % targetfile)) 277 | libc.execve(full_path,argv,NULL) 278 | 279 | #parent thread 280 | elif pid > 0: 281 | if self.elf: 282 | del self.elf 283 | self.elf = ELF(*parse_elf(full_path)) 284 | 285 | if self.completer: 286 | self.completer.addSymbols(self.elf.symbol_dict.keys()) 287 | 288 | if self.stack: 289 | del self.stack 290 | self.stack = Stack() 291 | self._child_init(pid) 292 | #error case 293 | elif pid < 0: 294 | return -1 295 | 296 | ########################## 297 | # attach to pre-existing process 298 | def attach(self,pid): 299 | pid = int(pid) 300 | if not os.path.isdir("/proc/%d"%pid): 301 | self.output(WARN("No such pid %d" % pid) ) 302 | return 303 | 304 | if self.debug_pid != 0: 305 | if self.debug_pid == pid: 306 | WARN("Already attached to pid: %d" % self.debug_pid) 307 | return 308 | WARN("Detatching from old pid: %d" % self.debug_pid) 309 | self.detach() 310 | 311 | try: 312 | self.elf = ELF(*parse_elf("/proc/%d/exe"%pid)) 313 | if self.completer: 314 | self.completer.addSymbols(self.elf.symbol_dict.keys()) 315 | if self.stack: 316 | del self.stack 317 | self.stack = Stack() 318 | except Exception as e: 319 | self.output(WARN("Could not find elf for process %d" % pid)) 320 | self.output(INFO(str(e))) 321 | 322 | libc.ptrace(PTRACE_ATTACH,pid,NULL,NULL) 323 | self.debug_pid = pid 324 | self._child_init(pid) 325 | 326 | ########################## 327 | def detach(self): 328 | if self.debug_pid > 0: 329 | try: 330 | libc.ptrace(PTRACE_DETACH,self.debug_pid,NULL,0) 331 | except Exception as e: 332 | self.output(str(e) + ":109" ) 333 | 334 | self.output(INFO("Detatched from pid: %s%d"%(GREEN,self.debug_pid))) 335 | self.thread_count = 0 336 | self.threads = [] 337 | self.debug_pid = 0 338 | 339 | if self.elf: 340 | self.elf = None 341 | 342 | 343 | # We seperate TEXT/DATA for portability 344 | # linux doesn't really care about text vs. data 345 | # but no guarentee on others 346 | ########################## 347 | def getMem(self,addr,count=sizeof(c_ulong),quiet=False): 348 | ptval = PTRACE_PEEKTEXT 349 | addr = c_ulong(self.filter_addr(addr)) 350 | count = self.filter_addr(count) 351 | # Make sure > 0 352 | m = self.proc_map.find_region(addr.value) 353 | 354 | if not m: 355 | if not quiet: 356 | self.output(WARN("Unable to read address:0x%x"%addr.value)) 357 | return None 358 | 359 | ret_bytes = "" 360 | 361 | while len(ret_bytes) < count: 362 | try: 363 | long_val = libc.ptrace(ptval,self.current_thread.pid,addr,NULL) 364 | tmp=struct.pack(" 0: 383 | if tmp != None: 384 | addrList.append(tmp) 385 | limit = limit - 1 386 | tmp = self.getMem(tmp,quiet=True) 387 | ## 388 | ''' 389 | if tmp in self.elf.symbol_dict.keys(): 390 | addr = self.elf.symbol_dict[tmp][0] 391 | addrList.append("SYM:" + self.elf.symbol_dict[addr][0]) 392 | break 393 | ''' 394 | else: 395 | # if we hit a symbol, show/stop chain 396 | try: 397 | if addrList[-1] in self.elf.symbol_dict.keys(): 398 | addr = self.elf.symbol_dict[tmp][0] 399 | addrList.append("SYM:" + self.elf.symbol_dict[addr][0]) 400 | else: 401 | tmpStr = self.getString(addrList[-2],verbose=False) 402 | if tmpStr and tmpStr != "\x00": 403 | addrList.pop() 404 | addrList.append("STR:" + tmpStr[:-1]) 405 | break 406 | except IndexError: 407 | break 408 | except Exception as e: 409 | print str(e) 410 | break 411 | return addrList 412 | 413 | 414 | 415 | ########################## 416 | def setMem(self,addr,new_val): 417 | ptval = PTRACE_POKETEXT 418 | #print "SETMEM: addr:%s new_val:%s" % (addr,repr(new_val)) 419 | n = True #can still pass without n being set 420 | addr = self.filter_addr(addr) 421 | byteStr = self.filter_val(new_val) 422 | if not byteStr: 423 | self.output(WARN("Empty value given")) 424 | return 425 | blen = len(byteStr) 426 | leftover = blen % sizeof(c_ulong) 427 | 428 | m = self.proc_map.find_region(addr) 429 | 430 | if blen > sizeof(c_ulong): 431 | n = self.proc_map.find_region(addr+blen) 432 | if not m or not n: 433 | self.output(WARN("Non-mapped address provided!")) 434 | return None 435 | 436 | for i in range(0,len(byteStr)-leftover,sizeof(c_ulong)): 437 | mem_chunk = byteStr[i:i+sizeof(c_ulong)] 438 | val = self.filter_addr(mem_chunk) 439 | c_addr = c_ulong(addr+i) 440 | intval = c_ulong(val) 441 | 442 | ret = libc.ptrace(ptval,self.current_thread.pid,c_addr,intval) 443 | 444 | if leftover: 445 | orig_mem = self.getMem(addr+(len(byteStr)-leftover)+1,sizeof(c_long)-leftover) 446 | #print repr(orig_mem) 447 | mem_chunk = byteStr[-1*leftover:] + orig_mem 448 | val = self.filter_addr(mem_chunk) 449 | c_addr = c_ulong(addr+(len(byteStr)-leftover)) 450 | int_val = c_ulong(val) 451 | 452 | ret = libc.ptrace(ptval,self.current_thread.pid,c_addr,int_val) 453 | 454 | return ret 455 | 456 | ########################## 457 | def getByte(self,addr): 458 | return self.getMem(addr,1,False) 459 | 460 | ########################## 461 | def getString(self,addr,verbose=True): 462 | ret = "" 463 | curr_addr = self.filter_addr(addr) 464 | null_loc = -1 465 | 466 | while null_loc == -1 and len(ret) < self.MAX_STR: 467 | try: 468 | ret += self.getMem(curr_addr,sizeof(c_ulong),quiet=True) 469 | curr_addr += sizeof(c_ulong) 470 | 471 | except TypeError: 472 | ret += "\x00" 473 | 474 | null_loc = ret.rfind("\x00") 475 | 476 | for i in range(0,len(ret)): 477 | c = ord(ret[i]) 478 | if c < 0x20 or c > 0x7f: 479 | ret = ret[:i] + "\x00" 480 | break 481 | 482 | if null_loc == 0: 483 | return "" 484 | 485 | if verbose: 486 | if null_loc < self.MAX_STR: 487 | self.output(WARN("STRING:0x%x:%s"%(curr_addr,repr(ret[:null_loc])))) 488 | else: 489 | self.output(WARN("STRING:0x%x:%s"%(curr_addr,ret[:null_loc]))) 490 | self.output("Truncated at %d chars..." % self.MAX_STR ) 491 | 492 | return ret[:null_loc+1] 493 | 494 | 495 | ########################## 496 | def getRegs(self,regs): 497 | ret = "" 498 | print "" # If this is commented, PTRACE_GETREGS always returns -1. 499 | # I have no clue. Halp, gooby plz. 500 | status = libc.ptrace(PTRACE_GETREGS,self.debug_pid,0,byref(regs)) 501 | 502 | if status != 0: 503 | self.output(WARN("Error getting updated registers, pid: %d" % self.debug_pid)) 504 | self.output(WARN("Dumping last known state")) 505 | self.output(status) 506 | return -1 507 | 508 | ret = regs.get_stored_registers() 509 | return ret 510 | 511 | ########################## 512 | # Pid = program to change register for 513 | # newRegs = regStruct 514 | def setRegs(self,pid,newRegs): 515 | return libc.ptrace(PTRACE_SETREGS,pid,NULL,byref(newRegs)) 516 | 517 | ########################## 518 | # Prints registers for current thread 519 | def printRegs(self,*argv): 520 | ret = "" 521 | deref = None 522 | 523 | regs = self._update_regs() 524 | 525 | for reg in regs.display_order: 526 | 527 | if argv and reg not in argv: 528 | continue 529 | 530 | try: 531 | addr = self.filter_addr(getattr(regs,reg)) 532 | deref = self.derefChain(addr) 533 | except Exception as e: 534 | print str(e) 535 | break 536 | ret += reg_format(reg,addr,deref) + "\n" 537 | return ret 538 | 539 | ########################## 540 | 541 | def getStackBounds(self,pid,regs=None): 542 | 543 | if not regs: 544 | regStruct = self._find_thread(pid).regStruct 545 | else: 546 | regStruct = regs 547 | 548 | top = regStruct.get_register(self.stack_reg) 549 | bottom = regStruct.get_register(self.bot_reg) 550 | 551 | return (top,bottom) 552 | 553 | ########################## 554 | 555 | def printStack(self,limit=10,start_addr=None,regs=None): 556 | if start_addr: 557 | start_addr = filter_addr(start_addr) 558 | 559 | limit = int(limit) 560 | ret = "" 561 | stack,bottom = self.getStackBounds(self.debug_pid,regs) 562 | 563 | for i in range(0,limit): 564 | offset = sizeof(c_ulong) * i 565 | deref = self.derefChain(stack+offset) 566 | ret+=reg_format("%s+%d"%(self.stack_reg,offset),stack+offset,deref) + "\n" 567 | if (offset+stack) == bottom: 568 | ret = ret[:-1] 569 | ret+=YELLOW + " <=[%s]\n" %self.bot_reg 570 | if (offset+stack) == bottom+sizeof(c_ulong): 571 | # try to dereference what function 572 | ret = ret[:-1] 573 | ret +=YELLOW + " <=[ret_addr]\n" 574 | return ret 575 | 576 | 577 | ########################## 578 | def backtrace(self,limit=5): 579 | self.stack.clear() 580 | 581 | t = self._find_thread(self.debug_pid) 582 | instr_ptr = t.regStruct.get_register(self.instr_reg) 583 | 584 | try: 585 | nearestSym = self.findNearestSymbol(instr_ptr) 586 | except: 587 | nearestSym = self.findNearestSymbol(instr_ptr,reloc=False) 588 | 589 | 590 | # x86 591 | 592 | # EBP + 0x4 => OLD EIP 593 | if sys.maxsize <= 2**32: 594 | top,bottom = self.getStackBounds(self.debug_pid) 595 | self.stack.push_frame(StackFrame(top,bottom,instr_ptr,nearestSym)) 596 | self.stack[-1].registers = self._update_regs() 597 | 598 | for i in range(1,limit): 599 | f = self.stack.stack_frames[0] 600 | try: 601 | old_esp = self.getMem(f.bottom,sizeof(c_long),True) 602 | old_eip = self.getMem(f.bottom+sizeof(c_long),sizeof(c_long),True) 603 | old_ebp = self.getMem(old_esp,sizeof(c_long),True) 604 | 605 | old_esp = struct.unpack(" 2**32: 623 | 624 | pass 625 | 626 | 627 | 628 | buf = "\n" 629 | buf += repr(self.stack) 630 | self.output(INFO(buf)) 631 | #print self.stack.dump_stack() 632 | 633 | 634 | ########################## 635 | # still in the problem of needing regs for each frame... 636 | def dumpFrame(self,frame_index): 637 | if not len(self.stack): 638 | return None 639 | 640 | try: 641 | stack_frame = self.stack[frame_index] 642 | except: 643 | return None 644 | 645 | printStack(stack_frame.registers) 646 | 647 | 648 | ########################## 649 | def stepInstruction(self,quiet=False,break_restore=False): 650 | pid = self.debug_pid 651 | libc.ptrace(PTRACE_SINGLESTEP,pid,NULL,NULL) 652 | self._update_regs(pid) 653 | if not quiet: 654 | try: 655 | # no disassembly 656 | self.pp_disassemble(count=1) 657 | except: 658 | self.output(self.printRegs(self.instr_reg)) 659 | 660 | # so we don't loop 661 | if not break_restore: 662 | self.break_restore_check() 663 | 664 | 665 | ########################## 666 | def nextInstruction(self): 667 | # ignore _pp_syscall 668 | self.next_flag = True 669 | 670 | call_instr = [ "\xe8" ] 671 | # check next instruction for "call" 672 | 673 | t = self.current_thread 674 | addr = t.regStruct.get_register(self.instr_reg) 675 | tmp = bytes(self.getMem(addr,count=1,quiet=False)) 676 | 677 | if tmp in call_instr: 678 | disasm_list = self.disassemble(addr,2,verbose=False) # get len of instr 679 | instr_len = len(disasm_list[0][3]) # (addr,mnem,op_str,bytes) 680 | old_byte = ord(self.getMem(addr+instr_len,count=1,quiet=False)) # save old byte 681 | 682 | self.setMem(addr+instr_len,0xcc) # set break 683 | self.cont(update_regs=True) 684 | self.wait_child() 685 | self.setMem(addr+instr_len,old_byte) # rem break 686 | 687 | # heuristics? break isn't getting rolled back when hit... 688 | self._set_reg(self.instr_reg,addr+instr_len) 689 | self.pp_disassemble(count=1,verbose=False) 690 | else: 691 | self.stepInstruction(quiet=False) 692 | 693 | # ignore _pp_syscall 694 | self.next_flag = False 695 | 696 | ########################## 697 | def break_restore_check(self,instr_ptr=None): 698 | # 'next' instr gets wonky if we don't 699 | # verify that our breaks are in the dict 700 | # since we sidestep that shit w/next 701 | dict_check = True 702 | if instr_ptr: 703 | dict_check = False 704 | for addr in self.break_dict: 705 | addr_val = self.filter_addr(addr) 706 | if addr == instr_ptr: 707 | dict_check = True 708 | break 709 | 710 | if self.break_restore_loc != None and dict_check == True: 711 | self.stepInstruction(quiet=True,break_restore=True) 712 | # restore soft break 713 | self.output("Restoring 0x%x" % self.break_restore_loc) 714 | self.setBreak(self.break_restore_loc,comment=False) 715 | self.break_restore_loc = None 716 | 717 | ########################## 718 | def cont(self,update_regs=True,systrace=False): 719 | 720 | pid = self.debug_pid 721 | if update_regs: 722 | self._update_regs(pid) 723 | 724 | t = self.current_thread 725 | addr = t.regStruct.get_register(self.instr_reg) 726 | self.break_restore_check(addr) 727 | 728 | #clear status flags if any 729 | #self.output(INFO("Resuming execution...")) 730 | if systrace: 731 | self.sysflag = True 732 | libc.ptrace(PTRACE_SYSCALL,pid,NULL,0) 733 | else: 734 | self.sysflag = False 735 | libc.ptrace(PTRACE_CONT,pid,NULL,0) 736 | 737 | ########################## 738 | 739 | def stop(self): 740 | pid = self.debug_pid 741 | if pid > 0: 742 | os.kill(pid,SIGSTOP) 743 | 744 | ########################## 745 | 746 | def syscall(self): 747 | self.cont(systrace=True) 748 | 749 | ########################## 750 | 751 | def die(self): 752 | self.output(INFO("Killing pid %d" % self.debug_pid) ) 753 | self._clean_up(kill=True) 754 | 755 | ########################## 756 | def setBreak(self,addr,comment=True): 757 | try: 758 | if self.break_dict[addr] > -1: 759 | self.output(INFO("Already a breakpoint there.")) 760 | return 761 | except: 762 | pass 763 | 764 | if comment: 765 | try: 766 | instr = self.disassemble(addr,1,False)[0] 767 | asm_str = instr[1] + " " + instr[2] 768 | self.add_comment("%s"%(asm_str),"%s"%addr) 769 | except Exception as e: 770 | print "setBreak " + str(e) 771 | return 772 | 773 | self.break_dict[addr] = ord(self.getByte(addr)) 774 | self.setMem(addr,0xcc) 775 | 776 | ########################## 777 | def listBreak(self): 778 | 779 | # break_dict[address] => original byte 780 | for i in self.break_dict: 781 | #we set to -1 if deleted 782 | if self.break_dict[i] > -1: 783 | try: 784 | self.output("Break %s: %s%s" % (CYAN,i,CLEAR)) 785 | except Exception as e: 786 | print "listBreak " + str(e) 787 | 788 | ########################## 789 | def delBreak(self,addr,quiet=False): 790 | self.setMem(addr,self.break_dict[addr]) 791 | self.break_dict[addr] = -1 792 | if not quiet: 793 | self.output("Deleted Break @%s" % addr) 794 | 795 | ########################## 796 | def disassemble(self,addr=None,count=10,verbose=True): 797 | if not self.AsmUtil: 798 | self.output(WARN("Asm Utils disabled. Keystone/capstone installed?")) 799 | return 800 | 801 | addr = self.filter_addr(addr) 802 | 803 | if not addr: 804 | t = self.current_thread 805 | addr = t.regStruct.get_register(self.instr_reg) 806 | 807 | if verbose: 808 | self.output(PURP("COUNT: %d, addr: 0x%x" % (count,addr))) 809 | 810 | disasm_list = [] 811 | index_tracker = 0 812 | 813 | 814 | while True: 815 | # grab 32 bytes at a time 816 | tmp = bytes(self.getMem(addr,count=32,quiet=False)) 817 | if tmp == "None" and len(disasm_list) == 0: 818 | self.output("Invalid Address provided") 819 | break 820 | 821 | tmp_dis = self.AsmUtil.disassemble(tmp,addr) 822 | 823 | disasm_list += tmp_dis 824 | 825 | if not len(disasm_list): 826 | self.output(WARN("Unable to disassemble, probz not instructions.")) 827 | return tmp 828 | 829 | if not len(tmp_dis): 830 | self.output(WARN("Disassembly ended early, probz started mid-instr...")) 831 | break 832 | 833 | if count <= len(disasm_list): 834 | disasm_list = disasm_list[:count] 835 | break 836 | else: 837 | tote_len = 0 838 | for instr in disasm_list[index_tracker:]: 839 | tote_len+=len(instr[3]) 840 | addr += tote_len 841 | index_tracker = len(disasm_list) 842 | 843 | # save for future use if needed 844 | #''.join(str(x) for x in (a,b)) 845 | if len(disasm_list): 846 | tmp = "" 847 | for instr in disasm_list: 848 | tmp += "" 849 | tmp += b''.join("%02x"%x for x in instr[3]) 850 | 851 | self.saveBytes("dis.%d"%self.cmd_count,tmp) 852 | self.cmd_count += 1 853 | 854 | return disasm_list 855 | 856 | def pp_disassemble_str(self,bytestr=""): 857 | off = 0x0 858 | if not self.AsmUtil: 859 | self.output(WARN("Asm Utils disabled. Keystone/capstone installed?")) 860 | return 861 | 862 | try: 863 | tmp = filter(None,bytestr.split("\\x")) 864 | except: 865 | tmp = filter(None,bytestr.split(" ")) 866 | dis_list = "".join([chr(int(i,16)) for i in tmp]) 867 | 868 | try: 869 | disasm_list = self.AsmUtil.disassemble(dis_list,off) 870 | except Exception as e: 871 | print e 872 | 873 | if len(disasm_list): 874 | try: 875 | self.output(disasm_format(disasm_list)+"\n") 876 | except: 877 | # incase we couldn't actually disassemble... 878 | return disasm_list 879 | 880 | 881 | 882 | def pp_disassemble(self,addr=None,count=10,verbose=True): 883 | off = 0xffffffff 884 | sym = "" 885 | 886 | if not self.AsmUtil: 887 | self.output(WARN("Asm Utils disabled. Keystone/capstone installed?")) 888 | return 889 | 890 | if addr: 891 | addr = self.filter_addr(addr) 892 | 893 | try: 894 | count = int(count) 895 | except ValueError: 896 | try: 897 | count = int(count,16) 898 | except ValueError: 899 | self.output(WARN("Invalid instruction count given")) 900 | return "" 901 | 902 | disasm_list = self.disassemble(addr,count,verbose) 903 | 904 | # disasm_list[0][0] == addr of first address 905 | try: 906 | _,sym,off = self.findNearestSymbol(disasm_list[0][0]) 907 | except: 908 | try: 909 | _,sym,off = self.findNearestSymbol(disasm_list[0][0],reloc=False) 910 | except: 911 | pass 912 | 913 | # Don't bother if it's pretty far away 914 | if off < 0x100000: 915 | self.output(INFO("<%s+0x%x>"%(sym,off))) 916 | 917 | try: 918 | self.output(disasm_format(disasm_list)+"\n") 919 | except: 920 | # incase we couldn't actually disassemble... 921 | return disasm_list 922 | 923 | 924 | ########################## 925 | 926 | def assemble(self,*instructions): 927 | if not self.AsmUtil: 928 | self.output(WARN("Asm Utils disabled. Keystone/capstone installed?")) 929 | return 930 | 931 | #self.output(instructions) 932 | instr = ' '.join(instructions) 933 | instr = instr.replace('"',"") 934 | instr = instr.replace("'","") 935 | self.output(WARN(instr)) 936 | 937 | retBytes,count = self.AsmUtil.assemble(instr) 938 | 939 | save_buff = "\\x" + "\\x".join(["%02x"%b for b in retBytes]) 940 | # save for future use if needed 941 | self.saveBytes("asm.%d"%self.cmd_count,save_buff) 942 | self.cmd_count += 1 943 | 944 | pp_asm = "\\x" + "\\x".join(["%02x"%x for x in retBytes]) 945 | self.output(INFO(pp_asm+"\n")) 946 | 947 | ########################## 948 | def emulate(self,instr_count=-1,timeout=0): 949 | err_regs = None 950 | if not self.EmuUtil.init_flag: 951 | regStruct = self._find_thread(self.debug_pid).regStruct 952 | self.EmuUtil.initEmulator(self.debug_pid,self.current_thread.regStruct) 953 | self.EmuUtil.init_flag = True 954 | 955 | err_regs = self.EmuUtil.startEmulator(int(instr_count),int(timeout)) 956 | # interactive? 957 | if err_regs: 958 | err_msg = "" 959 | 960 | for reg in err_regs.display_order: 961 | try: 962 | addr = self.filter_addr(getattr(err_regs,reg)) 963 | deref = self.derefChain(addr) 964 | except Exception as e: 965 | print str(e) 966 | break 967 | err_msg += reg_format(reg,addr,deref) + "\n" 968 | self.output(err_msg) 969 | 970 | 971 | def restart_emu(self): 972 | self.output(INFO("Emu Reinitialization Requested.")) 973 | self.EmuUtil.init_flag = False 974 | del self.EmuUtil 975 | self.EmuUtil = EmuUtil() 976 | regStruct = self._find_thread(self.debug_pid).regStruct 977 | self.EmuUtil.initEmulator(self.debug_pid,self.current_thread.regStruct) 978 | self.EmuUtil.init_flag = True 979 | # so disasm can be commented from emu (jump taken/rets/etc) 980 | self.EmuUtil.init_commenting_function(self.AsmUtil.emu_append_comment) 981 | 982 | def load_emu_context(self,name): 983 | self.EmuUtil.load_emu_context(name) 984 | 985 | def save_emu_context(self,name): 986 | self.EmuUtil.save_emu_context(name) 987 | ########################## 988 | 989 | def get_emu_mem(self,address,length=16): 990 | addr = self.filter_val(address) 991 | return self.EmuUtil.get_mem(addr,length) 992 | 993 | def set_emu_mem(self,address,value): 994 | addr = self.filter_val(address) 995 | value = self.filter_val(value) 996 | self.EmuUtil.set_mem(addr,value) 997 | 998 | def get_emu_reg(self,registers="*"): 999 | emu_regs = self.EmuUtil.getAtrophyRegs() 1000 | ret_buf = "" 1001 | 1002 | if registers == "*": 1003 | disp_queue = emu_regs.display_order 1004 | else: 1005 | disp_queue = filter(None,registers.split(" ")) 1006 | 1007 | if emu_regs: 1008 | for reg in disp_queue: 1009 | try: 1010 | addr = self.filter_addr(getattr(emu_regs,reg)) 1011 | #print addr 1012 | deref = self.derefChain(addr) 1013 | #print deref 1014 | except Exception as e: 1015 | print str(e) 1016 | break 1017 | 1018 | ret_buf += reg_format(reg,addr,deref) + "\n" 1019 | else: 1020 | self.output(WARN("Could nto get Emulator's registers")) 1021 | self.output(ret_buf) 1022 | 1023 | def set_emu_reg(self,register,value): 1024 | self.EmuUtil.set_reg(register,value) 1025 | 1026 | def bb_dump(self): 1027 | self.EmuUtil.bb_dump() 1028 | 1029 | ########################## 1030 | def switchThread(self,tid): 1031 | self.current_thread = self.threads[self._find_thread(tid)] 1032 | self.output(INFO("Switching current thread to PID:%d" % self.current_thread.pid)) 1033 | 1034 | ########################## 1035 | ## For comments in history ## 1036 | def ignore_line(self,*_): 1037 | pass 1038 | ########################## 1039 | 1040 | # add comment that can be viewed when disassembling 1041 | # by default adds to address $PC. 1042 | def add_comment(self,*comment): 1043 | ## 1044 | addr = None 1045 | comment_buff = "" 1046 | for i in range(0,len(comment)): 1047 | if i == len(comment)-1 and len(comment) > 1: ## see if it's an address or not 1048 | try: 1049 | addr = "0x%x" % self.filter_addr(comment[i]) 1050 | 1051 | except Exception as e: 1052 | comment_buff+=comment[i] 1053 | addr = None 1054 | else: 1055 | comment_buff+=comment[i] + " " 1056 | #print comment_buff 1057 | 1058 | if not addr: 1059 | t = self.current_thread 1060 | addr = "0x%x" % t.regStruct.get_register(self.instr_reg) 1061 | 1062 | formatted_comment = "%s #%s" % (PURPLE,comment_buff) 1063 | self.AsmUtil.comment_add(addr,formatted_comment) 1064 | self.output("Added comment '%s' to %s" % (comment_buff,addr)) 1065 | 1066 | ########################## 1067 | 1068 | # delete comment at address, by default deletes at $PC 1069 | def del_comment(self,address=None,index=-1): 1070 | if address: 1071 | addr = "0x%x" % self.filter_addr(address) 1072 | else: 1073 | t = self.current_thread 1074 | addr = "0x%x" % t.regStruct.get_register(self.instr_reg) 1075 | 1076 | self.AsmUtil.comment_del(addr) 1077 | self.output("Deleted comment@%s"%addr) 1078 | 1079 | ########################## 1080 | 1081 | def print_history(self,count=0): 1082 | buff = self.completer.print_history(int(count)) 1083 | self.output(buff) 1084 | 1085 | ########################## 1086 | def save_project(self,proj_name): 1087 | self.completer.save_project(proj_name) 1088 | self.output(GOOD("Saved project %s" % proj_name)) 1089 | 1090 | def load_project(self,proj_name): 1091 | proj_buff = self.completer.load_project(proj_name) 1092 | for cmd in proj_buff.split('\n'): 1093 | self.sendCommand(cmd) 1094 | self.output(GOOD("Loaded project %s"%proj_name)) 1095 | 1096 | 1097 | ########################## 1098 | # END Atrophy commands 1099 | ########################## 1100 | def get_input(self,sock): 1101 | if sock == None: 1102 | return raw_input(GOOD("")) 1103 | else: 1104 | return self.get_bytes(sock) 1105 | 1106 | 1107 | def sendCommand(self,inp=None,inpSource=None,loop=True): 1108 | signal = 0 1109 | skip = False 1110 | 1111 | if inp: 1112 | skip=True 1113 | loop=False 1114 | 1115 | while True: 1116 | #if self.realtime: 1117 | # self.cont(update_regs=False) 1118 | if self.proc_map: 1119 | self.proc_map.update_map() 1120 | try: 1121 | if not skip: 1122 | inp = self.get_input(inpSource) 1123 | 1124 | except KeyboardInterrupt: 1125 | self.output("") 1126 | continue 1127 | #self.output("\n%s" % ERROR("CTRL-C detected, exiting...")) 1128 | #if self.debug_pid > 0: 1129 | # os.kill(self.debug_pid,SIGKILL) 1130 | #sys.exit() 1131 | 1132 | cmd_list = self._parse_args(inp) 1133 | cmd = cmd_list 1134 | 1135 | if not cmd: 1136 | continue 1137 | 1138 | if cmd[0] == "?" or cmd[0] == "help": 1139 | self.output(usage()) 1140 | continue 1141 | 1142 | if cmd[0] == "exit" or cmd[0] == "quit": 1143 | self._clean_up(kill=False) 1144 | 1145 | elif cmd[0] == "sys" or cmd[0] == 'c': 1146 | try: 1147 | if cmd[0] == "sys": 1148 | self.syscall() 1149 | else: 1150 | self.cont() 1151 | 1152 | self.wait_child() 1153 | #if not self.realtime: 1154 | 1155 | except KeyboardInterrupt: 1156 | self.output("\n%s" % WARN("CTRL-C detected, breaking...")) 1157 | self.stop() 1158 | self._update_regs() 1159 | skip = False 1160 | loop = True 1161 | continue 1162 | 1163 | try: 1164 | args = cmd[1:] 1165 | cmd = cmd[0] 1166 | except: 1167 | pass 1168 | 1169 | ret = "" 1170 | 1171 | #if self.realtime: 1172 | # self.stop() 1173 | 1174 | if cmd in self.cmd_dict: 1175 | 1176 | if self.debug_pid <= 0: 1177 | if cmd != "run" and cmd != "attach": 1178 | self.output(WARN("Must have a running process!") ) 1179 | continue 1180 | 1181 | 1182 | if len(args): 1183 | ''' 1184 | #!TODO 1185 | try: 1186 | tmp = args.index("|") 1187 | print "asdf" 1188 | if tmp > -1: 1189 | output = self.sendCommand(cmd + " " + " ".join(args[:tmp])) 1190 | print output 1191 | 1192 | cmd = "echo %s %s" % output," ".join(args[tmp:]) 1193 | ret = subprocess.check_output(cmd,shell=True) 1194 | self.output("\n" + ret + CLEAR) 1195 | except: 1196 | pass 1197 | ''' 1198 | 1199 | try: 1200 | ret = self.cmd_dict[cmd](*args) 1201 | cmd = "" 1202 | except Exception as e: 1203 | self.output(e) 1204 | #self.output(WARN("Invalid args for cmd %s: %s" % (cmd,str(args)))) 1205 | else: 1206 | #! For debugging 1207 | #print "executing without 'try'" 1208 | #ret = self.cmd_dict[cmd]() 1209 | try: 1210 | 1211 | ret = self.cmd_dict[cmd]() 1212 | except Exception as e: 1213 | self.output(str(e) + ":389") 1214 | self.output(WARN("Missing args for cmd %s" % cmd)) 1215 | 1216 | else: 1217 | try: 1218 | ret = subprocess.check_output(inp,shell=True,env={'COLUMNS':'249'}) 1219 | self.output("\n" + GREEN + ret + CLEAR) 1220 | continue 1221 | except KeyboardInterrupt: 1222 | pass 1223 | except Exception as e: 1224 | print str(e) 1225 | pass 1226 | 1227 | # 00[m in ret => already formatted, just print ugly. 1228 | if ret: 1229 | try: 1230 | if "[00m" in ret: 1231 | self.output(ret ) 1232 | if not loop: 1233 | break 1234 | continue 1235 | except: 1236 | pass 1237 | try: 1238 | self.output(hexdump(ret,addr=self.filter_addr(args[0]))) 1239 | except: 1240 | self.output(ret) 1241 | 1242 | if not loop: 1243 | break 1244 | 1245 | 1246 | 1247 | #### Helper/internal class functions 1248 | # - Wait pid on child pid 1249 | # - See if EXITED || CONTINUED || STOPPED 1250 | # - If Exited: try to examine a little 1251 | # - If continue: don't really care 1252 | # - if stopped: figure out what happened 1253 | def wait_child(self,inp_pid=-1,option=0): 1254 | stopsig = 0 1255 | 1256 | if inp_pid == -1: 1257 | inp_pid = self.debug_pid 1258 | pid = libc.waitpid(inp_pid,byref(self.status),option) 1259 | 1260 | #Examine signal 1261 | #######EXITED 1262 | if os.WIFEXITED(self.status.value): 1263 | self.output(ERROR("Child died.")) 1264 | self._clean_up() 1265 | 1266 | if os.WIFSIGNALED(self.status.value): 1267 | self.output(INFO("Cause of death: SIG %d"%WSTOPSIG(self.status.value)) ) 1268 | if os.WCOREDUMP(self.status.value): 1269 | self.output(INFO("Coredump generated")) 1270 | 1271 | ######CONTINUED ## don't really care for now 1272 | elif os.WIFCONTINUED(self.status.value): 1273 | self.output(INFO("CONT PID:%d"%pid) ) 1274 | 1275 | 1276 | #######STOPPED ## Lots of logic here, since we'll be hitting this most 1277 | elif os.WIFSTOPPED(self.status.value): 1278 | #print repr(self.status.value) 1279 | self._update_regs() 1280 | 1281 | # case only occurs on first stop 1282 | if not self.traceme_flag: 1283 | self.traceme_flag = True 1284 | self.output(GOOD("Attached successfully: %d" % pid)) 1285 | return 1286 | 1287 | stopsig = os.WSTOPSIG(self.status.value) 1288 | #print "stopsig: 0x%s" % repr(stopsig) 1289 | 1290 | # Defined as CTRL-C 1291 | if stopsig == SIGUSR1: 1292 | #self.output("SIGUSR1") 1293 | return 1294 | elif stopsig == SIGINT: 1295 | #self.output("SIGINT") 1296 | return 1297 | elif stopsig == SIGWINCH: 1298 | #self.output("SIGWINCH") 1299 | return 1300 | #self.output(INFO("STOPSIG: %d" % stopsig)) 1301 | elif stopsig == 17: 1302 | return 1303 | elif stopsig == 11: 1304 | self.output(ERROR("SEGFAULT")) 1305 | self.output(self._print_regs()) 1306 | self.pp_disassemble() 1307 | self._clean_up() 1308 | elif stopsig == 9: 1309 | self.output(ERROR("SIGKILL")) 1310 | self.output(self._print_regs()) 1311 | 1312 | elif stopsig == 19: 1313 | #stop due to sys?idk 1314 | if self.sysflag: 1315 | self.sendCommand("sys") 1316 | self.sysflag = False 1317 | else: 1318 | self.sendCommand("c") 1319 | return 1320 | 1321 | # 5 => SIGTRAP 1322 | elif stopsig == 5 or stopsig == 19: 1323 | if self.sysflag: 1324 | self.sendCommand("sys") 1325 | self.sysflag = False 1326 | return 1327 | 1328 | # If we get here, generally something we set, 1329 | # need to distinguish if it's a breakpoint, 1330 | # or a PTRACE_O_* options being hit 1331 | #self.output(INFO("SIGTRAP instruction executed")) 1332 | 1333 | # First check if it's a valid breakpoint 1334 | t = self._find_thread(pid) 1335 | instr_ptr = t.regStruct.get_register(self.instr_reg)-1 1336 | #print "REG:%s==0x%x"%(self.instr_reg,instr_ptr) 1337 | #self.output("EIP: 0x%08x" % eip) 1338 | 1339 | # Flag for valid breakpoint or not 1340 | break_p = False 1341 | 1342 | for addr_str in self.break_dict: 1343 | # since addr is now a string >_> 1344 | try: 1345 | addr_int = int(addr_str,16) 1346 | except: 1347 | addr_int = self.filter_addr(addr_str) 1348 | 1349 | if instr_ptr == addr_int: 1350 | try: 1351 | _,sym,off = self.findNearestSymbol(instr_ptr,reloc=True) 1352 | except: 1353 | try: 1354 | _,sym,off = self.findNearestSymbol(instr_ptr,reloc=False) 1355 | except: 1356 | sym,off = None,None 1357 | pass 1358 | 1359 | if sym: 1360 | self.output(GOOD("Breakpoint Detected\nInstr:0x%x %s<%s+%d>" % (instr_ptr,PURPLE,sym,off))) 1361 | else: 1362 | self.output(GOOD("Breakpoint Detected\nInstr:0x%x" % (instr_ptr))) 1363 | # Restore break after moving on 1364 | self.break_restore_loc = addr_int 1365 | # restore instruction 1366 | try: 1367 | self.setMem(instr_ptr,self.break_dict[instr_ptr]) 1368 | except: 1369 | self.setMem(instr_ptr,self.break_dict[addr_str]) 1370 | 1371 | # rewind eip by 1 for "\xcc" 1372 | self._set_reg(self.instr_reg,instr_ptr) 1373 | break_p = True 1374 | 1375 | 1376 | # As per systrace: 1377 | # WIFSTOPPED(s) && WSTOPSIG == SIGTRAP | 0x80 => syscall 1378 | # but above doesn't work? 1379 | # assume: SIGTRAP and not breakpoint => syscall 1380 | if not break_p and not self.next_flag: 1381 | # at this point, orig_eax == syscallnum 1382 | t = self._find_thread(pid) 1383 | #syscall_num= t.regStruct.get_register("orig_eax") 1384 | syscall = syscall_lookup(t.regStruct) 1385 | self.output(self._pp_syscall(syscall)) 1386 | #sc = syscall_bynum(syscall_num) 1387 | 1388 | # TODO impliment the rest of these 1389 | elif stopsig in self.sig_handlers.keys(): 1390 | self.sig_handlers[stopsig](stopsig) 1391 | else: 1392 | print self.sig_handlers.keys() 1393 | self.output(WARN("Unknown signal: %d" % stopsig)) 1394 | 1395 | # If not a breakpoint, throw to event handler 1396 | event = ((stopsig >> 16) & 0xffff) 1397 | self._event_handler(event) 1398 | 1399 | # killed while stopped 9? 1400 | else: 1401 | self.output(ERROR("Killed while stopped, signal: %d" % self.status.value)) 1402 | self._clean_up() 1403 | 1404 | 1405 | ################# 1406 | 1407 | # TODO: impliment the rest of the PTRACE_EVENT_* 1408 | def _event_handler(self,event): 1409 | #for detecting new child threads 1410 | # www.linuxjournal.com/article/6100 1411 | if event == PTRACE_EVENT_CLONE: 1412 | new_pid = c_uint(0) 1413 | if libc.ptrace(PTRACE_GETEVENTMSG,pid,0,byref(new_pid)) != -1: 1414 | self._generate_debugged(self.thread_count,new_pid.value,pid) 1415 | self.current_thread = self.threads[self._find_thread(new_pid.value)] 1416 | elif event == PTRACE_EVENT_EXIT: 1417 | self.output(INFO("Program exit hit, dumping regs")) 1418 | self.output(self._print_regs()) 1419 | 1420 | elif event == PTRACE_EVENT_EXEC: 1421 | pass 1422 | 1423 | 1424 | def _parse_args(self,args,full_path=None): 1425 | i = 0 1426 | try: 1427 | argv = filter(None,args.split(" ")) 1428 | if full_path: 1429 | argv.insert(0,full_path) 1430 | except: 1431 | argv = [full_path] 1432 | 1433 | c_argv = (c_char_p * len(argv))() 1434 | 1435 | for i in range(0,len(argv)): 1436 | dir(c_argv[i]) 1437 | c_argv[i] = argv[i] 1438 | 1439 | return c_argv 1440 | 1441 | 1442 | ######### Register functions 1443 | 1444 | def _print_regs(self,reg="*",pid=-1): 1445 | registers = self._update_regs(pid) 1446 | 1447 | if reg != "*": 1448 | self.output(reg_format(reg,getattr(registers,reg)) ) 1449 | else: 1450 | self.output(registers) 1451 | 1452 | 1453 | # "Macro" for updating regStruct of thread 1454 | def _update_regs(self,pid=-1): 1455 | #!#! 1456 | 1457 | if pid == -1: 1458 | t = self.current_thread 1459 | else: 1460 | t = self._find_thread(pid) 1461 | #print t.regStruct 1462 | 1463 | if t != -1: 1464 | self.getRegs(t.regStruct) 1465 | return t.regStruct 1466 | 1467 | return False 1468 | 1469 | 1470 | # set a register to a given value 1471 | # reg = "eax","ebx".... 1472 | # value = "0xab", "\xab\xbc", 10... 1473 | def _set_reg(self,reg,value): 1474 | pid = self.debug_pid 1475 | #make sure we're up to date first 1476 | self._update_regs(pid) 1477 | t = self._find_thread(pid) 1478 | setattr(t.regStruct,reg,value) 1479 | self.setRegs(t.pid,t.regStruct) 1480 | 1481 | def set_reg_state(self,regs): 1482 | self.setRegs(t.pid,t.regStruct) 1483 | 1484 | ############## 1485 | # check to see if a given address is listed 1486 | # in the breakpoint dictionary 1487 | def _is_breakpoint(self,addr): 1488 | addr = self.filter_addr(value) 1489 | for i in self.break_dict: 1490 | if i == addr: 1491 | return True 1492 | return False 1493 | 1494 | # Generate ProcMap object for the given pid, 1495 | # - populate process info 1496 | # - Eat the Elf 1497 | # - init the emu 1498 | def _generate_debugged(self,uid,pid,ppid): 1499 | self.proc_map = ProcMap(pid) 1500 | self.threads.append(initDebugProcess(uid,pid,ppid)) 1501 | self.thread_count += 1 1502 | 1503 | ''' 1504 | if self.EmuUtil: 1505 | regStruct = self._find_thread(pid).regStruct 1506 | self.EmuUtil.initEmulator(pid,regStruct) 1507 | ''' 1508 | 1509 | # Search threads for a given pid 1510 | # returns thread if found, else -1 1511 | def _find_thread(self,pid): 1512 | for t in self.threads: 1513 | if t.pid == pid: 1514 | return t 1515 | return -1 1516 | 1517 | # dipset 1518 | def _clean_up(self,kill=False): 1519 | if kill: 1520 | for t in self.threads: 1521 | os.kill(t.pid,SIGKILL) 1522 | else: 1523 | self.detach() 1524 | sys.exit() 1525 | 1526 | 1527 | # take input string of various forms => bytestr 1528 | def filter_val(self,val): 1529 | # case hex string 1530 | #print "f_v:%s:%s" % (type(val),repr(val)) 1531 | 1532 | try: 1533 | return self.filter_val(self.byte_dict[val]) 1534 | except: 1535 | pass 1536 | 1537 | try: 1538 | if "\\x" in val: 1539 | return b''.join([chr(int(i,16)) for i in filter(None,val.split("\\x"))]) 1540 | except: 1541 | pass 1542 | 1543 | try: 1544 | if val[0:2] == "0x": 1545 | return b''.join([chr(int(val[i:i+2],16)) for i in range(2,len(val),2)]) 1546 | except: 1547 | pass 1548 | 1549 | for size in ["B","L","Q"]: 1550 | try: 1551 | return struct.pack("<%s"%size,val) 1552 | except Exception as e: 1553 | #print str(e) 1554 | pass 1555 | 1556 | try: 1557 | return self.reg_deref(val) 1558 | except Exception as e: 1559 | #print str(e) 1560 | pass 1561 | 1562 | return val 1563 | 1564 | 1565 | 1566 | # take various inputs, output address as unsigned long 1567 | def filter_addr(self,val): 1568 | #print "f_a:%s:%s" % (type(val),repr(val)) 1569 | if val == None: 1570 | return None 1571 | 1572 | # Dereferencing pointers 1573 | try: 1574 | if val[0] == "*": 1575 | val = val[1:] 1576 | #print "New val:%s" % str(val) 1577 | tmp = self.filter_addr(val) 1578 | #print "Pointer found, attempting to derefe9rence: 0x%x" % tmp 1579 | return self.filter_addr(self.getMem(tmp,quiet=False)) 1580 | 1581 | except Exception as e: 1582 | #print e 1583 | pass 1584 | 1585 | try: 1586 | return int(val) 1587 | except ValueError: 1588 | try: 1589 | return int(val,16) 1590 | except: 1591 | pass 1592 | 1593 | # case symbol 1594 | try: 1595 | if self.elf: 1596 | if val in self.elf.symbol_dict.keys(): 1597 | addr = self.elf.symbol_dict[val][0] 1598 | if self.proc_map: 1599 | if addr < self.proc_map.base_relocation: 1600 | addr += self.proc_map.base_relocation 1601 | return addr 1602 | except Exception as e: 1603 | #print str(e) 1604 | pass 1605 | 1606 | try: 1607 | return self.reg_deref(val) 1608 | except Exception as e: 1609 | pass 1610 | 1611 | # offsets (e.g. main+5) 1612 | try: 1613 | if "-" in val: 1614 | tmp = val.split("-") 1615 | return (self.filter_addr(tmp[0]) - self.filter_addr(tmp[1])) 1616 | 1617 | if "+" in val: 1618 | tmp = val.split("+") 1619 | return (self.filter_addr(tmp[0]) + self.filter_addr(tmp[1])) 1620 | except: 1621 | pass 1622 | 1623 | try: 1624 | if len(val) == 1: 1625 | return struct.unpack(" 0: 1701 | buf += s 1702 | else: 1703 | buf += "(char *):0x%x" % value 1704 | buf += ",%s" % tail 1705 | elif ctype == c_void_p: 1706 | # is pointer, could be struct 1707 | # grab the first word in safe manner 1708 | buf += "void *:0x%x" % value 1709 | buf +=",%s" % tail 1710 | else: 1711 | # deal with pointer(c_XYZ) here 1712 | if "LP_c_" in str(ctype): 1713 | b_str = self.getMem(value,sizeof(c_long),True) 1714 | if b_str: 1715 | buf+= "%s:0x%x" % (pp_pointer_ctype(ctype),value) 1716 | val = struct.unpack(" 0 and diff < min_distance: 1761 | min_distance = diff 1762 | min_sym = (sym_addr,self.elf.symbol_dict[sym_addr][0],min_distance) 1763 | 1764 | return min_sym 1765 | 1766 | 1767 | ########################## 1768 | 1769 | def getSymbols(self,queryStr=""): 1770 | 1771 | if self.proc_map.base_relocation > 0x400000: 1772 | self.output("ASL Base: %s0x%x%s"%(GREEN,self.proc_map.base_relocation,CLEAR)) 1773 | else: 1774 | self.proc_map.base_relocation = 0x0 1775 | 1776 | 1777 | if queryStr: 1778 | queryStr = queryStr.upper() 1779 | 1780 | if self.elf: 1781 | # symbol_dict[addr] => ("sym",TYPE) 1782 | for i in self.elf.symbol_dict: 1783 | if i == 0: # unresolved symbols list 1784 | for unresolved in self.elf.symbol_dict[0]: 1785 | if queryStr in unresolved.upper(): 1786 | self.output("%s0x%x:%s %s" % (CYAN,i,CLEAR,unresolved)) 1787 | elif not queryStr: 1788 | self.output("%s0x%x:%s %s" % (CYAN,i,CLEAR,unresolved)) 1789 | try: 1790 | if queryStr in str(self.elf.symbol_dict[i]).upper(): 1791 | # CASE symbol type 1792 | self.output("%s0x%x:%s %s" % (CYAN,i+self.proc_map.base_relocation,CLEAR,self.elf.symbol_dict[i][0]) ) 1793 | elif not queryStr: 1794 | self.output("%s0x%x:%s %s" % (CYAN,i+self.proc_map.base_relocation,CLEAR,self.elf.symbol_dict[i][0]) ) 1795 | elif queryStr: 1796 | try: 1797 | if queryStr[:2] == "0X": 1798 | #print self.elf.symbol_dict[i] 1799 | if int(queryStr,16) == i: 1800 | self.output("%s0x%08x:%s %s" % (CYAN,i+self.proc_map.base_relocation,CLEAR,self.elf.symbol_dict[i][0]) ) 1801 | except: 1802 | pass 1803 | except Exception as e: 1804 | # we skip the hidden "name" => addr mappings 1805 | # since the print statements error out 1806 | # no need for dups. 1807 | # Unless of course "0x" is in the query str 1808 | pass 1809 | 1810 | else: 1811 | self.output(WARN("No valid elf loaded")) 1812 | 1813 | ########################## 1814 | 1815 | # takes assorted bytestrings and turns it into "\xab\x12"... 1816 | def saveBytes(self,label,byteStr): 1817 | self.byte_dict[label] = "" 1818 | 1819 | # case "ab192384401cd..." -> "\xab\x19\x23\x84\x40..." 1820 | if re.match(r'^([a-f0-9A-F][a-f0-9A-F])+$',byteStr): 1821 | for i in range(0,len(byteStr),2): 1822 | self.byte_dict[label] += "\\x" 1823 | self.byte_dict[label] += byteStr[i:i+2] 1824 | # "\\x12\\x23..." => bytes 1825 | elif re.match(r'^(\\x[a-f0-9A-F][a-f0-9A-F])+$',byteStr): 1826 | self.byte_dict[label] = byteStr 1827 | else: 1828 | for i in byteStr: 1829 | self.byte_dict[label] += "\\x%02x" % ord(i) 1830 | 1831 | 1832 | ########################## 1833 | 1834 | def showSavedBytes(self): 1835 | for label in self.byte_dict: 1836 | self.output("%s%s : %s%s%s" % (YELLOW, label, CYAN, self.byte_dict[label], CLEAR)) 1837 | 1838 | ########################## 1839 | 1840 | # we assume we have the process map, since it limits the 1841 | # memory to search by very large amounts 1842 | def findBytes(self,byteStr,queryStr=None,limit=1,update=True): 1843 | addr_list = [] 1844 | # for interactive mode 1845 | # can't use filter_val 1846 | if "\\x" in byteStr: 1847 | byteStr = b''.join([chr(int(i,16)) for i in filter(None,byteStr.split("\\x"))]) 1848 | #print repr(byteStr) 1849 | 1850 | if not self.proc_map: 1851 | self.output(ERROR("No mem map loaded...")) 1852 | return "" 1853 | 1854 | if update: 1855 | self.proc_map.update_map() 1856 | bounds = self.proc_map.get_memory_ranges() 1857 | 1858 | self.output(PURP("Searching all memory regions...")) 1859 | for b in bounds: 1860 | if queryStr and queryStr not in b[-1]: 1861 | continue 1862 | 1863 | tmp = loadMemoryMap(b,self.debug_pid) 1864 | lb = 0 1865 | offset = 0 1866 | 1867 | while True: 1868 | lb = tmp[offset:].find(byteStr) 1869 | offset+=lb 1870 | 1871 | if lb < 0: 1872 | break 1873 | 1874 | # add in lowerbound offset 1875 | loc = offset + b[0] 1876 | self.output("%s0x%x%s : %s" % (GREEN,loc,CLEAR,repr(byteStr))) 1877 | addr_list.append(loc) 1878 | 1879 | #so we don't hit the same bytes 1880 | offset+=len(byteStr) 1881 | 1882 | if not len(addr_list): 1883 | self.output(ERROR("No results")) 1884 | 1885 | 1886 | self.output(WARN("Searching Finished")) 1887 | self.query_addr_list = addr_list 1888 | 1889 | ########################## 1890 | 1891 | def save(self,ind,label="Savepoint"): 1892 | try: 1893 | ind = int(ind) 1894 | except: 1895 | # treat as label 1896 | label = ind 1897 | ind = len(self.memory_slots)+1 1898 | 1899 | if len(self.memory_slots) >= self.savepoint_limit and not ind: 1900 | self.output(WARN("Over savepoint limit, please overwrite a slot")) 1901 | return 1902 | 1903 | mem_blocks = [] 1904 | self.output(INFO("")) 1905 | tmp = 0 1906 | 1907 | for i in self.proc_map.get_memory_ranges(): 1908 | mem_blocks.append((i,loadMemoryMap(i,self.debug_pid))) 1909 | self.output("%s.%s"%(rainbow[tmp%len(rainbow)],CLEAR),newline=False) 1910 | tmp+=1 1911 | 1912 | tmp = self._update_regs(self.debug_pid) 1913 | regs = copy.deepcopy(tmp) 1914 | 1915 | s = Savepoint(regs,mem_blocks,label) 1916 | 1917 | if ind and ind <= len(self.memory_slots): 1918 | self.memory_slots[ind] = s 1919 | else: 1920 | self.memory_slots.append(s) 1921 | 1922 | self.output(CYAN + "Saved gameplay! Kupo [+.+]" + CLEAR) 1923 | 1924 | ########################## 1925 | 1926 | def load(self,ind,label="default"): 1927 | try: 1928 | ind = int(ind) 1929 | except: 1930 | for i in range(0,len(self.memory_slots)): 1931 | if self.memory_slots[i].label == ind: 1932 | ind = i 1933 | if ind > len(self.memory_slots): 1934 | self.output(ERROR("Invalid memory slot index given. Valid: 0-%d"%self.savepoint_limit)) 1935 | return 1936 | try: 1937 | l = self.memory_slots[ind] 1938 | except IndexError: 1939 | self.output(ERROR("That memory slot does not exist!")) 1940 | return 1941 | if l: 1942 | tmp = libc.ptrace(PTRACE_SETREGS,self.debug_pid,0,byref(l.regs)) 1943 | for bounds,mem in l.blocks: 1944 | self.writeMemoryBlock(bounds,mem) 1945 | self.output("%s.%s"%(rainbow[tmp%len(rainbow)],CLEAR),newline=False) 1946 | tmp+=1 1947 | else: 1948 | self.output(ERROR("That memory slot is empty!")) 1949 | return 1950 | 1951 | self.output(CYAN + "Loaded gameplay! Kupo [+.+]" + CLEAR) 1952 | 1953 | ########################## 1954 | 1955 | def listSavepoints(self): 1956 | for i in range(0,len(self.memory_slots)): 1957 | s = self.memory_slots[i] 1958 | self.output("%s[ %d-%s ]%s"%(rainbow[i%len(rainbow)],i,s.label,CLEAR),newline=False) 1959 | self.output("") 1960 | 1961 | ########################## 1962 | 1963 | #! TODO 1964 | def summaryInfo(self): 1965 | pass 1966 | 1967 | 1968 | ########################## 1969 | 1970 | def addSigHandler(self,sig,action): 1971 | # I know, already imported, sue me. 1972 | # Can't getattr w/ from x import * 1973 | import signal as s 1974 | try: 1975 | sig = getattr(s,sig) 1976 | except: 1977 | try: 1978 | sig = int(sig) 1979 | except: 1980 | self.output(ERROR("Invalid signal identifier given")) 1981 | return 1982 | 1983 | if action in self.sig_actions.keys(): 1984 | self.sig_handlers[sig] = self.sig_actions[action] 1985 | else: 1986 | self.output(WARN("Action %s not valid. 'lsig' to list valid cmd"%action)) 1987 | 1988 | ########################## 1989 | def listSigAction(self): 1990 | for action in self.sig_actions.keys(): 1991 | self.output(INFO("%s"%action)) 1992 | if self.sig_handlers: 1993 | for act in self.sig_handlers: 1994 | self.output(INFO("SIG:%d => %s"%(act,self.sig_handlers[act]))) 1995 | 1996 | ########################## 1997 | #"pass2prog":self.pass2prog, 1998 | #"ignore":self.ignoreSig, 1999 | 2000 | # begin all built-in signal actions 2001 | 2002 | def pass2prog(self,signal): 2003 | self.output(INFO("Sending SIG:%d to prog"%signal)) 2004 | pid = self.debug_pid 2005 | if pid > 0: 2006 | os.kill(pid,signal) 2007 | 2008 | def ignoreSig(self,signal): 2009 | self.output(INFO("Ignoring SIG:%d"%signal)) 2010 | self.sendCommand("c") 2011 | 2012 | def shitSelf(self,signal): 2013 | self.output(self.printRegs()) 2014 | # can't just use cont() or we get 2015 | # the prompt back... 2016 | self.sendCommand("c") 2017 | 2018 | ########################## 2019 | 2020 | def sendSig(self,sig): 2021 | pid = self.debug_pid 2022 | if pid > 0: 2023 | os.kill(pid,int(sig)) 2024 | 2025 | 2026 | ########################## 2027 | 2028 | def toggleDesync(self): 2029 | self.realtime = True if not self.realtime else False 2030 | 2031 | ########################## 2032 | 2033 | def writeMemoryBlock(self,bounds,memory): 2034 | self.setMem(bounds[0],memory) 2035 | 2036 | ########################## 2037 | 2038 | def getSections(self,queryStr=""): 2039 | if self.elf: 2040 | for i in self.elf.section_dict: 2041 | self.output("%s: %s" % (i,self.elf.section_dict[i])) 2042 | 2043 | ########################## 2044 | 2045 | 2046 | 2047 | ### Utility non-class methods 2048 | 2049 | def pp_ctype(ctype): 2050 | c = str(ctype) 2051 | start = c.find("_")+1 2052 | end = c.rfind("'") 2053 | 2054 | return c[start:end] 2055 | 2056 | def pp_pointer_ctype(pointer_ctype): 2057 | c = str(pointer_ctype) 2058 | # grab the type 2059 | start = c.find("_") + 3 2060 | end = c.find(" ") 2061 | ctype = "(" + c[start:end] 2062 | ctype += " *)" 2063 | return ctype 2064 | 2065 | def get_bytes(self,sock): 2066 | tmp = "" 2067 | buf = "" 2068 | sock.settimeout(.2) 2069 | try: 2070 | while True: 2071 | tmp = sock.recv(4096) 2072 | 2073 | if len(tmp): 2074 | buf+=tmp 2075 | if len(tmp) < 4096: 2076 | break 2077 | except: 2078 | pass 2079 | 2080 | return buf 2081 | 2082 | #### END PIDIFUL 2083 | if __name__ == "__main__": 2084 | a = Atrophy() 2085 | print CYAN + help_text.split('\n')[1] + CLEAR 2086 | try: 2087 | a.attach(sys.argv[1]) 2088 | except IndexError as e: 2089 | pass 2090 | except TypeError as e: 2091 | print str(e) + ":413" 2092 | usage() 2093 | except ValueError as e: 2094 | a.run(sys.argv[1],sys.argv[1:]) 2095 | except Exception as e: 2096 | print str(e) + ":418" 2097 | 2098 | a.sendCommand(loop=True) 2099 | 2100 | -------------------------------------------------------------------------------- /bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import time 3 | from ctypes import * 4 | import re 5 | 6 | haystack = ''.join([ chr(c)*0xfffff for c in range(0,255)]) 7 | needle = "\x90\x90\x91" 8 | 9 | #print len(testBuff) 10 | 11 | def python_way(needle,haystack): 12 | lb = -1 13 | offset=0 14 | off_list = [] 15 | while lb: 16 | lb = haystack[offset:].find(needle) 17 | offset = lb 18 | off_list.append(offset) 19 | 20 | if lb == -1: 21 | break 22 | 23 | print off_list 24 | 25 | def ctypes_rk_way(needle,haystack): 26 | pt = c_char_p(haystack) 27 | 28 | sig = 0 29 | test_sig = 0 30 | 31 | for x in needle: 32 | sig ^= ord(x) 33 | print "SIG: %d" % sig 34 | 35 | for x in haystack[0:len(needle)]: 36 | test_sig ^= ord(x) 37 | 38 | ind = 0 39 | for i in range(0,len(haystack): 40 | test_sig ^= 41 | 42 | test_sig -= neg 43 | test_sig += ord(i) 44 | 45 | if test_sig == sig: 46 | if needle == haystack[ind:ind+len(needle)]: 47 | print "Found:%d" % ind 48 | 49 | pt+=1 50 | 51 | 52 | 53 | 54 | def re_way(needle,haystack): 55 | a = [m.start() for m in re.finditer(needle,haystack)] 56 | #print a 57 | 58 | def file_way(needle,haystack): 59 | pass 60 | 61 | def benchmark(function,needle,haystack): 62 | t1 = time.time() 63 | function(needle,haystack) 64 | t2 = time.time() - t1 65 | print "Time taken for function:%s" % str(function) 66 | print "%f seconds" % t2 67 | 68 | 69 | benchmark(python_way,needle,haystack) 70 | benchmark(re_way,needle,haystack) 71 | benchmark(rk_way,needle,haystack) 72 | 73 | -------------------------------------------------------------------------------- /defines/AtrophyCompleter.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import readline 3 | import rlcompleter 4 | import atexit 5 | import sys 6 | 7 | class AtrophyCompleter(rlcompleter.Completer): 8 | def __init__(self,cmdcomplete,init_flag=True): 9 | self.text = "" 10 | self.matches = [] 11 | self.cmdcomplete = cmdcomplete 12 | self.symbols = [] 13 | self.index = 0 14 | 15 | self.cleanup_flag = True 16 | self.init_flag = init_flag 17 | self.session_start_index = 0 18 | 19 | self.HISTLEN = 2000 20 | self.HISTFILE = ".atrophy-history" 21 | self.DEFAULT_HIST_DISPLAY_LEN = 20 22 | 23 | self.delims = readline.get_completer_delims() 24 | readline.set_completer_delims(self.delims.replace("/",'')) 25 | self.delims = readline.get_completer_delims() 26 | readline.set_completer_delims(self.delims.replace("?",'')) 27 | self.delims = readline.get_completer_delims() 28 | readline.set_completer_delims(self.delims.replace("@",'')) 29 | readline.parse_and_bind('tab: complete') 30 | readline.set_completer(self.complete) 31 | 32 | # persistant commands that actually affect a session 33 | # (e.g. breakpoints, mem writes, comments) 34 | self.project_cmds = [ "sd", "b", "db", "sb","#", "//" ] 35 | 36 | 37 | if self.init_flag == True: 38 | self.init_flag = False 39 | try: 40 | readline.read_history_file(self.HISTFILE) 41 | self.session_start_index = readline.get_current_history_length() 42 | readline.set_history_length(self.HISTLEN) 43 | except Exception as e: 44 | pass 45 | 46 | atexit.register(self.on_exit,self.HISTFILE) 47 | 48 | def on_exit(self,histfile): 49 | if self.cleanup_flag == True: 50 | self.cleanup_flag = False 51 | readline.set_history_length(self.HISTLEN) 52 | readline.write_history_file(histfile) 53 | 54 | 55 | def print_history(self,count=0): 56 | buf = "" 57 | length = readline.get_current_history_length() 58 | if count == 0: 59 | count = self.DEFAULT_HIST_DISPLAY_LEN 60 | 61 | for i in range(length-count,length): 62 | tmp = readline.get_history_item(i) 63 | if tmp: 64 | buf += tmp 65 | buf += "\n" 66 | return buf 67 | 68 | def addSymbols(self,symbols): 69 | for i in symbols: 70 | try: 71 | int(i) 72 | except: 73 | if i not in self.symbols: 74 | self.symbols.append(i) 75 | 76 | def complete(self,text,index): 77 | if text != self.text or not text: 78 | self.text = text 79 | if not readline.get_begidx(): 80 | self.matches = [ w for w in self.cmdcomplete if w.startswith(text) ] 81 | else: 82 | context = readline.get_line_buffer().split(" ") 83 | #check first word, see if it's an atrophy command 84 | 85 | 86 | if context[0] in self.cmdcomplete and context[0] != "run": 87 | self.matches = [ s for s in self.symbols if s.startswith(text)] 88 | else: 89 | self.matches = [ f for f in glob.glob(context[-1]+'*')] 90 | else: 91 | try: 92 | return self.matches[index] 93 | except: 94 | pass 95 | try: 96 | return self.matches[index] 97 | except: 98 | return None 99 | 100 | 101 | def save_project(self,proj_name): 102 | session_length = readline.get_current_history_length() 103 | 104 | project_file = ".atrophy-project-%s" % proj_name 105 | project_buff = "" 106 | 107 | try: 108 | # same directory 109 | with open(project_file,"r") as f: 110 | project_buff = f.read() 111 | except: 112 | # project == full path??? 113 | try: 114 | with open(proj_name,"r") as f: 115 | project_buff = f.read() 116 | project_file = proj_name 117 | except: ## no file found 118 | pass 119 | 120 | for i in range(self.session_start_index,session_length,-1): 121 | buf = readline.get_history_item(i) 122 | 123 | if buf: 124 | cmd = buf.split(" ")[0] 125 | if cmd in self.project_cmds: 126 | project_buff += buf 127 | project_buff += "\n" 128 | 129 | with open(project_file,"w") as f: 130 | f.write(project_buff) 131 | 132 | def load_project(self,proj_name): 133 | project_file = ".atrophy-project-%s" % proj_name 134 | project_buff = "" 135 | 136 | try: 137 | # same directory 138 | with open(project_file,"r") as f: 139 | project_buff = f.read() 140 | except: 141 | # project == full path??? 142 | try: 143 | with open(proj_name,"r") as f: 144 | project_buff = f.read() 145 | project_file = proj_name 146 | except: ## no file found 147 | pass 148 | 149 | return project_buff 150 | 151 | -------------------------------------------------------------------------------- /defines/ElfEater.py: -------------------------------------------------------------------------------- 1 | from elf_defines import * 2 | from utility import raw_to_cstruct_args 3 | from ctypes import CDLL 4 | import sys 5 | 6 | elf_raw = "" 7 | sheaders = [] 8 | pheaders = [] 9 | DEBUG = False 10 | 11 | def parse_elf(path): 12 | 13 | global elf_raw 14 | global pheaders 15 | 16 | with open(path,"rb") as f: 17 | elf_raw = f.read() 18 | args = raw_to_cstruct_args(elf_raw[0:sizeof(ElfHeader)],ElfHeader) 19 | elf_headers = ElfHeader(*args) 20 | 21 | #Things needed: .shoff,.shnum,.shstrtab,.shentsize 22 | # null separated section names 23 | section_names = get_sheaders(elf_headers) 24 | 25 | # program header objects 26 | pheaders = get_pheaders(elf_headers) 27 | 28 | #print repr(section_names) 29 | symtab_index = -1 30 | dyn_symtab_index = -1 31 | dynamic_index = -1 32 | rel_list = [] 33 | rela_list = [] 34 | dyn_list = [] 35 | section_dict = {} 36 | 37 | for i in range(0,len(sheaders)): 38 | # get the symbol table 39 | typ = getattr(sheaders[i],"sh_type") 40 | name_ndx = getattr(sheaders[i],"sh_name") 41 | nullbyte = section_names[name_ndx:].find('\x00') 42 | str_name = section_names[name_ndx:name_ndx+nullbyte] 43 | #print "%s %s" % (str_name,sheaders[i]) 44 | # save for returning 45 | section_dict[str_name] = sheaders[i] 46 | 47 | if typ == SHT_SYMTAB: 48 | symtab_index = i 49 | if typ == SHT_DYNSYM: 50 | dyn_symtab_index = i 51 | if typ == SHT_REL: 52 | rel_list.append(i) 53 | if typ == SHT_RELA: 54 | rela_list.append(i) 55 | if typ == SHT_DYNAMIC: 56 | dynamic_index = i 57 | 58 | if typ == SHT_STRTAB: 59 | #print repr(str_name) 60 | if str_name == ".strtab": 61 | str_index = i 62 | str_table = get_section(i) 63 | if DEBUG: 64 | print "STR_TAB:%d"%str_index 65 | if str_name == ".dynstr": 66 | dyn_str_index = i 67 | dyn_str_table = get_section(i) 68 | if DEBUG: 69 | print "DYN_STR_TAB:%d"%dyn_str_index 70 | 71 | # at this point, dyn_str_table and str_table are in raw form 72 | # and we know the indexes of the dynamic symbol and regular symbol tables 73 | symbol_table = {} 74 | dyn_symbol_table = {} 75 | dynamic_table = {} 76 | 77 | if symtab_index > -1: 78 | symbol_table = parse_symbols(get_section(symtab_index)) 79 | if dyn_symtab_index > -1: 80 | dyn_symbol_table = parse_symbols(get_section(dyn_symtab_index)) 81 | if dynamic_index > -1: 82 | dynamic_table = parse_dynamic(get_section(dynamic_index)) 83 | 84 | # store all results in the dict 85 | # map addr => (symbol_string,type) 86 | symbol_dict = {} 87 | symbol_dict['dyn_sym'] = [] 88 | 89 | try: 90 | for sym in symbol_table: 91 | save_symbol(symbol_dict,str_table,sym) 92 | except: 93 | pass 94 | 95 | try: 96 | for sym in dyn_symbol_table: 97 | save_symbol(symbol_dict,dyn_str_table,sym,dyn=True) 98 | except: 99 | pass 100 | 101 | for rel_sec in rel_list: 102 | for rel in parse_rel(get_section(rel_sec)): 103 | save_reloc(symbol_dict,dyn_str_table,rel) 104 | 105 | for rela_sec in rela_list: 106 | tmp = parse_rela(get_section(rela_sec)) 107 | try: 108 | for rela in parse_rela(get_section(rela_sec)): 109 | if rela.is_jump_slot(): 110 | save_reloc(symbol_dict,dyn_str_table,rela) 111 | except IndexError: 112 | break 113 | 114 | if DEBUG: 115 | print "----------------" 116 | print repr(dyn_str_table) 117 | print "----------------" 118 | print repr(str_table) 119 | 120 | ''' 121 | for i in dynamic_table: 122 | if str(i): 123 | print i 124 | if "NULL" in str(i): 125 | break 126 | ''' 127 | 128 | return elf_headers,section_dict,pheaders,symbol_dict 129 | 130 | def save_reloc(symbol_dict,str_table,relocation): 131 | symbol_list = symbol_dict['dyn_sym'] 132 | r_offset,r_sym_indx,r_type = relocation.parse_rel() 133 | dyn_sym_name = symbol_list[r_sym_indx] 134 | 135 | symbol_dict[dyn_sym_name] = (r_offset,"DYN_FUNC") 136 | symbol_dict[r_offset] = (dyn_sym_name,"DYN_FUNC") 137 | 138 | 139 | def save_symbol(symbol_dict,str_table,sym,dyn=False): 140 | 141 | st_type = getattr(sym,"st_info") & 0xf 142 | st_value = getattr(sym,"st_value") 143 | st_shndx = getattr(sym,"st_shndx") 144 | st_name = getattr(sym,"st_name") 145 | 146 | ## the st_name field isn't always 100% at begin? 147 | end_nullbyte = str_table[st_name:].find('\x00') 148 | #begin_nullbyte = str_table[:st_name].rfind("\x00") 149 | #symbol_string = str_table[begin_nullbyte+1:st_name+end_nullbyte] 150 | symbol_string= str_table[st_name:st_name+end_nullbyte] 151 | 152 | 153 | if symbol_string and DEBUG: 154 | print "SYM: %s NAME: %s" % (str(sym),symbol_string) 155 | try: 156 | # if its already in dynamic, return 157 | symbol_dict['dyn_sym'].index(symbol_string) 158 | return 159 | except ValueError: 160 | pass 161 | 162 | if len(symbol_string) and st_value > 0: 163 | # Non-dynamic symbol 164 | try: 165 | # if it's already in regular, return 166 | symbol_dict[symbol_string] 167 | except KeyError: 168 | # add forward and backward lookup for easier access 169 | symbol_dict[st_value] = (symbol_string,SYM_TYPE[st_type]) 170 | symbol_dict[symbol_string] = (st_value,SYM_TYPE[st_type]) 171 | 172 | elif not st_value and dyn==True: 173 | symbol_dict['dyn_sym'].append(symbol_string) 174 | symbol_dict[symbol_string] = -1 175 | 176 | 177 | ##### Section header parsing 178 | def get_sheaders(elf_headers): 179 | global sheaders 180 | 181 | shoff = getattr(elf_headers,"e_shoff") 182 | shentsize = getattr(elf_headers,"e_shentsize") 183 | shnum = getattr(elf_headers,"e_shnum") 184 | s_header_raw = elf_raw[shoff:shoff+(shentsize*shnum)] 185 | 186 | shstrndx = getattr(elf_headers,"e_shstrndx") 187 | 188 | s_header_list_raw = [raw_to_cstruct_args(s_header_raw[i:i+shentsize],SectionHeader) for i in xrange(0,len(s_header_raw),shentsize)] 189 | 190 | sheaders = [SectionHeader(*s_header_list_raw[i]) for i in range(0,len(s_header_list_raw))] 191 | 192 | SectionHeaderNames = get_section(shstrndx) 193 | 194 | return SectionHeaderNames 195 | 196 | 197 | ##### Program header parsing 198 | def get_pheaders(elf_headers): 199 | phoff = getattr(elf_headers,"e_phoff") 200 | phnum = getattr(elf_headers,"e_phnum") 201 | phentsize = getattr(elf_headers,"e_phentsize") 202 | 203 | p_header_raw = elf_raw[phoff:phoff+(phnum*phentsize)] 204 | 205 | p_header_list_raw = [ raw_to_cstruct_args(p_header_raw[i:i+phentsize],ProgramHeader) for i in xrange(0,len(p_header_raw),phentsize) ] 206 | 207 | pheaders = [ProgramHeader(*p_header_list_raw[i]) for i in range (0,len(p_header_list_raw))] 208 | 209 | return pheaders 210 | 211 | ## Section parsting 212 | def get_section(index): 213 | try: 214 | section_beg = getattr(sheaders[index],"sh_offset") 215 | section_end = section_beg + getattr(sheaders[index],"sh_size") 216 | return elf_raw[section_beg:section_end] 217 | except: 218 | return "\x00" 219 | 220 | #### Parsing the Symbol Table (finally) 221 | def parse_symbols(raw_symbols): 222 | symbol_list_raw = [raw_to_cstruct_args(raw_symbols[i:i+sizeof(Elf_Sym)],Elf_Sym) for i in xrange(0,len(raw_symbols),sizeof(Elf_Sym)) ] 223 | symbol_list = [ Elf_Sym(*symbol_list_raw[i]) for i in range(0,len(symbol_list_raw)) ] 224 | return symbol_list 225 | 226 | #### Rel Parsing 227 | def parse_rel(raw_reloc): 228 | reloc_list_raw = [raw_to_cstruct_args(raw_reloc[i:i+sizeof(Elf_Rel)],Elf_Rel) for i in xrange(0,len(raw_reloc),sizeof(Elf_Rel)) ] 229 | reloc_list = [ Elf_Rel(*reloc_list_raw[i]) for i in range(0,len(reloc_list_raw)) ] 230 | return reloc_list 231 | 232 | #### Rela Parsing 233 | def parse_rela(raw_reloc): 234 | i = 0 235 | reloc_list_raw = [raw_to_cstruct_args(raw_reloc[i:i+sizeof(Elf_Rela)],Elf_Rela) for i in xrange(0,len(raw_reloc),sizeof(Elf_Rela)) ] 236 | 237 | try: 238 | reloc_list = [ Elf_Rela(*reloc_list_raw[i]) for i in range(0,len(reloc_list_raw)) ] 239 | except: 240 | print i 241 | 242 | return reloc_list 243 | 244 | 245 | def parse_dynamic(raw_dynamic): 246 | dynamic_list_raw = [raw_to_cstruct_args(raw_dynamic[i:i+sizeof(DynamicEnt)],DynamicEnt) for i in xrange(0,len(raw_dynamic),sizeof(DynamicEnt)) ] 247 | dynamic_list = [ DynamicEnt(*dynamic_list_raw[i]) for i in range(0,len(dynamic_list_raw)) ] 248 | return dynamic_list 249 | 250 | 251 | if __name__ == "__main__": 252 | elf = ELF(*parse_elf(sys.argv[1])) 253 | if DEBUG: 254 | print "Elf Headers" 255 | print elf.elf_headers 256 | print "Section Dict" 257 | for i in elf.section_dict: 258 | print "%s: %s" % (i,elf.section_dict[i]) 259 | print "Symbol Dict" 260 | for i in elf.symbol_dict: 261 | try: 262 | print "0x%x: %s" % (i,elf.symbol_dict[i]) 263 | except: 264 | pass 265 | print "Pheaders" 266 | for i in elf.pheaders: 267 | print i 268 | -------------------------------------------------------------------------------- /defines/ProcMap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | from utility import INFO,WARN 4 | 5 | class MemRegion(object): 6 | def __init__(self,lb,ub,perm,filtered): 7 | 8 | self.lowerbound = lb 9 | self.upperbound = ub 10 | self.permissions = perm 11 | self.filtered = filtered 12 | 13 | 14 | def is_mapped(self,addr): 15 | try: 16 | addr = int(addr,16) 17 | except: 18 | pass 19 | 20 | #print "0x%x-0x%x <- %s" % (self.lowerbound,self.upperbound,addr) 21 | if addr >= self.lowerbound and addr <= self.upperbound: 22 | return True 23 | 24 | return False 25 | 26 | 27 | 28 | class ProcMap(object): 29 | def __init__(self,pid): 30 | self.pid = pid 31 | self.memory_map = [] 32 | self.raw_map = "" 33 | self.base_reloc = 0x0 34 | 35 | #self.mtime = 0 36 | 37 | def find_region(self,addr): 38 | 39 | for region in self.memory_map: 40 | if region.is_mapped(addr): 41 | return region 42 | return False 43 | 44 | def search_labels(self,query): 45 | buf = "" 46 | for region in self.memory_map: 47 | # assume label is always -1st element 48 | if query in region.filtered[-1]: 49 | buf += str(region.filtered) 50 | buf += "\n" 51 | 52 | if len(buf): 53 | return buf 54 | else: 55 | return "No results" 56 | 57 | def update_map(self): 58 | procmap = "/proc/%d/maps" % self.pid 59 | 60 | # apparently /proc/pid/maps modtime only gets 61 | # updated on x86? lol >_> 62 | ''' 63 | if os.stat(procmap).st_mtime == self.mtime: 64 | # not modified, no changes 65 | #print "No changes" 66 | return 67 | 68 | self.mtime = os.stat(procmap).st_mtime 69 | ''' 70 | tmp = "" 71 | with open(procmap,"r") as f: 72 | tmp = f.read() 73 | if self.raw_map == tmp: 74 | return 75 | self.raw_map = tmp 76 | 77 | self.parse_map(self.raw_map) 78 | 79 | 80 | def get_memory_ranges(self): 81 | ret_list = [] 82 | if not self.memory_map: 83 | return None 84 | 85 | #print self.memory_map 86 | for i in self.memory_map: 87 | ret_list.append((i.lowerbound,i.upperbound,i.permissions,i.filtered[-1])) 88 | return ret_list 89 | 90 | def parse_map(self,raw_map): 91 | offset,dev,inode,label,extra = ("","","","","") 92 | 93 | self.memory_map=[] 94 | for region in filter(None,raw_map.split("\n")): 95 | filtered = filter(None,region.split(" ")) 96 | try: 97 | bounds = filtered[0] 98 | perms = filtered[1] 99 | except: 100 | # badddd. Going to hit shit fast. 101 | raise Exception("Unable to read /proc/pid") 102 | 103 | bounds = bounds.split("-") 104 | lowerbound = int(bounds[0],16) 105 | upperbound = int(bounds[1],16) 106 | 107 | #print filtered[-1] 108 | self.memory_map.append(MemRegion(lowerbound,upperbound,perms,filtered)) 109 | 110 | try: 111 | self.base_relocation = self.memory_map[0].lowerbound 112 | except: 113 | pass 114 | #print "BASE RELOC: 0x%x" % self.base_relocation 115 | 116 | 117 | 118 | 119 | 120 | # take lowerbound/upperbound, return huge string 121 | # of memory. Assuming Bounds are aligned 122 | 123 | def loadMemoryMap(bounds,pid,verbose=False): 124 | lbound,ubound,perm,region_name = bounds 125 | retbuf = "" 126 | filename = "/proc/%d/mem"%pid 127 | seek = True 128 | 129 | if os.path.isfile(region_name): 130 | filename = region_name 131 | seek = False 132 | 133 | try: 134 | with open(filename,"rb") as f: 135 | if seek: 136 | f.seek(lbound) 137 | 138 | retbuf = f.read(ubound-lbound) 139 | except: 140 | if verbose: 141 | print WARN("0x%x - 0x%x Unreadable" % (lbound,ubound)) 142 | 143 | if verbose: 144 | print INFO("0x%x - 0x%x" % (lbound,ubound)) 145 | 146 | return retbuf 147 | 148 | -------------------------------------------------------------------------------- /defines/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["ptrace_defines","ProcMap","utility","ElfEater"] 2 | 3 | -------------------------------------------------------------------------------- /defines/ascii_art.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | class BlockChart(): 5 | def __init__(self): 6 | self.vertical_separation = 3 7 | self.horizontal_separation = 3 8 | self.block_list = [] 9 | 10 | 11 | def display( 12 | 13 | class Block(): 14 | 15 | def __init__(self,length,width): 16 | self.length = length 17 | self.width = width 18 | self.horiz_char = "-" 19 | self.vert_char = "|" 20 | 21 | self.dcd = None 22 | 23 | 24 | 25 | def connect_horizontal(self,block): 26 | 27 | def extend_vertical(self,block): 28 | 29 | def contract_vertical(self,block): 30 | 31 | def display(self): 32 | 33 | 34 | -------------------------------------------------------------------------------- /defines/asm_utils.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from utility import GREEN,PURPLE,CYAN,CLEAR,ORANGE,YELLOW,BLU,fmtstr,RED 3 | from sys import maxsize 4 | 5 | from capstone import * 6 | from keystone import * 7 | 8 | ''' 9 | leave == (mov esp,ebp; pop ebp) 10 | ''' 11 | CtrlInstrDict = { 12 | # 13 | "\xe9":("jmp",5), # + "\xff\x00\x00\x00" 14 | "\xeb":("jmp",2), 15 | "\x70":("jo",2), 16 | "\x0f\x80":("jo",6), 17 | "\x71":("jno",2), 18 | "\x0f\x81":("jno",6), 19 | "\x72":("jb",2), 20 | "\x0f\x82":("jb",6), 21 | "\x73":("jae",2), 22 | "\x0f\x83":("jae",6), 23 | "\x74":("je",2), 24 | "\x0f\x84":("je",6), 25 | "\x75":("jne",2), 26 | "\x0f\x85":("jne",6), 27 | "\x74":("jz",2), 28 | "\x0f\x84":("jz",6), 29 | "\x75":("jnz",2), 30 | "\x0f\x85":("jnz",6), 31 | "\x76":("jbe",2), 32 | "\x0f\x86":("jbe",6), 33 | "\x77":("ja",2), 34 | "\x0f\x87":("ja",6), 35 | "\x78":("js",2), 36 | "\x0f\x88":("js",6), 37 | "\x79":("jns",2), 38 | "\x0f\x89":("jns",6), 39 | "\x7c":("jl",2), 40 | "\x0f\x8c":("jl",6), 41 | "\x7d":("jge",2), 42 | "\x0f\x8d":("jge",6), 43 | "\x7e":("jle",2), 44 | "\x0f\x8e":("jle",6), 45 | "\x7f":("jg",2), 46 | "\x0f\x8f":("jg",6), 47 | } 48 | 49 | 50 | # Things that we could probably get from emulation... 51 | # "\xe2":("loop",2), 52 | # reg,reg 53 | # "\x48\x39":("cmp64",3), 54 | # reg,imm32 55 | # "\x48\x3d":("cmp64",6), 56 | # reg,[reg] 57 | # "\x48\x3b":("cmp64",3), 58 | # "\x48":("test",7), 59 | # "enter":"\x" 60 | # "\xc9":("leave",1), 61 | # "\xc3":("ret",1), 62 | 63 | 64 | # "\xe8":("call",5), # call imm32 65 | # "\xff":("call",2), # call reg 66 | 67 | class AsmUtil(): 68 | 69 | def __init__(self): 70 | self.NullLocs = [] 71 | self.cntrlInstrDict = CtrlInstrDict 72 | 73 | # functions lended by Atrophy 74 | self.getString = None 75 | self.getMem = None 76 | self.getReg = None 77 | 78 | if maxsize < 2**32: 79 | ks_mode = KS_MODE_32 80 | cs_mode = CS_MODE_32 81 | else: 82 | ks_mode = KS_MODE_64 83 | cs_mode = CS_MODE_64 84 | 85 | self.ks = Ks(KS_ARCH_X86, ks_mode) 86 | self.cs = Cs(CS_ARCH_X86, cs_mode) 87 | 88 | # addr:[comment1, comment2] 89 | self.comments = {} 90 | 91 | # For potential strings, discard strings of len < str_len_filter 92 | self.str_len_filter = 3 93 | 94 | 95 | # allow asm_utils to utilize atrophy functions 96 | def share_functions(self,getString,getMem,getReg): 97 | self.getString = getString 98 | self.getMem = getMem 99 | self.getReg = getReg 100 | 101 | # find places to insert shellcode for 102 | # hooks to stay 103 | def findNullLocsCanidates(self,limit=5): 104 | pass 105 | 106 | # redirect code execution of targetFuncAddr to 107 | # dstHookAddr, which does whatever it needs, 108 | # and then returns code flow back 109 | def hookFunction(self,targetFuncAddr,dstHookAddr): 110 | pass 111 | 112 | # find args and locals 113 | def analyzeFunction(self,targetFuncAddr): 114 | pass 115 | 116 | def disassemble_single(self,byteStr): 117 | # Since it's an iterator, we still have to loop 118 | for i in self.cs.disasm(byteStr,0x0): 119 | return "%s %s" % (i.mnemonic,i.op_str) 120 | 121 | def disassemble(self, byteStr, offset): 122 | ret = [] 123 | for i in self.cs.disasm(byteStr,offset): 124 | # add comments to disassembly if needed 125 | self.op_append_comments(i) 126 | 127 | try: 128 | comments = ' '.join(self.comments["0x%x"%i.address]) 129 | ret.append((i.address,i.mnemonic,i.op_str+comments,i.bytes)) 130 | except KeyError: 131 | ret.append((i.address,i.mnemonic,i.op_str,i.bytes)) 132 | 133 | return ret 134 | 135 | def assemble(self, instructions): 136 | byteList, count = self.ks.asm(instructions) 137 | return byteList, count 138 | 139 | # take actions if certain operations are detected. 140 | def op_append_comments(self,instr): 141 | if instr.mnemonic == "call": 142 | self.comment_add(instr.op_str," %sC:0x%x"%(ORANGE,instr.address)) 143 | 144 | # (address mnemonic opstr) 145 | # rip relative dereference 146 | pc = "[rip" if maxsize > 2**32 else "[esp" 147 | ind = instr.op_str.find(pc) 148 | if ind > -1: 149 | self.comment_add("0x%x"%instr.address, \ 150 | self.rip_rel_calc(instr.address,instr.op_str[ind:]), \ 151 | prepend=True) 152 | 153 | ## Assorted address in src operand, try to interpret 154 | if instr.op_str.startswith("0x"): 155 | comment = self.interpret_address(instr) 156 | if comment: 157 | self.comment_add("0x%x"%instr.address," #%s%s"%(ORANGE,comment)) 158 | 159 | def emu_append_comment(self,block_address,instr,comment=""): 160 | if comment: 161 | self.comment_add("0x%x"%block_address,comment) 162 | 163 | 164 | # ****** address needs to be a string ****** 165 | def comment_add(self,address,content,prepend=False): 166 | if not content or not address: 167 | return 168 | try: 169 | # No dups, yo 170 | for comment in self.comments[address]: 171 | if content.find(comment) > 0: 172 | return 173 | 174 | if not content in self.comments[address]: 175 | if prepend: 176 | self.comments[address].insert(content,0) 177 | else: 178 | self.comments[address].append(content) 179 | except KeyError: 180 | self.comments[address] = [content,] 181 | 182 | 183 | # ****** address needs to be a string ****** 184 | def comment_del(self,address,index=-1): 185 | if not address: 186 | return 187 | if index == -1: 188 | try: 189 | self.comments[address].pop() 190 | except: 191 | pass 192 | else: 193 | self.comments[address] = self.comments[address][0:index] + \ 194 | self.comments[address][index+1:] 195 | 196 | 197 | # Input == capstone instruction 198 | # 199 | # Attempts to decode/interpret the op_str address 200 | # based on then context of the mnemonic/address 201 | # 202 | # Returns datatype in correct display type (e.g symbol||string||const...) 203 | def interpret_address(self,instr): 204 | # long jump 205 | #print repr(instr.bytes[0]) 206 | if instr.bytes[0] == 0xf and len(instr.bytes) == 6: 207 | return "<(^-^)>" 208 | 209 | #instr.op_str == "0xabc..." 210 | if instr.op_str in self.comments.keys(): 211 | # remove old DstComments if needed 212 | try: 213 | for comment in self.comments["0x%x"%instr.address]: 214 | if "**" in comment: 215 | self.comment_del("0x%x"%instr.address,self.comments["0x%x"%instr.address].index(comment)) 216 | break 217 | except KeyError: 218 | pass 219 | 220 | dst_comment = "" 221 | for comment in self.comments[instr.op_str]: 222 | if "**" not in comment: 223 | dst_comment+=comment[2:] 224 | 225 | return "**" + dst_comment 226 | 227 | #! Todo, what do I want at jumps? 228 | # short jump 229 | if chr(instr.bytes[0]) in CtrlInstrDict.keys(): 230 | pass #return "doop" 231 | 232 | # callz 233 | #"\xff":("call",2), # call reg (need more info for this.. -_-) 234 | # "\xe8":("call",5), # call imm32 235 | if instr.bytes[0] == 0xe8: 236 | pass #return "loop" 237 | 238 | if instr.op_str == "lea": 239 | pass #return "herp" 240 | 241 | potential_string = self.getString(instr.address,verbose=False) 242 | 243 | # "str\x00" 244 | if len(potential_string) > self.str_len_filter: 245 | return potential_string 246 | 247 | # Samples: 248 | # eax, dword ptr [rip + 0x221fc7] 249 | # 0x7f28c994acb2: lea rsp, qword ptr [rsp + rax*8] 250 | # addr == long/int 251 | def rip_rel_calc(self,addr,op_str): 252 | right_bracket = op_str.find("]") 253 | op_str_format = op_str[1:right_bracket].split(' ') #strip brackets 254 | 255 | if "+" in op_str: 256 | absolute_addr = addr + int(op_str_format[-1],16) 257 | comment = " #0x%x" % absolute_addr 258 | 259 | if "-" in op_str: 260 | absolute_addr = addr - int(op_str_format[-1],16) 261 | comment = " #0x%x" % absolute_addr 262 | 263 | # get some color in there... 264 | addr = "0x%x"%addr 265 | self.comment_add(addr,ORANGE+comment,prepend=True) 266 | -------------------------------------------------------------------------------- /defines/elf_defines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from ctypes import * 3 | from sys import maxsize 4 | 5 | 6 | fmtstr = "0x%x" 7 | if maxsize > 2**32: 8 | shift_len = 32 9 | #fmtstr = "0x%016x" 10 | else: 11 | shift_len = 8 12 | #fmtstr = "0x%08x" 13 | 14 | #Due to how ctypes works, 15 | # sizeof(c_ulong) = 8 on x64, and 16 | # sizeof(c_ulong) = 4 on x32 17 | #So only 1 set of vars is macroed 18 | ELF_Addr = c_ulong 19 | ELF_Off = c_ulong 20 | ELF_Half = c_ushort 21 | ELF_Word = c_uint 22 | ELF_Sword = c_int 23 | ELF_Xword = c_ulong 24 | ELF_Sxword = c_long 25 | 26 | class ElfHeader(Structure): 27 | 28 | # same headers regardle 29 | _fields_ = [ 30 | ("e_indent",c_char*16), # Mark file as object file, "ELF Identification" 31 | ("e_type", ELF_Half), # Identifies object file type [NONE,RELOC,EXEC,DYN] 32 | ("e_machine", ELF_Half), # Architecture: [AT&T,SPARC,I386,I686...] 33 | ("e_version", ELF_Word), # 0 = invalid version, 1 = Current 34 | ("e_entry", ELF_Addr), # Entry point for program 35 | # 36 | ("e_phoff", ELF_Off), # Program header table's file offset 37 | ("e_shoff", ELF_Off), # Section header table's file offset 38 | # 39 | ("e_eflags", ELF_Word), # Processor specific flags 40 | ("e_ehsize", ELF_Half), # ELF header's size in bytes 41 | # 42 | ("e_phentsize",ELF_Half ), # Size of one entry in program header table(all are equal) 43 | ("e_phnum",ELF_Half ), # Number of entries in program header table 44 | # 45 | ("e_shentsize",ELF_Half ), # Section header size of one entry (all same) 46 | ("e_shnum",ELF_Half ), # Number of Section headers 47 | ("e_shstrndx",ELF_Half ), # Section header table index that's the string table 48 | ] 49 | 50 | def __repr__(self): 51 | buf = "(" 52 | buf += "e_type: 0x%04x, " % getattr(self,"e_type") 53 | buf += "e_machine: 0x%04x, " % getattr(self,"e_machine") 54 | buf += "e_entry:" 55 | buf += fmtstr % getattr(self,"e_entry") 56 | buf += ")" 57 | return buf 58 | 59 | class e_identifier(Structure): 60 | #32-bit i386 = { EI_CLASS = ELFCLASS32, EIDATA=ELFDATA2LSB 61 | _fields_ = [ 62 | ("EI_MAGIC",c_ubyte * 4), # "\x7fELF" 63 | ("EI_CLASS",c_ubyte), # "file's capacity [INVALID,32bit,64bit] 64 | ("EI_DATA",c_ubyte), # ELFDATA2LSB | ELFDATA2MSB (endianess) 65 | ("EI_VERSION",c_ubyte), # Must be 1 66 | ("EI_PAD",c_ubyte), # Marks beginning of unused bytes in e_ident 67 | ("EI_NINDENT",c_ubyte), 68 | ] 69 | 70 | 71 | class ProgramHeader(Structure): 72 | if maxsize > 2**32: 73 | _fields_ = [ 74 | ("p_type",ELF_Word), 75 | ("p_flags", ELF_Word), 76 | ("p_offset",ELF_Off), 77 | ("p_vaddr", ELF_Addr), 78 | ("p_paddr", ELF_Addr), 79 | ("p_filesz", ELF_Xword), 80 | ("p_memsz", ELF_Xword), 81 | ("p_align", ELF_Xword), 82 | ] 83 | 84 | else: 85 | _fields_ = [ 86 | ("p_type",ELF_Word), 87 | ("p_offset",ELF_Off), 88 | ("p_vaddr", ELF_Addr), 89 | ("p_paddr", ELF_Addr), 90 | ("p_filesz", ELF_Word), 91 | ("p_memsz", ELF_Word), 92 | ("p_flags", ELF_Word), 93 | ("p_align", ELF_Word), 94 | ] 95 | 96 | 97 | def __repr__(self): 98 | p_type = getattr(self,"p_type") 99 | try: 100 | p_type_str = P_TYPE[p_type] 101 | except: 102 | p_type_str = "LO_HIGH_PROC" 103 | 104 | buf = "(" 105 | buf += "p_type: " 106 | buf += fmtstr % p_type 107 | buf += "|%s, " % p_type_str 108 | buf += "p_perm: %s, " % permissions_string(getattr(self,"p_flags")) 109 | buf += "p_offset: " 110 | buf += fmtstr % getattr(self,"p_offset") 111 | buf += ", p_vaddr: " 112 | buf += fmtstr % getattr(self,"p_vaddr") 113 | buf += ", p_paddr:" 114 | buf += fmtstr % getattr(self,"p_paddr") 115 | buf += ", p_memsz:" 116 | buf += fmtstr % getattr(self,"p_memsz") 117 | buf += ", p_filesz:" 118 | buf += fmtstr % getattr(self,"p_filesz") 119 | buf += ")" 120 | 121 | return buf 122 | 123 | 124 | 125 | P_TYPE = ["NULL","LOAD","DYNAMIC","INTERP","NOTE","SHLIB","PHDR","LOPROC","HIPROC"] 126 | 127 | 128 | # - OBJ file's section header table is an array of Elf32_Shdr structures 129 | # - Section header table index is subscript into array 130 | # - ELF_Header's e_shoff gives byte offset to the section header table 131 | # - Some SHT indexes are reserved (0x0,0xff00,0xff1f,0xfff1,0xfff2,0xffff) 132 | 133 | class SectionHeader(Structure): 134 | _fields_ = [ 135 | ("sh_name",ELF_Word), # Name of section, index into section header string table for c-string 136 | ("sh_type", ELF_Word), # Section contents/semantics (0x2=SHT_SYMTAB,0xb=SHT_DYNSYM|0x3=SHT_STRTAB) 137 | ("sh_flags", ELF_Word), # Misc attributes 138 | ("sh_addr",ELF_Addr), # Addr at which section's first byte should reside in memory 139 | ("sh_offset",ELF_Off), # byte offset at which section begins in file 140 | ("sh_size", ELF_Word), # Sectino size 141 | ("sh_link", ELF_Word), # Interp. depends on section type 142 | ("sh_info", ELF_Word), # Interp depends on section type 143 | ("sh_addralign", ELF_Xword), # Addr. alignment constraints. 144 | ("sh_entsize", ELF_Xword) # if fixed-size entry array, size of each entry 145 | ] 146 | 147 | def __repr__(self): 148 | buf = "(" 149 | buf += "sh_type:" 150 | buf += fmtstr % getattr(self,"sh_type") 151 | buf += ", sh_offset:" 152 | buf += fmtstr % getattr(self,"sh_offset") 153 | buf += ", sh_size: " 154 | buf += fmtstr % getattr(self,"sh_size") 155 | buf += ")" 156 | return buf 157 | 158 | #E_TYPE 159 | ET_NONE = 0x0 160 | ET_REL = 0x1 161 | ET_EXEC = 0x2 162 | ET_DYN = 0x3 163 | ET_CORE = 0x4 164 | ET_LOOS = 0xFE00 165 | ET_HIOS = 0xFEFF 166 | ETLOPROC = 0xFF00 167 | ET_HIPROC = 0xFFFF 168 | 169 | #SH_TYPE 170 | SHT_NULL = 0x0 # Inactive header, other members of header are undefined 171 | SHT_PROGBITS = 0x1 # holds info defined by program 172 | SHT_SYMTAB = 0x2 # Holds a symbol table, can only have 1, complete symbol table 173 | SHT_STRTAB = 0x3 # holds a string table 174 | SHT_RELA = 0x4 # holds relocation entries 175 | SHT_HASH = 0x5 # symbol hash table, necessary for dynamic linking 176 | SHT_DYNAMIC = 0x6 # Info for dynamic linking 177 | SHT_NOTE = 0x7 # Info that marks file in some wya 178 | SHT_NOBITS = 0x8 # Occupies no space in file, sh_offset contains conceptual file offset 179 | SHT_REL = 0x9 # Relocation entries 180 | SHT_SHLIB = 0xa # Unspecified semantics 181 | SHT_DYNSYM = 0xb # just like symtab, but minimal instead of full 182 | SHT_LOPROC = 0x70000000 # specifies mem range lower for process mem 183 | SHT_HIPROC = 0x7fffffff # specifies mem range higher for process mem 184 | SHT_LOUSER = 0x80000000 # specifies mem range lower for kernel mem 185 | SHT_HIUSER = 0xffffffff # specifies mem range higher for kernel mem 186 | #SH_FLAGS 187 | SHF_WRITE = 0x1 #data shoudl be writable 188 | SHF_ALLOC = 0x2 # Occupies memory during process execution 189 | SHF_EXECINSTR = 0x4 # Contains executable machine instructions 190 | SHF_MASCPROC = 0xf0000000 # processor specific stuff 191 | 192 | class Elf_Sym(Structure): 193 | if maxsize > 2**32: 194 | _fields_ = [ 195 | ("st_name", ELF_Word), # symbol name (index into obj's symbol string table 196 | ("st_info", c_ubyte), # type/bind attr 197 | ("st_other", c_ubyte), # reserved 198 | ("st_shndx" , ELF_Half), # section table index 199 | ("st_value", ELF_Addr), # symbol value 200 | ("st_size", ELF_Xword), # Size of object 201 | ] 202 | else: 203 | _fields_ = [ 204 | ("st_name", ELF_Word), # symbol name (index into obj's symbol string table 205 | ("st_value", ELF_Addr), # symbol value (addr/absolute value...) 206 | ("st_size", ELF_Xword), # Size of object 207 | # ELF32_ST_BIND(i) = ((i)>>4) 208 | # ELF32_ST_TYPE(i) = ((i)&0xf) 209 | # ELF32_ST_INFO(b,t) = (((b)<<4)+((t)&0xf)) 210 | ("st_info", c_ubyte), # type/bind attr 211 | ("st_other", c_ubyte), # reserved (0) 212 | ("st_shndx" , ELF_Half), # section table index 213 | #(every symbol table entry defined relatively to the section w/ this index) 214 | ] 215 | 216 | def __repr__(self): 217 | buf = "(" 218 | buf += "st_name: " 219 | buf += fmtstr % getattr(self,"st_name") 220 | buf += ", st_value: " 221 | buf += fmtstr % getattr(self,"st_value") 222 | buf += ", st_info: 0x%02x, " % getattr(self,"st_info") 223 | buf += "st_shndx: " 224 | buf += fmtstr % getattr(self,"st_shndx") 225 | buf += ")" 226 | return buf 227 | 228 | #Symbol Bindings 229 | STB_LOCAL = 0x0 230 | STB_GLOBAL = 0x1 231 | STB_WEAK = 0x2 232 | SYM_BIND = [ "LOCAL", "GLOBAL", "WEAK" ] 233 | 234 | #Symbol Types 235 | STT_NOTYPE= 0x0 236 | STT_OBJECT = 0x1 237 | STT_FUNC = 0x2 238 | STT_SECTION = 0x3 239 | STT_FILE = 0x4 240 | SYM_TYPE = ["NOTYPE","OBJECT","FUNC","SECTION","FILE"] 241 | 242 | class Elf_Rel(Structure): 243 | _fields_ = [ 244 | ("r_offset",ELF_Addr), 245 | ("r_info",ELF_Xword) 246 | ] 247 | 248 | def __repr__(self): 249 | buf = "(" 250 | buf += "r_offset:" 251 | buf += fmtstr % getattr(self,"r_offset") 252 | buf += ", r_info:" 253 | buf += fmtstr % getattr(self,"r_info") 254 | buf += ", r_sym:0x%01x, " % self.REL_SYM_INFO(getattr(self,"r_info")) 255 | buf += "r_type:0x%01x" % self.REL_SYM_TYPE(getattr(self,"r_info")) 256 | buf += "|%s" % I386_REL_TYPES[self.REL_SYM_TYPE(getattr(self,"r_info"))] 257 | buf += ")" 258 | return buf 259 | 260 | def REL_SYM_INFO(self,r_info): 261 | return r_info >> shift_len 262 | 263 | def REL_SYM_TYPE(self,r_info): 264 | return r_info & 0xf 265 | 266 | def REL_SYM_FIND(self,r_type,r_sym): 267 | return (r_sym << shift_len) + r_type 268 | 269 | class Elf_Rela(Structure): 270 | _fields_ = [ 271 | ("r_offset",ELF_Addr), # Location where reloc should be applied 272 | # for exe, virtual address of the storage unit being relocated 273 | ("r_info",ELF_Xword), 274 | ("r_addend",ELF_Sxword) 275 | ] 276 | 277 | def __repr__(self): 278 | buf = "(" 279 | buf += "r_offset:" 280 | buf += fmtstr % getattr(self,"r_offset") 281 | buf += ", r_info:" 282 | buf += fmtstr % getattr(self,"r_info") 283 | buf += ", r_sym:0x%01x, " % self.RELA_SYM_INFO(getattr(self,"r_info")) 284 | buf += "r_type:0x%01x" % self.RELA_SYM_TYPE(getattr(self,"r_info")) 285 | buf += "|%s" % AMD64_REL_TYPES[self.RELA_SYM_TYPE(getattr(self,"r_info"))] 286 | buf += " + r_addend:0x%x" % getattr(self,"r_addend") 287 | buf += ")" 288 | return buf 289 | 290 | def is_jump_slot(self): 291 | if AMD64_REL_TYPES[self.RELA_SYM_TYPE(getattr(self,"r_info"))] == "JMP_SLOT": 292 | return True 293 | return False 294 | 295 | def parse_rel(self): 296 | r_offset = getattr(self,"r_offset") 297 | r_sym = self.RELA_SYM_INFO(getattr(self,"r_info")) 298 | r_type = self.RELA_SYM_TYPE(getattr(self,"r_info")) 299 | return (r_offset,r_sym,r_type) 300 | 301 | def RELA_SYM_INFO(self,r_info): 302 | return r_info >> shift_len 303 | 304 | def RELA_SYM_TYPE(self,r_info): 305 | return r_info & 0xffffffff 306 | 307 | def RELA_SYM_FIND(self,r_type,r_sym): 308 | return (r_sym << shift_len) + (r_type & 0xffffffff) 309 | 310 | 311 | I386_REL_TYPES = [ "NONE", "32", "PC32", "GOT32", "PLT32", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE","GOTOFF","GOTPC" ] 312 | 313 | AMD64_REL_TYPES = [ 314 | "NONE","64","PC32","GOT32","PLT32","COPY","GLOB_DAT","JMP_SLOT","RELATIVE","GOTPCREL","32", 315 | "32S","16","PC16","8","PC8","PC64","GOTOFF64","GOTPC32","SIZE32","SIZE64" ] 316 | 317 | class ELF(Structure): 318 | def __init__(self,elf=[],sec={},phe=[],sym={}): 319 | self.elf_headers = elf 320 | self.section_dict = sec 321 | self.pheaders = phe 322 | self.symbol_dict = sym 323 | 324 | 325 | def permissions_string(perm_int): 326 | buf = "" 327 | 328 | if perm_int & 0x4: 329 | buf+="r" 330 | else: 331 | buf+="-" 332 | 333 | if perm_int & 0x2: 334 | buf+="w" 335 | else: 336 | buf+="-" 337 | 338 | if perm_int & 0x1: 339 | buf+="x" 340 | else: 341 | buf+="-" 342 | 343 | return buf 344 | 345 | #! Assorted Reference materials/notes 346 | # Special sections: 347 | # .bss - SHT_NOBITS - iniitalized to zeros 348 | # .comment - SHT_PROGBITS - version control info 349 | # .data - SHT_PROGBITS - Iniitalized data 350 | # .data1 - SHT_PROGBITS - Iniitialized data 351 | # .debug - SHT_PROGBITS - symbolic debugging info 352 | # .dynamic - SHT_DYNAMIC - dynamic linking info, SHF_ALLOC 353 | # .dynstr - SHT_DYNAMIC - Strings needed for dynamic linking(symbol table entries) 354 | # .dynsym - SHT_STRTAB - Dynamic linking symbol table 355 | # .fini - SHT_PROGBITS - Executable instructions for process termination code 356 | # .got - SHT_ PROGBITS - Global offset table 357 | # .hash - SHT_HASH - Symbol hash table 358 | # .init - SHT_PROGBITS - Executble instructions for process init code (called before main) 359 | # .interp- SHT_PROGBITS path name of program interpreter 360 | # .line - SHT_PROGBITS - line number infor for symbolic debugging 361 | # .note - SHT_NOTE - info in format of "Note section" 362 | # .plt - - SHT_PROGBITS - procedure linkage table 363 | # .rel[name]- SHT_PROGBITS - relocation info 364 | # .rela[name] SHT_REL - relocation info 365 | # .rodata - SHT_PROGBITS - read only data 366 | # .rodata1- SHT_PROGBITS - read only data 367 | # .shstrtab - SHT_STRTAB - holds section names 368 | # .strtab - SHT_STRTAB - holds strings used for symbol table entrie 369 | # .symtab - SHT_STRTAB - s- holds a symbol table 370 | # .text - SHT_PROGBITS - Executable instructions 371 | 372 | # String table entry format: 373 | # "\x00" + "str1" + "str2" + ... + "\x00" 374 | # Indexes: 0,1,4,...,-1 375 | 376 | DTAG = [ 377 | ("DT_NULL","none"), # Marks the end of the dynamic array 378 | ("DT_NEEDED","d_val"), # str table offset for name of needed library 379 | ("DT_PLTRELSZ","d_val"), # Total size of relocation entries of PLT 380 | ("DT_PLTGOT","d_ptr"), # Address of the linkage table 381 | ("DT_HASH","d_ptr"), # Addr of symbol hash table 382 | ("DT_STRTAB","d_ptr"), # Address of dynamic str table 383 | ("DT_SYMTAB","d_ptr"), # addr of dynamic symbol table. 384 | ("DT_RELA","d_ptr"), # Addr of relocation table with ELF64_rela entries 385 | ("DT_RELASZ", "d_val"), # Size of DT_RELA 386 | ("DT_RELANT","d_val"), # Size of each DT_RELA entry 387 | ("DT_STRSZ","d_val"), # Size of str table 388 | ("DT_SYMENT", "d_val"), # size of each sym table entry 389 | ("DT_INIT","d_ptr"), # Addr of init funcktion 390 | ("DT_FINI","d_ptr"), # addr of termination function 391 | ("DT_SONAME","d_ptr"), # Str tale offset of this shared object 392 | ("DT_RPATH","d_val"), # Str table offset of shared libry search path 393 | ("DT_SYMBOLIC","d_val"), # Resolve symbols before usual search (no value) 394 | ("DT_REL","d_ptr"), # Addr of relocation table 395 | ("DT_RELSZ","d_val"), # Total size of bytes of DT_REL reloc table 396 | ("DT_RELENT","d_val"), # Size of bytes of each entry 397 | ("DT_PLTREL","d_val"), # Type of reloc entry for PLT. DT_REL or DT_RELA 398 | ("DT_DEBUG","d_ptr"), # Reserved for debugger use 399 | ("DT_TEXTREL","none"), # Relocations for non-writable 400 | ("DT_JMPREL","d_ptr"), # Addr of relocs for PLT 401 | ("DT_BIND_NOW","none"), # Process all relocations before passing off control 402 | ("DT_INIT_ARRAY","d_ptr"), # Pointer to array of init funcs 403 | ("DT_FINI_ARRAY","d_ptr"), # pointer to array of destructor funcs 404 | ("DT_INIT_ARRAYSZ","d_val"), # size of DT_INIT_ARRAY 405 | ("DT_FINI_ARRAYSZ","d_val"), # size of DT_FINI_ARRAY 406 | # processor specific stuff, whatevs. 407 | #"DT_LOOS", 408 | #"DT_HIOS", 409 | #"DT_LOPROC", 410 | #"DT_HIPROC", 411 | ] 412 | 413 | 414 | class DynamicEnt(Structure): 415 | _fields_ = [ 416 | ("d_tag",ELF_Sxword ), 417 | ("d_un",ELF_Addr ), 418 | ] 419 | 420 | def __repr__(self): 421 | try: 422 | entry = DTAG[getattr(self,"d_tag")] 423 | except: 424 | return '' 425 | #return "Undefined: " + fmtstr % getattr(self,"d_tag") 426 | buf = "" 427 | buf += entry[0] 428 | if entry[1] == "d_ptr": 429 | buf+=" PTR:" 430 | elif entry[1] == "d_val": 431 | buf+= " VAL:" 432 | buf += fmtstr % getattr(self,"d_un") 433 | return buf 434 | 435 | 436 | -------------------------------------------------------------------------------- /defines/emu_utils.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from copy import deepcopy 3 | from sys import maxsize,exit,stdout 4 | 5 | from ProcMap import * 6 | from asm_utils import * 7 | from ptrace_defines import * 8 | from utility import * 9 | 10 | #! TODO: 11 | # Snapshot for replay (save writable memory) 12 | class EmuUtil(): 13 | 14 | def __init__(self,pid=0): 15 | 16 | self.proc_map = None 17 | # init == True => don't intiallize memory/registers 18 | self.init_flag = False 19 | # Used for savestate hook, decrements on instruction. 20 | # 0 => savestate, -1 => do nothing 21 | self.instr_count = -1 22 | 23 | if pid > 0: 24 | self.proc_map = ProcMap(pid) 25 | self.proc_map.update_map() 26 | 27 | self.regStruct = RegisterStruct() 28 | self.pid = pid 29 | self.asm = AsmUtil() 30 | 31 | # init Unicorn 32 | if maxsize > 2**32: 33 | self.uc_mode = UC_MODE_64 34 | else: 35 | self.uc_mode = UC_MODE_64 36 | 37 | self.uc_arch = UC_ARCH_X86 38 | self.uc_mode += UC_MODE_LITTLE_ENDIAN 39 | self.uni = Uc(self.uc_arch,self.uc_mode) 40 | self.uni_dict = {} # translations from "reg" -> UC_X86_REG_* 41 | 42 | 43 | self.comms_sock = None 44 | 45 | # mapped by context name, context==regs,mem==...mem. 46 | self.context_dict = {} 47 | self.mem_dict = {} 48 | 49 | # comment function for commenting the disassembly 50 | self.comment_disasm = None 51 | 52 | # for jump determination 53 | self.last_instr = None 54 | 55 | # list of all basic blocks encountered 56 | self.bb = [] 57 | 58 | self.dyn_dep_stack = [] 59 | 60 | 61 | def getEmuRegs(self,pid=0,regStruct=None): 62 | if regStruct: 63 | self.regStruct = deepcopy(regStruct) 64 | return 0 65 | elif pid > 0: 66 | tmp_status = c_int(0) 67 | libc.ptrace(PTRACE_ATTACH,pid,NULL,NULL) 68 | pid = libc.waitpid(pid,byref(tmp_status),0) 69 | ret = libc.ptrace(PTRACE_GETREGS,pid,0,byref(self.regStruct)) 70 | return ret 71 | 72 | # provide method for emulator to comment disassembly 73 | def init_commenting_function(self,comment_func): 74 | self.comment_disasm = comment_func 75 | 76 | 77 | # send instruction over to asm_util for commenting 78 | def add_comment(self,block_address,instr): 79 | if not self.comment_disasm: 80 | self.output("Unable to comment %s" % block_address) 81 | return 82 | # should be AsmUtil.emu_append_comment 83 | self.comment_disasm(block_address,instr) 84 | 85 | def add_comment_raw(self,address,comment): 86 | if not comment or not address: 87 | self.output("Unable to comment") 88 | return 89 | self.comment_disasm(address,None,comment) 90 | 91 | 92 | def getAtrophyRegs(self,regStruct=None): 93 | if not regStruct: 94 | regStruct = RegisterStruct() 95 | 96 | self.uni_dict = regStruct.get_uni_regs() 97 | for reg in regStruct.display_order: 98 | try: 99 | uni_reg = self.uni.reg_read(self.uni_dict[reg]) 100 | regStruct.set_register(reg,uni_reg) 101 | except Exception as e: 102 | #print e 103 | pass 104 | 105 | return regStruct 106 | 107 | # Creates Snapshot of the current process, 108 | def initEmulator(self,new_pid=0,newRegs=None): 109 | uni_dict = {} 110 | args = None 111 | 112 | # in case no pid was initially given 113 | if self.pid == 0: 114 | self.pid = new_pid 115 | self.proc_map = ProcMap(self.pid) 116 | self.proc_map.update_map() 117 | 118 | if self.getEmuRegs(self.pid,newRegs) == 0: 119 | self.output(GOOD("Registers Acquired")) 120 | uni_dict = self.regStruct.get_uni_regs() 121 | for reg in uni_dict: 122 | try: 123 | self.uni.reg_write(uni_dict[reg],self.regStruct.get_register(reg)) 124 | #self.output(INFO("%s : 0x%x"%(reg,self.regStruct.get_register(reg)))) 125 | except: 126 | #self.output(WARN("%s: unwritable"%reg)) 127 | pass 128 | else: 129 | self.output(ERROR("Could not get Regs for emulation, exiting!")) 130 | return 131 | 132 | # fs/gs regs 133 | self.uni.mem_map(0x0,0x1000,3) 134 | self.uni.mem_map(0x1000,0x1000,3) 135 | self.uni.reg_write(uni_dict["fs"],0x800) 136 | self.uni.reg_write(uni_dict["gs"],0x1800) 137 | # hack needed for negative offset to fs... 138 | self.uni.mem_map(0xfffffffffffff000,0x1000,3) 139 | 140 | # get/set the memory mappings 141 | self.initMemory(self.pid) 142 | self.addHooks() 143 | 144 | try: 145 | self.start_addr = self.regStruct.get_register("rip") 146 | except: 147 | self.start_addr = self.regStruct.get_register("eip") 148 | 149 | 150 | def addHooks(self): 151 | self.uni.hook_add(UC_HOOK_CODE,self.instr_hook) 152 | self.uni.hook_add(UC_HOOK_BLOCK,self.block_hook) 153 | 154 | 155 | self.uni.hook_add(UC_HOOK_MEM_INVALID,self.segfault_hook) 156 | ''' 157 | self.uni.hook_add(UC_HOOK_MEM_READ_INVALID,self.segfault_hook) 158 | self.uni.hook_add(UC_HOOK_MEM_WRITE_INVALID,self.segfault_hook) 159 | self.uni.hook_add(UC_HOOK_MEM_FETCH_INVALID,self.segfault_hook) 160 | ''' 161 | 162 | self.uni.hook_add(UC_HOOK_MEM_UNMAPPED,self.segfault_hook) 163 | ''' 164 | self.uni.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED,self.segfault_hook) 165 | self.uni.hook_add(UC_HOOK_MEM_READ_UNMAPPED,self.segfault_hook) 166 | self.uni.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED,self.segfault_hook) 167 | ''' 168 | 169 | ''' 170 | #! hooks 171 | UC_HOOK_INTR = 1 172 | UC_HOOK_INSN = 2 173 | UC_HOOK_CODE = 4 174 | UC_HOOK_BLOCK = 8 175 | UC_HOOK_MEM_READ_UNMAPPED = 16 176 | UC_HOOK_MEM_WRITE_UNMAPPED = 32 177 | UC_HOOK_MEM_FETCH_UNMAPPED = 64 178 | UC_HOOK_MEM_READ_PROT = 128 179 | UC_HOOK_MEM_WRITE_PROT = 256 180 | UC_HOOK_MEM_FETCH_PROT = 512 181 | UC_HOOK_MEM_READ = 1024 182 | UC_HOOK_MEM_WRITE = 2048 183 | UC_HOOK_MEM_FETCH = 4096 184 | UC_HOOK_MEM_READ_AFTER = 8192 185 | UC_HOOK_MEM_UNMAPPED = 112 186 | UC_HOOK_MEM_PROT = 896 187 | UC_HOOK_MEM_READ_INVALID = 144 188 | UC_HOOK_MEM_WRITE_INVALID = 288 189 | UC_HOOK_MEM_FETCH_INVALID = 576 190 | UC_HOOK_MEM_INVALID = 1008 191 | UC_HOOK_MEM_VALID = 7168 192 | ''' 193 | 194 | def startEmulator(self,instr_count=0,timeout=0): 195 | # since we can't really save state if we don't know 196 | # when we're going to end 197 | if instr_count <= 0: 198 | self.instr_count == -1 199 | else: 200 | self.instr_count = instr_count 201 | 202 | try: 203 | self.uni.context_update(self.context_dict["current"]) 204 | except KeyError: 205 | pass 206 | 207 | if maxsize > 2**32: 208 | start_addr = self.uni.reg_read(41) 209 | else: 210 | start_addr = self.uni.reg_read(26) 211 | 212 | #x86_const.UC_X86_REG_RIP 41 213 | #x86_const.UC_X86_REG_EIP 26 214 | if not self.init_flag: 215 | self.addHooks() 216 | else: 217 | try: 218 | self.uni.emu_start(start_addr,-1,timeout,instr_count) 219 | self.uni.emu_stop() 220 | return None 221 | except unicorn.UcError as e: 222 | self.output(ERROR(e)) 223 | return self.getAtrophyRegs() 224 | 225 | def block_hook(self,emulator,block_address,block_size,user_data): 226 | self.output("Basic Block: 0x%x-0x%x" % (block_address,block_address+block_size)) 227 | bb_type = "" 228 | bb_info = "" 229 | 230 | i = self.last_instr 231 | 232 | if i: 233 | self.add_comment(block_address,i) 234 | if i.mnemonic == "call": 235 | bb_type = "call" 236 | bb_info = i.op_str 237 | self.dyn_dep_stack.append((block_address,block_address+block_size,bb_type)) 238 | 239 | elif i.mnemonic == "ret": 240 | bb_type = "ret" 241 | if maxsize > 2**32: 242 | bb_info = self.get_reg("rax") 243 | else: 244 | bb_info = self.get_reg("eax") 245 | 246 | self.add_comment_raw(i.address," %s#RET:0x%x"%(GREEN,bb_info)) 247 | 248 | try: 249 | tmp = self.dyn_dep_stack.pop() 250 | print "popped: %s" % repr(tmp) 251 | except Exception as e: 252 | # print str(e) 253 | # potentially started in middle of block 254 | pass 255 | 256 | elif i.mnemonic == "syscall": 257 | bb_type = "syscall" 258 | if maxsize > 2**32: 259 | bb_info = self.get_reg("rax") 260 | else: 261 | bb_info = self.get_reg("eax") 262 | # no dyn_dep action 263 | 264 | elif i.mnemonic[0] == "j": 265 | bb_type = "jump" 266 | 267 | # determine direction of jump 268 | if (i.address + 2) == block_address or (i.address+6) == block_address: # 2 bytes for len(jmp)/6 bytes for len of long jump 269 | tmp = None 270 | bb_info = "%s not taken" % i.mnemonic 271 | self.add_comment_raw(i.address," %s#Jump not taken"%RED) 272 | # check for loop exit 273 | try: 274 | while self.dyn_dep_stack[-1][2] == "loop" and block_address > self.dyn_dep_stack[-1][1]: 275 | tmp = self.dyn_dep_stack.pop() 276 | print "popped: %s" % repr(tmp) 277 | except Exception as e: 278 | #print str(e) 279 | pass 280 | 281 | if not tmp: 282 | self.dyn_dep_stack.append((block_address,int(i.op_str,16),"branch")) 283 | 284 | else: 285 | bb_info = "%s taken" % i.mnemonic 286 | self.add_comment_raw(i.address," %s#Jump taken"%GREEN) 287 | 288 | # jump forward 289 | if i.address < block_address: 290 | try: 291 | # check for loop exit 292 | while self.dyn_dep_stack[-1][2] == "loop" and block_address > self.dyn_dep_stack[-1][1]: 293 | tmp = self.dyn_dep_stack.pop() 294 | print "popped: %s" % repr(tmp) 295 | except Exception as e: 296 | #print str(e) 297 | pass 298 | 299 | # jump backwards 300 | elif i.address > block_address: 301 | # loop start # loop end 302 | self.dyn_dep_stack.append((block_address,i.address,"loop")) 303 | else: 304 | # should never happen 305 | self.output("Lol, whut?") 306 | 307 | else: 308 | bb_type = "unknown" 309 | bb_info = "%s0x%x %s %s" % (YELLOW,i.address,i.mnemonic,i.op_str) 310 | 311 | self.bb.append((block_address,block_size,bb_type,bb_info)) 312 | 313 | 314 | 315 | last_addrs = [] # loop detection 316 | current_loop = [] 317 | loop_stack = [[0,[]]] # bottom of stack, always the same 318 | state = 0 # 0 == no loop, 1 == loop 319 | 320 | def segfault_hook(self,emulator,access,address,size,value,user_data): 321 | ''' 322 | uni_dict = self.regStruct.get_uni_regs() 323 | for reg in uni_dict: 324 | val = self.uni.reg_read(uni_dict[reg]) 325 | self.output("%s : 0x%x" % (reg,val)) 326 | ''' 327 | self.dyn_dep_stack = set(self.dyn_dep_stack) 328 | for i in range(0,len(self.dyn_dep_stack)): 329 | bb = self.dyn_dep_stack.pop() 330 | print repr(bb) 331 | self.add_comment_raw(bb[0]," !%sDDChain"%GREEN) 332 | 333 | self.output(YELLOW + "======= <(x.x)> ========" + CLEAR) 334 | 335 | ''' 336 | rax = self.uni.reg_read(uni_dict["rax"]) 337 | fs = self.uni.reg_read(uni_dict["fs"]) 338 | mem = self.uni.mem_read(fs-104,100) 339 | ''' 340 | 341 | self.output("address 0x%x" % address) 342 | self.output("size 0x%x" % size) 343 | self.output("access 0x%x" % access) 344 | self.output("value 0x%x" % value) 345 | self.output(YELLOW + "======= <(;.;)> ========" + CLEAR) 346 | #print "user_data 0x%x" % user_data 347 | i = self.last_instr 348 | self.output("%s0x%x:%s %s %s%s" % (ORANGE,i.address,YELLOW,i.mnemonic,i.op_str,CLEAR)) 349 | 350 | 351 | self.add_comment_raw(i.address," %s-----EmuSegfault------" % ORANGE) 352 | 353 | 354 | def emu_disassemble(self,code, addr): 355 | tmp = "" 356 | i = None 357 | 358 | for i in self.asm.cs.disasm(str(code),addr): 359 | tmp_bytes = "\\x" + b'\\x'.join("%02x"%x for x in i.bytes) 360 | self.output("%s0x%x:%s %s %s %s%s%s" % (GREEN,i.address,CYAN,i.mnemonic,i.op_str,YELLOW,tmp_bytes,CLEAR)) 361 | 362 | self.last_instr = i 363 | return i 364 | 365 | 366 | # - print instructions 367 | # - save state before emu stop 368 | def instr_hook(self,emulator,address,size,verbose=False): 369 | code = emulator.mem_read(address,size) 370 | instr = self.emu_disassemble(code,address) 371 | 372 | if self.instr_count > 0: 373 | self.instr_count -= 1 374 | elif self.instr_count == 0: 375 | self.context_dict["current"] = self.uni.context_save() 376 | 377 | if verbose: 378 | uni_dict = self.regStruct.get_uni_regs() 379 | for reg in uni_dict: 380 | val = self.uni.reg_read(uni_dict[reg]) 381 | self.output("%s : 0x%x" % (reg,val)) 382 | 383 | #if instr.mnemonic == "call": 384 | 385 | 386 | 387 | def initMemory(self,pid): 388 | 389 | self.proc_map = ProcMap(pid) 390 | self.proc_map.update_map() 391 | 392 | self.output("[^.^] Loading Emu mem maps....") 393 | for i in self.proc_map.get_memory_ranges(): 394 | tmp = loadMemoryMap(i,self.pid,verbose=False) 395 | 396 | lbound,ubound,perms,filename = i 397 | perms = rwx_to_int(perms) 398 | 399 | #print "Emumap: 0x%x-0x%x, len 0x%x, perm:%d - %s" % (lbound,ubound,ubound-lbound,perms,filename) 400 | try: 401 | #print "uni.mem_map(0x%x,0x%x,%d)" % (lbound,ubound-lbound,perms) 402 | self.uni.mem_map(lbound,ubound-lbound,perms) 403 | if perms > 0: 404 | self.uni.mem_write(lbound,tmp) 405 | except unicorn.UcError as e: 406 | self.output(ERROR(e)) 407 | 408 | # For saving state 409 | self.init_flag = True 410 | self.context_dict["init"] = self.uni.context_save() 411 | 412 | def restart_emu(self): 413 | self.load_emu_context("init") 414 | self.initMemory() 415 | 416 | def load_emu_context(self,context_str): 417 | try: 418 | self.uni.context_restore(self.context_dict[context_str]) 419 | self.output( "[L.L] Content %s Loaded" % context_str) 420 | except: 421 | self.output("[;_;] Context %s not found!"%context_str) 422 | 423 | self.mem_dict[context_str] = [] 424 | for i in self.mem_dict[context_str]: 425 | lbound,ubound,perms,filename = i 426 | tmp = loadMemoryMap(i,self.pid,verbose=True) 427 | self.uni.mem_write(lbound,tmp) 428 | 429 | def save_emu_context(self,context_str): 430 | try: 431 | #context already exists 432 | self.uni.context_update(self.context_dict[context_str]) 433 | except: 434 | self.context_dict[context_str] = self.uni.context_save() 435 | 436 | self.mem_dict[context_str] = [] 437 | for i in self.get_writable_mem(): 438 | self.mem_dict[context_str].append(loadMemoryMap(i,self.pid,verbose=True)) 439 | 440 | self.output("[s.s] Content %s Saved" % context_str) 441 | 442 | # returns list of memory ranges with writable permissions. 443 | def get_writable_mem(self): 444 | writeable_ranges = [] 445 | self.proc_map.update_map() 446 | 447 | for i in self.proc_map.get_memory_ranges(): 448 | lbound,ubound,perms,filename = i 449 | perms = rwx_to_int(perms) 450 | if perms & 0x2: #r=1,w=2,x=4 451 | writeable_ranges.append(i) 452 | 453 | return writeable_ranges 454 | 455 | def bb_dump(self): 456 | 457 | # nested loops? 458 | for i in self.bb: 459 | addr,size,typ,info = i 460 | self.output("%s0x%x:%s %s %s (bb_len:%d)"%(GREEN,addr,CYAN,typ,info,size)) 461 | 462 | def get_reg(self,reg): 463 | if not len(self.uni_dict): 464 | self.uni_dict = self.regStruct.get_uni_regs() 465 | return self.uni.reg_read(self.uni_dict[reg]) 466 | 467 | def set_reg(self,reg,value): 468 | value = get_int(value) 469 | if not len(self.uni_dict): 470 | self.uni_dict = self.regStruct.get_uni_regs() 471 | self.uni.reg_write(self.uni_dict[reg],value) 472 | 473 | # return in reverse (since little endian) 474 | def get_mem(self,address,length): 475 | address = get_int(address) 476 | length = get_int(length) 477 | return self.uni.mem_read(address,length) 478 | 479 | # reverse for little endian 480 | def set_mem(self,address,bytestr): 481 | address = get_int(address) 482 | bytestr = bytes(bytestr[::-1]) 483 | return self.uni.mem_write(address,bytestr) 484 | 485 | # If I knew what I was doing, this would 486 | # not have to be redefined. 487 | # print or send over socket 488 | def output(self,msg,newline=True): 489 | msg = str(msg) 490 | if newline: 491 | msg+="\n" 492 | if self.comms_sock: 493 | self.comms_sock.send(msg) 494 | else: 495 | stdout.write(msg) 496 | stdout.flush() 497 | 498 | 499 | 500 | 501 | def get_int(value): 502 | try: 503 | val = int(value) 504 | except ValueError: 505 | val = int(value,16) 506 | except: 507 | raise 508 | return val 509 | 510 | 511 | -------------------------------------------------------------------------------- /defines/ptrace_defines.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from utility import GREEN,PURPLE,CYAN,CLEAR,ORANGE,YELLOW,BLU,fmtstr 3 | from sys import maxsize 4 | from os import popen 5 | 6 | 7 | MAX_PID = 32768 8 | #constants 9 | 10 | eflags = ["carry","parity","adjust","zero","sign","trap","interrupt","direction","overflow"] 11 | 12 | PTRACE_TRACEME = 0 13 | PTRACE_PEEKTEXT = 1 14 | PTRACE_PEEKDATA = 2 15 | PTRACE_PEEKUSR = 3 16 | PTRACE_POKETEXT = 4 17 | PTRACE_POKEDATA = 5 18 | PTRACE_POKEUSR = 6 19 | PTRACE_CONT = 7 20 | PTRACE_KILL = 8 21 | PTRACE_SINGLESTEP = 9 22 | PTRACE_GETREGS = 12 23 | PTRACE_SETREGS = 13 24 | PTRACE_GETFPREGS = 14 25 | PTRACE_SETFPREGS = 15 26 | PTRACE_ATTACH = 16 27 | PTRACE_DETACH = 17 28 | PTRACE_GETFPXREGS = 18 29 | PTRACE_SETFPXREGS = 19 30 | PTRACE_SYSCALL = 24 31 | 32 | PTRACE_SETOPTIONS = 0x4200 33 | PTRACE_GETEVENTMSG = 0x4201 34 | PTRACE_GETSIGINFO = 0x4202 35 | PTRACE_SETSIGINFO = 0x4203 36 | PTRACE_GETREGSET = 0x4204 37 | PTRACE_SETREGSET = 0x4205 38 | PTRACE_SEIZE = 0x4206 39 | PTRACE_INTERRUPT = 0x4207 40 | 41 | 42 | #PTRACE EVENT CODES 43 | PTRACE_EVENT_FORK = 1 44 | PTRACE_EVENT_VFORK = 2 45 | PTRACE_EVENT_CLONE = 3 46 | PTRACE_EVENT_EXEC = 4 47 | PTRACE_EVENT_VFORK_DONE = 5 48 | PTRACE_EVENT_EXIT = 6 49 | PTRACE_EVENT_SECCOMP = 7 50 | PTRACE_EVENT_STOP = 128 51 | 52 | #PTRACE OPTIONS 53 | PTRACE_O_TRACESYSGOOD = 1 54 | PTRACE_O_TRACEFORK = (1 << PTRACE_EVENT_FORK) 55 | PTRACE_O_TRACEVFORK = (1 << PTRACE_EVENT_VFORK) 56 | PTRACE_O_TRACECLONE = (1 << PTRACE_EVENT_CLONE) 57 | PTRACE_O_TRACEEXEC = (1 << PTRACE_EVENT_EXEC) 58 | PTRACE_O_TRACEVFORKDONE = (1 << PTRACE_EVENT_VFORK_DONE) 59 | PTRACE_O_TRACEEXIT = (1 << PTRACE_EVENT_EXIT) 60 | PTRACE_O_TRACESECCOMP = (1 << PTRACE_EVENT_SECCOMP) 61 | 62 | def getEvent(code): 63 | try: 64 | return PTRACE_EVENT_CODES[code] 65 | except: 66 | return code 67 | 68 | # Register/flag layouts 69 | 70 | EFLAGS = [ 71 | "CF", None, "PF", None, #0-3 72 | "AF", None, "ZF", "SF", #4-7 73 | "TF", "IF", "DF", "OF", #8-11 74 | ] 75 | 76 | class RegisterStruct(Structure): 77 | 78 | _packed_ = 1 79 | 80 | if maxsize <= 2**32: 81 | display_order = [ "eax","ebx","ecx","edx","esi","edi","esp","ebp","eip","orig_eax", 82 | "eflags", "xds","xfs","xgs","xcs","xss" ] 83 | 84 | _fields_ = [ 85 | ("ebx", c_ulong), 86 | ("ecx", c_ulong), 87 | ("edx", c_ulong), 88 | ("esi", c_ulong), 89 | ("edi", c_ulong), 90 | ("ebp", c_ulong), 91 | ("eax", c_ulong), 92 | ("xds", c_ulong), 93 | ("xes", c_ulong), 94 | ("xfs", c_ulong), 95 | ("xgs", c_ulong), 96 | ("orig_eax", c_ulong), 97 | ("eip", c_ulong), 98 | ("xcs", c_ulong), 99 | ("eflags", c_ulong), 100 | ("esp", c_ulong), 101 | ("xss", c_ulong), 102 | ] 103 | 104 | else: 105 | 106 | display_order = ["rdi","rsi","rdx","rcx","r8","r9", #First 6 function params 107 | "r10","r11","r12","r13","r14","r15", 108 | "rax","rbx","rbp","orig_rax","rip", 109 | "cs","eflags","rsp","ss","fs_base", 110 | "gs_base","ds","es","fs","gs"] 111 | 112 | _fields_ = [ 113 | ("r15",c_ulonglong), 114 | ("r14",c_ulonglong), 115 | ("r13",c_ulonglong), 116 | ("r12",c_ulonglong), 117 | ("rbp",c_ulonglong), 118 | ("rbx",c_ulonglong), 119 | # ^ belong to caller 120 | # v belong to callee 121 | ("r11",c_ulonglong), 122 | ("r10",c_ulonglong), 123 | ("r9",c_ulonglong), 124 | ("r8",c_ulonglong), 125 | ("rax",c_ulonglong), 126 | ("rcx",c_ulonglong), 127 | ("rdx",c_ulonglong), 128 | ("rsi",c_ulonglong), 129 | ("rdi",c_ulonglong), 130 | # 131 | ("orig_rax",c_ulonglong), 132 | ("rip",c_ulonglong), 133 | ("cs",c_ulonglong), 134 | ("eflags",c_ulonglong), 135 | ("rsp",c_ulonglong), 136 | ("ss",c_ulonglong), 137 | ("fs_base",c_ulonglong), 138 | ("gs_base",c_ulonglong), 139 | ("ds",c_ulonglong), 140 | ("es",c_ulonglong), 141 | ("fs",c_ulonglong), 142 | ("gs",c_ulonglong) 143 | ] 144 | 145 | def __repr__(self): 146 | buf="" 147 | for reg in self.display_order: 148 | value = fmtstr % getattr(self,reg) 149 | buf+= GREEN + "{0:10}".format(reg) + CYAN + "{0:>10}\n".format(value) 150 | buf += CLEAR 151 | return buf 152 | 153 | def get_stored_registers(self): 154 | ret=[] 155 | 156 | for reg in self.display_order: 157 | value = fmtstr % getattr(self,reg) 158 | ret.append((reg,value)) 159 | return ret 160 | 161 | def get_register(self,reg_str): 162 | ret = False 163 | for reg,__ in self._fields_: 164 | if reg_str == reg: 165 | ret = getattr(self,reg) 166 | return ret 167 | 168 | def set_register(self,reg,value): 169 | ret = False 170 | try: 171 | setattr(self,reg,value) 172 | ret = True 173 | except: 174 | pass 175 | 176 | return ret 177 | 178 | def get_uni_regs(self): 179 | reg_dict = {} 180 | for reg in self.display_order: 181 | # tmp import...bleh 182 | import unicorn.x86_const as c 183 | reg_str = "UC_X86_REG_%s" % reg.upper() 184 | try: 185 | reg_dict[reg] = getattr(c,reg_str) 186 | except: 187 | continue 188 | value = getattr(self,reg) 189 | return reg_dict 190 | 191 | 192 | class x64_user_regs_struct(Structure): 193 | def __repr__(self): 194 | buf="" 195 | for reg,val in self._fields_: 196 | buf+=reg_format(reg,val.value) 197 | return buf 198 | 199 | # takes list, returns pretty disassembly 200 | # dis_list[0] : address 201 | # dis_list[1] : mnemonic 202 | # dis_list[2] : op_str 203 | # dis_list[3] : raw bytes 204 | 205 | def disasm_format(dis_list): 206 | buf = "" 207 | # Credit to: 208 | # http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python 209 | rows, columns = popen('stty size', 'r').read().split() 210 | 211 | #!TODO: add symbol dereferencing for 212 | # the values of operands (if addr) 213 | 214 | for instr in dis_list: 215 | addr,mnem,op,byteStr = instr 216 | buf += "%s0x%x: " % (GREEN,addr) 217 | buf += "%s%s " % (YELLOW,mnem) 218 | buf += "%s%s" % (CYAN,op) 219 | #buf += " %s%s" % (YELLOW,byteStr) 220 | buf += "\n" 221 | return buf 222 | 223 | 224 | 225 | # deref == list of addrs... 226 | def reg_format(reg,val,deref=None): 227 | buf = "" 228 | value = fmtstr % val 229 | buf += GREEN + "{0:10}".format(reg) + PURPLE + "{0:7}".format(value) 230 | 231 | if deref: 232 | # if we hit deref loop 233 | for i in range(0,len(deref)): 234 | try: 235 | if deref[i] == deref[i+1]: 236 | deref = deref[:i+1] 237 | except: 238 | break 239 | 240 | for d in deref: 241 | buf+= " -> " 242 | buf+= CYAN 243 | if d[0:4] == "STR:": 244 | buf+=ORANGE 245 | buf+=repr(d[4:]) 246 | elif d[0:4] == "SYM:": 247 | buf+=YELLOW 248 | buf+=repr(d[4:]).strip("'") 249 | buf+="()" 250 | else: 251 | tmp = ''.join(["%02x"%ord(b) for b in d[::-1]]).lstrip("0") 252 | if not tmp: 253 | tmp = "0" 254 | buf+="0x" + tmp 255 | 256 | buf+=PURPLE 257 | buf += CLEAR 258 | return buf 259 | 260 | class DebugProcess(Structure): 261 | _fields_ = [ 262 | ("tid",c_uint ), 263 | ("pid",c_uint ), 264 | ("ppid",c_uint ), 265 | ("regStruct",RegisterStruct) 266 | ] 267 | 268 | def __repr__(self): 269 | ret = "<" 270 | ret += "UID:%d," % self.uid 271 | ret += "PID:%d," % self.pid 272 | ret += "PPID:%d" % self.ppid 273 | ret += ">" 274 | return ret 275 | 276 | def initDebugProcess(uid,pid,ppid): 277 | ret = DebugProcess 278 | ret.uid = uid 279 | ret.pid = pid 280 | ret.ppid = ppid 281 | ret.regStruct = RegisterStruct() 282 | return ret 283 | 284 | 285 | class Savepoint(): 286 | 287 | def __init__(self,regs,blocks,label): 288 | self.regs = regs 289 | self.blocks = blocks 290 | self.label = label 291 | 292 | class Stack(): 293 | 294 | def __init__(self): 295 | self.stack_frames = [] 296 | self.current_frame_index = -1 297 | self.current_frame = None 298 | 299 | def clear(self): 300 | self.stack_frames[:] = [] 301 | 302 | def push_frame(self,frame): 303 | self.stack_frames.append(frame) 304 | 305 | def prepend_frame(self,frame): 306 | self.stack_frames.insert(0,frame) 307 | 308 | 309 | def pop_frame(self): 310 | try: 311 | return self.stack_frames.pop() 312 | except: 313 | raise 314 | 315 | def switch_frame(self,frame_num): 316 | try: 317 | self.current_frame = self.stack_frames[frame_num] 318 | except: 319 | raise 320 | 321 | def dump_stack(self): 322 | buf = "" 323 | for i in self.stack_frames[::-1]: 324 | buf += i.dump_frame() 325 | return buf 326 | 327 | def __repr__(self): 328 | if len(self.stack_frames): 329 | buf = YELLOW + "INSTR_PTR: ($ESP-$EBP)\n" 330 | for i in self.stack_frames[::-1]: 331 | buf += repr(i) 332 | buf += "\n" 333 | return buf 334 | else: 335 | return "Empty Stack" 336 | 337 | def __getitem__(self,i): 338 | return self.stack_frames[i] 339 | 340 | def __len__(self): 341 | return len(self.stack_frames) 342 | 343 | class StackFrame(): 344 | 345 | def __init__(self,top,bottom,instr_ptr,nearestSymbol): 346 | self.top = top 347 | self.bottom = bottom 348 | self.instr_ptr = instr_ptr 349 | self.nearSym = nearestSymbol 350 | 351 | self.function_addr = -1 352 | self.function_name = "" 353 | self.args = [] 354 | self.locals = [] 355 | 356 | self.registers = RegisterStruct() 357 | 358 | def dump_frame(self): 359 | ret = "" 360 | ret += repr(self) + "\n" 361 | if self.registers.get_register("eip") or self.registers.get_register("rip"): 362 | ret += repr(self.registers) + "\n" 363 | if self.args: 364 | ret += "ARGS: " + str(self.args) + "\n" 365 | if self.locals: 366 | ret += "LOCALS: " + str(self.locals) + "\n" 367 | ret += "----------------------------\n" 368 | return ret 369 | 370 | def __repr__(self): 371 | ret = "" 372 | ret += GREEN 373 | ret += "0x%x:" % self.instr_ptr 374 | ret += BLU 375 | ret += "<%s+%d> " % (self.nearSym[1],self.nearSym[2]) 376 | ret += CLEAR 377 | ret += "0x%x - " % self.top 378 | ret += "0x%x" % self.bottom 379 | if self.function_addr >= 0: 380 | ret += "0x%x" % self.function_addr 381 | if self.function_name: 382 | ret += self.function_name 383 | if self.args: 384 | ret += "(" 385 | ret += ', '.join(self.args) 386 | ret += ")" 387 | 388 | return ret 389 | 390 | -------------------------------------------------------------------------------- /defines/syscalls.py: -------------------------------------------------------------------------------- 1 | from sys import maxsize 2 | from ctypes import * 3 | 4 | def syscall_byname(name): 5 | for i in syscalls: 6 | if i[0] == name: 7 | return i 8 | return 9 | 10 | def syscall_bynum(num): 11 | try: 12 | return syscalls[num] 13 | except: 14 | return ("Undefined",) 15 | 16 | # takes a reg struct and returns a list of correctly casted tuples 17 | # First tuple will be (orig_eax,"syscall_name") 18 | # Rest will be of the format (c_type,value) 19 | # since we know the order of the calls 20 | def syscall_lookup(reg_struct): 21 | retlist = [] 22 | name,c_type,value = "","","" 23 | 24 | if maxsize < 2**32: 25 | syscall_num_reg = "orig_eax" 26 | syscall_arg_reg = ("orig_eax","ebx","ecx","edx","esi","edi") 27 | else: 28 | syscall_num_reg = "rax" 29 | syscall_arg_reg = ("rax","rdi","rsi","rdx","r10","r8","r9") 30 | 31 | syscall_num = reg_struct.get_register(syscall_num_reg) 32 | syscall = syscall_bynum(syscall_num) 33 | retlist.append(syscall[0]) 34 | 35 | if len(syscall) == 1: 36 | return retlist 37 | 38 | # - iterate over all parameters of the syscall 39 | # - grab the appropriate register from the regstruct 40 | # - assign the ctype this value 41 | # - return list of tuples 42 | for i in range(1,len(syscall)): # start at 1 to ignore syscall name 43 | name = syscall_arg_reg[i] 44 | c_type = syscall[i] 45 | value = reg_struct.get_register(name) 46 | retlist.append((c_type,value)) 47 | 48 | return retlist 49 | 50 | 51 | # Begin the syscall definitions 52 | # x86 53 | 54 | if maxsize < 2**32: 55 | syscalls = [ 56 | ("restart_syscall",), # not used : 0 57 | ("exit",c_int), 58 | ("fork",c_void_p), 59 | ("read",c_uint,c_char_p,c_uint), 60 | ("write",c_uint,c_char_p,c_uint), 61 | ("open",c_char_p,c_int,c_int), 62 | ("close",c_uint), 63 | ("waitpid",c_uint,pointer(c_int(0)),c_int), 64 | ("creat",c_char_p,c_int), 65 | ("link",c_char_p,c_char_p), 66 | ("unlink", c_char_p), 67 | ("execve", c_void_p), 68 | ("chdir", c_char_p), 69 | ("time", pointer(c_int(0))), 70 | ("mknod", c_char_p,c_int,c_ushort), 71 | ("chmod", c_char_p,c_ushort), 72 | ("lchown", c_char_p,c_ushort,c_ushort), 73 | ("break",), # not used : 17 74 | ("oldstat", c_char_p,c_void_p), 75 | ("lseek", c_uint,c_long,c_uint), 76 | ("getpid",), 77 | ("mount", c_char_p,c_char_p,c_char_p), 78 | ("umount", c_char_p), 79 | ("setuid", c_ushort), 80 | ("getuid",), 81 | ("stime",pointer(c_int(0)) ), 82 | ("ptrace",c_long,c_long,c_long,c_long ), 83 | ("alarm", c_uint), 84 | ("oldfstat", c_uint,c_void_p), 85 | ("pause",),# 29 86 | ("utime", c_char_p,c_void_p), 87 | ("stty",), # 31 unused 88 | ("gtty",), # 32 unused 89 | ("access",c_char_p,c_int), 90 | ("nice", c_int), 91 | ("ftime",), # 35 unused 92 | ("sync", ), 93 | ("kill", c_int,c_int), 94 | ("rename", c_char_p, c_char_p ), 95 | ("mkdir", c_char_p, c_int), 96 | ("rmdir", c_char_p ), 97 | ("dup", c_uint), 98 | ("pipe", c_ulong), 99 | ("times", c_void_p), 100 | ("prof",), # 44 unused 101 | ("brk",c_ulong ), 102 | ("setgid", c_ushort), 103 | ("getgid",), 104 | ("signal",c_int, c_void_p ), 105 | ("geteuid",), 106 | ("getegid",), 107 | ("acct", c_char_p ), 108 | ("umount2", c_char_p, c_int ), 109 | ("lock", ), # 53 unused 110 | ("ioctl", c_uint, c_uint, c_ulong ), 111 | ("fcntl", c_uint, c_uint, c_ulong ), 112 | ("mpx", ), # 56 unused 113 | ("setpgid", c_ushort, c_ushort), 114 | ("ulimit",) , # 58 unused 115 | ("oldolduname",c_void_p ), 116 | ("umask",c_int ), 117 | ("chroot",c_char_p ), 118 | ("ustat",c_ushort,c_void_p ), 119 | ("dup2", c_uint,c_uint), 120 | ("getppid",), 121 | ("getpgrp",), 122 | ("setsid",), 123 | ("sigaction",c_int,c_void_p,c_void_p ), 124 | ("sgetmask",), 125 | ("ssetmask", c_int ), 126 | ("setreuid", c_ushort,c_ushort), 127 | ("setregid", c_ushort,c_ushort), 128 | ("sigsuspend", c_int,c_int,c_ulong), 129 | ("sigpending", pointer(c_ulong(0)) ), 130 | ("sethostname", c_char_p,c_int ), 131 | ("setrlimit", c_uint,c_char_p), 132 | ("getrlimit", c_uint,c_char_p), 133 | ("getrusage", c_int,c_void_p), 134 | ("gettimeofday", c_void_p,c_void_p), 135 | ("settimeofday", c_void_p,c_void_p), 136 | ("getgroups",c_int,pointer(c_ushort(0))), 137 | ("setgroups",c_int,pointer(c_ushort(0))), 138 | ("select", c_void_p), 139 | ("symlink", c_char_p,c_char_p), 140 | ("oldlstat", c_char_p,c_void_p), 141 | ("readlink", c_char_p,c_char_p,c_int), 142 | ("uselib", c_char_p), 143 | ("swapon", c_char_p,c_int), 144 | ("reboot", c_int,c_int,c_int,c_void_p), 145 | ("readdir", c_uint,c_void_p,c_uint), 146 | ("mmap", c_void_p), 147 | ("munmap", c_ulong,c_ushort), 148 | ("truncate", c_char_p,c_ulong), 149 | ("ftruncate", c_uint,c_ulong), 150 | ("fchmod", c_uint,c_ushort), 151 | ("fchown", c_uint,c_ushort,c_ushort), 152 | ("getpriority", c_int,c_int), 153 | ("setpriority", c_int,c_int,c_int), 154 | ("profil",), # 98 unused 155 | ("statfs", c_char_p,c_void_p), 156 | ("fstatfs", c_uint,c_void_p), 157 | ("ioperm", c_ulong,c_ulong,c_int), 158 | ("socketcall", c_int,c_ulong), 159 | ("syslog", c_int,c_char_p,c_int), 160 | ("setitimer", c_int,c_void_p,c_void_p), 161 | ("getitimer", c_int,c_void_p), 162 | ("stat", c_char_p,c_void_p), 163 | ("lstat", c_char_p,c_void_p), 164 | ("fstat", c_uint,c_void_p), 165 | ("olduname", c_void_p ), 166 | ("iopl", c_ulong), 167 | ("vhangup",), 168 | ("idle",), 169 | ("vm86old",c_ulong,c_void_p ), 170 | ("wait4",c_ushort,pointer(c_ulong(0)),c_int,c_void_p), 171 | ("swapoff",c_char_p), 172 | ("sysinfo", c_void_p), 173 | #("ipc", c_uint,c_int,c_int,c_int,c_void_p,c_long), # six args => ptr of array of args in ebx 174 | ("ipc",c_void_p), 175 | ("fsync", c_uint), 176 | ("sigreturn", c_ulong), 177 | ("clone", c_void_p), 178 | ("setdomainname",c_char_p,c_int), 179 | ("uname", c_void_p), 180 | ("modify_ldt", c_int,c_void_p,c_ulong), 181 | ("adjtimex", c_void_p), 182 | ("mprotect", c_ulong,c_ushort,c_ulong), 183 | ("sigprocmask", c_int,pointer(c_ulong(0)),pointer(c_ulong(0))), 184 | ("create_module", c_char_p,c_ushort), 185 | ("init_module", c_char_p,c_void_p), 186 | ("delete_module", c_char_p), 187 | ("get_kernel_syms", c_void_p), 188 | ("quotactl", c_int,c_char_p,c_int,c_char_p), 189 | ("getpgid",c_int ), 190 | ("fchdir", c_uint), 191 | ("bdflush", c_int,c_long), 192 | ("sysfs", c_int,c_ulong,c_ulong), 193 | ("personality", c_ulong), 194 | ("afs_syscall", c_ushort), 195 | ("setfsuid", c_ushort), 196 | ("setfsgid", c_ushort), 197 | ("_llseek", c_uint,c_ulong,c_ulong,pointer(c_long(0)),c_uint), 198 | ("getdents", c_uint,c_void_p,c_uint), 199 | ("_newselect", c_int,c_void_p,c_void_p,c_void_p,c_void_p), 200 | ("flock", c_uint,c_uint), 201 | ("msync", c_ulong,c_ushort,c_int), 202 | ("readv", c_ulong,c_void_p,c_ulong), 203 | ("writev", c_ulong,c_void_p,c_ulong), 204 | ("getsid", c_ushort), 205 | ("fdatasync", c_uint), 206 | ("_sysctl", c_void_p), 207 | ("mlock", c_ulong,c_ushort), 208 | ("munlock", c_ulong,c_ushort), 209 | ("mlockall", c_int), 210 | ("munlockall",), 211 | ("sched_setparam",c_ushort,c_void_p), 212 | ("sched_getparam", c_ushort,c_void_p), 213 | ("sched_setscheduler", c_ushort,c_int,c_void_p), 214 | ("sched_getscheduler", c_ushort), 215 | ("sched_yield",), 216 | ("sched_get_priority_max", c_int), 217 | ("sched_get_priority_min", c_int), 218 | ("sched_rr_get_interval", c_ushort,c_void_p), 219 | ("nanosleep", c_void_p,c_void_p), 220 | ("mremap", c_ulong,c_ulong,c_ulong,c_ulong), 221 | ("setresuid", c_ushort,c_ushort,c_ushort), 222 | ("getresuid", pointer(c_ushort(0)),pointer(c_ushort(0)),pointer(c_ushort(0))), 223 | ("vm86", c_void_p), 224 | ("query_module", c_char_p,c_int,c_char_p,c_ushort,pointer(c_ushort(0))), 225 | ("poll", c_void_p,c_uint,c_long), 226 | ("nfsservctl", c_int,c_void_p,c_void_p), 227 | ("setresgid", c_ushort,c_ushort,c_ushort), 228 | ("getresgid", pointer(c_ushort(0)),pointer(c_ushort(0)),pointer(c_ushort(0))), 229 | ("prctl", c_int,c_ulong,c_ulong,c_ulong,c_ulong), 230 | ("rt_sigreturn", c_ulong), 231 | ("rt_sigaction", c_int,c_void_p,c_void_p,c_ushort), 232 | ("rt_sigprocmask", c_int,pointer(c_ulong(0)),pointer(c_ulong(0)),c_ushort), 233 | ("rt_sigpending", pointer(c_ulong(0)),c_ushort), 234 | ("rt_sigtimedwait", pointer(c_ulong(0)),c_void_p,c_void_p,c_ushort), 235 | ("rt_sigqueueinfo", c_int,c_int,c_void_p), 236 | ("rt_sigsuspend", pointer(c_ulong(0)),c_ushort), 237 | ("pread64", c_uint,c_char_p,c_uint,c_long), 238 | ("pwrite64", c_int,c_char_p,c_uint,c_long), 239 | ("chown", c_char_p,c_ushort,c_uint), 240 | ("getcwd", c_char_p,c_ulong), 241 | ("capget", c_void_p,c_void_p), 242 | ("capset", c_void_p,c_void_p), 243 | ("sigaltstack", c_void_p,c_void_p), 244 | ("sendfile", c_int,c_int,pointer(c_long(0)),c_uint), 245 | ("getpmsg",),#unused 188 246 | ("putpmsg",),#unused 189 247 | ("vfork",c_void_p), #not sure where the rest of these come from... 248 | ("ugetrlimit",c_uint,c_void_p), 249 | ("mmap2", c_void_p,c_uint,c_int,c_int,c_int), 250 | ("truncate64", c_char_p, c_uint), 251 | ("ftruncate64", c_int, c_uint), 252 | ("stat64", c_char_p, c_void_p), 253 | ("lstat64", c_char_p,c_void_p), 254 | ("fstat64", c_char_p,c_void_p), 255 | ("lchown32", c_char_p,c_ushort,c_ushort), 256 | ("getuid32",), 257 | ("getgid32",), 258 | ("geteuid32",), 259 | ("getegid32",), 260 | ("setreuid32", c_ushort,c_ushort), 261 | ("setregid32", c_ushort,c_ushort), 262 | ("getgroups32", c_int,pointer(c_ushort(0))), 263 | ("setgroups32", c_int,pointer(c_ushort(0))), 264 | ("fchown32", ), 265 | ("setresuid32",pointer(c_ushort(0)),pointer(c_ushort(0)),pointer(c_ushort(0))), 266 | ("getresuid32", pointer(c_ushort(0)),pointer(c_ushort(0)),pointer(c_ushort(0))), 267 | ("setresgid32",c_ushort,c_ushort,c_ushort), 268 | ("getresgid32",pointer(c_ushort(0)),pointer(c_ushort(0)),pointer(c_ushort(0))), 269 | ("chown32", c_char_p,c_ushort,c_ushort), 270 | ("setuid32", c_ushort), 271 | ("setgid32", c_ushort), 272 | ("setfsuid32", c_ushort), 273 | ("setfsgid32", c_ushort), 274 | ("pivot_root", c_char_p,c_char_p), 275 | ("mincore", c_ulong,c_uint,c_char_p), 276 | ("madvise", c_ulong,c_uint,c_int), 277 | ("getdents64", c_uint,c_void_p,c_uint), 278 | ("fcntl64", c_uint,c_uint,c_ulong), 279 | ("unimplimented_222",), 280 | ("unimplimented_223",), 281 | ("gettid",), 282 | ("readahead",c_int,c_uint,c_uint), 283 | ("setxattr",c_char_p,c_char_p,c_void_p,c_uint,c_int), 284 | ("lsetxattr",c_char_p,c_char_p,c_void_p,c_uint,c_int), 285 | ("fsetxattr",c_int,c_char_p,c_void_p,c_uint,c_int), 286 | ("getxattr",c_char_p,c_char_p,c_void_p,c_uint), 287 | ("lgetxattr",c_char_p,c_char_p,c_void_p,c_uint), 288 | ("fgetxattr",c_int,c_char_p,c_void_p,c_uint), 289 | ("listxattr", c_char_p,c_char_p,c_uint), 290 | ("llistxattr", c_char_p,c_char_p,c_uint), 291 | ("flistxattr", c_char_p,c_char_p,c_uint), 292 | ("removexattr",c_char_p,c_char_p), 293 | ("lremovexattr",c_char_p,c_char_p ), 294 | ("fremovexattr",c_int,c_char_p ), 295 | ("tkill", c_int,c_int), 296 | ("sendfile64",c_int,c_int,c_uint,c_uint), 297 | ("futex",), 298 | ("sched_setaffinity",c_ushort,c_uint,pointer(c_ulong(0))), 299 | ("sched_getaffinity",c_ushort,c_uint,pointer(c_ulong(0))), 300 | ("set_thread_area", c_void_p), 301 | ("get_thread_area", c_void_p), 302 | ("io_setup", c_ulong,pointer(c_ulong(0))), 303 | ("io_destroy", c_ulong), 304 | ("io_getevents",c_ulong,c_long,c_long,c_void_p,c_void_p ), 305 | ("io_submit", c_ulong,c_long,pointer(c_void_p(0))), 306 | ("io_cancel", c_ulong,c_void_p,c_void_p), 307 | ("fadvise64", c_int,c_void_p,c_uint,c_int), 308 | ("unimplimented_251",), 309 | ("exit_group",c_int), 310 | ("lookup_dcookie",c_ulonglong,c_char_p,c_uint), 311 | ("epoll_create",c_int), 312 | ("epoll_ctl", c_int,c_int,c_int,c_void_p), 313 | ("epoll_wait",c_int,c_void_p,c_int,c_int), 314 | ("remap_file_pages",c_ulong,c_ulong,c_ulong,c_ulong,c_ulong), 315 | ("set_tid_address",pointer(c_int(0))), 316 | ("timer_create",c_int,c_void_p,c_void_p), 317 | ("timer_settime", c_int,c_int,c_void_p,c_void_p), 318 | ("timer_gettime",c_int,c_void_p), 319 | ("timer_getoverrun",c_int), 320 | ("timer_delete", c_int), 321 | ("clock_settime", c_int,c_void_p), 322 | ("clock_gettime", c_int,c_void_p), 323 | ("clock_getres", c_int,c_void_p), 324 | ("clock_nanosleep", c_int,c_int,c_void_p,c_void_p), 325 | ("statfs64", c_char_p,c_uint,c_void_p), 326 | ("fstatfs64", c_uint,c_uint,c_void_p), 327 | ("tgkill", c_int,c_int,c_int), 328 | ("utimes", c_char_p,c_void_p), 329 | ("fadvise64_64",c_int,c_ulonglong,c_ulonglong,c_int), 330 | ("vserver",), #unimpliemented 273 331 | ("mbind",), 332 | ("get_mempolicy",pointer(c_int(0)),pointer(c_ulong(0)),c_ulong,c_ulong,c_ulong), 333 | ("set_mempolicy",c_int,pointer(c_ulong(0)),c_ulong), 334 | ("mq_open", c_char_p,c_int,c_uint,c_void_p), 335 | ("mq_unlink",c_char_p), 336 | ("mq_timedsend",c_int,c_char_p,c_uint,c_uint,c_void_p), 337 | ("mq_timedreceive",c_int,c_char_p,c_uint,pointer(c_uint(0)),c_void_p), 338 | ("mq_notify",c_int,c_void_p), 339 | ("mq_getsetattr",c_int,c_void_p,c_void_p), 340 | ("kexec_load",c_ulong,c_ulong,c_void_p,c_ulong), 341 | ("waitid",c_int,c_ushort,c_void_p,c_int,c_void_p), 342 | ("add_key",c_char_p,c_char_p,c_void_p,c_uint,c_long), 343 | ("request_key",c_char_p,c_char_p,c_char_p,c_long), 344 | ("keyctl",c_int,c_ulong,c_ulong,c_ulong,c_ulong,c_ulong), 345 | ("ioprio_set",c_int,c_int), 346 | ("ioprio_get", c_int,c_int), 347 | ("inotify_init",), 348 | ("inotify_add_watch", c_int,c_char_p,c_uint), 349 | ("inotify_rm_watch",c_int,c_uint ), 350 | ("migrate_pages", c_ushort,c_ulong,pointer(c_ulong(0)),pointer(c_ulong(0))), 351 | ("openat",c_int,c_char_p,c_int,c_int), 352 | ("mkdirat",c_int,c_char_p,c_int), 353 | ("mknodat",c_int,c_char_p,c_int,c_uint), 354 | ("fchownat",c_int,c_char_p,c_ushort,c_ushort,c_int), 355 | ("futimesat",c_int,c_char_p,c_void_p), 356 | ("fstatat64",c_int,c_char_p,c_void_p,c_int), 357 | ("unlinkat",c_int,c_char_p,c_int), 358 | ("renameat",c_int,c_char_p,c_int,c_char_p), 359 | ("linkat",c_int,c_char_p,c_int,c_char_p,c_int), 360 | ("symlinkat",c_char_p,c_int,c_char_p), 361 | ("readlinkat",c_int,c_char_p,c_char_p,c_int), 362 | ("fchmodat", c_int,c_char_p,c_uint), 363 | ("faccessat", c_int,c_char_p,c_int), 364 | ("pselect6",), 365 | ("ppoll", c_void_p,c_uint,c_void_p,c_void_p,c_uint), 366 | ("unshare",c_ulong ), 367 | ("set_robust_list",c_void_p,c_uint ), 368 | ("get_robust_list", c_int,c_void_p,c_void_p), 369 | ("splice",), 370 | ("sync_file_range",c_int,c_ulong,c_ulong,c_uint), 371 | ("tee",c_int,c_int,c_uint,c_uint), 372 | ("vmsplice", c_int,c_void_p,c_ulong,c_uint), 373 | ("move_pages",), 374 | ("getcpu",c_void_p,c_void_p,c_void_p), 375 | ("epoll_pwait",), 376 | ("utimensat",c_int,c_char_p,c_void_p,c_int), 377 | ("signalfd",c_int,c_void_p,c_uint), 378 | ("timerfd_create",c_int,c_int), 379 | ("eventfd",c_uint), 380 | ("fallocate",c_int,c_int,c_ulong,c_ulong), 381 | ("timerfd_settime",c_int,c_int,c_void_p,c_void_p), 382 | ("timerfd_gettime",c_int,c_int), 383 | ("signalfd4",c_int,c_void_p,c_uint,c_int), 384 | ("eventfd2",c_uint,c_int), 385 | ("epoll_create1", c_int ), 386 | ("dup3",c_uint,c_uint,c_int), 387 | ("pipe2",pointer(c_int(0)),c_int), 388 | ("inotify_init1",c_int), 389 | ("preadv",c_ulong,c_void_p,c_ulong,c_ulong,c_ulong), 390 | ("pwritev",c_ulong,c_void_p,c_ulong,c_ulong,c_ulong), 391 | ("rt_tgsigqueueinfo",c_ushort,c_ushort,c_int,c_void_p), 392 | ("perf_event_open",c_void_p,c_ushort,c_int,c_int,c_ulong), 393 | ("recvmmsg",c_int,c_void_p,c_uint,c_uint,c_void_p) 394 | ] 395 | 396 | ''' 397 | ("fanotify_init", ), 398 | ("fanotify_mark", ), 399 | ("prlimit64", ), 400 | ("name_to_handle_at", ), 401 | ("open_by_handle_at", ), 402 | ("clock_adjtime", ), 403 | ("syncfs", ), 404 | ("sendmmsg", ), 405 | ("setns", ), 406 | ("process_vm_readv", ), 407 | ("process_vm_writev", ), 408 | ("kcmp", ), 409 | ("finit_module", ), 410 | ("sched_setattr", ), 411 | ("sched_getattr", ), 412 | ("renameat2", ), 413 | ("seccomp", ), 414 | ("getrandom", ), 415 | ("memfd_create", ), 416 | ("bpf", ), 417 | ("execveat", ), 418 | ("socket", ), 419 | ("socketpair", ), 420 | ("bind", ), 421 | ("connect", ), 422 | ("listen", ), 423 | ("accept4", ), 424 | ("getsockopt", ), 425 | ("setsockopt", ), 426 | ("getsockname", ), 427 | ("getpeername", ), 428 | ("sendto", ), 429 | ("sendmsg", ), 430 | ("recvfrom", ), 431 | ("recvmsg", ), 432 | ("shutdown", ), 433 | ("userfaultfd", ), 434 | ("membarrier", ), 435 | ("mlock2", ), 436 | ''' 437 | 438 | else: 439 | 440 | # Begin syscall definitions 441 | # x64 442 | syscalls = [ 443 | ("read", c_uint,c_char_p,c_ulong), 444 | ("write", c_uint,c_char_p,c_ulong), 445 | ("open", c_char_p,c_int,c_int), 446 | ("close", c_uint), 447 | ("stat", c_char_p,c_void_p), 448 | ("fstat", c_uint,c_void_p), 449 | ("lstat", c_char_p,c_void_p), 450 | ("poll", c_void_p,c_uint,c_long), 451 | ("lseek", c_uint,c_ulong,c_uint), 452 | ("mmap", c_ulong,c_ulong,c_ulong,c_ulong,c_ulong,c_ulong), 453 | ("mprotect", c_ulong,c_ulong,c_ulong), 454 | ("munmap",c_ulong,c_ulong), 455 | ("brk", c_ulong), 456 | ("rt_sigaction", c_int,c_void_p,c_void_p,c_ulong), 457 | ("rt_sigprocmask", c_int,c_void_p,c_void_p,c_ulong), 458 | ("rt_sigreturn",c_ulong), 459 | ("ioctl", c_uint,c_uint,c_ulong), 460 | ("pread64",c_ulong,c_char_p,c_ulong,c_ulong ), 461 | ("pwrite64",c_uint,c_char_p,c_ulong,c_ulong ), 462 | ("readv",c_ulong,c_char_p,c_ulong), 463 | ("writev", c_ulong,c_char_p,c_ulong), 464 | ("access", c_char_p,c_int), 465 | ("pipe", pointer(c_int(0))), 466 | ("select", c_int,c_void_p,c_void_p,c_void_p,c_void_p), 467 | ("sched_yield"), 468 | ("mremap",c_ulong,c_ulong,c_ulong,c_ulong,c_ulong ), 469 | ("msync", c_ulong,c_ulong,c_int), 470 | ("mincore", c_ulong,c_ulong,c_char_p), 471 | ("madvise", c_ulong,c_ulong,c_int), 472 | ("shmget", ), 473 | ("shmat", ), 474 | ("shmctl", ), 475 | ("dup", ), 476 | ("dup2", ), 477 | ("pause", ), 478 | ("nanosleep", ), 479 | ("getitimer", ), 480 | ("alarm", ), 481 | ("setitimer", ), 482 | ("getpid", ), 483 | ("sendfile", ), 484 | ("socket",c_int,c_int,c_int ), 485 | ("connect",c_int,c_void_p,c_int), 486 | ("accept", c_int,c_void_p,c_int), 487 | ("sendto", c_int,c_void_p,c_ulong,c_uint,c_void_p,pointer(c_int(0))), 488 | ("recvfrom", c_int,c_void_p,c_ulong,c_uint,c_void_p,pointer(c_int(0))), 489 | ("sendmsg", c_int, c_void_p,c_uint), 490 | ("recvmsg", c_int,c_void_p,c_uint), 491 | ("shutdown", c_int,c_int), 492 | ("bind", c_int,c_void_p,c_int), 493 | ("listen", c_int,c_int), 494 | ("getsockname", c_int,c_void_p,c_int), 495 | ("getpeername", c_int,c_void_p,c_int), 496 | ("socketpair", c_int,c_int,c_int,pointer(c_int(0))), 497 | ("setsockopt", c_int,c_int,c_int,c_char_p,c_int), 498 | ("getsockopt", c_int,c_int,c_int,c_char_p,pointer(c_int(0))), 499 | ("clone", c_ulong,c_ulong,c_void_p,c_void_p), 500 | ("fork"), 501 | ("vfork"), 502 | ("execve",c_char_p,c_char_p,c_char_p), 503 | ("exit", c_int), 504 | ("wait4", c_int), 505 | ("kill", c_int,c_int), 506 | ("uname", ), 507 | ("semget", ), 508 | ("semop", ), 509 | ("semctl", ), 510 | ("shmdt", ), 511 | ("msgget", ), 512 | ("msgsnd", ), 513 | ("msgrcv", ), 514 | ("msgctl", ), 515 | ("fcntl", c_uint,c_uint,c_ulong), 516 | ("flock", ), 517 | ("fsync", ), 518 | ("fdatasync", ), 519 | ("truncate", ), 520 | ("ftruncate", ), 521 | ("getdents", ), 522 | ("getcwd", c_char_p,c_ulong ), 523 | ("chdir", c_char_p), 524 | ("fchdir", c_uint), 525 | ("rename", c_char_p,c_char_p), 526 | ("mkdir", c_char_p,c_int), 527 | ("rmdir", c_char_p), 528 | ("creat", c_char_p,c_int), 529 | ("link", c_char_p,c_char_p), 530 | ("unlink", c_char_p), 531 | ("symlink", c_char_p,c_char_p), 532 | ("readlink", c_char_p,c_char_p,c_int), 533 | ("chmod", c_char_p,c_int), 534 | ("fchmod", c_uint,c_int), 535 | ("chown", c_char_p,c_int,c_int), 536 | ("fchown", c_uint,c_int,c_int), 537 | ("lchown", c_char_p,c_int,c_int), 538 | ("umask", c_int), 539 | ("gettimeofday", ), 540 | ("getrlimit", ), 541 | ("getrusage", ), 542 | ("sysinfo", ), 543 | ("times", ), 544 | ("ptrace", c_long,c_long,c_ulong,c_ulong), 545 | ("getuid"), 546 | ("syslog",c_int,c_char_p,c_int ), 547 | ("getgid" ), 548 | ("setuid", ), 549 | ("setgid", ), 550 | ("geteuid", ), 551 | ("getegid", ), 552 | ("setpgid", ), 553 | ("getppid", ), 554 | ("getpgrp", ), 555 | ("setsid", ), 556 | ("setreuid", ), 557 | ("setregid", ), 558 | ("getgroups", ), 559 | ("setgroups", ), 560 | ("setresuid", ), 561 | ("getresuid", ), 562 | ("setresgid", ), 563 | ("getresgid", ), 564 | ("getpgid", ), 565 | ("setfsuid", ), 566 | ("setfsgid", ), 567 | ("getsid", ), 568 | ("capget", ), 569 | ("capset", ), 570 | ("rt_sigpending", ), 571 | ("rt_sigtimedwait", ), 572 | ("rt_sigqueueinfo", ), 573 | ("rt_sigsuspend", ), 574 | ("sigaltstack", ), 575 | ("utime", ), 576 | ("mknod", ), 577 | ("uselib", ), 578 | ("personality", ), 579 | ("ustat", ), 580 | ("statfs", ), 581 | ("fstatfs", ), 582 | ("sysfs", ), 583 | ("getpriority", ), 584 | ("setpriority", ), 585 | ("sched_setparam", ), 586 | ("sched_getparam", ), 587 | ("sched_setscheduler", ), 588 | ("sched_getscheduler", ), 589 | ("sched_get_priority_max", ), 590 | ("sched_get_priority_min", ), 591 | ("sched_rr_get_interval", ), 592 | ("mlock", ), 593 | ("munlock", ), 594 | ("mlockall", ), 595 | ("munlockall", ), 596 | ("vhangup", ), 597 | ("modify_ldt", ), 598 | ("pivot_root", ), 599 | ("_sysctl", ), 600 | ("prctl", ), 601 | ("arch_prctl", ), 602 | ("adjtimex", ), 603 | ("setrlimit", ), 604 | ("chroot", ), 605 | ("sync", ), 606 | ("acct", ), 607 | ("settimeofday", ), 608 | ("mount", ), 609 | ("umount2", ), 610 | ("swapon", ), 611 | ("swapoff", ), 612 | ("reboot", ), 613 | ("sethostname", ), 614 | ("setdomainname", ), 615 | ("iopl", ), 616 | ("ioperm", ), 617 | ("create_module", ), 618 | ("init_module", ), 619 | ("delete_module", ), 620 | ("get_kernel_syms", ), 621 | ("query_module", ), 622 | ("quotactl", ), 623 | ("nfsservctl", ), 624 | ("getpmsg", ), 625 | ("putpmsg", ), 626 | ("afs_syscall", ), 627 | ("tuxcall", ), 628 | ("security", ), 629 | ("gettid", ), 630 | ("readahead", ), 631 | ("setxattr", ), 632 | ("lsetxattr", ), 633 | ("fsetxattr", ), 634 | ("getxattr", ), 635 | ("lgetxattr", ), 636 | ("fgetxattr", ), 637 | ("listxattr", ), 638 | ("llistxattr", ), 639 | ("flistxattr", ), 640 | ("removexattr", ), 641 | ("lremovexattr", ), 642 | ("fremovexattr", ), 643 | ("tkill", ), 644 | ("time", ), 645 | ("futex", ), 646 | ("sched_setaffinity", ), 647 | ("sched_getaffinity", ), 648 | ("set_thread_area", ), 649 | ("io_setup", ), 650 | ("io_destroy", ), 651 | ("io_getevents", ), 652 | ("io_submit", ), 653 | ("io_cancel", ), 654 | ("get_thread_area", ), 655 | ("lookup_dcookie", ), 656 | ("epoll_create", ), 657 | ("epoll_ctl_old", ), 658 | ("epoll_wait_old", ), 659 | ("remap_file_pages", ), 660 | ("getdents64", ), 661 | ("set_tid_address", ), 662 | ("restart_syscall", ), 663 | ("semtimedop", ), 664 | ("fadvise64", ), 665 | ("timer_create", ), 666 | ("timer_settime", ), 667 | ("timer_gettime", ), 668 | ("timer_getoverrun", ), 669 | ("timer_delete", ), 670 | ("clock_settime", ), 671 | ("clock_gettime", ), 672 | ("clock_getres", ), 673 | ("clock_nanosleep", ), 674 | ("exit_group", ), 675 | ("epoll_wait", ), 676 | ("epoll_ctl", ), 677 | ("tgkill", ), 678 | ("utimes", ), 679 | ("vserver", ), 680 | ("mbind", ), 681 | ("set_mempolicy", ), 682 | ("get_mempolicy", ), 683 | ("mq_open", ), 684 | ("mq_unlink", ), 685 | ("mq_timedsend", ), 686 | ("mq_timedreceive", ), 687 | ("mq_notify", ), 688 | ("mq_getsetattr", ), 689 | ("kexec_load", ), 690 | ("waitid", ), 691 | ("add_key", ), 692 | ("request_key", ), 693 | ("keyctl", ), 694 | ("ioprio_set", ), 695 | ("ioprio_get", ), 696 | ("inotify_init", ), 697 | ("inotify_add_watch", ), 698 | ("inotify_rm_watch", ), 699 | ("migrate_pages", ), 700 | ("openat", ), 701 | ("mkdirat", ), 702 | ("mknodat", ), 703 | ("fchownat", ), 704 | ("futimesat", ), 705 | ("newfstatat", ), 706 | ("unlinkat", ), 707 | ("renameat", ), 708 | ("linkat", ), 709 | ("symlinkat", ), 710 | ("readlinkat", ), 711 | ("fchmodat", ), 712 | ("faccessat", ), 713 | ("pselect6", ), 714 | ("ppoll", ), 715 | ("unshare", ), 716 | ("set_robust_list", ), 717 | ("get_robust_list", ), 718 | ("splice", ), 719 | ("tee", ), 720 | ("sync_file_range", ), 721 | ("vmsplice", ), 722 | ("move_pages", ), 723 | ("utimensat", ), 724 | ("epoll_pwait", ), 725 | ("signalfd", ), 726 | ("timerfd_create", ), 727 | ("eventfd", ), 728 | ("fallocate", ), 729 | ("timerfd_settime", ), 730 | ("timerfd_gettime", ), 731 | ("accept4", ), 732 | ("signalfd4", ), 733 | ("eventfd2", ), 734 | ("epoll_create1", ), 735 | ("dup3", ), 736 | ("pipe2", ), 737 | ("inotify_init1", ), 738 | ("preadv", ), 739 | ("pwritev", ), 740 | ("rt_tgsigqueueinfo", ), 741 | ("perf_event_open", ), 742 | ("recvmmsg", ), 743 | ("fanotify_init", ), 744 | ("fanotify_mark", ), 745 | ("prlimit64", ), 746 | ("name_to_handle_at", ), 747 | ("open_by_handle_at", ), 748 | ("clock_adjtime", ), 749 | ("syncfs", ), 750 | ("sendmmsg", ), 751 | ("setns", ), 752 | ("getcpu", ), 753 | ("process_vm_readv", ), 754 | ("process_vm_writev", ), 755 | ("kcmp", ), 756 | ("finit_module", ), 757 | ("sched_setattr", ), 758 | ("sched_getattr", ), 759 | ("renameat2", ), 760 | ("seccomp", ), 761 | ("getrandom", ), 762 | ("memfd_create", ), 763 | ("kexec_file_load", ), 764 | ("bpf", ), 765 | ("execveat", ), 766 | ("userfaultfd", ), 767 | ("membarrier", ), 768 | ("mlock2", ), 769 | ] 770 | 771 | 772 | 773 | -------------------------------------------------------------------------------- /defines/utility.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from ctypes.util import find_library 3 | from sys import maxsize,byteorder 4 | import struct 5 | 6 | libc = CDLL(find_library("c")) 7 | 8 | fmtstr = "0x%x" 9 | ''' 10 | fmtstr = "0x%08x" 11 | if maxsize > 2**32: 12 | fmtstr = "0x%016x" 13 | ''' 14 | 15 | #For verbosity 16 | NULL = 0 17 | 18 | #colors 19 | RED = '\001\033[31m\002' 20 | ORANGE = '\001\033[91m\002' 21 | GREEN = '\001\033[92m\002' 22 | LIME = '\001\033[99m\002' 23 | YELLOW = '\001\033[93m\002' 24 | BLU = '\001\033[94m\002' 25 | PURPLE = '\001\033[95m\002' 26 | CYAN = '\033[96m' 27 | CLEAR = '\001\033[00m\002' 28 | 29 | def INFO(string): 30 | return "%s[!.!] %s%s" % (CYAN,string,CLEAR) 31 | 32 | def ERROR(string): 33 | return "%s[X_X] %s%s" % (RED,string,CLEAR) 34 | 35 | def WARN(string): 36 | return "%s[-.-] %s%s" % (YELLOW,string,CLEAR) 37 | 38 | def GOOD(string): 39 | return "%s[^~^] %s%s" % (GREEN,string,CLEAR) 40 | 41 | def BLUE(string): 42 | return "%s['.'] %s%s" % (BLU,string,CLEAR) 43 | 44 | def PURP(string): 45 | return "%s[#.#] %s%s" % (PURPLE,string,CLEAR) 46 | 47 | 48 | def hexdump(src,addr=0x0,length=4): 49 | if maxsize > 2**32: 50 | length=8 51 | #based from http://code.activestate.com/recipes/142812-hex-dumper 52 | result=[] 53 | digits = 4 if isinstance(src,unicode) else 2 54 | for i in xrange(0,len(src),length): 55 | s=src[i:i+length] 56 | 57 | null = "00000000" 58 | COLOR = CYAN 59 | if src == null or src == null*2: 60 | COLOR = ORANGE 61 | ''' 62 | if byteorder == "little": 63 | s = s[::-1] 64 | ''' 65 | hexa = b''.join(["%0*x" %(digits,ord(x)) for x in s]) 66 | text = b''.join([x if 0x20 <= ord(x) < 0x7f else b'.' for x in s]) 67 | 68 | 69 | result.append(b"%s0x%x %s0x%-*s %s" % (GREEN,i+addr,COLOR,length*(digits+1),hexa, text)) 70 | 71 | tmp = PURPLE + "Offset%sValue" + CLEAR 72 | tmp = tmp % (" " * 7) if maxsize <= 2**32 else tmp % (" " * 15) 73 | return tmp + "\n" + b'\n'.join(result) 74 | 75 | 76 | #assume all unsigned 77 | def raw_to_cstruct_args(raw_bytes,cstruct): 78 | if len(raw_bytes) != sizeof(cstruct): 79 | print repr(raw_bytes) 80 | print "raw_to_cstruct: len mismatch: %d,%d"%(len(raw_bytes),sizeof(cstruct)) 81 | 82 | fmtstr = "" 83 | for f in cstruct._fields_: 84 | type_size = sizeof(f[1]) 85 | 86 | if type_size == 1: 87 | fmtstr+="B" 88 | elif type_size == 2: 89 | fmtstr+="H" 90 | elif type_size == 4: 91 | fmtstr+="I" 92 | elif type_size == 8: 93 | fmtstr+="Q" 94 | else: 95 | fmtstr+="%ds" % type_size 96 | 97 | return struct.unpack(fmtstr,raw_bytes) 98 | 99 | 100 | # for Emulation purposes 101 | def rwx_to_int(permissions): 102 | ret = 0 103 | if permissions[0] == "r": 104 | ret+=1 105 | if permissions[1] == "w": 106 | ret+=2 107 | if permissions[2] == "x": 108 | ret+=4 109 | return ret 110 | 111 | 112 | def usage(newline=True): 113 | buf = "" 114 | tmp = help_text.split('\n') 115 | for i in range(0,len(tmp)): 116 | buf+= "%s%s%s" % (rainbow[i%len(rainbow)],tmp[i],CLEAR) 117 | if newline: 118 | buf+="\n" 119 | return buf 120 | 121 | rainbow = [RED,ORANGE,YELLOW,GREEN,CYAN,PURPLE] 122 | help_text = """ 123 | (^.^) Atrophy Debugger (>.>) 124 | ------------------------ Program flow ------------------------ 125 | help - help 126 | ? - help 127 | run - 128 | attach - attach 129 | detach - detach from current process 130 | die - exit and kill program 131 | exit - exit and detach 132 | desync - toggle ability to send commands while program is running 133 | ------------------------ Get/Set bytes ------------------------ 134 | print|x|gd - 135 | sd - setData 136 | gr - getRegister - no args for all regs, or space separated list for specific 137 | sr - setRegister 138 | gs - - print out data as null-delimeted string 139 | fb - findByte - Searches for byteStr of form "\\xab\\x23\\x..." 140 | ------------------------ Assembly Stuff ---------------------- 141 | dis - disassemble 142 | asm - assemble "instr1; instr2" 143 | bt - backtrace (x86 only for now) 144 | shb - shows Byte Strings saved by 'dis','asm',and 'sb' 145 | sb - saveBytes