Package Libs :: Module libanalyze
[hide private]
[frames] | no frames]

Source Code for Module Libs.libanalyze

   1  #!/usr/bin/env python
 
   2  
 
   3  """
 
   4  (c) Immunity, Inc. 2004-2007
 
   5  
 
   6  
 
   7  U{Immunity Inc.<http://www.immunityinc.com>}
 
   8  
 
   9  
 
  10  """ 
  11  
 
  12  __VERSION__ = '1.3' 
  13  
 
  14  import UserList 
  15  import debugger 
  16  
 
  17  # REGISTER STATUS
 
  18  RST_INVALID  =  0               # Register undefined 
  19  RST_VALUE    =  1               # Register contains regdata 
  20  RST_VFIXUP   =  2               # Reg contains regdata that is fixup 
  21  RST_INDIRECT =  3               # Register contains [regdata] 
  22  
 
  23  
 
  24  # DISASM MODE
 
  25  DISASM_SIZE   = 0              # Determine command size only 
  26  DISASM_DATA   = 1              # Determine size and analysis data 
  27  DISASM_TRACE  = 2              # Trace integer registers 
  28  DISASM_FILE   = 3              # Disassembly, no symbols/registers 
  29  DISASM_CODE   = 4              # Disassembly, registers undefined 
  30  DISASM_ALL    = 5              # Completely disassembly 
  31  DISASM_RTRACE = 6              # Disassemble with run-trace registers 
  32  
 
  33  # Types for Opcode
 
  34  C_TYPEMASK =  0xF0            # Mask for command type 
  35  C_CMD =       0x00            # Ordinary instruction 
  36  C_PSH =       0x10            # PUSH instruction 
  37  C_POP =       0x20            # POP instruction 
  38  C_MMX =       0x30            # MMX instruction 
  39  C_FLT =       0x40            # FPU instruction 
  40  C_JMP =       0x50            # JUMP instruction 
  41  C_JMC =       0x60            # Conditional JUMP instruction 
  42  C_CAL =       0x70            # CALL instruction 
  43  C_RET =       0x80            # RET instruction 
  44  C_FLG =       0x90            # Changes system flags 
  45  C_RTF =       0xA0            # C_JMP and C_FLG simultaneously 
  46  C_REP =       0xB0            # Instruction with REPxx prefix 
  47  C_PRI =       0xC0            # Privileged instruction 
  48  C_SSE =       0xD0            # SSE instruction 
  49  C_NOW =       0xE0            # 3DNow! instruction 
  50  C_BAD =       0xF0            # Unrecognized command 
  51  
 
  52  # Decode type
 
  53  DEC_TYPEMASK = 0x1F     # Type of memory byte 
  54  DEC_UNKNOWN  = 0x00     # Unknown type 
  55  DEC_BYTE     = 0x01     # Accessed as byte 
  56  DEC_WORD     = 0x02     # Accessed as short 
  57  DEC_NEXTDATA = 0x03     # Subsequent byte of data 
  58  DEC_DWORD    = 0x04     # Accessed as long 
  59  DEC_FLOAT4   = 0x05     # Accessed as float 
  60  DEC_FWORD    = 0x06     # Accessed as descriptor/long pointer 
  61  DEC_FLOAT8   = 0x07     # Accessed as double 
  62  DEC_QWORD    = 0x08     # Accessed as 8-byte integer 
  63  DEC_FLOAT10  = 0x09     # Accessed as long double 
  64  DEC_TBYTE    = 0x0A     # Accessed as 10-byte integer 
  65  DEC_STRING   = 0x0B     # Zero-terminated ASCII string 
  66  DEC_UNICODE  = 0x0C     # Zero-terminated UNICODE string 
  67  DEC_3DNOW    = 0x0D     # Accessed as 3Dnow operand 
  68  DEC_SSE      = 0x0E     # Accessed as SSE operand 
  69  DEC_TEXT     = 0x10     # For use in t_result only 
  70  DEC_BYTESW   = 0x11     # Accessed as byte index to switch 
  71  DEC_NEXTCODE = 0x13     # Subsequent byte of command 
  72  DEC_COMMAND  = 0x1D     # First byte of command 
  73  DEC_JMPDEST  = 0x1E     # Jump destination 
  74  DEC_CALLDEST = 0x1F     # Call (and maybe jump) destination 
  75  
 
  76  DEC_PROCMASK = 0x60     # Procedure analysis 
  77  DEC_PROC     = 0x20     # Start of procedure 
  78  DEC_PBODY    = 0x40     # Body of procedure 
  79  DEC_PEND     = 0x60     # End of procedure 
  80  
 
  81  DEC_CHECKED  = 0x80     # Byte was analysed 
  82  DEC_SIGNED   = 0x100    # For use in t_result only 
  83  
 
  84  DECR_TYPEMASK = 0x3F    # Type of register or memory 
  85  DECR_BYTE     = 0x21    # Byte register 
  86  DECR_WORD     = 0x22    # Short integer register 
  87  DECR_DWORD    = 0x24    # Long integer register 
  88  DECR_QWORD    = 0x28    # MMX register 
  89  DECR_FLOAT10  = 0x29    # Floating-point register 
  90  DECR_SEG      = 0x2A    # Segment register 
  91  DECR_3DNOW    = 0x2D    # 3Dnow! register 
  92  DECR_SSE      = 0x2E    # SSE register 
  93  
 
  94  DECR_ISREG    = 0x20    # Mask to check that operand is register 
  95  DEC_CONST     = 0x40    # Immediate constant, used by Analyser 
  96  
 
  97  Registers32BitsOrder = [ "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" ] 
  98  Registers16BitsOrder = [ "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI" ] 
  99  Registers8BitsOrder  = [ "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" ] 
 100  
 
 101  RegisterName = { (0,0,0,0,0,0,0,0):"", (1,0,0,0,0,0,0,0):"EAX",(0,1,0,0,0,0,0,0):"ECX",\
 
 102                   (0,0,1,0,0,0,0,0):"EDX", (0,0,0,1,0,0,0,0):"EBX",(0,0,0,0,1,0,0,0):"ESP",\
 
 103                   (0,0,0,0,0,1,0,0):"EBP", (0,0,0,0,0,0,1,0):"ESI", (0,0,0,0,0,0,0,1):"EDI"} 
 104  
 
 105  COUNT = 100 
106 -class opCode:
107 - def __init__(self, imm, addr):
108 self.imm = imm 109 self.address = addr 110 self.operand = []
111 112
113 - def _getfromtuple(self, opcode):
114 self.ip=opcode[0] # Instruction pointer 115 self.dump=opcode[1] # Hexadecimal dump of the command 116 self.result=opcode[2] # Disassembled command 117 self.comment=opcode[3] # Brief comment 118 self.opinfo=opcode[4] # Comments to command's operands (tuple[3]) 119 self.cmdtype=opcode[5] # One of C_xxx 120 self.memtype=opcode[6] # Type of addressed variable in memory 121 self.nprefix=opcode[7] # Number of prefixes 122 self.indexed=opcode[8] # Address contains register(s) 123 self.jmpconst=opcode[9] # Constant jump address 124 self.jmptable=opcode[10] # Possible address of switch table 125 self.adrconst=opcode[11] # Constant part of address 126 self.immconst=opcode[12] # Immediate constant 127 self.zeroconst=opcode[13] # Whether contains zero constant 128 self.fixupoffset=opcode[14] # Possible offset of 32-bit fixups 129 self.fixupsize=opcode[15] # Possible total size of fixups or 0 130 self.jmpaddr=opcode[16] # Destination of jump/call/return 131 self.condition=opcode[17] # 0xFF:unconditional, 0:false, 1:true 132 self.error=opcode[18] # Error while disassembling command 133 self.warnings=opcode[19] # Combination of DAW_xxx 134 self.optype=opcode[20] # Type of operand (extended set DEC_xxx) (tuple[3]) 135 self.operandsize=opcode[21] # Size of operand, bytes (tuple[3]) 136 self.opsize=opcode[22] #common opsize in bytes (this is the one you want, almost sure) 137 self.opgood=opcode[23] # Whether address and data valid (tuple[3]) 138 self.opaddr=opcode[24] # Address if memory, index if register (tuple[3]) 139 self.opdata=opcode[25] # Actual value (only integer operands) (tuple[3]) 140 self.operand=opcode[26] # Full description of operand (tuple[3]) 141 #NOTE ABOUT self.operand: 142 #self.operand[n][0] = operand type DEC_xxx (mem), DECR_xxx (reg) or DEC_CONST (const) 143 #self.operand[n][1] = operand size (in bytes) 144 #self.operand[n][2][x] = registers scale 145 # (use Registers32BitsOrder,Registers16BitsOrder,Registers8BitsOrder depending on operand size) 146 # Note: more than one register could be used in some memory addressing modes. 147 #self.operand[n][3] = constant 148 149 150 self.regdata=opcode[27] # Registers after command is executed / status of registers list[(reg,status)] 151 self.addrdata=opcode[28] # Traced memory address 152 self.addrstatus=opcode[29] # Status of addrdata, one of RST_xxx 153 self.regstack=opcode[30] # Stack tracing buffer / status of stack items list[(stack,status)]
154 #self.nregstack=opcode[32] # Number of items in stack trace buffer 155 156 # We need to include more than one register 157 # ex: [EAX+EDI+2]
158 - def getOperandRegister(self, num):
159 try: 160 return RegisterName[ self.operand[num][2] ] 161 except KeyError: 162 return "[]"
163
164 - def getIP(self):
165 return self.ip
166
167 - def getAddress(self):
168 return self.address
169
170 - def getDump(self):
171 return self.dump
172
173 - def getResult(self):
174 return self.result
175
176 - def getDisasm(self):
177 return self.result
178
179 - def getComment(self):
180 return self.comment
181
182 - def getOpInfo(self):
183 return self.opinfo
184
185 - def isCmd(self):
186 return self.getCmdType() == C_CMD
187
188 - def isPush(self):
189 return self.getCmdType() == C_PSH
190
191 - def isPop(self):
192 return self.getCmdType() == C_POP
193
194 - def isCall(self):
195 return self.getCmdType() == C_CAL
196
197 - def isJmp(self):
198 return self.getCmdType() == C_JMP
199
200 - def isConditionalJmp(self):
201 return self.getCmdType() == C_JMC
202
203 - def isRet(self):
204 return self.getCmdType() == C_RET
205
206 - def isRep(self):
207 return self.getCmdType() == C_REP
208
209 - def getCmd(self):
210 return self.cmdtype
211
212 - def getCmdType(self):
213 # types are defined as C_* 214 return self.cmdtype & C_TYPEMASK
215
216 - def getMemType(self):
217 return self.memtype
218
219 - def getnPrefix(self):
220 return self.nprefix
221
222 - def getIndexed(self):
223 return self.indexed
224
225 - def getJmpConst(self):
226 return self.jmpconst
227
228 - def getJmpTable(self):
229 return self.jmptable
230
231 - def getAddrConst(self):
232 return self.adrconst
233
234 - def getImmConst(self):
235 return self.immconst
236
237 - def getZeroConst(self):
238 return self.zeroconst
239
240 - def getFixUpOffset(self):
241 return self.fixupoffset
242
243 - def getFixUpSize(self):
244 return self.fixupsize
245
246 - def getJmpAddr(self):
247 return self.jmpaddr
248
249 - def getCondition(self):
250 return self.condition
251
252 - def getError(self):
253 return self.error
254
255 - def getWarnings(self):
256 return self.warnings
257
258 - def getOpType(self):
259 return self.optype
260
261 - def getOpSize(self):
262 return self.opsize
263
264 - def getSize(self):
265 return self.opsize
266
267 - def getOpGood(self):
268 return self.opgood
269
270 - def getOpAddr(self):
271 return self.opaddr
272
273 - def getOpData(self):
274 return self.opdata
275
276 - def getRegData(self):
277 return self.regdata
278
279 - def getRegStatus(self):
280 return self.regdata
281
282 - def getAddrData(self):
283 return self.addrdata
284
285 - def getAddrStatus(self):
286 return self.addrstatus
287
288 - def getRegStack(self):
289 return self.regstack
290
291 - def getRstStatus(self):
292 return self.regstack
293
294 - def getnRegStack(self):
295 return "deprecated"
296 297 #NOTE: info panel is runtime information, no matter which opcode you use to fetch it 298 # you'll have the info IP linked. 299
300 - def getInfoPanel(self):
301 return debugger.get_info_panel()
302
303 - def getVariable(self):
304 return debugger.get_variable( self.address )
305
306 - def setVariable(self, variable_name ):
307 return debugger.set_variable( self.address, variable_name )
308
309 -class Decode(UserList.UserList):
310 - def __init__(self, address):
311 """ 312 Internal Information of the Analyzed Code 313 314 @type address: DWORD 315 @param address: Address in the range of the analized code you want to retrieve 316 """ 317 UserList.UserList.__init__(self) 318 self.address = address 319 self.data = debugger.find_decode( address )
320
321 - def __getitem__(self, i):
322 try: 323 return ord( self.data[ i - self.address ] ) 324 except IndexError: 325 raise IndexError, "Address 0x%08x not in this Decode" % i
326
327 - def __setitem__(self, i, item):
328 self.data[ i - self.address ] = item
329
330 - def isJmpDestination(self, i):
331 """ 332 Check Whether or not the provided address is a destination for a jmp instruction 333 334 @type i: DWORD 335 @param i: Address to check 336 337 @rtype: BOOLEAN 338 @return: Whether or not the provided address is a destination for a jmp instruction 339 """ 340 return ( self.__getitem__( i ) & DEC_TYPEMASK ) == DEC_JMPDEST
341
342 - def isCallDestination(self, i):
343 """ 344 Check Whether or not the provided address is a destination for a call instruction 345 346 @type i: DWORD 347 @param i: Address to check 348 349 @rtype: BOOLEAN 350 @return: Whether or not the provided address is a destination for a call instruction 351 """ 352 return ( self.__getitem__( i ) & DEC_TYPEMASK ) == DEC_CALLDEST
353
354 - def isCommand(self, i):
355 """ 356 Check Whether or not the provided address has a command (regular opcode) 357 358 @type i: DWORD 359 @param i: Address to check 360 361 @rtype: BOOLEAN 362 @return: Whether or not the provided address a command (regular opcode) 363 """ 364 return ( self.__getitem__( i ) & DEC_TYPEMASK ) == DEC_COMMAND
365
366 - def isFunctionStart(self, i):
367 """ 368 Check Whether or not the provided address is the begging of a Function 369 370 @type i: DWORD 371 @param i: Address to check 372 373 @rtype: BOOLEAN 374 @return: Whether or not the provided address is the begging of a Function 375 """ 376 return ( self.__getitem__( i ) & DEC_PROCMASK ) == DEC_PROC
377
378 - def isFunctionBody(self, i):
379 """ 380 Check Whether or not the provided address is part of a Function 381 382 @type i: DWORD 383 @param i: Address to check 384 385 @rtype: BOOLEAN 386 @return: Check Whether or not the provided address is part of a Function 387 """ 388 return ( self.__getitem__( i ) & DEC_PROCMASK ) == DEC_PBODY
389 390
391 -class Function:
392 """ 393 Class that contains information about a Function 394 """
395 - def __init__(self, imm, start):
396 """ 397 Class that contains information about a Function 398 399 @type imm: Debbuger OBJECT 400 @param imm: Debbuger 401 402 @type start: DWORD 403 @param start: Address of the begging of the function 404 """ 405 if not start: 406 raise Exception, "Wrong Function Address: 0x%08x" % start 407 408 self.start = start 409 self.imm = imm 410 self.bb = [] 411 self.bbhash = {} # Hash that contains the visited Blocks
412
413 - def setStart(self,address):
414 """ 415 Change the start of a Function 416 417 @type address: DWORD 418 @param address: New address of the function 419 """ 420 self.start = address
421 422
423 - def getStart(self):
424 """ 425 Get the Address of the Function 426 427 @rtype: DWORD 428 @return: Address of the function 429 """ 430 return self.start
431
432 - def getName(self):
433 """ 434 Get the name of the Function 435 436 @rtype: STRING 437 @return: Name of the Function 438 """ 439 return self.imm.decodeAddress(self.start)
440
441 - def getFunctionEnd(self):
442 ret = [] 443 endblocks = self.getEnd() 444 for bb in endblocks: 445 op = self.imm.disasmBackward( bb.getEnd() ) 446 ret.append( op.getAddress() ) 447 return ret
448
449 - def getEnd(self):
450 """ 451 Get the end of the Function (Understanding end as the Basic Block with a ret inside) 452 453 @rtype: LIST of BasicBlock 454 @return: A list of all the basic block that end the function 455 """ 456 ret = [] 457 bb = self.getBasicBlocks() 458 for a in bb: 459 if a.isRet(): 460 ret.append( a ) 461 return ret
462
463 - def findRetValue(self):
464 """ 465 Find all the possible ret values on a function (Beta) 466 Note: This function only check the modifiers on a Ret BasicBlock, so the result might not be precise. 467 468 @type start: LIST OF OPCODE 469 @param start: Return all the possible modifiers of EAX 470 """ 471 ret = [] 472 endblocks = self.getEnd() # Grab all the Blocks with "Ret" on it. 473 for bb in endblocks: 474 opcodes = bb.getInstructions(self.imm) 475 # We are gonna loop over the instruction on the block backwardly, in order to 476 # find who is modifying eax before the ret. 477 for a in range( len(opcodes)-1, 0, -1): 478 op = opcodes[a] 479 if op.getOperandRegister(0) == "EAX" and op.optype[0] == 36: 480 ret.append( op ) 481 break 482 return ret
483 484
485 - def hasAddress(self, address):
486 """ 487 Check if the given address is part of the Function 488 489 @type address: DWORD 490 @param force: Address of the instruction to check 491 492 @rtype: BasicBlock object 493 @return: If true, returns the corresponding Basic block else returns None 494 """ 495 bb = self.getBasicBlocks() 496 for b in bb: 497 if address >= b.start and address <= b.end: 498 return b 499 return None
500
501 - def getBasicBlocks(self, force = False):
502 """ 503 Get basic block from the current Function 504 505 @type force: BOOLEAN 506 @param force: (Optional, Def: False) Force to Function to reparse the basic blocks 507 508 @rtype: LIST of BasicBlock objects 509 @return: Basic blocks of the current function 510 511 512 TODO: Recursion here is bad - we need to make this an iterative process with a work queue 513 """ 514 if self.bb and not force: 515 return self.bb 516 517 op = None 518 if not self.imm.isAnalysed( self.start ): 519 self.imm.analyseCode( self.start ) 520 521 #self.decode = self.imm.findDecode( self.start ) 522 #self.imm.log("Decode Len: %d" % len(self.decode)) 523 #if not self.decode: 524 # raise Exception, "Couldn't find a proper Decode" 525 self._getBB(self.start) 526 527 return self.bb
528 529 # Depth First construction of Basic block 530 # This is the real recursive function that iterates over the function code flow creating basic block. 531 # The function iterate over every assembly code always following first the jmp/jmc
532 - def _getBB(self, address):
533 decode = self.imm.findDecode( address ) 534 if not decode: 535 raise Exception, "Couldn't find a proper Decode for address 0x%08x" % address 536 start = address 537 calls = [] 538 while 1: 539 # XREF BASIC BLOCK: 540 # If we find our address has an xref, we know is the end the basic block 541 if decode.isJmpDestination( address ) and start != address: 542 543 if self.bbhash.has_key(start): 544 return 545 #self.imm.log("BB created (xref): %08x %08x" % ( start, address ) ) 546 op = self.imm.disasm( address ) 547 bb = XREFBasicBlock( start, address ) 548 bb.setFunction( self ) 549 bb.addTrueEdge( address ) 550 bb.setCalls( calls ) 551 if calls: 552 bb.setCalls( calls ) 553 calls = [] # cleaning calls 554 self.bb.append( bb ) 555 self.bbhash[ start ] = 1 556 start = address 557 if self.bbhash.has_key( address ): 558 return 559 560 #op = self.imm.disasmData( address ) XXX: change it for this one 561 op = self.imm.disasm( address ) 562 #self.imm.log( op.getResult(), address = address) 563 564 # JMC Basic block: 565 # If we find a conditional jmp, its the end of a basic block. We recursively follow the jmp 566 if op.isConditionalJmp(): 567 #self.imm.log("BB conditional (JMC): %08x %08x" % ( start, address ) ) 568 self.bbhash[ start ] = 1 569 bb = JMCBasicBlock( start, address + op.getSize() ) 570 if calls: 571 bb.setCalls( calls ) 572 calls = [] # cleaning calls 573 start = address + op.getSize() 574 bb.setFunction( self ) 575 bb.addTrueEdge( op.getJmpConst() ) 576 bb.addFalseEdge( start ) # the next instruction 577 self.bb.append( bb ) 578 579 # if the jmp address is not on our current basic block list, we follow that leaf 580 if not self.bbhash.has_key( op.getJmpConst() ): 581 self._getBB( op.getJmpConst() ) 582 op = self.imm.disasm( address ) 583 584 if self.bbhash.has_key( start ) : 585 return 586 587 # JMP Basic Block: 588 # If we find a jmp, we create a new basic block. 589 elif op.isJmp(): 590 if not self.bbhash.has_key( address): 591 #self.imm.log("BB conditional (JMP): %08x %08x" % ( start, address ) ) 592 self.bbhash[ start ] = 1 593 bb = JMPBasicBlock( start, address + op.getSize() ) 594 bb.setFunction( self ) 595 bb.addTrueEdge( op.getJmpConst() ) 596 if calls: 597 bb.setCalls( calls ) 598 calls = [] # cleaning calls 599 self.bb.append( bb ) 600 start = address + op.getSize() 601 if not self.bbhash.has_key( op.getJmpConst() ): 602 # We limit the jmp only on a decode we control. 603 # That means, it has to jmp into our own dll 604 try: 605 decode[op.getJmpConst()] 606 self._getBB( op.getJmpConst() ) 607 except Exception: 608 pass 609 return 610 611 # RET Basic Block 612 # Whenever we find a ret, its the end of the tree. We create a Basic Block and return 613 elif op.isRet(): 614 #self.imm.log("BB conditional (RET): %08x %08x\n" % ( start, address ) ) 615 self.bbhash[ start ] = 1 616 bb = RETBasicBlock( start, address + op.getSize() ) 617 bb.setFunction( self ) 618 if calls: 619 bb.setCalls( calls ) 620 calls = [] # cleaning calls 621 self.bb.append( bb ) 622 return 623 elif op.isCall(): 624 calls.append( address ) 625 626 address += op.getSize()
627 628 629
630 -class BasicBlock:
631 - def __init__(self, start, end):
632 """ 633 Basic Block class 634 635 @type start: DWORD 636 @param start: Address of the begging of the Basic Block 637 638 @type end: DWORD 639 @param end: Address of the end of the Basic Block 640 """ 641 self.edgeamount = 0 642 self.start = start 643 self.end = end 644 self.calls = [] 645 #self.Function is a pointer to our parent so we always have it available 646 self.Function = None
647 #TODO: Flesh this out - let's store as much information as possible in the basic blocks 648 #for example, if we write to the stack or heap or if we have various macros in us, etc 649
650 - def setFunction(self, function):
651 self.Function = function
652
653 - def getFunction(self):
654 return self.Function
655
656 - def setCalls(self, calls):
657 self.calls = calls
658
659 - def getCalls(self):
660 return self.calls
661
662 - def __cmp__(self, other):
663 """ 664 Comparision by the start address of the BB 665 """ 666 return cmp(self.start, other.start)
667
668 - def setStart(self, address):
669 """ 670 Change the start of a Basic Block 671 672 @type address: DWORD 673 @param address: New address of the Basic Block 674 """ 675 self.start = address
676
677 - def addTrueEdge(self, addr):
678 self.trueedge = addr
679
680 - def addFalseEdge(self, addr):
681 self.falseedge = addr
682
683 - def getEdges(self):
684 if not self.edgeamount: 685 return (0,0) 686 elif self.edgeamount == 1: 687 if self.trueedge == 0: 688 return (0,0) 689 else: 690 return (self.trueedge,0) 691 else: 692 return ( self.trueedge, self.falseedge )
693
694 - def getTrueEdge(self):
695 """ 696 Get the 'true' Edge 697 698 @rtype: DWORD 699 @return: 'True' Edge of the Basic Block 700 """ 701 if not self.edgeamount: 702 return None 703 elif self.edgeamount != 1: 704 return self.trueedge
705
706 - def getFalseEdge(self):
707 """ 708 Get the 'false' Edge 709 710 @rtype: DWORD 711 @return: 'False' Edge of the Basic Block (The 'false' edge, is not always present. Depends of the Basic Block) 712 """ 713 if not self.edgeamount: 714 return None 715 elif self.edgeamount != 1: 716 return self.falseedge
717
718 - def getDirectEdge(self):
719 """ 720 Get the Edges of a Basic Block 721 722 @rtype: TUPLE of DWORD 723 @return: The Edge of the Basic Block (Might change depending of the basic block type) 724 """ 725 if not self.edgeamount: 726 return () 727 elif self.edgeamount == 1: 728 if self.trueedge == 0: 729 return () 730 else: 731 return self.trueedge
732
733 - def getSize(self):
734 """ 735 Return the Size of the Basic Block 736 737 @rtype: DWORD 738 @return: Size of the Basic Block 739 """ 740 return self.end - self.start
741
742 - def setEnd(self, address):
743 """ 744 Change the end of a Basic Block 745 746 @type address: DWORD 747 @param address: New address of the Basic Block end 748 """ 749 750 self.end = address
751 - def getLimits(self):
752 """ 753 Get the limits of the basic block 754 755 @rtype: TUPLE OF DWORD 756 @return: (Beginning of BB, End of BB) 757 """ 758 return ( self.start,self.end )
759
760 - def getStart(self):
761 """ 762 Get the begging of a Basic Block 763 764 @rtype: DWORD 765 @return: Beginning of the Basic Block 766 """ 767 return self.start
768
769 - def getEnd(self):
770 """ 771 Get the End of a Basic Block 772 773 @rtype: DWORD 774 @return: End of the Basic Block 775 """ 776 return self.end
777 778
779 - def getInstructions(self, imm):
780 """ 781 Get the disassembled instructions from a Basic Block 782 783 @type imm: Debugger OBJECT 784 @param imm: Debugger 785 786 @rtype: LIST of opCode OBJECT 787 @return: List of disassembled instructions 788 """ 789 addr = self.start 790 instructions = [] 791 792 while addr < self.end: 793 op = imm.disasm( addr ) 794 instructions.append( op ) 795 addr += op.getSize() 796 797 return instructions
798
799 - def isXref(self):
800 """ 801 Check if a Basic Block was created from an XREF 802 803 @rtype: BOOLEAN 804 @return: Whether the Basic Block was created from an XREF 805 """ 806 return isinstance(self, XREFBasicBlock)
807
808 - def isConditionalJmp(self):
809 """ 810 Check if a Basic Block was created from a Conditional Jump instruction 811 812 @rtype: BOOLEAN 813 @return: Whether the Basic Block was created from a Conditional Jump instruction 814 """ 815 return isinstance(self, JMCBasicBlock)
816
817 - def isJmp(self):
818 """ 819 Check if a Basic Block was created from a Jump instruction 820 821 @rtype: BOOLEAN 822 @return: Whether the Basic Block was created from a Jump instruction 823 """ 824 return isinstance(self, JMPBasicBlock)
825
826 - def isRet(self):
827 """ 828 Check if a Basic Block was created from a RET instruction 829 830 @rtype: BOOLEAN 831 @return: Whether the Basic Block was created from a RET instruction 832 """ 833 return isinstance(self, RETBasicBlock)
834
835 -class XREFBasicBlock(BasicBlock):
836 - def __init__(self, start, end):
837 """ 838 XREF Basic Block, Basic Block created from a code reference 839 840 @type start: DWORD 841 @param start: Address of the begging of the Basic Block 842 843 @type end: DWORD 844 @param end: Address of the end of the Basic Block 845 """ 846 BasicBlock.__init__(self, start, end) 847 self.edgeamount = 1
848
849 -class JMCBasicBlock(BasicBlock):
850 - def __init__(self, start, end):
851 """ 852 Conditional Jump Basic Block, Basic Block created from a conditional jump instruction (branch node) 853 854 @type start: DWORD 855 @param start: Address of the begging of the Basic Block 856 857 @type end: DWORD 858 @param end: Address of the end of the Basic Block 859 """ 860 BasicBlock.__init__(self, start, end) 861 self.edgeamount = 2
862 863 # Important Note: 864 # Keep in mind, that the Edge of a JMP Basic block could be 0x0 865 # (For example, in case like jmp [...]), we still don't take care of this special cases
866 -class JMPBasicBlock(BasicBlock):
867 - def __init__(self, start, end):
868 """ 869 Jump Basic Block, Basic Block created from a jump instruction 870 871 @type start: DWORD 872 @param start: Address of the begging of the Basic Block 873 874 @type end: DWORD 875 @param end: Address of the end of the Basic Block 876 """ 877 BasicBlock.__init__(self, start, end) 878 self.edgeamount = 1
879
880 -class RETBasicBlock(BasicBlock):
881 - def __init__(self, start, end):
882 """ 883 RET Basic Block, Basic Block created from a RET instruction (exit node) 884 885 @type start: DWORD 886 @param start: Address of the begging of the Basic Block 887 888 @type end: DWORD 889 @param end: Address of the end of the Basic Block 890 """ 891 BasicBlock.__init__(self, start, end) 892 self.edgeamount = 0
893
894 -class TraceArgs():
895 - def __init__(self, imm, func_address, tracedarg, shownonusersupplied = False):
896 self.imm = imm 897 self.func_address = func_address 898 self.tracedarg = tracedarg 899 self.shownonusersupplied = shownonusersupplied
900
901 - def get(self):
902 idx = 0 903 stack =[] 904 address = self.func_address 905 906 # Find the corresponding PUSH 907 while idx < COUNT: 908 op = self.imm.disasmBackward( address ) 909 if op.isPush(): 910 stack.append(1) 911 if len(stack) == self.tracedarg: 912 break 913 elif op.isPop(): 914 if len(stack): 915 stack.pop(0) 916 else: 917 return 918 address = op.getAddress() 919 del op 920 idx += 1 921 922 # Is this a PUSH? 923 if idx < COUNT: 924 # Double check, just in case 925 dotraceback = True 926 if not op.isPush(): 927 #imm.log("XXX: Error, Opcode should be a Push") 928 return () 929 930 # If the PUSH has no register, its a PUSH CONSTANT 931 # PUSH 0x400 932 if op.getOperandRegister(0) == "": 933 if not self.shownonusersupplied: 934 return () 935 else: 936 return (op, []) 937 938 # If the Operand of the push is EBP, no need to get the traceback. 939 # Cause is probably a PUSH of arguments or a local variable. 940 # (At least, not now) 941 # PUSH [EBP+C] 942 elif op.getOperandRegister(0) == "EBP" and op.operand[0][3]: 943 dotraceback = False 944 #return (op, []) 945 946 show = [] 947 948 # DOING THE TRACEBACK 949 if dotraceback: 950 self.modarg = [] 951 self.visited = [] 952 953 try: 954 self.traceArgBackWithDecode( op.getAddress(), op.operand[0][2] ) 955 except IndexError: 956 op = self.traceArgBack( op.getAddress(), op.operand[0][2]) 957 if op: 958 self.modarg.append(op) 959 960 newop = None 961 962 type = "" 963 for newop in self.modarg: 964 newop.type = "" 965 # If the second argument is a constant, then is not user-supplied 966 # MOV ESI, 0x200 967 if newop.getOperandRegister(1) == "": 968 if self.shownonusersupplied or newop.isCall(): 969 show.append( newop ) 970 else: 971 return () 972 else: 973 type = "" 974 # op.operand[1][3] constante 975 if newop.getOperandRegister(1) == "EBP": 976 if newop.operand[1][3] < 0x80000000: 977 newop.type = "VARS" 978 else: 979 newop.type = "ARGS" 980 981 show.append( newop ) 982 983 op.type = "" 984 # op.operand[1][3] constant 985 # 986 if op.getOperandRegister(0) == "EBP": 987 if op.operand[0][3] < 0x80000000 and op.operand[0][3] != 0: 988 op.type = "<VARS>" 989 elif op.operand[0][3] > 0x80000000: 990 op.type = "<ARGS>" 991 992 #imm.log("Found user-supplied for arg_%d in %s" % ( tracedarg, imm.disasm(ref[0]).result) , address = ref[0]) 993 #imm.log( "%s %s" % (op.getDisasm(), type), address = op.getAddress() ) 994 #for msg in show: 995 # imm.log( msg[0], address = msg[1] ) 996 #imm.log("------") 997 return (op, show) 998 999 return ()
1000 1001 # Note: 1002 # We just trace for MOV (We skip arymethic and lea opcodes) 1003 # This function search backward linearly, we should change it into changing using 1004 # xrefs and probably detecting more than one traceBack
1005 - def traceArgBackWithDecode(self, address, register):
1006 idx = 0 1007 decode = self.imm.findDecode( address ) 1008 1009 while idx < COUNT: 1010 if address in self.visited: 1011 return 0 1012 op = self.imm.disasmBackward( address ) 1013 #imm.log("> %s" % op.result, address = op.getAddress()) 1014 self.visited.append( address ) 1015 if op.isJmp(): 1016 return 0 1017 if op.getResult()[:3] in ("MOV", "XOR"): 1018 # Register is the source 1019 # ex: MOV EAX, ... 1020 if op.operand[0][2] == register: 1021 self.modarg.append( op ) 1022 return 0 1023 # If the register we are looking for is EAX, a CALL would be the one 1024 # the modifier 1025 # CALL ntdll.67225328 1026 elif register == (1,0,0,0,0,0,0,0) and op.isCall(): 1027 self.modarg.append( op ) 1028 return 0 1029 1030 if decode.isJmpDestination(address): 1031 for ref in self.imm.getXrefFrom( address ): 1032 self.traceArgBackWithDecode(ref[0], register) 1033 1034 address = op.getAddress() 1035 idx += 1 1036 if decode: 1037 # Finish looking if we reach the begging of the address 1038 if decode.isFunctionStart( address ): 1039 del decode 1040 return None 1041 del op 1042 1043 del decode 1044 return None
1045 1046 1047 # Note: 1048 # We just trace for MOV (We skip arymethic and lea opcodes) 1049 # This function search backward linearly, we should change it into changing using 1050 # xrefs and probably detecting more than one traceBack
1051 - def traceArgBack(self, address, register):
1052 idx = 0 1053 decode = self.imm.findDecode( address ) 1054 1055 while idx < COUNT: 1056 op = self.imm.disasmBackward( address ) 1057 if op.getResult()[:3] == "MOV": 1058 # Register is the source 1059 # ex: MOV EAX, ... 1060 if op.operand[0][2] == register: 1061 return op 1062 # If the register we are looking for is EAX, a CALL would be the one 1063 # the modifier 1064 # CALL ntdll.67225328 1065 elif register == (1,0,0,0,0,0,0,0) and op.isCall(): 1066 return op 1067 1068 address = op.getAddress() 1069 idx += 1 1070 if decode: 1071 # Finish looking if we reach the begging of the address 1072 if decode.isFunctionStart( address ): 1073 del decode 1074 return None 1075 del op 1076 1077 del decode 1078 return None
1079