├── README.md ├── RemoveVBAMacroPWD.py ├── function_plus.py ├── ida.idc ├── idaemu.py └── vb_DllFunctionCall.py /README.md: -------------------------------------------------------------------------------- 1 | # IDA_Scripts 2 | Các IDC, IDAPython scripts và plugins nhỏ, có ích trong quá trình dùng IDA. 3 | # ida.idc 4 | Backup file ida.idc cũ của bạn trong IDADIR\idc, copy file này vào. 5 | Bổ sung 6 cặp phím tắt: 6 | 1. Shift-U: convert to Unicode String tại current screen EA. 7 | 2. Alt+Shift+Up: Nhảy tới đầu hàm hiện tại. 8 | 3. Alt+Shift+Down: Nhảy tới cuối hàm hiện tại. 9 | 4. Ctrl+Alt+Up: Nhảy tới label ở kế trên. 10 | 5. Ctrl+Alt+Down: Nhảy tới label ở kế dưới. 11 | 6. Shift-P: name a pointer. 12 | 13 | 4 và 5 rất hửu ích khi chúng ta ở trong 1 vùng undefined code, data thiệt dài, nhảy tới label up và down để dễ define. 14 | 15 | 6 được dùng để auto name 1 label nếu label đó là 1 pointer. 16 | 17 | Vd ta có: 18 | 19 | off_xxxyyyzzz dd offset GetProcAddress 20 | 21 | Shift-P tại off_xxxyyyzzz ta sẽ có name là p_GetProcAdress. 22 | 23 | Tương tự cho các loại khác. Thử Shift-P thí xác sẽ thấy tác dụng của nó. Thuận tiện hơn cho chúng ta khi đọc mã ASM và mã decompile. 24 | 25 | File ida.idc chúng ta không cần phải load và run, mặc định IDA sẽ load và excute hàm main() cho chúng ta. 26 | 27 | Nên các hotkeys là có sẵn ngay. 28 | 29 | Nếu có tranh chấp với hotkeys khác bạn đang dùng (mình đã chọn và cân nhắc kỹ), các bạn vào Options - Shortcuts... và Show command palettes... 30 | 31 | Kiểm tra và edit lại với hotkeys bạn chọn trong file ida.idc này. 32 | 33 | # vb_DllFunctionCall.py 34 | Source gốc từ bài: https://blog.talosintelligence.com/discovering-dynamically-loaded-api-in/ 35 | 36 | Đã thay đổi các struct theo kết quả RE msvbvm60.dll với .dbg file. 37 | 38 | Fix vài lỗi và port hẳn qua IDA 7.x, IDAPython3 không BC95 39 | 40 | # idaemu 41 | Port lên Python 3 và IDA 7.x 42 | 43 | Source gốc: https://github.com/36hours/idaemu 44 | 45 | Copy idaemu.py vào %IDAUSRDIR%\python\3 46 | 47 | Xem hướng dẫn sử dung trong source hay từ link gốc trên 48 | 49 | # RemoveVBAMacroPWD.py 50 | Python 3 script (ported) để change, remove VBA password protected với các MS Office files 51 | 52 | # function_plus.py 53 | Run as IDAPython script file. 54 | 55 | Source gốc từ: https://gist.github.com/ox1111/2f2e6aee728d89062d03cf931fc2c720 56 | 57 | Đã port lên Python 3 và IDA 7.x. Đã test. 58 | 59 | Cung cấp tính năng mở rộng hơn so với "Functions" window của IDA, group các hàm, các child methods/functions... 60 | 61 | -------------------------------------------------------------------------------- /RemoveVBAMacroPWD.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0103,C0114,C0115,C0116 2 | 3 | import os 4 | import sys 5 | import string 6 | import struct 7 | import hashlib 8 | from random import randint 9 | import re 10 | import zipfile 11 | 12 | 13 | def PrintHash(Hash): 14 | if Hash == "": 15 | return "" 16 | HashStr = "" 17 | HashLen = len(Hash) 18 | for i in range(0, HashLen): 19 | A = (hex(ord(Hash[i])).lower())[2:] 20 | if len(A) == 1: 21 | A = ("0" + A) 22 | HashStr += A 23 | return HashStr 24 | 25 | 26 | def IsHexChar(CharX): 27 | return CharX in string.hexdigits 28 | 29 | 30 | def Hexify(contentX): 31 | if len(contentX) == 0: 32 | print("Input content is empty\r\n") 33 | return "" 34 | 35 | Second = False 36 | SkipNext = False 37 | FinalStr = "" 38 | NewStr = "" 39 | for X in contentX: 40 | if SkipNext: 41 | SkipNext = False 42 | continue 43 | if IsHexChar(X): 44 | SkipNext = True 45 | continue 46 | if not Second: 47 | NewStr += X 48 | Second = True 49 | else: 50 | NewStr += X 51 | FinalStr += "\\x" 52 | FinalStr += NewStr 53 | NewStr = "" 54 | Second = False 55 | 56 | XXX = "\"" + FinalStr + "\"" 57 | outputX = eval(XXX) 58 | return outputX 59 | 60 | 61 | def GetMyPrintables(): 62 | Printables = string.printable 63 | NewPrintables = "" 64 | lenPrintables = len(Printables) 65 | for i in range(0, lenPrintables): 66 | if ord(Printables[i]) >= 9 and ord(Printables[i]) <= 13: 67 | pass 68 | else: 69 | NewPrintables += Printables[i] 70 | return NewPrintables 71 | 72 | 73 | def GetHexDumpStr(XXX): 74 | Printables = GetMyPrintables() 75 | if XXX == "": 76 | return "" 77 | lenX = len(XXX) 78 | if lenX == 0: 79 | return "" 80 | NewConn = "" 81 | i = 0 82 | while i < lenX: 83 | XX = XXX[i] 84 | if Printables.find(XX) == -1: 85 | NewConn += "." 86 | else: 87 | NewConn += XX 88 | i = i + 1 89 | return NewConn 90 | 91 | 92 | def HexDump(Binary, _Size=2, Sep=" "): 93 | if Binary == "": 94 | return "" 95 | lenX = len(Binary) 96 | if lenX == 0: 97 | return "" 98 | i = 0 99 | FinalCon = "" 100 | RawCon = "" 101 | HexCon = "" 102 | StrCon = "" 103 | c = 0 104 | d = 0 105 | while i < lenX: 106 | X = Binary[i] 107 | RawCon += X 108 | XX = struct.unpack("B", X)[0] 109 | XXX = (hex(XX))[2:] 110 | if len(XXX) == 1: 111 | XXX = "0" + XXX 112 | HexCon += XXX 113 | c = c + 1 114 | HexCon += Sep 115 | if c == 8: 116 | HexCon += Sep 117 | c = 0 118 | d = d + 1 119 | if d == 16 or i == lenX - 1: 120 | StrCon = GetHexDumpStr(RawCon) 121 | if len(StrCon) < 16: 122 | ToAdd = 16 - len(StrCon) 123 | StrCon += (" "*ToAdd) 124 | RawCon = "" 125 | if len(HexCon) < 51: 126 | ToAdd = 51 - len(HexCon) 127 | HexCon += (" "*ToAdd) 128 | FinalCon += HexCon 129 | HexCon = "" 130 | FinalCon += " " 131 | FinalCon += StrCon 132 | StrCon = "" 133 | FinalCon += "\r\n" 134 | d = 0 135 | i = i + 1 136 | return FinalCon 137 | 138 | 139 | def GetNumberOfNulls(Input): 140 | if Input == "": 141 | return 0 142 | Num = 0 143 | InputLen = len(Input) 144 | for i in range(0, InputLen): 145 | if Input[i] == "\x00": 146 | Num += 1 147 | return Num 148 | 149 | 150 | # takes input of type string, returns input without nulls 151 | def EncodeNulls(Key): 152 | if Key == "": 153 | return "" 154 | Num = GetNumberOfNulls(Key) 155 | if Num == 0: 156 | return Key 157 | 158 | KK = "" 159 | KLen = len(Key) 160 | for i in range(0, KLen): 161 | if Key[i] == "\x00": 162 | KK += "\x01" 163 | else: 164 | KK += Key[i] 165 | return KK 166 | 167 | 168 | def GetGrbit(Input): 169 | KeyNoNulls = 0xFFFFFFFF 170 | Num = GetNumberOfNulls(Input) 171 | if Num == 0: 172 | return KeyNoNulls 173 | InputLen = len(Input) 174 | BinaryStr = "" 175 | for i in range(0, InputLen): 176 | if Input[i] == "\x00": 177 | BinaryStr += "0" 178 | else: 179 | BinaryStr += "1" 180 | KeyNoNulls = int(BinaryStr, 2) 181 | return KeyNoNulls 182 | 183 | 184 | def CreateHashStructure(Pwd, Key): 185 | Struc = "" 186 | Struc += "\xFF" 187 | sKey = struct.pack("L", Key) 188 | All = Pwd + sKey 189 | m = hashlib.sha1() 190 | m.update(All) 191 | Hash = m.digest() 192 | BothGrbits = GetGrbit(sKey + Hash) 193 | BothGrbits_ = struct.pack("L", BothGrbits) 194 | Struc += BothGrbits_[2] 195 | Struc += BothGrbits_[1] 196 | Struc += BothGrbits_[0] 197 | 198 | KeyNoNulls = EncodeNulls(sKey) 199 | Struc += KeyNoNulls 200 | PasswordHashNoNulls = EncodeNulls(Hash) 201 | Struc += PasswordHashNoNulls 202 | Struc += "\x00" 203 | if len(Struc) != 29: 204 | return "" 205 | return Struc 206 | 207 | 208 | def GetSeed(): 209 | return randint(0, 0xFF) 210 | 211 | 212 | def GetSeedX(Len): 213 | Seed = 0 214 | while True: 215 | Seed = randint(0, 0xFF) 216 | if (Seed & 6) / 2 == Len: 217 | break 218 | return Seed 219 | 220 | 221 | def GetIgnoredLengthGC(Len): 222 | if Len < 16: 223 | return 0 224 | return (Len - 16) / 2 225 | 226 | 227 | def GetIgnoredLengthCMG(Len): 228 | if Len < 22: 229 | return 0 230 | return (Len - 22) / 2 231 | 232 | 233 | def GetIgnoredLength(Len): 234 | if Len < 72: 235 | return 0 236 | return (Len - 72) / 2 237 | 238 | 239 | def EncodeGC(Value, Seed, DefaultIgnoredChar): 240 | GC = "" 241 | 242 | Data = struct.pack("B", Value) 243 | DataLen = len(Data) 244 | 245 | GC += chr(Seed) 246 | 247 | VersionEnc = Seed ^ 2 248 | GC += chr(VersionEnc) 249 | 250 | ProjId = "{00000000-0000-0000-0000-000000000000}" 251 | ProjIdLen = len(ProjId) 252 | ProjKey = 0 253 | for i in range(0, ProjIdLen): 254 | ProjKey += ord(ProjId[i]) 255 | ProjKey = ProjKey & 0xFF 256 | 257 | ProjKeyEnc = ProjKey ^ Seed 258 | GC += chr(ProjKeyEnc) 259 | 260 | UnencryptedByte1 = ProjKey 261 | EncryptedByte1 = ProjKeyEnc 262 | EncryptedByte2 = VersionEnc 263 | 264 | IgnoredLength = (Seed & 6) / 2 265 | 266 | ByteEnc = 0 267 | IgnoredEnc = "" 268 | for i in range(0, IgnoredLength): 269 | TempValue = DefaultIgnoredChar # Any Value 270 | ByteEnc = ((TempValue) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 271 | IgnoredEnc += chr(ByteEnc) 272 | EncryptedByte2 = EncryptedByte1 273 | EncryptedByte1 = ByteEnc 274 | UnencryptedByte1 = DefaultIgnoredChar # Any Value 275 | 276 | GC += IgnoredEnc 277 | 278 | DataLengthEnc = "" 279 | DataLen_ = struct.pack("L", DataLen) 280 | for i in range(0, 4): 281 | ByteEnc = ((ord(DataLen_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 282 | DataLengthEnc += chr(ByteEnc) 283 | EncryptedByte2 = EncryptedByte1 284 | EncryptedByte1 = ByteEnc 285 | UnencryptedByte1 = ord(DataLen_[i]) 286 | 287 | GC += DataLengthEnc 288 | 289 | # DataEnc 290 | DataEnc = "" 291 | Data_ = Data 292 | 293 | for i in range(0, DataLen): 294 | ByteEnc = ((ord(Data_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 295 | DataEnc += chr(ByteEnc) 296 | EncryptedByte2 = EncryptedByte1 297 | EncryptedByte1 = ByteEnc 298 | UnencryptedByte1 = ord(Data_[i]) 299 | 300 | GC += DataEnc 301 | return GC 302 | 303 | 304 | def EncodeCMG(Value, Seed, DefaultIgnoredChar): 305 | CMG = "" 306 | 307 | Data = struct.pack("L", Value) 308 | DataLen = len(Data) 309 | 310 | # print "Seed: " + hex(Seed) 311 | 312 | CMG += chr(Seed) 313 | 314 | VersionEnc = Seed ^ 2 315 | # print "VersionEnc: " + hex(VersionEnc) 316 | CMG += chr(VersionEnc) 317 | 318 | ProjId = "{00000000-0000-0000-0000-000000000000}" 319 | ProjIdLen = len(ProjId) 320 | ProjKey = 0 321 | for i in range(0, ProjIdLen): 322 | ProjKey += ord(ProjId[i]) 323 | ProjKey = ProjKey & 0xFF 324 | 325 | # print "ProjKey: " + hex(ProjKey) 326 | 327 | ProjKeyEnc = ProjKey ^ Seed 328 | # print "ProjKeyEnc: " + hex(ProjKeyEnc) 329 | CMG += chr(ProjKeyEnc) 330 | 331 | UnencryptedByte1 = ProjKey 332 | EncryptedByte1 = ProjKeyEnc 333 | EncryptedByte2 = VersionEnc 334 | 335 | IgnoredLength = (Seed & 6) / 2 336 | # print "IgnoredLength: " + str(IgnoredLength) 337 | 338 | # IgnoredEnc 339 | ByteEnc = 0 340 | IgnoredEnc = "" 341 | for i in range(0, IgnoredLength): 342 | TempValue = DefaultIgnoredChar # Any Value 343 | ByteEnc = ((TempValue) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 344 | IgnoredEnc += chr(ByteEnc) 345 | EncryptedByte2 = EncryptedByte1 346 | EncryptedByte1 = ByteEnc 347 | UnencryptedByte1 = DefaultIgnoredChar # Any Value 348 | 349 | CMG += IgnoredEnc 350 | 351 | # DataLengthEnc 352 | DataLengthEnc = "" 353 | DataLen_ = struct.pack("L", DataLen) 354 | for i in range(0, 4): 355 | ByteEnc = ((ord(DataLen_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 356 | DataLengthEnc += chr(ByteEnc) 357 | EncryptedByte2 = EncryptedByte1 358 | EncryptedByte1 = ByteEnc 359 | UnencryptedByte1 = ord(DataLen_[i]) 360 | 361 | CMG += DataLengthEnc 362 | 363 | # DataEnc 364 | DataEnc = "" 365 | Data_ = Data 366 | 367 | for i in range(0, DataLen): 368 | ByteEnc = ((ord(Data_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 369 | DataEnc += chr(ByteEnc) 370 | EncryptedByte2 = EncryptedByte1 371 | EncryptedByte1 = ByteEnc 372 | UnencryptedByte1 = ord(Data_[i]) 373 | 374 | CMG += DataEnc 375 | return CMG 376 | 377 | 378 | def RotateInteger(IntX): 379 | if IntX == 0: 380 | return 0 381 | h = hex(IntX)[2:] 382 | h_rev = h[::-1] 383 | return int("0x" + h_rev, 0x10) 384 | 385 | 386 | # return Key and Hash separated by : 387 | def DecodeHashStructure(Struc): 388 | if Struc == "": 389 | return "" 390 | if len(Struc) != 29: 391 | return "" 392 | 393 | Reserved = ord(Struc[0]) 394 | if Reserved != 0xFF: 395 | return "" 396 | 397 | A = Struc[1] 398 | B = Struc[2] 399 | C = Struc[3] 400 | 401 | BothGrbits = (struct.unpack("=L", "\x00" + C + B + A)[0]) >> 8 402 | 403 | GrbitKey = (ord(A) >> 4) & 0xF 404 | # GrbitKey_Again = (BothGrbits >> 20) & 0xF 405 | 406 | GrbitHashNull = BothGrbits & 0xFFFFF 407 | KeyNoNulls = Struc[4:8] 408 | Key = PrintHash(ApplyNulls(KeyNoNulls, GrbitKey)) 409 | 410 | HashNoNulls = Struc[8:28] 411 | Hash = PrintHash(ApplyNulls(HashNoNulls, GrbitHashNull)) 412 | 413 | Terminator = ord(Struc[28:29]) 414 | if Terminator != 0: 415 | return "" 416 | return Key + ":" + Hash 417 | 418 | 419 | # returns DPB string 420 | def Encode(Pwd, Key, Seed, DefaultIgnoredChar): 421 | Data = CreateHashStructure(Pwd, Key) 422 | if Data == "": 423 | return "" 424 | 425 | DataLen = len(Data) 426 | if DataLen != 29: 427 | return "" 428 | 429 | DPB = "" 430 | DPB += chr(Seed) 431 | 432 | VersionEnc = Seed ^ 2 433 | DPB += chr(VersionEnc) 434 | 435 | ProjId = "{00000000-0000-0000-0000-000000000000}" 436 | ProjIdLen = len(ProjId) 437 | ProjKey = 0 438 | for i in range(0, ProjIdLen): 439 | ProjKey += ord(ProjId[i]) 440 | ProjKey = ProjKey & 0xFF 441 | 442 | ProjKeyEnc = ProjKey ^ Seed 443 | DPB += chr(ProjKeyEnc) 444 | 445 | UnencryptedByte1 = ProjKey 446 | EncryptedByte1 = ProjKeyEnc 447 | EncryptedByte2 = VersionEnc 448 | 449 | IgnoredLength = (Seed & 6) / 2 450 | 451 | # IgnoredEnc 452 | ByteEnc = 0 453 | IgnoredEnc = "" 454 | for i in range(0, IgnoredLength): 455 | TempValue = DefaultIgnoredChar # Any Value 456 | ByteEnc = ((TempValue) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 457 | IgnoredEnc += chr(ByteEnc) 458 | EncryptedByte2 = EncryptedByte1 459 | EncryptedByte1 = ByteEnc 460 | UnencryptedByte1 = DefaultIgnoredChar # Any Value 461 | 462 | DPB += IgnoredEnc 463 | 464 | # DataLengthEnc 465 | DataLengthEnc = "" 466 | DataLen_ = struct.pack("L", DataLen) 467 | for i in range(0, 4): 468 | ByteEnc = ((ord(DataLen_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 469 | DataLengthEnc += chr(ByteEnc) 470 | EncryptedByte2 = EncryptedByte1 471 | EncryptedByte1 = ByteEnc 472 | UnencryptedByte1 = ord(DataLen_[i]) 473 | 474 | DPB += DataLengthEnc 475 | 476 | # DataEnc 477 | DataEnc = "" 478 | Data_ = Data 479 | 480 | for i in range(0, DataLen): 481 | ByteEnc = ((ord(Data_[i])) ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF 482 | DataEnc += chr(ByteEnc) 483 | EncryptedByte2 = EncryptedByte1 484 | EncryptedByte1 = ByteEnc 485 | UnencryptedByte1 = ord(Data_[i]) 486 | 487 | DPB += DataEnc 488 | 489 | return DPB 490 | 491 | 492 | def ApplyNulls(K, Bitmap): 493 | if K == "": 494 | return "" 495 | 496 | KLen = len(K) 497 | KK = "" 498 | ii = 0 499 | for i in range(KLen, 0, -1): 500 | Y = 1 << (i-1) 501 | if Bitmap & Y == 0: 502 | KK += "\x00" 503 | else: 504 | KK += K[ii] 505 | ii += 1 506 | return KK 507 | 508 | # returns Password Hash Data Structure 509 | 510 | 511 | def Decode(In, InLen): 512 | if In == "" or InLen == 0 or len(In) != InLen: 513 | return "" 514 | 515 | hxIN = Hexify(In) 516 | if hxIN == "": 517 | return "" 518 | hxLength = len(hxIN) 519 | if hxLength < 3: 520 | return "" 521 | 522 | Seed = ord(hxIN[0]) 523 | print("Seed: " + hex(Seed)) 524 | 525 | VersionEnc = ord(hxIN[1]) 526 | print("VersionEnc: " + hex(VersionEnc)) 527 | Version = Seed ^ VersionEnc 528 | print("Version: " + hex(Version)) 529 | if Version != 2: 530 | print("Invalid version") 531 | return "" 532 | 533 | ProjKeyEnc = ord(hxIN[2]) 534 | print("ProjKeyEnc: " + hex(ProjKeyEnc)) 535 | ProjKey = Seed ^ ProjKeyEnc 536 | print("Project Key: " + hex(ProjKey)) 537 | 538 | UnencryptedByte1 = ProjKey 539 | EncryptedByte1 = ProjKeyEnc 540 | EncryptedByte2 = VersionEnc 541 | 542 | IgnoredLength = (Seed & 6) / 2 543 | print("IgnoredLength: " + str(IgnoredLength)) 544 | if IgnoredLength + 3 > hxLength: 545 | return "" 546 | Off = 3 547 | 548 | IgnoredEnc = hxIN[Off:Off+IgnoredLength] 549 | Off += IgnoredLength 550 | 551 | AllIgnored = [] 552 | for X in IgnoredEnc: 553 | Byte = ((ord(X)) ^ ((EncryptedByte2 + UnencryptedByte1) & 0xFF)) 554 | EncryptedByte2 = EncryptedByte1 555 | EncryptedByte1 = ord(X) 556 | UnencryptedByte1 = Byte 557 | AllIgnored.append(Byte) 558 | print("Ignored: " + str(AllIgnored)) 559 | 560 | Left = hxLength - Off 561 | # print Left 562 | if Left < 4: 563 | return "" 564 | 565 | Rest = hxIN[Off:] 566 | DataLengthEnc = Rest[0:4] 567 | DataEnc = Rest[4:] 568 | 569 | DataLength = 0 570 | 571 | ByteIndex = 0 572 | 573 | for ByteEnc in DataLengthEnc: 574 | Byte = ((ord(ByteEnc)) ^ ((EncryptedByte2 + UnencryptedByte1) & 0xFF)) 575 | TempValue = int(pow(256, ByteIndex)) 576 | # print TempValue 577 | # print type(TempValue) 578 | TempValue = TempValue * Byte 579 | DataLength += TempValue 580 | EncryptedByte2 = EncryptedByte1 581 | EncryptedByte1 = ord(ByteEnc) & 0xFF 582 | UnencryptedByte1 = Byte 583 | ByteIndex = ByteIndex + 1 584 | 585 | # print hex(DataLength) 586 | if DataLength != len(DataEnc): 587 | print("Invalid data length") 588 | return "" 589 | Data = "" 590 | for ByteEnc in DataEnc: 591 | Byte = ((ord(ByteEnc)) ^ ((EncryptedByte2 + UnencryptedByte1) & 0xFF)) 592 | Data += chr(Byte) 593 | EncryptedByte2 = EncryptedByte1 594 | EncryptedByte1 = ord(ByteEnc) & 0xFF 595 | UnencryptedByte1 = Byte 596 | return Data 597 | 598 | 599 | # Extract salt 600 | def GetKey(Data): 601 | if Data == "": 602 | return "" 603 | Both_x = Data.split(":") 604 | if len(Both_x) == 0: 605 | return "" 606 | return Both_x[0] 607 | 608 | 609 | # Extract Password Hash 610 | def GetPasswordHash(Data): 611 | if Data == "": 612 | return "" 613 | Both_x = Data.split(":") 614 | if len(Both_x) < 2: 615 | return "" 616 | return Both_x[1] 617 | 618 | 619 | # The chosen character for Ignored data 620 | def GetIgnoredCharacter(In, InLen): 621 | if In == "" or InLen == 0 or len(In) != InLen: 622 | return 0 623 | 624 | hxIN = Hexify(In) 625 | if hxIN == "": 626 | return 0 627 | hxLength = len(hxIN) 628 | if hxLength < 3: 629 | return 0 630 | 631 | Seed = ord(hxIN[0]) 632 | 633 | VersionEnc = ord(hxIN[1]) 634 | # Version = Seed ^ VersionEnc 635 | 636 | ProjKeyEnc = ord(hxIN[2]) 637 | ProjKey = Seed ^ ProjKeyEnc 638 | 639 | UnencryptedByte1 = ProjKey 640 | EncryptedByte1 = ProjKeyEnc 641 | EncryptedByte2 = VersionEnc 642 | 643 | IgnoredLength = (Seed & 6) / 2 644 | if IgnoredLength + 3 > hxLength: 645 | return 0 646 | 647 | IgnoredEnc = hxIN[3:3+IgnoredLength] 648 | 649 | AllIgnored = [] 650 | for X in IgnoredEnc: 651 | Byte = ((ord(X)) ^ ((EncryptedByte2 + UnencryptedByte1) & 0xFF)) 652 | EncryptedByte2 = EncryptedByte1 653 | EncryptedByte1 = ord(X) 654 | UnencryptedByte1 = Byte 655 | AllIgnored.append(Byte) 656 | 657 | if len(AllIgnored) > 0: 658 | return AllIgnored[0] 659 | 660 | return 0 661 | 662 | 663 | def CreateZip(inD, Ext="docx"): 664 | if not os.path.exists(inD): 665 | print("Directory does not exist\r\n") 666 | return False 667 | 668 | if os.path.isfile(inD): 669 | print("Input is not a directory\r\n") 670 | return False 671 | 672 | with zipfile.ZipFile(inD + "." + Ext, 'w', zipfile.ZIP_DEFLATED) as hZip: 673 | for Dir, _SubDirs, Files in os.walk(inD): 674 | for FileX in Files: 675 | fFile = os.path.join(Dir, FileX) 676 | with open(fFile, "rb") as fIn: 677 | fCon = fIn.read() 678 | 679 | NewfFile = fFile[len(inD)+1:] 680 | hZip.writestr(NewfFile, fCon) 681 | 682 | return True 683 | 684 | 685 | def ExtractOffice2007(inFile): 686 | if inFile == "": 687 | return 0 688 | filenameX, _fileextX = os.path.splitext(inFile) 689 | try: 690 | with zipfile.ZipFile(inFile) as ZipZ: 691 | ZipZ.extractall(filenameX) 692 | print("Zip file was successfully extracted to " + "\\" + filenameX + "\\") 693 | return 1 694 | except RuntimeError: 695 | print("Zip file is corrupt") 696 | return 0 697 | 698 | 699 | def GetOffice2007VbaBin(inFile): 700 | if inFile == "": 701 | return "" 702 | ret = ExtractOffice2007(inFile) 703 | if ret == 0: 704 | return "" 705 | filenameX, _fileextX = os.path.splitext(inFile) 706 | dirX = filenameX 707 | for fYf in os.listdir(dirX): 708 | tgt = dirX + "\\" + fYf 709 | if not os.path.isfile(tgt) and os.path.exists(tgt + "\\vbaproject.bin"): 710 | return tgt + "\\vbaproject.bin" 711 | return "" 712 | 713 | 714 | def IsOffice2007(inFile): 715 | if inFile == "": 716 | return False 717 | 718 | if os.path.getsize(inFile) < 4: 719 | return False 720 | 721 | with open(inFile, "rb") as fInZ: 722 | fConZ = fInZ.read(4) 723 | 724 | if fConZ[0] != "P" or fConZ[1] != "K": 725 | return False 726 | 727 | mjv = fConZ[2] 728 | mnv = fConZ[3] 729 | 730 | v1 = (mjv == "\x03" and mnv == "\x04") 731 | v2 = (mjv == "\x05" and mnv == "\x06") 732 | v3 = (mjv == "\x07" and mnv == "\x08") 733 | return v1 and v2 and v3 734 | 735 | 736 | def RemoveProtectionAndUpdatePassword(inDoc, NewPass): 737 | if inDoc == "": 738 | return "" 739 | 740 | ProjIds = re.findall( 741 | b"ID=\"\{([a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12})\}\"", inDoc, re.I) 742 | NumProjIds = len(ProjIds) 743 | ProjIdFound = False 744 | if NumProjIds != 0: 745 | for ProjId in ProjIds: 746 | if ProjId == "00000000-0000-0000-0000-000000000000": 747 | ProjIdFound = True 748 | break 749 | if not ProjIdFound: 750 | print("Input file does not have a password-protected macro.\r\n") 751 | return "" 752 | 753 | DPB = re.findall("DPB=\"([0-9a-zA-Z]{72,})\"", inDoc, re.I) 754 | CMG = re.findall("CMG=\"([0-9a-zA-Z]{22,})\"", inDoc, re.I) 755 | GC = re.findall("GC=\"([0-9a-zA-Z]{16,})\"", inDoc, re.I) 756 | 757 | if len(DPB) == 0: 758 | print("DPB value not found\r\n") 759 | return "" 760 | 761 | if len(CMG) == 0: 762 | print("CMG value not found\r\n") 763 | return "" 764 | 765 | if len(GC) == 0: 766 | print("GC value not found\r\n") 767 | return "" 768 | 769 | iDPB = DPB[0] 770 | iCMG = CMG[0] 771 | iGC = GC[0] 772 | 773 | print("## DPB: " + iDPB) 774 | hDPB = Decode(iDPB, len(iDPB)) 775 | print(HexDump(hDPB)) 776 | KeyNHash = DecodeHashStructure(hDPB) 777 | Key = GetKey(KeyNHash) 778 | print("Key: " + Key) 779 | Hash = GetPasswordHash(KeyNHash) 780 | print("Hash: " + Hash) 781 | 782 | print("\r\n## CMG: " + iCMG) 783 | hCMG = Decode(iCMG, len(iCMG)) 784 | print(HexDump(hCMG)) 785 | 786 | print("\r\n## GC: " + iGC) 787 | hGC = Decode(iGC, len(iGC)) 788 | print(HexDump(hGC)) 789 | 790 | IgnoredChar = GetIgnoredCharacter(iDPB, len(iDPB)) 791 | 792 | print("Using Password: " + NewPass) 793 | 794 | # ---------- DPB 795 | DPB = re.findall("DPB=\"([0-9a-zA-Z]{72,})\"", inDoc, re.I) 796 | All = DPB[0] 797 | if All == "": 798 | print("Found DPB value is empty\r\n") 799 | return "" 800 | 801 | print("\r\n\r\n") 802 | print("Old DPB: " + All) 803 | DPBLen = len(All) 804 | IgnoredLen = GetIgnoredLength(DPBLen) 805 | Seed = GetSeedX(IgnoredLen) 806 | key = randint(0, 0xFFFFFFFF) 807 | 808 | PDB = Encode(NewPass, key, Seed, IgnoredChar) 809 | sDPB = PrintHash(PDB).upper() 810 | print("New DPB: " + sDPB) 811 | 812 | inDocX = re.sub("DPB=\"([0-9a-zA-Z]{72,})\"", "DPB=\""+sDPB+"\"", inDoc, re.I) 813 | # ---------- CMG 814 | DPB = re.findall("CMG=\"([0-9a-zA-Z]{22,})\"", inDoc, re.I) 815 | All = DPB[0] 816 | if All == "": 817 | print("Found CMG value is empty\r\n") 818 | return "" 819 | 820 | print("Old CMG: " + All) 821 | CMGLen = len(All) 822 | IgnoredLen = GetIgnoredLengthCMG(CMGLen) 823 | Seed = GetSeedX(IgnoredLen) 824 | 825 | CMG = EncodeCMG(0, Seed, IgnoredChar) 826 | sCMG = PrintHash(CMG).upper() 827 | print("New CMG: " + sCMG) 828 | 829 | inDocXX = re.sub("CMG=\"([0-9a-zA-Z]{22,})\"", "CMG=\""+sCMG+"\"", inDocX, re.I) 830 | # ---------------- GC 831 | CMG = re.findall("GC=\"([0-9a-zA-Z]{16,})\"", inDoc, re.I) 832 | All = CMG[0] 833 | if All == "": 834 | print("Found GC value is empty\r\n") 835 | return "" 836 | 837 | print("Old GC: " + All) 838 | GCLen = len(All) 839 | IgnoredLen = GetIgnoredLengthGC(GCLen) 840 | Seed = GetSeedX(IgnoredLen) 841 | 842 | GC = EncodeGC(0xFF, Seed, IgnoredChar) 843 | sGC = PrintHash(GC).upper() 844 | print("New GC: " + sGC) 845 | 846 | inDocXXX = re.sub("GC=\"([0-9a-zA-Z]{16,})\"", "GC=\""+sGC+"\"", inDocXX, re.I) 847 | 848 | return inDocXXX 849 | 850 | 851 | def main(): 852 | argC = len(sys.argv) 853 | if argC != 3: 854 | print("Usage: \r\n") 855 | print("RemoveVBAMacroPWD.py input.doc NewPassword\r\n") 856 | print("RemoveVBAMacroPWD.py input.docx NewPassword\r\n") 857 | sys.exit(-1) 858 | 859 | inF = sys.argv[1] 860 | NewPassX = (sys.argv[2]).rstrip().lstrip() 861 | 862 | OrigInf = inF 863 | Office2007 = False 864 | VbaBinFile = "" 865 | 866 | if not os.path.exists(inF): 867 | print("Input file does not exist\r\n") 868 | sys.exit(-2) 869 | 870 | if IsOffice2007(inF): 871 | Office2007 = True 872 | 873 | if Office2007: 874 | VbaBinFile = GetOffice2007VbaBin(inF) # Will also extract zip contents 875 | if VbaBinFile == "": 876 | print("VbaProject.bin was not found in input Office2007 file\r\n") 877 | sys.exit(-3) 878 | inF = VbaBinFile 879 | 880 | with open(inF, "rb") as fDoc: 881 | inDocX = fDoc.read() 882 | 883 | NewContent = RemoveProtectionAndUpdatePassword(inDocX, NewPassX) 884 | if NewContent == "": 885 | print("Error removing protection\r\n") 886 | sys.exit(-4) 887 | 888 | fileN, fileExt = os.path.splitext(inF) 889 | NewFileName = fileN + "_" + fileExt 890 | with open(NewFileName, "wb") as fOut: 891 | fOut.write(NewContent) 892 | 893 | if Office2007: 894 | os.remove(inF) 895 | OldFileName = NewFileName 896 | NewFileName = inF 897 | os.rename(OldFileName, NewFileName) 898 | OrigInfX, OrigInfExtX = os.path.splitext(OrigInf) 899 | 900 | NewExt = "doc" 901 | if OrigInfExtX[1:3].lower() == "xl": 902 | NewExt = "xls" 903 | 904 | ret = CreateZip(OrigInfX, NewExt) 905 | if ret: 906 | NewFileName = (OrigInfX + "_." + NewExt) 907 | try: 908 | os.rename((OrigInfX + "." + NewExt), (OrigInfX + "_." + NewExt)) 909 | except IOError: 910 | print("Error: file already exists") 911 | sys.exit(-4) 912 | else: 913 | print("Error creating new Office2007 file\r\n") 914 | sys.exit(-5) 915 | 916 | print("New unprotected file written to " + NewFileName) 917 | 918 | 919 | if __name__ == "__main__": 920 | main() 921 | -------------------------------------------------------------------------------- /function_plus.py: -------------------------------------------------------------------------------- 1 | # 2 | # pylint: disable=C0103,C0111 3 | # -*- coding: utf-8 -*- 4 | # ida >= 7.2 5 | # 6 | 7 | ''' 8 | Functions+ IDA Pro plugin -- better version (imho) of functions window. Splits 9 | functions names and groups by namespaces. 10 | ''' 11 | 12 | import re 13 | from collections import OrderedDict 14 | 15 | import idaapi 16 | import idautils 17 | 18 | from idaapi import PluginForm 19 | from PyQt5 import QtWidgets, QtGui 20 | 21 | import idc 22 | 23 | __author__ = 'Arthur Gerkis - HTC' 24 | __version__ = '0.1.0' 25 | 26 | 27 | class FunctionState: 28 | '''Holds the state of the current function.''' 29 | 30 | def __init__(self): 31 | self._args = '' 32 | self._flags = 0 33 | self._addr = 0 34 | 35 | @property 36 | def args(self): 37 | '''Returns args property.''' 38 | return self._args 39 | 40 | @args.setter 41 | def args(self, value): 42 | '''Sets args property.''' 43 | self._args = value 44 | 45 | @property 46 | def flags(self): 47 | '''Returns flags property.''' 48 | return self._flags 49 | 50 | @flags.setter 51 | def flags(self, value): 52 | '''Sets flags property.''' 53 | self._flags = value 54 | 55 | @property 56 | def addr(self): 57 | '''Returns addr property.''' 58 | return self._addr 59 | 60 | @addr.setter 61 | def addr(self, value): 62 | '''Sets addr property.''' 63 | self._addr = value 64 | 65 | 66 | class FunctionData: 67 | '''Holds the data of the function.''' 68 | 69 | def __init__(self, state): 70 | self._args = state.args 71 | self._flags = state.flags 72 | self._addr = state.addr 73 | 74 | @property 75 | def args(self): 76 | '''Returns args property.''' 77 | return self._args 78 | 79 | @property 80 | def flags(self): 81 | '''Returns flags property.''' 82 | return self._flags 83 | 84 | @property 85 | def addr(self): 86 | '''Returns ea property.''' 87 | return self._addr 88 | 89 | 90 | class Cols: 91 | '''Class which is responsible for handling cols.''' 92 | 93 | def __init__(self): 94 | self.addr = None 95 | self.flags = None 96 | self.names = [ 97 | 'Function name', 'Address', 'Segment', 'Length', 'Locals', 98 | 'Arguments', 'R', 'F', 'L', 'S', 'B', 'T', '=' 99 | ] 100 | self.handlers = { 101 | 0: lambda: None, 102 | 1: lambda: self.ptr().format(self.addr), 103 | 2: lambda: f'{idc.get_segm_name(self.addr)}', 104 | 3: lambda: self.halfptr().format(idc.get_func_attr( 105 | self.addr, idc.FUNCATTR_END) - self.addr), 106 | 4: lambda: self.set_if_true(idc.get_func_attr( 107 | self.addr, idc.FUNCATTR_FRSIZE)), 108 | 5: lambda: self.set_if_true(idc.get_func_attr( 109 | self.addr, idc.FUNCATTR_ARGSIZE)), 110 | 6: lambda: self.is_true(not self.flags & idc.FUNC_NORET, 'R'), 111 | 7: lambda: self.is_true(self.flags & idc.FUNC_FAR, 'F'), 112 | 8: lambda: self.is_true(self.flags & idc.FUNC_LIB, 'L'), 113 | 9: lambda: self.is_true(self.flags & idc.FUNC_STATIC, 'S'), 114 | 10: lambda: self.is_true(self.flags & idc.FUNC_FRAME, 'B'), 115 | 11: lambda: self.is_true(idc.get_type(self.addr), 'T'), 116 | 12: lambda: self.is_true(self.flags & idc.FUNC_BOTTOMBP, '=') 117 | } 118 | 119 | def set_data(self, addr, flags): 120 | '''Sets data actual to current function.''' 121 | self.addr = addr 122 | self.flags = flags 123 | 124 | def col(self, index): 125 | '''Gets the data according to requested col index.''' 126 | return self.handlers[index]() 127 | 128 | def ptr(self): 129 | '''Returns ptr for format.''' 130 | if idc.__EA64__: 131 | return '{:16x}' 132 | return '{:08x}' 133 | 134 | def halfptr(self): 135 | '''Returns half ptr for format.''' 136 | if idc.__EA64__: 137 | return '{:08x}' 138 | return '{:04x}' 139 | 140 | def is_true(self, flag, char): 141 | '''Wrapper to conform IDA default UI view.''' 142 | if flag: 143 | return char 144 | return '.' 145 | 146 | def set_if_true(self, value): 147 | '''Wrapper to conform IDA default UI view.''' 148 | if value: 149 | return self.halfptr().format(value) 150 | return '' 151 | 152 | 153 | class FunctionsTree: 154 | '''Builds tree of functions with all relevant information.''' 155 | 156 | def __init__(self): 157 | return 158 | 159 | def get(self): 160 | '''Returns functions tree.''' 161 | functions_list = self.get_list_of_functions() 162 | functions_tree = self.build_functions_tree(functions_list) 163 | return functions_tree 164 | 165 | def get_list_of_functions(self): 166 | '''Get all functions list.''' 167 | 168 | seg_ea = idc.get_inf_attr(idc.INF_START_EA) 169 | functions_list = {} 170 | for func_ea in idautils.Functions(idc.get_segm_start(seg_ea), idc.get_segm_end(seg_ea)): 171 | function_name = self.maybe_demangle(idc.get_func_name(func_ea)) 172 | functions_list[function_name] = func_ea 173 | return functions_list 174 | 175 | def build_functions_tree(self, functions_list): 176 | '''Build tree of functions.''' 177 | 178 | func_state = FunctionState() 179 | functions_tree = OrderedDict() 180 | for function_name in sorted(functions_list): 181 | func_state.args = '' 182 | func_state.addr = functions_list[function_name] 183 | func_state.flags = idc.get_func_flags(func_state.addr) 184 | chunks = self.get_chunks(function_name, func_state) 185 | self.maybe_push(chunks, functions_tree, func_state) 186 | return functions_tree 187 | 188 | def maybe_push(self, chunks, functions_tree, func_state): 189 | '''Add new function name or properties to the tree.''' 190 | 191 | name = chunks.pop(0) 192 | if not name: 193 | return 194 | 195 | # If this is the last (or one) chunk 196 | if not chunks: 197 | functions_tree[name + func_state.args] = FunctionData(func_state) 198 | return 199 | 200 | # If this is a new namespace, create a tree 201 | if name not in functions_tree: 202 | functions_tree[name] = OrderedDict() 203 | 204 | return self.maybe_push(chunks, functions_tree[name], func_state) 205 | 206 | def get_chunks(self, func_string, func_state): 207 | '''Splits function name by namespaces.''' 208 | 209 | new_chunks = [] 210 | matches = re.match(r'(.*?)(?:|\((.*?)\))$', func_string) 211 | if not matches: 212 | return [] 213 | 214 | args = '' 215 | if matches.group(2): 216 | args = f"({matches.group(2)})" 217 | func_state.args = args 218 | 219 | chunks = list(matches.group(1)) 220 | if chunks[0] == '`': 221 | return [matches.group(1)] 222 | 223 | open_left_tpl = 0 224 | tmp_chunk = '' 225 | for chunk in chunks: 226 | if chunk == ':' and open_left_tpl == 0: 227 | if tmp_chunk: 228 | new_chunks.append(tmp_chunk) 229 | tmp_chunk = '' 230 | continue 231 | if chunk == '<': 232 | open_left_tpl += 1 233 | if chunk == '>': 234 | open_left_tpl -= 1 235 | tmp_chunk += chunk 236 | new_chunks.append(tmp_chunk) 237 | return new_chunks 238 | 239 | def maybe_demangle(self, function_name): 240 | '''Maybe demangle name.''' 241 | 242 | if function_name.find('@') != -1: 243 | function_name = self.demangle(function_name) 244 | return function_name 245 | 246 | def demangle(self, name): 247 | '''Demangle name.''' 248 | 249 | mask = idc.get_inf_attr(idc.INF_SHORT_DN) 250 | demangled = idc.demangle_name(name, mask) 251 | if demangled is None: 252 | return name 253 | return demangled 254 | 255 | 256 | class FunctionsPlus(PluginForm): 257 | '''Functions+ plugin.''' 258 | 259 | def __init__(self): 260 | super(FunctionsPlus, self).__init__() 261 | 262 | if idc.get_inf_attr(idc.INF_PROCNAME).lower() != 'metapc': 263 | print("Functions+ warning: not tested in this configuration") 264 | self.cols = Cols() 265 | self.tree = None 266 | self.parent = None 267 | 268 | def populate_tree(self): 269 | '''Populate functions tree.''' 270 | self.tree.clear() 271 | self.build_tree(FunctionsTree().get(), self.tree) 272 | return 273 | 274 | def build_tree(self, function_tree, root): 275 | '''Build Qt Widget tree.''' 276 | 277 | if not function_tree: 278 | return 279 | 280 | if isinstance(function_tree, FunctionData): 281 | flags = int(function_tree.flags) 282 | addr = function_tree.addr 283 | 284 | self.cols.set_data(addr, flags) 285 | 286 | for index in range(0, len(self.cols.names)): 287 | if index > 0: 288 | root.setText(index, self.cols.col(index)) 289 | if flags & idc.FUNC_THUNK: 290 | root.setBackground(index, QtGui.QColor("violet")) 291 | root.setBackground(index, QtGui.QColor("violet")) 292 | if flags & idc.FUNC_LIB: 293 | root.setBackground(index, QtGui.QColor("cyan")) 294 | root.setBackground(index, QtGui.QColor("cyan")) 295 | return 296 | 297 | for name, tree in sorted(function_tree.items()): 298 | func_item = QtWidgets.QTreeWidgetItem(root) 299 | if not isinstance(tree, FunctionData): 300 | word = 'items' 301 | tree_keys_len = len(list(tree.keys())) 302 | if tree_keys_len % 10 == 1: 303 | word = 'item' 304 | name = f"{name} ({tree_keys_len} {word})" 305 | func_item.setText(0, name) 306 | self.build_tree(tree, func_item) 307 | 308 | def dblclick(self, item): 309 | '''Handle double click event.''' 310 | if item.text(1): 311 | idaapi.jumpto(int(item.text(1), 16)) 312 | 313 | def OnCreate(self, form): 314 | '''Called when the plugin form is created''' 315 | 316 | self.parent = self.FormToPyQtWidget(form) 317 | 318 | self.tree = QtWidgets.QTreeWidget() 319 | self.tree.setColumnCount(len(self.cols.names)) 320 | self.tree.setHeaderLabels(self.cols.names) 321 | self.tree.itemDoubleClicked.connect(self.dblclick) 322 | 323 | layout = QtWidgets.QVBoxLayout() 324 | layout.addWidget(self.tree) 325 | layout.setSpacing(0) 326 | layout.setContentsMargins(0, 0, 0, 0) 327 | 328 | self.populate_tree() 329 | 330 | self.tree.setColumnWidth(0, 512) 331 | for index in range(6, len(self.cols.names)): 332 | self.tree.setColumnWidth(index, 32) 333 | self.tree.setAlternatingRowColors(True) 334 | 335 | self.parent.setLayout(layout) 336 | 337 | def OnClose(self, form): 338 | '''Called when the plugin form is closed.''' 339 | del self 340 | 341 | def Show(self, caption, options=0): 342 | '''Creates the form is not created or focuses it if it was.''' 343 | return PluginForm.Show(self, caption, options=PluginForm.WOPN_PERSIST) 344 | 345 | 346 | def main(): 347 | '''Main entry.''' 348 | funp = FunctionsPlus() 349 | funp.Show("Functions+") 350 | 351 | 352 | if __name__ == '__main__': 353 | main() 354 | -------------------------------------------------------------------------------- /ida.idc: -------------------------------------------------------------------------------- 1 | // 2 | // This file is automatically executed when IDA is started. 3 | // You can define your own IDC functions and assign hotkeys to them. 4 | // 5 | // You may add your frequently used functions here and they will 6 | // be always available. 7 | // 8 | // You can customize the initial behaviour of IDA without modifying 9 | // this file but by putting your logic in 10 | // %appdata%/Hex-Rays/IDA Pro/idauser.idc (Windows) 11 | // $HOME/.idapro/idauser.idc (Linux & Mac) 12 | // Define the function called user_main() there and it will be called. 13 | // 14 | #include 15 | #softinclude // please define user_main() in this file. 16 | 17 | //----------------------------------------------------------------------- 18 | // A singleton class for managing breakpoints 19 | class BreakpointManager 20 | { 21 | // Return the breakpoint quantity 22 | Count() 23 | { 24 | return get_bpt_qty(); 25 | } 26 | 27 | // Return a breakpoint object 28 | Get(index) 29 | { 30 | auto count = this.Count(); 31 | if (index >= count) 32 | { 33 | throw sprintf("Invalid breakpoint index %d (0..%d expected).", index, count); 34 | } 35 | return Breakpoint(index); 36 | } 37 | 38 | // Add a breakpoint 39 | Add(bpt) 40 | { 41 | return bpt._add(); 42 | } 43 | 44 | // Add a breakpoint to group 45 | AddToGroup(bpt, group_name) 46 | { 47 | return bpt._add(group_name); 48 | } 49 | 50 | // Delete a breakpoint 51 | Delete(bpt) 52 | { 53 | return bpt._delete(); 54 | } 55 | 56 | // Update a breakpoint 57 | // Note: Location attributes cannot be updated, recreation of the 58 | // breakpoint is required 59 | Update(bpt) 60 | { 61 | return bpt._update(); 62 | } 63 | 64 | // Find a breakpoint using its location attributes and 65 | // returns a new breakpoint object or 0 66 | Find(bpt) 67 | { 68 | return bpt._find(); 69 | } 70 | } 71 | 72 | // Breakpoint manager class instance 73 | extern Breakpoints; 74 | 75 | //----------------------------------------------------------------------- 76 | // Get signed extended 16-bit value 77 | static SWord(ea) 78 | { 79 | auto v = get_wide_word(ea); 80 | if (v & 0x8000) 81 | { 82 | v = v | ~0xFFFF; 83 | } 84 | return v; 85 | } 86 | // 87 | // Appcall functions 88 | //----------------------------------------------------------------------- 89 | extern last_cmd, last_opt; 90 | 91 | static Appcall_Here() 92 | { 93 | auto t, h; 94 | 95 | t = parse_decl("void x(void);", 0); 96 | h = get_screen_ea; 97 | 98 | last_opt = get_inf_attr(INF_APPCALL_OPTIONS); 99 | set_inf_attr(INF_APPCALL_OPTIONS, APPCALL_MANUAL); 100 | dbg_appcall(get_first_seg(), t); 101 | set_inf_attr(INF_APPCALL_OPTIONS, last_opt); 102 | #ifdef Eip 103 | Eip = h; 104 | #endif 105 | } 106 | 107 | static Appcall_Start() 108 | { 109 | auto s = ask_str(last_cmd, 0, "Enter Appcall"); 110 | if (s == "") 111 | { 112 | return; 113 | } 114 | last_cmd = s; 115 | last_opt = get_inf_attr(INF_APPCALL_OPTIONS); 116 | set_inf_attr(INF_APPCALL_OPTIONS, APPCALL_MANUAL); 117 | msg(">%s<", s); 118 | eval(s); 119 | set_inf_attr(INF_APPCALL_OPTIONS, last_opt); 120 | } 121 | 122 | // 123 | // HTC - begin 124 | //----------------------------------------------------------------------- 125 | static CreateUnicodeString() 126 | { 127 | auto old_type = 0; 128 | 129 | // Check user selected a range 130 | auto start_sel = read_selection_start(); 131 | auto end_sel = read_selection_end(); 132 | if ((start_sel != BADADDR) && (end_sel != BADADDR)) 133 | { 134 | old_type = get_inf_attr(INF_STRTYPE); 135 | set_inf_attr(INF_STRTYPE, STRTYPE_C_16); 136 | create_strlit(start_sel, end_sel - start_sel); 137 | set_inf_attr(INF_STRTYPE, old_type); 138 | } 139 | else 140 | { 141 | auto w = 0; 142 | auto ea = get_screen_ea(); 143 | auto endEA = ea; 144 | 145 | // User not selected a range, begin scan 0x00 0x00 byte from current screen ea 146 | while (endEA != BADADDR) 147 | { 148 | w = get_wide_word(endEA); 149 | if (0 == w) 150 | { 151 | break; 152 | } 153 | else 154 | { 155 | endEA = endEA + 2; 156 | } 157 | } 158 | 159 | if (endEA > ea) 160 | { 161 | del_items(ea, DELIT_DELNAMES, endEA - ea + 2); 162 | 163 | old_type = get_inf_attr(INF_STRTYPE); 164 | set_inf_attr(INF_STRTYPE, STRTYPE_C_16); 165 | create_strlit(ea, endEA - ea + 2); 166 | set_inf_attr(INF_STRTYPE, old_type); 167 | } 168 | } 169 | } 170 | 171 | //----------------------------------------------------------------------- 172 | static JumpToPrevLabel() 173 | { 174 | auto f = 0; 175 | auto ea = prev_not_tail(get_screen_ea()); 176 | while (BADADDR != ea) 177 | { 178 | f = get_full_flags(ea); 179 | if (f & FF_ANYNAME) 180 | { 181 | break; 182 | } 183 | 184 | ea = prev_not_tail(ea); 185 | } 186 | 187 | // Found an ea ? 188 | if (BADADDR != ea) 189 | { 190 | jumpto(ea); 191 | } 192 | else 193 | { 194 | msg("Could not find prev label\n"); 195 | } 196 | } 197 | 198 | //----------------------------------------------------------------------- 199 | static JumpToNextLabel() 200 | { 201 | auto f = 0; 202 | auto ea = next_not_tail(get_screen_ea()); 203 | while (BADADDR != ea) 204 | { 205 | f = get_full_flags(ea); 206 | if (f & FF_ANYNAME) 207 | { 208 | break; 209 | } 210 | 211 | ea = next_not_tail(ea); 212 | } 213 | 214 | // Found an ea ? 215 | if (BADADDR != ea) 216 | { 217 | jumpto(ea); 218 | } 219 | else 220 | { 221 | msg("Could not find next label\n"); 222 | } 223 | } 224 | 225 | //----------------------------------------------------------------------- 226 | static JumpToBeginOfFunction() 227 | { 228 | auto ea; 229 | ea = get_func_attr(get_screen_ea(), FUNCATTR_START); 230 | if (BADADDR != ea) 231 | { 232 | jumpto(ea); 233 | } 234 | } 235 | 236 | //----------------------------------------------------------------------- 237 | static JumpToEndOfFunction() 238 | { 239 | auto ea; 240 | ea = get_func_attr(get_screen_ea(), FUNCATTR_END); 241 | if (BADADDR != ea) 242 | { 243 | ea = prev_not_tail(ea); 244 | jumpto(ea); 245 | } 246 | } 247 | 248 | //----------------------------------------------------------------------- 249 | // bad code to avoid rva offset 32 :( 250 | static NamePointer() 251 | { 252 | auto ea, f, s_asm, s_name, p_first, p_second; 253 | auto i, len, ch; 254 | 255 | ea = get_screen_ea(); 256 | f = get_flags(ea); 257 | if (is_data(f) && is_off0(f) && (is_qword(f) || is_dword(f))) 258 | { 259 | s_asm = generate_disasm_line(ea, 0); 260 | p_first = strstr(s_asm, " offset "); 261 | if (-1 != p_first) 262 | { 263 | s_name = substr(s_asm, p_first + 8, -1); // 8 = strlen(" offset ") 264 | 265 | // Remove comment trailing 266 | p_second = strstr(s_name, ";"); 267 | if (-1 != p_second) 268 | { 269 | s_name = substr(s_name, 0, p_second); 270 | } 271 | 272 | // replcae +, :, space... with _ char 273 | len = strlen(s_name); 274 | if (len > 0) 275 | { 276 | for (i = 0; i < len; ++i) 277 | { 278 | ch = s_name[i]; 279 | if (("+" == ch) || (":" == ch) || (" " == ch)) 280 | { 281 | s_name[i] = "_"; 282 | } 283 | } 284 | 285 | s_name = "p_" + s_name; 286 | set_name(ea, s_name, SN_CHECK | SN_NOWARN | SN_FORCE); 287 | 288 | return 1; 289 | } 290 | else 291 | { 292 | msg("0x%X: name is empty\n"); 293 | } 294 | } 295 | else 296 | { 297 | msg("0x%X: string 'offset' not found\n"); 298 | } 299 | } 300 | else 301 | { 302 | msg("ea: 0x%X, flags = 0x%X is not a pointer\n", ea, f); 303 | } 304 | 305 | return 0; 306 | } 307 | 308 | //----------------------------------------------------------------------- 309 | static RegisterHotkeys() 310 | { 311 | msg("\n------------------------------ HTCHotkeys --------------------------------------\n"); 312 | 313 | del_idc_hotkey("Shift+U"); 314 | add_idc_hotkey("Shift+U", "CreateUnicodeString"); 315 | msg("Shift+U: create Unicode string\n"); 316 | 317 | del_idc_hotkey("Alt+Shift+Up"); 318 | add_idc_hotkey("Alt+Shift+Up", "JumpToBeginOfFunction"); 319 | msg("Alt+Shift+Up: jump to begin of current function\n"); 320 | 321 | del_idc_hotkey("Alt+Shift+Down"); 322 | add_idc_hotkey("Alt+Shift+Down", "JumpToEndOfFunction"); 323 | msg("Alt+Shift+Down: jump to end of current function\n"); 324 | 325 | del_idc_hotkey("Ctrl+Alt+Up"); 326 | add_idc_hotkey("Ctrl+Alt+Up", "JumpToPrevLabel"); 327 | msg("Ctrl+Alt+Up: jump to previous label\n"); 328 | 329 | del_idc_hotkey("Ctrl+Alt+Down"); 330 | add_idc_hotkey("Ctrl+Alt+Down", "JumpToNextLabel"); 331 | msg("Ctrl+Alt+Down: jump to next label\n"); 332 | 333 | del_idc_hotkey("Shift+P"); 334 | add_idc_hotkey("Shift+P", "NamePointer"); 335 | msg("Shift+P: name pointer\n"); 336 | 337 | // Appcalls, only enable in PE mode 338 | if (FT_PE == get_inf_attr(INF_FILETYPE)) 339 | { 340 | last_cmd = ""; 341 | add_idc_hotkey("Ctrl-Alt-F9", "Appcall_Start"); 342 | msg("Ctrl-Alt-F9: Appcall_Start\n"); 343 | add_idc_hotkey("Ctrl-Alt-F10", "cleanup_appcall"); 344 | msg("Ctrl-Alt-F10: cleanup_appcall\n"); 345 | add_idc_hotkey("Ctrl-Alt-F4", "Appcall_Here"); 346 | msg("Ctrl-Alt-F4: Appcall_Here\n"); 347 | } 348 | 349 | msg("--------------------------------------------------------------------------------\n\n"); 350 | } 351 | 352 | // HTC - end 353 | // 354 | 355 | //----------------------------------------------------------------------- 356 | static main(void) 357 | { 358 | // 359 | // This function is executed when IDA is started. 360 | // 361 | // Add statements to fine-tune your IDA here. 362 | // 363 | 364 | // Instantiate the breakpoints singleton object 365 | Breakpoints = BreakpointManager(); 366 | 367 | // uncomment this line to remove full paths in the debugger process options: 368 | set_inf_attr(INF_LFLAGS, LFLG_DBG_NOPATH | get_inf_attr(INF_LFLAGS)); 369 | 370 | // HTC 371 | RegisterHotkeys(); 372 | 373 | // idauser.idc should define user_main(), call it here. 374 | auto e; 375 | try 376 | { 377 | user_main(); 378 | } 379 | catch (e) 380 | { 381 | // if idauser.idc did not exist, silently ignore the call error. 382 | if (e.description != "Attempt to call undefined function 'user_main'") 383 | { 384 | throw e; 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /idaemu.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0103,C0114,C0115,C0116,W0401,W0614 2 | 3 | # https://github.com/36hours/idaemu 4 | 5 | """ 6 | USAGE: 7 | 8 | Example1 9 | This is easy function for add. 10 | 11 | .text:000000000040052D public myadd 12 | .text:000000000040052D myadd proc near ; CODE XREF: main+1B?p 13 | .text:000000000040052D 14 | .text:000000000040052D var_4 = dword ptr -4 15 | .text:000000000040052D 16 | .text:000000000040052D push rbp 17 | .text:000000000040052E mov rbp, rsp 18 | .text:0000000000400531 mov [rbp+var_4], edi 19 | .text:0000000000400534 mov edx, cs:magic ; magic dd 64h 20 | .text:000000000040053A mov eax, [rbp+var_4] 21 | .text:000000000040053D add eax, edx 22 | .text:000000000040053F pop rbp 23 | .text:0000000000400540 retn 24 | .text:0000000000400540 myadd endp 25 | Running the idapython scritp: 26 | 27 | from idaemu import * 28 | a = Emu(UC_ARCH_X86, UC_MODE_64) 29 | print a.eFunc(0x040052D, None, [7]) 30 | Get the function result: 31 | 32 | 107 33 | Example2 34 | If there is a library function call inner the function, we couldn't call it directly. We should use alt to hook the library function first. 35 | 36 | .text:0000000000400560 public myadd 37 | .text:0000000000400560 myadd proc near ; CODE XREF: main+27?p 38 | .text:0000000000400560 39 | .text:0000000000400560 var_8 = dword ptr -8 40 | .text:0000000000400560 var_4 = dword ptr -4 41 | .text:0000000000400560 42 | .text:0000000000400560 push rbp 43 | .text:0000000000400561 mov rbp, rsp 44 | .text:0000000000400564 sub rsp, 10h 45 | .text:0000000000400568 mov [rbp+var_4], edi 46 | .text:000000000040056B mov [rbp+var_8], esi 47 | .text:000000000040056E mov eax, [rbp+var_8] 48 | .text:0000000000400571 mov edx, [rbp+var_4] 49 | .text:0000000000400574 add eax, edx 50 | .text:0000000000400576 mov esi, eax 51 | .text:0000000000400578 mov edi, offset format ; "a+b=%d\n" 52 | .text:000000000040057D mov eax, 0 53 | .text:0000000000400582 call _printf 54 | .text:0000000000400587 leave 55 | .text:0000000000400588 retn 56 | .text:0000000000400588 myadd endp 57 | Running the idapython scritp: 58 | 59 | from idaemu import * 60 | 61 | a = Emu(UC_ARCH_X86, UC_MODE_64) 62 | 63 | def myprint(uc, out, args): 64 | out.append("this is hook output: %d" % args[1]) 65 | return 0 66 | 67 | myadd_addr = 0x00400560 68 | printf_addr = 0x00400410 69 | a.alt(printf_addr, myprint, 2, False) 70 | a.eFunc(myadd_addr, None, [1, 7]) 71 | print "---- below is the trace ----" 72 | a.showTrace() 73 | Get the result: 74 | 75 | ---- below is the trace ---- 76 | this is hook output: 8 77 | Well Done. We can alter every function in this way. 78 | 79 | Example3 80 | Sometimes it emulates fail with some abort: 81 | 82 | Python>from idaemu import * 83 | Python>a = Emu(UC_ARCH_ARM, UC_MODE_THUMB) 84 | Python>print a.eFunc(here(), 0xbeae, [4]) 85 | #ERROR: Invalid instruction (UC_ERR_INSN_INVALID) 86 | 1048576 87 | Then we can use setTrace and showTrace for debugging. 88 | 89 | Python>from idaemu import * 90 | Python>a = Emu(UC_ARCH_ARM, UC_MODE_THUMB) 91 | Python>a.setTrace(TRACE_CODE) 92 | Python>a.eFunc(here(), 0xbeae, [4]) 93 | #ERROR: Invalid instruction (UC_ERR_INSN_INVALID) 94 | 1048576 95 | Python>a.showTrace() 96 | ### Trace Instruction at 0x13dc, size = 2 97 | ### Trace Instruction at 0x13de, size = 2 98 | ### Trace Instruction at 0x13e0, size = 2 99 | ...... 100 | ### Trace Instruction at 0x19c6, size = 2 101 | ### Trace Instruction at 0x19c8, size = 2 102 | ### Trace Instruction at 0x19ca, size = 2 103 | ### Trace Instruction at 0xbeae, size = 2 104 | So we found the abort reason (the RA is wrong) 105 | """ 106 | 107 | from __future__ import print_function 108 | 109 | from struct import unpack, pack, unpack_from, calcsize 110 | 111 | from unicorn import * 112 | from unicorn.x86_const import * 113 | from unicorn.arm_const import * 114 | from unicorn.arm64_const import * 115 | 116 | from idaapi import get_func 117 | from idautils import XrefsTo 118 | from idc import get_qword, get_bytes, read_selection_start, read_selection_end, here, get_item_size 119 | 120 | PAGE_ALIGN = 0x1000 # 4k 121 | 122 | COMPILE_GCC = 1 123 | COMPILE_MSVC = 2 124 | 125 | TRACE_OFF = 0 126 | TRACE_DATA_READ = 1 127 | TRACE_DATA_WRITE = 2 128 | TRACE_CODE = 4 129 | 130 | 131 | class Emu: 132 | def __init__(self, arch, mode, compiler=COMPILE_GCC, stack=0xf000000, ssize=3): 133 | assert (arch in [UC_ARCH_X86, UC_ARCH_ARM, UC_ARCH_ARM64]) 134 | self.arch = arch 135 | self.mode = mode 136 | self.compiler = compiler 137 | self.stack = self._alignAddr(stack) 138 | self.ssize = ssize 139 | self.data = [] 140 | self.regs = [] 141 | self.curUC = None 142 | self.traceOption = TRACE_OFF 143 | self.logBuffer = [] 144 | self.altFunc = {} 145 | self._init() 146 | 147 | def _addTrace(self, logInfo): 148 | self.logBuffer.append(logInfo) 149 | 150 | # callback for tracing invalid memory access (READ or WRITE, FETCH) 151 | def _hook_mem_invalid(self, uc, _access, address, _size, _value, _user_data): 152 | addr = self._alignAddr(address) 153 | uc.mem_map(addr, PAGE_ALIGN) 154 | data = self._getOriginData(addr, PAGE_ALIGN) 155 | uc.mem_write(addr, data) 156 | return True 157 | 158 | def _hook_mem_access(self, _uc, access, address, size, value, _user_data): 159 | if access == UC_MEM_WRITE and self.traceOption & TRACE_DATA_WRITE: 160 | self._addTrace(f"### Memory WRITE at 0x{address:X}, data size = {size}, data value = 0x{value:X}") 161 | elif access == UC_MEM_READ and self.traceOption & TRACE_DATA_READ: 162 | self._addTrace(f"### Memory READ at 0x{address:X}, data size = {size}") 163 | 164 | def _hook_code(self, uc, address, size, _user_data): 165 | if self.traceOption & TRACE_CODE: 166 | self._addTrace(f"### Trace Instruction at 0x{address:X}, size = {size}") 167 | if address in self.altFunc: 168 | func, argc, balance = self.altFunc[address] 169 | try: 170 | sp = uc.reg_read(self.REG_SP) 171 | if self.REG_RA == 0: 172 | RA = unpack(self.pack_fmt, str(uc.mem_read(sp, self.step)))[0] 173 | sp += self.step 174 | else: 175 | RA = uc.reg_read(self.REG_RA) 176 | 177 | args = [] 178 | i = 0 179 | while i < argc and i < len(self.REG_ARGS): 180 | args.append(uc.reg_read(self.REG_ARGS[i])) 181 | i += 1 182 | sp2 = sp 183 | while i < argc: 184 | args.append(unpack(self.pack_fmt, str(uc.mem_read(sp2, self.step)))[0]) 185 | sp2 += self.step 186 | i += 1 187 | 188 | res = func(uc, self.logBuffer, args) 189 | if not isinstance(res, int): 190 | res = 0 191 | uc.reg_write(self.REG_RES, res) 192 | uc.reg_write(self.REG_PC, RA) 193 | if balance: 194 | uc.reg_write(self.REG_SP, sp2) 195 | else: 196 | uc.reg_write(self.REG_SP, sp) 197 | except Exception as e: 198 | self._addTrace(f"alt exception: {e}") 199 | 200 | def _alignAddr(self, addr): 201 | return addr // PAGE_ALIGN * PAGE_ALIGN 202 | 203 | def _getOriginData(self, address, size): 204 | res = [] 205 | for offset in range(0, size, 64): 206 | tmp = get_bytes(address + offset, 64) 207 | if tmp is None: 208 | res.extend([pack(" 0 else 0 290 | 291 | def _showRegs(self, uc): 292 | print(">>> regs:") 293 | try: 294 | if self.mode == UC_MODE_16: 295 | ax = uc.reg_read(UC_X86_REG_AX) 296 | bx = uc.reg_read(UC_X86_REG_BX) 297 | cx = uc.reg_read(UC_X86_REG_CX) 298 | dx = uc.reg_read(UC_X86_REG_DX) 299 | di = uc.reg_read(UC_X86_REG_SI) 300 | si = uc.reg_read(UC_X86_REG_DI) 301 | bp = uc.reg_read(UC_X86_REG_BP) 302 | sp = uc.reg_read(UC_X86_REG_SP) 303 | ip = uc.reg_read(UC_X86_REG_IP) 304 | eflags = uc.reg_read(UC_X86_REG_EFLAGS) 305 | 306 | print(" AX = 0x%X BX = 0x%X CX = 0x%X DX = 0x%X" % (ax, bx, cx, dx)) 307 | print(" DI = 0x%X SI = 0x%X BP = 0x%X SP = 0x%X" % (di, si, bp, sp)) 308 | print(" IP = 0x%X" % ip) 309 | elif self.mode == UC_MODE_32: 310 | eax = uc.reg_read(UC_X86_REG_EAX) 311 | ebx = uc.reg_read(UC_X86_REG_EBX) 312 | ecx = uc.reg_read(UC_X86_REG_ECX) 313 | edx = uc.reg_read(UC_X86_REG_EDX) 314 | edi = uc.reg_read(UC_X86_REG_ESI) 315 | esi = uc.reg_read(UC_X86_REG_EDI) 316 | ebp = uc.reg_read(UC_X86_REG_EBP) 317 | esp = uc.reg_read(UC_X86_REG_ESP) 318 | eip = uc.reg_read(UC_X86_REG_EIP) 319 | eflags = uc.reg_read(UC_X86_REG_EFLAGS) 320 | 321 | print(" EAX = 0x%X EBX = 0x%X ECX = 0x%X EDX = 0x%X" % (eax, ebx, ecx, edx)) 322 | print(" EDI = 0x%X ESI = 0x%X EBP = 0x%X ESP = 0x%X" % (edi, esi, ebp, esp)) 323 | print(" EIP = 0x%X" % eip) 324 | elif self.mode == UC_MODE_64: 325 | rax = uc.reg_read(UC_X86_REG_RAX) 326 | rbx = uc.reg_read(UC_X86_REG_RBX) 327 | rcx = uc.reg_read(UC_X86_REG_RCX) 328 | rdx = uc.reg_read(UC_X86_REG_RDX) 329 | rdi = uc.reg_read(UC_X86_REG_RSI) 330 | rsi = uc.reg_read(UC_X86_REG_RDI) 331 | rbp = uc.reg_read(UC_X86_REG_RBP) 332 | rsp = uc.reg_read(UC_X86_REG_RSP) 333 | rip = uc.reg_read(UC_X86_REG_RIP) 334 | r8 = uc.reg_read(UC_X86_REG_R8) 335 | r9 = uc.reg_read(UC_X86_REG_R9) 336 | r10 = uc.reg_read(UC_X86_REG_R10) 337 | r11 = uc.reg_read(UC_X86_REG_R11) 338 | r12 = uc.reg_read(UC_X86_REG_R12) 339 | r13 = uc.reg_read(UC_X86_REG_R13) 340 | r14 = uc.reg_read(UC_X86_REG_R14) 341 | r15 = uc.reg_read(UC_X86_REG_R15) 342 | eflags = uc.reg_read(UC_X86_REG_EFLAGS) 343 | 344 | print(" RAX = 0x%X RBX = 0x%X RCX = 0x%X RDX = 0x%X" % (rax, rbx, rcx, rdx)) 345 | print(" RDI = 0x%X RSI = 0x%X RBP = 0x%X RSP = 0x%X" % (rdi, rsi, rbp, rsp)) 346 | print(" R8 = 0x%X R9 = 0x%X R10 = 0x%X R11 = 0x%X R12 = 0x%X " \ 347 | "R13 = 0x%X R14 = 0x%X R15 = 0x%X" % (r8, r9, r10, r11, r12, r13, r14, r15)) 348 | print(" RIP = 0x%X" % rip) 349 | if eflags: 350 | print(" EFLAGS:") 351 | print(" CF=%d PF=%d AF=%d ZF=%d SF=%d TF=%d IF=%d DF=%d OF=%d IOPL=%d " \ 352 | "NT=%d RF=%d VM=%d AC=%d VIF=%d VIP=%d ID=%d" 353 | % (self._getBit(eflags, 0), 354 | self._getBit(eflags, 2), 355 | self._getBit(eflags, 4), 356 | self._getBit(eflags, 6), 357 | self._getBit(eflags, 7), 358 | self._getBit(eflags, 8), 359 | self._getBit(eflags, 9), 360 | self._getBit(eflags, 10), 361 | self._getBit(eflags, 11), 362 | self._getBit(eflags, 12) + self._getBit(eflags, 13) * 2, 363 | self._getBit(eflags, 14), 364 | self._getBit(eflags, 16), 365 | self._getBit(eflags, 17), 366 | self._getBit(eflags, 18), 367 | self._getBit(eflags, 19), 368 | self._getBit(eflags, 20), 369 | self._getBit(eflags, 21))) 370 | except UcError as e: 371 | print(f"#ERROR: {e}") 372 | 373 | def _initData(self, uc): 374 | for address, data, init in self.data: 375 | addr = self._alignAddr(address) 376 | size = PAGE_ALIGN 377 | while size < len(data): 378 | size += PAGE_ALIGN 379 | uc.mem_map(addr, size) 380 | if init: 381 | uc.mem_write(addr, self._getOriginData(addr, size)) 382 | uc.mem_write(address, data) 383 | 384 | def _initRegs(self, uc): 385 | for reg, value in self.regs: 386 | uc.reg_write(reg, value) 387 | 388 | def _emulate(self, startAddr, stopAddr, args=[], TimeOut=0, Count=0): 389 | try: 390 | self.logBuffer = [] 391 | uc = Uc(self.arch, self.mode) 392 | self.curUC = uc 393 | 394 | self._initStackAndArgs(uc, stopAddr, args) 395 | self._initData(uc) 396 | self._initRegs(uc) 397 | 398 | # add the invalid memory access hook 399 | uc.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | \ 400 | UC_HOOK_MEM_FETCH_UNMAPPED, self._hook_mem_invalid) 401 | 402 | # add the trace hook 403 | if self.traceOption & (TRACE_DATA_READ | TRACE_DATA_WRITE): 404 | uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self._hook_mem_access) 405 | uc.hook_add(UC_HOOK_CODE, self._hook_code) 406 | 407 | # start emulate 408 | uc.emu_start(startAddr, stopAddr, timeout=TimeOut, count=Count) 409 | except UcError as e: 410 | print(f"#ERROR: {e}") 411 | 412 | # set the data before emulation 413 | def setData(self, address, data, init=False): 414 | self.data.append((address, data, init)) 415 | 416 | def setReg(self, reg, value): 417 | self.regs.append((reg, value)) 418 | 419 | def showRegs(self, *regs): 420 | if self.curUC is None: 421 | print("current uc is None.") 422 | return 423 | for reg in regs: 424 | print(f"0x{self.curUC.reg_read(reg):X}") 425 | 426 | def readStack(self, fmt, count): 427 | if self.curUC is None: 428 | print("current uc is none.") 429 | return None 430 | stackData = [] 431 | stackPointer = self.curUC.reg_read(self.REG_SP) 432 | for i in range(count): 433 | dataSize = calcsize(fmt) 434 | data = self.curUC.mem_read(stackPointer + i * dataSize, dataSize) 435 | st = unpack_from(fmt, data) 436 | stackData.append((stackPointer + i * dataSize, st[0])) 437 | return stackData 438 | 439 | def showData(self, fmt, addr, count=1): 440 | if self.curUC is None: 441 | print("current uc is none.") 442 | return 443 | if count > 1: 444 | print('[') 445 | for i in range(count): 446 | dataSize = calcsize(fmt) 447 | data = self.curUC.mem_read(addr + i * dataSize, dataSize) 448 | if count > 1: 449 | print(' ', end='') 450 | _st = unpack_from(fmt, data) 451 | if count > 1: 452 | print(',') 453 | print(']') if count > 1 else print('') 454 | 455 | def setTrace(self, opt): 456 | if opt != TRACE_OFF: 457 | self.traceOption |= opt 458 | else: 459 | self.traceOption = TRACE_OFF 460 | 461 | def showTrace(self): 462 | logs = "\n".join(self.logBuffer) 463 | print(logs) 464 | 465 | def alt(self, address, func, argc, balance=False): 466 | """ 467 | If call the address, will call the func instead. 468 | the arguments of func : func(uc, consoleouput, args) 469 | """ 470 | assert callable(func) 471 | self.altFunc[address] = (func, argc, balance) 472 | 473 | def eFunc(self, address=None, retAddr=None, args=[]): 474 | if address is None: 475 | address = here() 476 | func = get_func(address) 477 | if retAddr is None: 478 | refs = [ref.frm for ref in XrefsTo(func.startEA, 0)] 479 | if len(refs) != 0: 480 | retAddr = refs[0] + get_item_size(refs[0]) 481 | else: 482 | print("Please offer the return address.") 483 | return None 484 | self._emulate(func.startEA, retAddr, args) 485 | res = self.curUC.reg_read(self.REG_RES) 486 | return res 487 | 488 | def eBlock(self, codeStart=None, codeEnd=None): 489 | if codeStart is None: 490 | codeStart = read_selection_start() 491 | if codeEnd is None: 492 | codeEnd = read_selection_end() 493 | self._emulate(codeStart, codeEnd) 494 | self._showRegs(self.curUC) 495 | 496 | def eUntilAddress(self, startAddr, stopAddr, args=[], TimeOut=0, Count=0): 497 | self._emulate(startAddr=startAddr, stopAddr=stopAddr, args=args, TimeOut=TimeOut, Count=Count) 498 | self._showRegs(self.curUC) 499 | -------------------------------------------------------------------------------- /vb_DllFunctionCall.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0103,C0114,C0116 2 | 3 | ################################################################################ 4 | # 5 | # Copyright (C) 2014 Cisco Systems, Inc./SourceFire, Inc. 6 | # 7 | # Author: Angel M. Villegas (anvilleg [at] sourcefire [dot] com) 8 | # Frederick W Sell (frsell [at] cisco [dot] com) 9 | # 10 | # This program is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along 21 | # with this program; if not, write to the Free Software Foundation, Inc., 22 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | # 24 | # Last Modified: July 29, 2022 25 | # Description: 26 | # IDA Python script that locates all references to DllFunctionCall, 27 | # creates structures, applies structures to the argument provided to 28 | # DllFunctionCall, labels structures based on the API to be loaded, 29 | # defines and renames functions used to dynamically load API, and 30 | # registers the function dynamically loaded. Upon completion, a list of 31 | # all dynamically loaded API is printed out 32 | # 33 | # 34 | # VB DllFunctionCall(DllFunctionCallStruct * dllInfo); 35 | # ============================================================================ 36 | # typedef struct _DynamicHandles { 37 | # 0x00 38 | # 0x04 HANDLE hModule; 39 | # 0x08 VOID * fnAddress 40 | # 0x0C 41 | # } DynamicHandles; 42 | # 43 | # typedef struct _DllFunctionCallStruct { 44 | # 0x00 LPCSTR lpDllName; 45 | # 0x04 LPTSTR lpExportName; 46 | # 0x08 47 | # 0x09 48 | # // 4 bytes means it is a LPTSTR * 49 | # // 2 bytes means it is a WORD (the export's numeric Ordinal) 50 | # 0x0A char addressAlignment; 51 | # 0x0B 52 | # 0x0C DynamicHandles * sHandleData; 53 | # 0x10 54 | # } DllFunctionCallStruct; 55 | # 56 | ################################################################################ 57 | 58 | import idc 59 | import idaapi 60 | import idautils 61 | 62 | # Print out dynamically loaded API 63 | def printAPI(data): 64 | formatStr = '0x{0:<12X} {1:32} {2}' 65 | for dll in sorted(data.keys()): 66 | for fn in sorted(data[dll]): 67 | print(formatStr.format(fn[0], fn[1], dll)) 68 | 69 | # Find the start of the function 70 | # Expects ea to be the address of loc_XXXXXX 71 | def defineFunction(ea): 72 | # Move to where we believe the stub starts 73 | eaStart = ea - 11 74 | 75 | # Do check if this is the stub we support 76 | if ((idc.get_wide_byte(eaStart) == 0xA1) and (idc.get_wide_word(eaStart + 5) == 0xC00B) and \ 77 | (idc.get_wide_byte(eaStart + 7) == 0x74) and (idc.get_wide_word(eaStart + 9) == 0xE0FF)): 78 | 79 | # Create function if IDA did not create it already 80 | if not idaapi.get_func(eaStart): 81 | idc.del_items(eaStart, idc.DELIT_SIMPLE, 0x18) 82 | idc.create_insn(eaStart) 83 | if 0 == idc.add_func(eaStart, idc.BADADDR): 84 | print(f'\t[!] Error: Unable to define function at 0x{eaStart:X}') 85 | else: 86 | print(f'\t[!] Error: Unable to find function start address at 0x{eaStart:X}') 87 | 88 | return eaStart 89 | 90 | def createDllFunctionCallStruct(): 91 | # Create DynamicHandles argument sub structure 92 | _subStructId = idc.add_struc(-1, HANDLES_STRUCT_NAME, 0) 93 | idc.add_struc_member(_subStructId, 'dwErrorCode', 0x0, idc.FF_DWORD | idc.FF_DATA, -1, 4) 94 | idc.add_struc_member(_subStructId, 'hModule', 0x4, idc.FF_DWORD | idc.FF_DATA, -1, 4) 95 | idc.add_struc_member(_subStructId, 'pfnFunction', 0x8, idc.FF_DWORD | idc.FF_DATA, -1, 4) 96 | 97 | # Create DllFunctionCall argument structure 98 | _structId = idc.add_struc(-1, DLL_FUNCTION_CALL_STRUCT_NAME, 0) 99 | idc.add_struc_member(_structId, 'pszDllName', 0x0, idc.FF_DWORD | idc.FF_0OFF | idc.FF_DATA, -1, 4) 100 | idc.add_struc_member(_structId, 'pszFunctionName', 0x4, idc.FF_DWORD | idc.FF_0OFF | idc.FF_DATA, -1, 4) 101 | idc.add_struc_member(_structId, 'wExportOrd', 0x8, idc.FF_WORD | idc.FF_DATA, -1, 2) 102 | idc.add_struc_member(_structId, 'fFlags', 0xA, idc.FF_WORD | idc.FF_DATA, -1, 2) 103 | idc.add_struc_member(_structId, 'pDllTemplateInfo', 0xC, idc.FF_DWORD | idc.FF_0OFF | idc.FF_DATA, -1, 4) 104 | idc.SetType(idc.get_member_id(_structId, 0xC), HANDLES_STRUCT_NAME + " *") 105 | idc.add_struc_member(_structId, 'pResource', 0x10, idc.FF_DWORD | idc.FF_DATA, -1, 4) 106 | idc.add_struc_member(_structId, 'pEntry', 0x14, idc.FF_DWORD | idc.FF_DATA, -1, 4) 107 | 108 | return _structId 109 | 110 | DLL_FUNCTION_CALL_STRUCT_NAME = 'serDllTemplate' 111 | HANDLES_STRUCT_NAME = 'epiDllTemplateInfo' 112 | dynamicAPI = {} 113 | loadAPI = 0 114 | 115 | print("Starting...") 116 | 117 | # Check if struct exists, if not, create it 118 | structId = idc.get_struc_id(DLL_FUNCTION_CALL_STRUCT_NAME) 119 | if idc.BADADDR == structId: 120 | print(f'\t[+] Structure "{DLL_FUNCTION_CALL_STRUCT_NAME}" does not exist, creating structure...') 121 | structId = createDllFunctionCallStruct() 122 | 123 | for xref in idautils.CodeRefsTo(idc.get_name_ea_simple('DllFunctionCall'), 1): 124 | structInstr = xref - 0xA 125 | 126 | # The instruction should be push 0x???????? 127 | if idc.print_insn_mnem(structInstr) == 'push' and idc.get_operand_type(structInstr, 0) == idc.o_imm: 128 | # Set the operand type to an offset 129 | idc.op_plain_offset(structInstr, 0, 0) 130 | 131 | # Get struct offset and apply structure to it 132 | structOffset = idc.get_operand_value(structInstr, 0) 133 | idc.del_items(structOffset, idc.DELIT_DELNAMES, 0x18) 134 | idc.create_struct(structOffset, -1, DLL_FUNCTION_CALL_STRUCT_NAME) 135 | strOffset = idc.get_wide_dword(structOffset) 136 | lpDllName = idc.get_strlit_contents(strOffset, -1, idc.STRTYPE_C) 137 | idc.del_items(strOffset, idc.DELIT_SIMPLE, len(lpDllName) + 1) 138 | idc.create_strlit(strOffset, strOffset + len(lpDllName) + 1) 139 | strOffset = idc.get_wide_dword(structOffset + 4) 140 | lpFunctionName = idc.get_strlit_contents(strOffset, -1, idc.STRTYPE_C) 141 | if not lpFunctionName: 142 | lpFunctionName = lpDllName 143 | if idc.get_wide_word(structOffset + 0xA) & 2: 144 | lpFunctionName = f"{lpDllName}_Ord_{idc.get_wide_word(structOffset + 0x8):d}" 145 | lpDllName = lpDllName.decode("utf-8") 146 | lpFunctionName = lpFunctionName.decode("utf-8") 147 | idc.del_items(strOffset, idc.DELIT_SIMPLE, len(lpFunctionName) + 1) 148 | idc.create_strlit(strOffset, strOffset + len(lpFunctionName) + 1) 149 | idc.set_name(structOffset, f'struct_{lpFunctionName}', idaapi.SN_FORCE) 150 | 151 | # Get sub structure address, apply structure, and apply name to it 152 | subStructAddr = idc.get_wide_dword(structOffset + 0xC) 153 | idc.del_items(subStructAddr, idc.DELIT_DELNAMES, 0xC) 154 | idc.create_struct(subStructAddr, 0xC, HANDLES_STRUCT_NAME) 155 | idc.set_name(subStructAddr, f'subStruct_{lpFunctionName}', idaapi.SN_FORCE) 156 | 157 | # Check if a function is already defined 158 | pfn = idaapi.get_func(structInstr) 159 | if not pfn: 160 | fnAddress = defineFunction(structInstr) 161 | else: 162 | fnAddress = pfn.start_ea 163 | if not idc.set_name(fnAddress, lpFunctionName, idaapi.SN_FORCE): 164 | print(f'\t[!] Error: Failed to set function name {lpFunctionName} at 0x{fnAddress:X}') 165 | 166 | idc.set_cmt(fnAddress, f'pfn{lpFunctionName}', 0) 167 | idc.set_func_cmt(fnAddress, f"{lpDllName}_{lpFunctionName}", 0) 168 | 169 | # Add API to dynamically loaded API 170 | if lpDllName not in dynamicAPI: 171 | dynamicAPI[lpDllName] = [] 172 | if lpFunctionName not in dynamicAPI[lpDllName]: 173 | dynamicAPI[lpDllName].append((fnAddress, lpFunctionName)) 174 | loadAPI += 1 175 | 176 | if loadAPI == 0: 177 | print("Not import DllFunctionCall function from msvbvmxx.dll !") 178 | else: 179 | print(f'Printing dynamically loaded API ({loadAPI} total)...') 180 | printAPI(dynamicAPI) 181 | --------------------------------------------------------------------------------