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

Source Code for Module Libs.libanalize

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