├── README.md ├── vmpWindowsApiAddressDecoder.py └── vmpWindowsApiAddressDecoder_for_dump.py /README.md: -------------------------------------------------------------------------------- 1 | # VMProtect Windows API Address Decoder 2 | IDA script for vmprotect Windows Api address decoder 3 | 4 | # Usage 5 | - Load the vmpr.py file from IDA. 6 | - instance = vwaad() 7 | - When loaded, it automatically outputs decoding information. 8 | - Use the patchOllyScript() method to print the OllyScript that can be patched. 9 | 10 | API Decoder anaysis post [Link] (https://sfkino.tistory.com/74) 11 | 12 | 13 | -------------------------------------------------------------------------------- /vmpWindowsApiAddressDecoder.py: -------------------------------------------------------------------------------- 1 | #-*- encoding:utf-8 -*- 2 | ''' 3 | Autor : JHJ 4 | Project : VMProtect Windows Api Address Decoder 5 | TEST Ver : IDA 7.0 6 | Target Version : IDA 7.4 7 | ''' 8 | import idc 9 | import idaapi 10 | import idautils 11 | 12 | 13 | #Set Your name 14 | AUTOR = "JHJ" 15 | 16 | #VMProtect Windows Api Address Decoder 17 | class vwaad: 18 | 19 | vmp_list = [] #virtualization code Entrypoint 20 | fail_list = [] 21 | call_list = [] 22 | 23 | gadget_list = [] 24 | dummy_gadget_list = [] 25 | dummy_fail_list = [] 26 | 27 | dummy_patch_list = [] 28 | 29 | decoded_api = [] 30 | 31 | depth = 300 32 | 33 | 34 | 35 | def __init__(self, targetSeg = ".text", vmpSeg = ".asp0"): 36 | #Get Segment Info (start, end, size) 37 | 38 | if self.getVmpCallList(targetSeg, vmpSeg) is not True: 39 | print ("Get Call list Fail") 40 | return 41 | 42 | for x in self.call_list: 43 | result = self.functionTracer(x) 44 | if result is False: 45 | self.fail_list.append(x) 46 | else: 47 | self.gadget_list.append(result) 48 | self.decodeAddress() 49 | 50 | for x in self.call_list: 51 | #print "" 52 | result = self.functionDummyTracer(x) 53 | if result is False: 54 | self.dummy_fail_list.append(x) 55 | else: 56 | self.dummy_patch_list.append(x + 5) 57 | self.dummy_gadget_list.append(result) 58 | if idaapi.get_bytes(x + 5,1) != '\x90': 59 | self.dummy_patch_list.append(x + 5) 60 | self.dummy_gadget_list.append(result) 61 | else: 62 | self.dummy_patch_list.append(x + 5) 63 | self.dummy_gadget_list.append(result) 64 | 65 | print("################### vmp0 Call List ###################") 66 | self.printCallList() 67 | 68 | print("################### Trace Fail List ###################") 69 | self.printFailList() 70 | 71 | print("################### Gadget List ###################") 72 | self.printGadget() 73 | 74 | print("################### Decoded Api List ###################") 75 | self.printDecodedApi() 76 | 77 | print("################### Decoded Dummy Api List ###################") 78 | self.printDummyGadget() 79 | 80 | for x in self.dummy_patch_list: 81 | print hex(x).rstrip('L') 82 | 83 | 84 | ############################## PRINT FUNCTION ############################## 85 | def printDecodedApi(self): 86 | for gadget in self.decoded_api: 87 | print ('{0} : {1} -> call {2}'.format(hex(gadget['original_addr']).rstrip('L'), gadget['original_assembly'], hex(gadget['decoded_address']).rstrip("L"))) 88 | 89 | print ("") 90 | 91 | def printGadget(self): 92 | idx = 0 93 | print ("gadget_list") 94 | for gadget in self.gadget_list: 95 | print ('gadget address : {0}'.format(hex(gadget[0]['entrypoint']).rstrip('L'))) 96 | for y in gadget: 97 | print( '{0} {1} : {2} '.format(y['flow'],hex(y['addr']).rstrip('L'), y['gadget'])) 98 | print ("") 99 | data = 0 100 | print ("") 101 | 102 | def printCallList(self): 103 | print ("call_list") 104 | for x in self.call_list: 105 | print hex(x).rstrip("L"), 106 | print ("") 107 | 108 | def printFailList(self): 109 | print ("fail_list") 110 | for x in self.fail_list: 111 | print hex(x).rstrip("L"), 112 | print ("") 113 | 114 | def printDebugFailList(self): 115 | print ("Debug Fail List") 116 | 117 | for x in self.fail_list: 118 | self.functionTracer(x, debug = True) 119 | print ("") 120 | 121 | def printDebugDummyFailList(self): 122 | print ("Debug Fail List") 123 | 124 | for x in self.dummy_fail_list: 125 | self.functionDummyTracer(x, debug = True) 126 | print ("") 127 | 128 | 129 | ############################## PRINT DUMMY FUNCTION ############################## 130 | 131 | def printDummyGadget(self): 132 | idx = 0 133 | print ("gadget_list") 134 | for gadget in self.dummy_gadget_list: 135 | print ('DUMMY gadget address : {0}'.format(hex(gadget[0]['entrypoint']).rstrip('L'))) 136 | #self.dummy_patch_list.append(hex(gadget[0]['entrypoint']).rstrip('L')+5) 137 | for y in gadget: 138 | print( '{0} {1} : {2} '.format(y['flow'],hex(y['addr']).rstrip('L'), y['gadget'])) 139 | print ("") 140 | data = 0 141 | print ("") 142 | 143 | def printDebugFailList(self): 144 | print ("Debug Fail List") 145 | 146 | for x in self.fail_list: 147 | self.functionTracer(x, debug = True) 148 | print ("") 149 | 150 | ############################## UTILITY FUNCTION ############################## 151 | 152 | ''' 153 | patch script for ollydbg 154 | arg : None 155 | return : None 156 | ''' 157 | def getbyte_test(self,addr,size): 158 | if get_bytes(addr, size) == '\x90': 159 | print data 160 | else: 161 | print "NONONO" 162 | 163 | 164 | def patchOllyScript(self): 165 | for gadget in self.decoded_api: 166 | print ('asm {0}, \"call {1}\"'.format(hex(gadget['original_addr']).rstrip("L").lstrip("0x"), 167 | hex(gadget['decoded_address']).rstrip("L"))) 168 | 169 | def patchDummyOllyScript(self): 170 | for gadget in self.dummy_patch_list: 171 | print ('fill {0}, 1, 90'.format(hex(gadget).rstrip("L").lstrip("0x"))) 172 | 173 | 174 | 175 | ''' 176 | Verify that eip is in the vmp segment. 177 | arg : eip, vmp segment address, vmp segment end address 178 | return : Ture/False 179 | ''' 180 | def checkAddrInSegment(self, eip, vmp_ea, vmp_end): 181 | if eip > vmp_ea and eip < vmp_end: 182 | return True 183 | else: 184 | return False 185 | 186 | ############################## GET DATA FUNCTION ############################## 187 | ''' 188 | Collect All calls thet .vmp0 from .text 189 | arg : targetSeg Address, vmpSeg Address 190 | return : True 191 | 192 | ''' 193 | def getVmpCallList(self, targetSeg, vmpSeg): 194 | idx = 0 195 | addr = 0 196 | dis_ea, dis_end, dis_size = self.getSegName(targetSeg) 197 | vmp_ea, vmp_end, vmp_size = self.getSegName(vmpSeg) 198 | 199 | 200 | while dis_ea < dis_end: 201 | if idc.GetDisasm(dis_ea)[:4] == "call": 202 | addr = get_operand_value(dis_ea,0) 203 | if self.checkAddrInSegment(addr, vmp_ea, vmp_end) is True: 204 | idx = idx + 1 205 | self.vmp_list.append(addr) 206 | self.call_list.append(dis_ea) 207 | #under IDA 7.4 208 | #dis_ea = idc.NextHead(dis_ea) 209 | #over IDA 7.4 210 | dis_ea = idc.next_head(dis_ea) 211 | 212 | 213 | return True 214 | 215 | 216 | 217 | ''' 218 | Function Tracing without conditional branching 219 | arg : function address, debug falg 220 | return : gadget list / False 221 | ''' 222 | def functionTracer(self, addr, debug = False): 223 | idx = 0 224 | depth = self.depth 225 | result = [] 226 | ep = addr 227 | 228 | dummy = False 229 | 230 | while idx < depth: 231 | idx = idx + 1 232 | if debug is True: 233 | print( '{0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 234 | 235 | #dummy = self.findDummyGadget(addr) 236 | 237 | 238 | if idc.GetDisasm(addr)[:4] == "retn": 239 | break 240 | 241 | elif idc.GetDisasm(addr)[:3] == "jmp" or idc.GetDisasm(addr)[:4] == "call": 242 | #print get_operand_value(addr,0) 243 | addr = get_operand_value(addr,0) 244 | 245 | elif self.findFirstGadget(addr) == True: 246 | if len(result) > 0: 247 | result = [] 248 | result.append(self.getGadget(addr, ep, 1)) 249 | #under IDA 7.4 250 | #addr = idc.NextHead(addr) 251 | #over IDA 7.4 252 | addr = idc.next_head(addr) 253 | 254 | elif len(result) == 1 and self.findSecondGadget(addr, result[0]['reg']) == True: 255 | #input gadget 256 | result.append(self.getGadget(addr,ep, 2)) 257 | #under IDA 7.4 258 | #addr = idc.NextHead(addr) 259 | #over IDA 7.4 260 | addr = idc.next_head(addr) 261 | 262 | elif len(result) == 2 and self.findThirdGadget(addr, result[0]['reg']) == True: 263 | result.append(self.getGadget(addr,ep, 3)) 264 | #under IDA 7.4 265 | #addr = idc.NextHead(addr) 266 | #over IDA 7.4 267 | addr = idc.next_head(addr) 268 | 269 | else: 270 | #under IDA 7.4 271 | #addr = idc.NextHead(addr) 272 | #over IDA 7.4 273 | addr = idc.next_head(addr) 274 | 275 | if debug is True and len(result) > 0: 276 | for x in result: 277 | print ("{0} : {1} {2} ".format(hex(x['addr']).rstrip('L'), hex(x['const']), x['gadget'] )) 278 | 279 | 280 | print "" 281 | if len(result) == 3: 282 | return result 283 | else: 284 | return False 285 | 286 | 287 | ''' 288 | Function Tracing without conditional branching 289 | arg : function address, debug falg 290 | return : gadget list / False 291 | ''' 292 | def functionDummyTracer(self, addr, debug = False): 293 | idx = 0 294 | depth = self.depth 295 | result = [] 296 | ep = addr 297 | 298 | dummy = False 299 | 300 | 301 | 302 | 303 | while idx < depth: 304 | idx = idx + 1 305 | #print( 'DUMMY : {0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 306 | if debug is True: 307 | print( '{0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 308 | 309 | #dummy = self.findDummyGadget(addr) 310 | 311 | 312 | 313 | if idc.GetDisasm(addr)[:4] == "retn": 314 | break 315 | 316 | elif idc.GetDisasm(addr)[:3] == "jmp" or idc.GetDisasm(addr)[:4] == "call": 317 | #print get_operand_value(addr,0) 318 | addr = get_operand_value(addr,0) 319 | 320 | elif self.findFirstDummyGadget(addr) == True: 321 | #print "MATCH FIRST" 322 | if len(result) > 0: 323 | result = [] 324 | result.append(self.getGadget(addr, ep, 1)) 325 | #under IDA 7.4 326 | #addr = idc.NextHead(addr) 327 | #over IDA 7.4 328 | addr = idc.next_head(addr) 329 | 330 | elif len(result) == 1 and self.findSecondDummyGadget(addr, result[0]['reg']) == True: 331 | #input gadget 332 | result.append(self.getGadget(addr,ep, 2)) 333 | #under IDA 7.4 334 | #addr = idc.NextHead(addr) 335 | #over IDA 7.4 336 | addr = idc.next_head(addr) 337 | 338 | elif len(result) == 2 and self.findThirdDummyGadget(addr, result[0]['reg']) == True: 339 | result.append(self.getGadget(addr,ep, 3)) 340 | #under IDA 7.4 341 | #addr = idc.NextHead(addr) 342 | #over IDA 7.4 343 | addr = idc.next_head(addr) 344 | 345 | else: 346 | #under IDA 7.4 347 | #addr = idc.NextHead(addr) 348 | #over IDA 7.4 349 | addr = idc.next_head(addr) 350 | 351 | if debug is True and len(result) > 0: 352 | for x in result: 353 | print ("{0} : {1} {2} ".format(hex(x['addr']).rstrip('L'), hex(x['const']), x['gadget'] )) 354 | 355 | 356 | 357 | print "" 358 | if len(result) == 3: 359 | return result 360 | else: 361 | return False 362 | 363 | #Utility 364 | 365 | def fullMakeCode(self): 366 | segStart, segEnd, segSize = self.getSegName('.text') 367 | 368 | eip = segStart 369 | 370 | while eip < segEnd: 371 | MakeCode(eip) 372 | eip = NextHead(eip) 373 | 374 | 375 | 376 | def patchDummyCode(self): 377 | for x in self.dummy_patch_list: 378 | self.idaBytePatch(x,0x90) 379 | 380 | def idaBytePatch(self,addr, opcode): 381 | PatchByte(addr, opcode) 382 | ''' 383 | Get information for a specific segment. 384 | arg : segment name 385 | return : segment start address, segment end address, segment size 386 | ''' 387 | def getSegName(self, segName): 388 | for seg in idautils.Segments(): 389 | #under IDA 7.4 390 | #if idc.SegName(seg) == segName: 391 | #over IDA 7.4 392 | if idc.get_segm_name(seg) == segName: 393 | #under IDA 7.4 394 | #return idc.SegStart(seg), idc.SegEnd(seg), idc.SegEnd(seg) - idc.SegStart(seg) 395 | #over IDA 7.4 396 | return idc.get_segm_start(seg), idc.get_segm_end(seg), idc.get_segm_end(seg) - idc.get_segm_start(seg) 397 | 398 | 399 | #API 가젯 처리 400 | ''' 401 | Find the first gadget in the form "mov R32, CONST". 402 | arg : address 403 | return : True/False 404 | ''' 405 | def findFirstGadget(self, eip): 406 | if idc.GetDisasm(eip)[:4] == "mov ": 407 | #under IDA 7.4 408 | #if GetOpnd(eip,0)[0] == "e" and (GetOpnd(eip,1)[:7] == "(offset" or GetOpnd(eip,1)[:6] == "offset" or GetOpnd(eip,1)[-1] == "h"): 409 | #over IDA 7.4 410 | if idc.print_operand(eip,0)[0] == "e" and (idc.print_operand(eip,1)[:7] == "(offset" or idc.print_operand(eip,1)[:6] == "offset" or idc.print_operand(eip,1)[-1] == "h"): 411 | return True 412 | else: 413 | return False 414 | else: 415 | return False 416 | 417 | ''' 418 | Find the second gadget in the form "mov R32, [R32 + CONST]". 419 | arg : address, register 420 | return : True/False 421 | ''' 422 | def findSecondGadget(self, eip,reg): 423 | 424 | if idc.GetDisasm(eip)[:4] == "mov ": 425 | #under IDA 7.4 426 | #if GetOpnd(eip,0) == reg and (GetOpnd(eip,1)[:2] == "[e" or GetOpnd(eip,1)[:-2] == "h]"): 427 | #over IDA 7.4 428 | if idc.print_operand(eip,0) == reg and (idc.print_operand(eip,1)[:2] == "[e" or idc.print_operand(eip,1)[:-2] == "h]"): 429 | return True 430 | else: 431 | return False 432 | else: 433 | return False 434 | 435 | ''' 436 | Find the third gadget in the form "lea R32, [R32 + CONST]". 437 | arg : address, register 438 | ''' 439 | def findThirdGadget(self, eip,reg): 440 | if idc.GetDisasm(eip)[:4] == "lea ": 441 | #under IDA 7.4 442 | #if GetOpnd(eip,0) == reg and (GetOpnd(eip,1)[:2] == "[e" or GetOpnd(eip,1)[:-2] == "h]"): 443 | #over IDA 7.4 444 | if idc.print_operand(eip,0) == reg and (idc.print_operand(eip,1)[:2] == "[e" or idc.print_operand(eip,1)[:-2] == "h]"): 445 | return True 446 | else: 447 | return False 448 | else: 449 | return False 450 | 451 | #Dummy 가젯 처리 452 | 453 | ''' 454 | Find the first dummy gadget in the form "mov R32, [ESP+CONST]". 455 | arg : address 456 | return : True/False 457 | ''' 458 | def findFirstDummyGadget(self, eip): 459 | 460 | if idc.GetDisasm(eip)[:4] == "mov ": 461 | #under IDA 7.4 462 | #if GetOpnd(eip,0)[0] == "e" and GetOpnd(eip,1)[:5] == "[esp+": 463 | #over IDA 7.4 464 | if idc.print_operand(eip,0)[0] == "e" and idc.print_operand(eip,1)[:5] == "[esp+": 465 | print "FIRST MATCH TRUE", idc.GetDisasm(eip) 466 | return True 467 | else: 468 | return False 469 | else: 470 | return False 471 | 472 | ''' 473 | Find the second gadget in the form "lea R32, [R32 + 1]". 474 | arg : address, register 475 | return : True/False 476 | ''' 477 | def findSecondDummyGadget(self, eip,reg): 478 | if idc.GetDisasm(eip)[:4] == "lea ": 479 | #under IDA 7.4 480 | #if GetOpnd(eip,0) == reg and GetOpnd(eip,1)[1:4] == reg and GetOpnd(eip,1)[-3:] == "+1]": 481 | #over IDA 7.4 482 | if idc.print_operand(eip,0) == reg and idc.print_operand(eip,1)[1:4] == reg and idc.print_operand(eip,1)[-3:] == "+1]": 483 | return True 484 | else: 485 | return False 486 | else: 487 | return False 488 | 489 | ''' 490 | Find the third gadget in the form "lea R32, [R32 + CONST]". 491 | arg : address, register 492 | return : True/False 493 | ''' 494 | def findThirdDummyGadget(self, eip,reg): 495 | 496 | if idc.GetDisasm(eip)[:4] == "mov ": 497 | #under IDA 7.4 498 | #if GetOpnd(eip,0)[:5] == "[esp+" and GetOpnd(eip,1) == reg: 499 | #over IDA 7.4 500 | if idc.print_operand(eip,0)[:5] == "[esp+" and idc.print_operand(eip,1) == reg: 501 | return True 502 | else: 503 | return False 504 | else: 505 | return False 506 | 507 | ''' 508 | get gadget from address 509 | arg : address, entrypoint address, gadget number 510 | return : True/False 511 | ''' 512 | def getGadget(self, eip, ep, flow): 513 | #under IDA 7.4 514 | #return dict({ 'flow' : flow,'entrypoint' :ep, 'addr': eip, 'gadget': idc.GetDisasm(eip), 'reg': GetOpnd(eip,0), 'const': get_operand_value(eip,1)}) 515 | #over IDA 7.4 516 | return dict({ 'flow' : flow,'entrypoint' :ep, 'addr': eip, 'gadget': idc.GetDisasm(eip), 'reg': idc.print_operand(eip,0), 'const': get_operand_value(eip,1)}) 517 | 518 | ''' 519 | decode address from gadget 520 | arg : None 521 | return : None 522 | ''' 523 | def decodeAddress(self): 524 | idx = 0 525 | data = 0 526 | 527 | for gadget in self.gadget_list: 528 | for y in gadget: 529 | if y['flow'] == 2: 530 | data = next(GetDataList(data + y['const'],1, itemsize=4)) 531 | else: 532 | data = data + y['const'] 533 | 534 | data = data & 0xFFFFFFFF 535 | self.decoded_api.append(dict( \ 536 | {'original_addr' : gadget[0]['entrypoint'], \ 537 | 'original_assembly' : idc.GetDisasm(gadget[0]['entrypoint']), \ 538 | 'decoded_address' : data})) 539 | data = 0 540 | 541 | ''' 542 | DummyGadget 543 | 10062696 8B4424 30 MOV EAX,DWORD PTR SS:[ESP+30] 544 | 10069014 8D80 01000000 LEA EAX,DWORD PTR DS:[EAX+1] 545 | 10069028 894424 38 MOV DWORD PTR SS:[ESP+38],EAX 546 | ''' 547 | 548 | ''' 549 | def findDummyGadget(self,eip): 550 | idx = 0 551 | data = 0 552 | 553 | disass = idc.GetDisasm(eip) 554 | print ("[DEBUG] DUMMY VALUE FIND : {0} : {1}\t{2}\t{3}".format(hex(eip).rstrip('L'), disass, GetOpnd(eip,0), GetOpnd(eip,1))) 555 | 556 | if disass[:4] == "lea" and (GetOpnd(eip,0)[:2] == "e" and GetOpnd(eip,1)[:2] == "[e" and GetOpnd(eip,1)[:-3] == "+1]"): 557 | print ("[DEBUG] DUMMY VALUE FIND : {0} : {1}\t{2}\t{3}".format(hex(eip).lstrip('L'), disass, GetOpnd(eip,1), GetOpnd(eip,1))) 558 | return True 559 | else: 560 | return False 561 | ''' 562 | 563 | 564 | 565 | 566 | -------------------------------------------------------------------------------- /vmpWindowsApiAddressDecoder_for_dump.py: -------------------------------------------------------------------------------- 1 | #-*- encoding:utf-8 -*- 2 | ''' 3 | Autor : JHJ 4 | Project : VMProtect Windows Api Address Decoder 5 | TEST Ver : IDA 7.0 6 | Target Version : IDA 7.4 7 | ''' 8 | import idc 9 | import idaapi 10 | import idautils 11 | 12 | 13 | #Set Your name 14 | AUTOR = "JHJ" 15 | 16 | #VMProtect Windows Api Address Decoder 17 | class vwaad: 18 | 19 | vmp_list = [] #virtualization code Entrypoint 20 | fail_list = [] 21 | call_list = [] 22 | 23 | gadget_list = [] 24 | dummy_gadget_list = [] 25 | dummy_fail_list = [] 26 | 27 | dummy_patch_list = [] 28 | 29 | decoded_api = [] 30 | 31 | depth = 300 32 | 33 | 34 | 35 | def __init__(self, targetSeg = ".text", vmpSeg = ".asp0"): 36 | #Get Segment Info (start, end, size) 37 | self.vmpSeg = ".asp0" 38 | vmpSeg = ".asp0" 39 | if self.getVmpCallList(targetSeg, vmpSeg) is not True: 40 | print ("Get Call list Fail") 41 | return 42 | 43 | for x in self.call_list: 44 | print "" 45 | result = self.functionDummyTracer(x) 46 | if result is False: 47 | self.dummy_fail_list.append(x) 48 | else: 49 | if idaapi.get_bytes(x + 5,1) != '\x90': 50 | self.dummy_patch_list.append(x + 5) 51 | self.dummy_gadget_list.append(result) 52 | else: 53 | self.dummy_patch_list.append(x + 5) 54 | self.dummy_gadget_list.append(result) 55 | 56 | self.patchDummyCode() 57 | 58 | self.call_list = [] 59 | 60 | if self.getVmpCallList(targetSeg, vmpSeg) is not True: 61 | print ("Get Call list Fail") 62 | return 63 | #self.dummy_patch_list = [] 64 | #self.dummy_gadget_list = [] 65 | 66 | for x in self.call_list: 67 | print "" 68 | result = self.functionDummyTracer(x) 69 | if result is False: 70 | self.dummy_fail_list.append(x) 71 | else: 72 | if idaapi.get_bytes(x + 5,1) != '\x90': 73 | self.dummy_patch_list.append(x + 5) 74 | self.dummy_gadget_list.append(result) 75 | ''' 76 | else: 77 | self.dummy_patch_list.append(x + 5) 78 | self.dummy_gadget_list.append(result) 79 | ''' 80 | 81 | self.patchDummyCode() 82 | self.call_list = [] 83 | 84 | if self.getVmpCallList(targetSeg, vmpSeg) is not True: 85 | print ("Get Call list Fail") 86 | return 87 | 88 | for x in self.call_list: 89 | result = self.functionTracer(x) 90 | if result is False: 91 | self.fail_list.append(x) 92 | else: 93 | self.gadget_list.append(result) 94 | self.decodeAddress() 95 | 96 | 97 | print("################### vmp0 Call List ###################") 98 | self.printCallList() 99 | 100 | print("################### Trace Fail List ###################") 101 | self.printFailList() 102 | 103 | print("################### Gadget List ###################") 104 | self.printGadget() 105 | 106 | print("################### Decoded Api List ###################") 107 | self.printDecodedApi() 108 | 109 | print("################### Decoded Dummy Api List ###################") 110 | self.printDummyGadget() 111 | 112 | for x in self.dummy_patch_list: 113 | print hex(x).rstrip('L') 114 | 115 | 116 | ############################## PRINT FUNCTION ############################## 117 | def printDecodedApi(self): 118 | for gadget in self.decoded_api: 119 | print ('{0} : {1} -> call {2}'.format(hex(gadget['original_addr']).rstrip('L'), gadget['original_assembly'], hex(gadget['decoded_address']).rstrip("L"))) 120 | 121 | print ("") 122 | 123 | def printGadget(self): 124 | idx = 0 125 | print ("gadget_list") 126 | for gadget in self.gadget_list: 127 | print ('gadget address : {0}'.format(hex(gadget[0]['entrypoint']).rstrip('L'))) 128 | for y in gadget: 129 | print( '{0} {1} : {2} '.format(y['flow'],hex(y['addr']).rstrip('L'), y['gadget'])) 130 | print ("") 131 | data = 0 132 | print ("") 133 | 134 | def printCallList(self): 135 | print ("call_list") 136 | for x in self.call_list: 137 | print hex(x).rstrip("L"), 138 | print ("") 139 | 140 | def printFailList(self): 141 | print ("fail_list") 142 | for x in self.fail_list: 143 | print hex(x).rstrip("L"), 144 | print ("") 145 | 146 | def printDebugFailList(self): 147 | print ("Debug Fail List") 148 | 149 | for x in self.fail_list: 150 | self.functionTracer(x, debug = True) 151 | print ("") 152 | 153 | def printDebugDummyFailList(self): 154 | print ("Debug Fail List") 155 | 156 | for x in self.dummy_fail_list: 157 | self.functionDummyTracer(x, debug = True) 158 | print ("") 159 | 160 | 161 | ############################## PRINT DUMMY FUNCTION ############################## 162 | 163 | def printDummyGadget(self): 164 | idx = 0 165 | print ("gadget_list") 166 | for gadget in self.dummy_gadget_list: 167 | print ('DUMMY gadget address : {0}'.format(hex(gadget[0]['entrypoint']).rstrip('L'))) 168 | #self.dummy_patch_list.append(hex(gadget[0]['entrypoint']).rstrip('L')+5) 169 | for y in gadget: 170 | print( '{0} {1} : {2} '.format(y['flow'],hex(y['addr']).rstrip('L'), y['gadget'])) 171 | print ("") 172 | data = 0 173 | print ("") 174 | 175 | def printDebugFailList(self): 176 | print ("Debug Fail List") 177 | 178 | for x in self.fail_list: 179 | self.functionTracer(x, debug = True) 180 | print ("") 181 | 182 | ############################## UTILITY FUNCTION ############################## 183 | 184 | ''' 185 | patch script for ollydbg 186 | arg : None 187 | return : None 188 | ''' 189 | def getbyte_test(self,addr,size): 190 | if get_bytes(addr, size) == '\x90': 191 | print data 192 | else: 193 | print "NONONO" 194 | 195 | 196 | def patchOllyScript(self): 197 | for gadget in self.decoded_api: 198 | print ('asm {0}, \"call {1}\"'.format(hex(gadget['original_addr']).rstrip("L").lstrip("0x"), 199 | hex(gadget['decoded_address']).rstrip("L"))) 200 | 201 | def patchDummyOllyScript(self): 202 | for gadget in self.dummy_patch_list: 203 | print ('fill {0}, 1, 90'.format(hex(gadget).rstrip("L").lstrip("0x"))) 204 | 205 | 206 | 207 | ''' 208 | Verify that eip is in the vmp segment. 209 | arg : eip, vmp segment address, vmp segment end address 210 | return : Ture/False 211 | ''' 212 | def checkAddrInSegment(self, eip, vmp_ea, vmp_end): 213 | if eip > vmp_ea and eip < vmp_end: 214 | return True 215 | else: 216 | return False 217 | 218 | ############################## GET DATA FUNCTION ############################## 219 | ''' 220 | Collect All calls thet .vmp0 from .text 221 | arg : targetSeg Address, vmpSeg Address 222 | return : True 223 | 224 | ''' 225 | def getVmpCallList(self, targetSeg, vmpSeg): 226 | idx = 0 227 | addr = 0 228 | dis_ea, dis_end, dis_size = self.getSegName(targetSeg) 229 | vmp_ea, vmp_end, vmp_size = self.getSegName(vmpSeg) 230 | 231 | 232 | while dis_ea < dis_end: 233 | if idc.GetDisasm(dis_ea)[:4] == "call": 234 | addr = get_operand_value(dis_ea,0) 235 | if self.checkAddrInSegment(addr, vmp_ea, vmp_end) is True: 236 | idx = idx + 1 237 | self.vmp_list.append(addr) 238 | self.call_list.append(dis_ea) 239 | 240 | dis_ea = idc.NextHead(dis_ea) 241 | 242 | return True 243 | 244 | 245 | 246 | ''' 247 | Function Tracing without conditional branching 248 | arg : function address, debug falg 249 | return : gadget list / False 250 | ''' 251 | def functionTracer(self, addr, debug = False): 252 | idx = 0 253 | depth = self.depth 254 | result = [] 255 | ep = addr 256 | 257 | dummy = False 258 | 259 | while idx < depth: 260 | idx = idx + 1 261 | if debug is True: 262 | print( '{0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 263 | 264 | #dummy = self.findDummyGadget(addr) 265 | 266 | 267 | if idc.GetDisasm(addr)[:4] == "retn": 268 | break 269 | 270 | elif idc.GetDisasm(addr)[:3] == "jmp" or idc.GetDisasm(addr)[:4] == "call": 271 | #print get_operand_value(addr,0) 272 | addr = get_operand_value(addr,0) 273 | 274 | elif self.findFirstGadget(addr) == True: 275 | if len(result) > 0: 276 | result = [] 277 | result.append(self.getGadget(addr, ep, 1)) 278 | addr = idc.NextHead(addr) 279 | 280 | elif len(result) == 1 and self.findSecondGadget(addr, result[0]['reg']) == True: 281 | #input gadget 282 | result.append(self.getGadget(addr,ep, 2)) 283 | addr = idc.NextHead(addr) 284 | 285 | elif len(result) == 2 and self.findThirdGadget(addr, result[0]['reg']) == True: 286 | result.append(self.getGadget(addr,ep, 3)) 287 | addr = idc.NextHead(addr) 288 | 289 | else: 290 | addr = idc.NextHead(addr) 291 | 292 | if debug is True and len(result) > 0: 293 | for x in result: 294 | print ("{0} : {1} {2} ".format(hex(x['addr']).rstrip('L'), hex(x['const']), x['gadget'] )) 295 | 296 | 297 | if len(result) == 3: 298 | return result 299 | else: 300 | return False 301 | 302 | 303 | ''' 304 | Function Tracing without conditional branching 305 | arg : function address, debug falg 306 | return : gadget list / False 307 | ''' 308 | def functionDummyTracer(self, addr, debug = False): 309 | idx = 0 310 | depth = self.depth 311 | result = [] 312 | ep = addr 313 | 314 | dummy = False 315 | 316 | 317 | 318 | 319 | while idx < depth: 320 | idx = idx + 1 321 | #print( 'DUMMY : {0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 322 | if debug is True: 323 | print( '{0} {1} {2} {3}'.format(idx, hex(addr).rstrip('L'), idc.GetDisasm(addr), hex(get_operand_value(addr,0)) )) 324 | 325 | #dummy = self.findDummyGadget(addr) 326 | 327 | 328 | 329 | if idc.GetDisasm(addr)[:4] == "retn": 330 | break 331 | 332 | elif idc.GetDisasm(addr)[:3] == "jmp" or idc.GetDisasm(addr)[:4] == "call": 333 | #print get_operand_value(addr,0) 334 | addr = get_operand_value(addr,0) 335 | 336 | elif self.findFirstDummyGadget(addr) == True: 337 | #print "MATCH FIRST" 338 | if len(result) > 0: 339 | result = [] 340 | result.append(self.getGadget(addr, ep, 1)) 341 | addr = idc.NextHead(addr) 342 | 343 | elif len(result) == 1 and self.findSecondDummyGadget(addr, result[0]['reg']) == True: 344 | #input gadget 345 | result.append(self.getGadget(addr,ep, 2)) 346 | addr = idc.NextHead(addr) 347 | 348 | elif len(result) == 2 and self.findThirdDummyGadget(addr, result[0]['reg']) == True: 349 | result.append(self.getGadget(addr,ep, 3)) 350 | addr = idc.NextHead(addr) 351 | 352 | else: 353 | addr = idc.NextHead(addr) 354 | 355 | if debug is True and len(result) > 0: 356 | for x in result: 357 | print ("{0} : {1} {2} ".format(hex(x['addr']).rstrip('L'), hex(x['const']), x['gadget'] )) 358 | 359 | 360 | 361 | print "" 362 | if len(result) == 3: 363 | return result 364 | else: 365 | return False 366 | 367 | 368 | #Utility 369 | 370 | def fullMakeCode(self): 371 | segStart, segEnd, segSize = self.getSegName('.text') 372 | 373 | eip = segStart 374 | 375 | while eip < segEnd: 376 | MakeCode(eip) 377 | eip = NextHead(eip) 378 | 379 | 380 | 381 | def patchDummyCode(self): 382 | for x in self.dummy_patch_list: 383 | self.idaBytePatch(x,0x90) 384 | 385 | def idaBytePatch(self,addr, opcode): 386 | PatchByte(addr, opcode) 387 | ''' 388 | Get information for a specific segment. 389 | arg : segment name 390 | return : segment start address, segment end address, segment size 391 | ''' 392 | def getSegName(self, segName): 393 | print segName 394 | for seg in idautils.Segments(): 395 | print idc.SegName(seg) 396 | if idc.SegName(seg) == segName: 397 | return idc.SegStart(seg), idc.SegEnd(seg), idc.SegEnd(seg) - idc.SegStart(seg) 398 | 399 | 400 | #API 가젯 처리 401 | ''' 402 | Find the first gadget in the form "mov R32, CONST". 403 | arg : address 404 | return : True/False 405 | ''' 406 | def findFirstGadget(self, eip): 407 | if idc.GetDisasm(eip)[:4] == "mov ": 408 | if GetOpnd(eip,0)[0] == "e" and (GetOpnd(eip,1)[:7] == "(offset" or GetOpnd(eip,1)[:6] == "offset" or GetOpnd(eip,1)[-1] == "h"): 409 | return True 410 | else: 411 | return False 412 | else: 413 | return False 414 | 415 | ''' 416 | Find the second gadget in the form "mov R32, [R32 + CONST]". 417 | arg : address, register 418 | return : True/False 419 | ''' 420 | def findSecondGadget(self, eip,reg): 421 | 422 | if idc.GetDisasm(eip)[:4] == "mov ": 423 | if GetOpnd(eip,0) == reg and (GetOpnd(eip,1)[:2] == "[e" or GetOpnd(eip,1)[:-2] == "h]"): 424 | return True 425 | else: 426 | return False 427 | else: 428 | return False 429 | 430 | ''' 431 | Find the third gadget in the form "lea R32, [R32 + CONST]". 432 | arg : address, register 433 | return : True/False 434 | ''' 435 | def findThirdGadget(self, eip,reg): 436 | if idc.GetDisasm(eip)[:4] == "lea ": 437 | if GetOpnd(eip,0) == reg and (GetOpnd(eip,1)[:2] == "[e" or GetOpnd(eip,1)[:-2] == "h]"): 438 | return True 439 | else: 440 | return False 441 | else: 442 | return False 443 | 444 | #Dummy 가젯 처리 445 | ''' 446 | DummyGadget 447 | 10062696 8B4424 30 MOV EAX,DWORD PTR SS:[ESP+30] 448 | 10069014 8D80 01000000 LEA EAX,DWORD PTR DS:[EAX+1] 449 | 10069028 894424 38 MOV DWORD PTR SS:[ESP+38],EAX 450 | ''' 451 | 452 | ''' 453 | Find the first dummy gadget in the form "mov R32, [ESP+CONST]". 454 | arg : address 455 | return : True/False 456 | ''' 457 | def findFirstDummyGadget(self, eip): 458 | 459 | if idc.GetDisasm(eip)[:4] == "mov ": 460 | if GetOpnd(eip,0)[0] == "e" and GetOpnd(eip,1)[:5] == "[esp+": 461 | print "FIRST MATCH TRUE", idc.GetDisasm(eip) 462 | return True 463 | else: 464 | return False 465 | else: 466 | return False 467 | 468 | ''' 469 | Find the second gadget in the form "lea R32, [R32 + 1]". 470 | arg : address, register 471 | return : True/False 472 | ''' 473 | def findSecondDummyGadget(self, eip,reg): 474 | if idc.GetDisasm(eip)[:4] == "lea ": 475 | if GetOpnd(eip,0) == reg and GetOpnd(eip,1)[1:4] == reg and GetOpnd(eip,1)[-3:] == "+1]": 476 | return True 477 | else: 478 | return False 479 | else: 480 | return False 481 | 482 | ''' 483 | Find the third gadget in the form "lea R32, [R32 + CONST]". 484 | arg : address, register 485 | return : True/False 486 | ''' 487 | def findThirdDummyGadget(self, eip,reg): 488 | 489 | if idc.GetDisasm(eip)[:4] == "mov ": 490 | if GetOpnd(eip,0)[:5] == "[esp+" and GetOpnd(eip,1) == reg: 491 | return True 492 | else: 493 | return False 494 | else: 495 | return False 496 | 497 | ''' 498 | get gadget from address 499 | arg : address, entrypoint address, gadget number 500 | return : True/False 501 | ''' 502 | def getGadget(self, eip, ep, flow): 503 | return dict({ 'flow' : flow,'entrypoint' :ep, 'addr': eip, 'gadget': idc.GetDisasm(eip), 'reg': GetOpnd(eip,0), 'const': get_operand_value(eip,1)}) 504 | 505 | ''' 506 | decode address from gadget 507 | arg : None 508 | return : None 509 | ''' 510 | def decodeAddress(self): 511 | idx = 0 512 | data = 0 513 | 514 | for gadget in self.gadget_list: 515 | for y in gadget: 516 | if y['flow'] == 2: 517 | data = next(GetDataList(data + y['const'],1, itemsize=4)) 518 | else: 519 | data = data + y['const'] 520 | 521 | data = data & 0xFFFFFFFF 522 | self.decoded_api.append(dict( \ 523 | {'original_addr' : gadget[0]['entrypoint'], \ 524 | 'original_assembly' : idc.GetDisasm(gadget[0]['entrypoint']), \ 525 | 'decoded_address' : data})) 526 | data = 0 527 | 528 | ''' 529 | DummyGadget 530 | 10062696 8B4424 30 MOV EAX,DWORD PTR SS:[ESP+30] 531 | 10069014 8D80 01000000 LEA EAX,DWORD PTR DS:[EAX+1] 532 | 10069028 894424 38 MOV DWORD PTR SS:[ESP+38],EAX 533 | ''' 534 | 535 | ''' 536 | def findDummyGadget(self,eip): 537 | idx = 0 538 | data = 0 539 | 540 | disass = idc.GetDisasm(eip) 541 | print ("[DEBUG] DUMMY VALUE FIND : {0} : {1}\t{2}\t{3}".format(hex(eip).rstrip('L'), disass, GetOpnd(eip,0), GetOpnd(eip,1))) 542 | 543 | if disass[:4] == "lea" and (GetOpnd(eip,0)[:2] == "e" and GetOpnd(eip,1)[:2] == "[e" and GetOpnd(eip,1)[:-3] == "+1]"): 544 | print ("[DEBUG] DUMMY VALUE FIND : {0} : {1}\t{2}\t{3}".format(hex(eip).lstrip('L'), disass, GetOpnd(eip,1), GetOpnd(eip,1))) 545 | return True 546 | else: 547 | return False 548 | ''' 549 | 550 | 551 | 552 | 553 | --------------------------------------------------------------------------------