github.com/saferwall/pe@v1.5.2/debug_test.go (about) 1 // Copyright 2018 Saferwall. All rights reserved. 2 // Use of this source code is governed by Apache v2 license 3 // license that can be found in the LICENSE file. 4 5 package pe 6 7 import ( 8 "reflect" 9 "testing" 10 ) 11 12 type TestDebugIn struct { 13 filepath string 14 index int // debug entry index 15 } 16 17 func TestDebugDirectoryCodeView(t *testing.T) { 18 19 type TestCodeView struct { 20 debugEntry DebugEntry 21 signature string 22 } 23 24 tests := []struct { 25 in TestDebugIn 26 out TestCodeView 27 }{ 28 { 29 TestDebugIn{ 30 index: 0, 31 filepath: getAbsoluteFilePath("test/kernel32.dll"), 32 }, 33 TestCodeView{ 34 debugEntry: DebugEntry{ 35 Struct: ImageDebugDirectory{ 36 Characteristics: 0x0, 37 TimeDateStamp: 0x38b369c4, 38 MajorVersion: 0x0, 39 MinorVersion: 0x0, 40 Type: 0x2, 41 SizeOfData: 0x25, 42 AddressOfRawData: 0x932f0, 43 PointerToRawData: 0x91cf0, 44 }, 45 Info: CVInfoPDB70{ 46 CVSignature: 0x53445352, 47 Signature: GUID{ 48 Data1: 0xdbe09e71, 49 Data2: 0xb370, 50 Data3: 0x9cb7, 51 Data4: [8]byte{34, 197, 94, 85, 115, 250, 123, 225}, 52 }, 53 Age: 0x1, 54 PDBFileName: "kernel32.pdb", 55 }, 56 Type: "CodeView", 57 }, 58 signature: "RSDS", 59 }, 60 }, 61 { 62 TestDebugIn{ 63 index: 0, 64 filepath: getAbsoluteFilePath("test/01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d"), 65 }, 66 TestCodeView{ 67 debugEntry: DebugEntry{ 68 Struct: ImageDebugDirectory{ 69 Characteristics: 0x0, 70 TimeDateStamp: 0x3b7d84d4, 71 MajorVersion: 0x0, 72 MinorVersion: 0x0, 73 Type: 0x2, 74 SizeOfData: 0x1d, 75 AddressOfRawData: 0x1cf4, 76 PointerToRawData: 0x10f4, 77 }, 78 Info: CVInfoPDB20{ 79 CVHeader: CVHeader{ 80 Signature: 0x3031424e, 81 Offset: 0x0, 82 }, 83 Signature: 0x3b7d84d4, 84 Age: 0x1, 85 PDBFileName: "routemon.pdb", 86 }, 87 Type: "CodeView", 88 }, 89 signature: "NB10", 90 }, 91 }, 92 } 93 94 for _, tt := range tests { 95 t.Run(tt.in.filepath, func(t *testing.T) { 96 ops := Options{Fast: true} 97 file, err := New(tt.in.filepath, &ops) 98 if err != nil { 99 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 100 } 101 102 err = file.Parse() 103 if err != nil { 104 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 105 } 106 107 var va, size uint32 108 109 if file.Is64 { 110 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 111 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 112 va = dirEntry.VirtualAddress 113 size = dirEntry.Size 114 } else { 115 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 116 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 117 va = dirEntry.VirtualAddress 118 size = dirEntry.Size 119 } 120 121 err = file.parseDebugDirectory(va, size) 122 if err != nil { 123 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", 124 tt.in.filepath, err) 125 } 126 127 debugEntry := file.Debugs[tt.in.index] 128 if !reflect.DeepEqual(debugEntry, tt.out.debugEntry) { 129 t.Fatalf("debug entry assertion failed, got %v, want %v", 130 debugEntry, tt.out.debugEntry) 131 } 132 133 cvSignature := "" 134 switch debugEntry.Info.(type) { 135 case CVInfoPDB70: 136 cvSignature = debugEntry.Info.(CVInfoPDB70).CVSignature.String() 137 case CVInfoPDB20: 138 cvSignature = debugEntry.Info.(CVInfoPDB20).CVHeader.Signature.String() 139 } 140 if cvSignature != tt.out.signature { 141 t.Fatalf("debug CV signature assertion failed, got %v, want %v", 142 cvSignature, tt.out.signature) 143 } 144 }) 145 } 146 } 147 148 func TestDebugDirectoryPOGO(t *testing.T) { 149 150 type TestPOGO struct { 151 imgDebugEntry ImageDebugDirectory 152 entriesCount int 153 debugType string 154 POGOItemIndex int 155 POGOItem ImagePGOItem 156 POGOSignature string 157 } 158 159 tests := []struct { 160 in TestDebugIn 161 out TestPOGO 162 }{ 163 { 164 TestDebugIn{ 165 index: 1, 166 filepath: getAbsoluteFilePath("test/kernel32.dll"), 167 }, 168 TestPOGO{ 169 imgDebugEntry: ImageDebugDirectory{ 170 Characteristics: 0x0, 171 TimeDateStamp: 0x38b369c4, 172 MajorVersion: 0x0, 173 MinorVersion: 0x0, 174 Type: 0xd, 175 SizeOfData: 0x574, 176 AddressOfRawData: 0x93318, 177 PointerToRawData: 0x91d18, 178 }, 179 debugType: "POGO", 180 entriesCount: 60, 181 POGOItemIndex: 0, 182 POGOItem: ImagePGOItem{ 183 RVA: 0x1000, 184 Size: 0x280, 185 Name: ".text$lp00kernel32.dll!20_pri7", 186 }, 187 POGOSignature: "PGU", 188 }, 189 }, 190 } 191 192 for _, tt := range tests { 193 t.Run(tt.in.filepath, func(t *testing.T) { 194 ops := Options{Fast: true} 195 file, err := New(tt.in.filepath, &ops) 196 if err != nil { 197 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 198 } 199 200 err = file.Parse() 201 if err != nil { 202 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 203 } 204 205 var va, size uint32 206 207 if file.Is64 { 208 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 209 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 210 va = dirEntry.VirtualAddress 211 size = dirEntry.Size 212 } else { 213 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 214 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 215 va = dirEntry.VirtualAddress 216 size = dirEntry.Size 217 } 218 219 err = file.parseDebugDirectory(va, size) 220 if err != nil { 221 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", tt.in.filepath, err) 222 } 223 224 imgDebugEntry := file.Debugs[tt.in.index].Struct 225 if !reflect.DeepEqual(imgDebugEntry, tt.out.imgDebugEntry) { 226 t.Fatalf("debug entry assertion failed, got %v, want %v", 227 imgDebugEntry, tt.out.imgDebugEntry) 228 } 229 230 debugTypeString := file.Debugs[tt.in.index].Type 231 if debugTypeString != tt.out.debugType { 232 t.Fatalf("debug type assertion failed, got %v, want %v", 233 debugTypeString, tt.out.debugType) 234 } 235 236 pogo := file.Debugs[tt.in.index].Info.(POGO) 237 entriesCount := len(pogo.Entries) 238 if entriesCount != tt.out.entriesCount { 239 t.Fatalf("debug entry count failed, got %v, want %v", 240 entriesCount, tt.out.entriesCount) 241 } 242 243 pogoItem := pogo.Entries[tt.out.POGOItemIndex] 244 if !reflect.DeepEqual(pogoItem, tt.out.POGOItem) { 245 t.Fatalf("debug pogo entry assertion failed, got %v, want %v", 246 pogoItem, tt.out.POGOItemIndex) 247 } 248 249 pogoItemSignature := pogo.Signature.String() 250 if pogoItemSignature != tt.out.POGOSignature { 251 t.Fatalf("debug pogo signature string assertion failed, got %v, want %v", 252 pogoItemSignature, tt.out.POGOSignature) 253 } 254 }) 255 } 256 } 257 258 func TestDebugDirectoryREPRO(t *testing.T) { 259 260 type TestREPRO struct { 261 debugType string 262 debugEntry DebugEntry 263 } 264 265 tests := []struct { 266 in TestDebugIn 267 out TestREPRO 268 }{ 269 270 { 271 TestDebugIn{ 272 index: 2, 273 filepath: getAbsoluteFilePath("test/kernel32.dll"), 274 }, 275 TestREPRO{ 276 debugEntry: DebugEntry{ 277 Struct: ImageDebugDirectory{ 278 Characteristics: 0x0, 279 TimeDateStamp: 0x38b369c4, 280 MajorVersion: 0x0, 281 MinorVersion: 0x0, 282 Type: 0x10, 283 SizeOfData: 0x24, 284 AddressOfRawData: 0x9388c, 285 PointerToRawData: 0x9228c, 286 }, 287 Info: REPRO{ 288 Size: 0x20, 289 Hash: []byte{113, 158, 224, 219, 112, 179, 183, 156, 34, 197, 94, 85, 115, 250, 123, 225, 130, 290 247, 187, 89, 220, 154, 207, 99, 80, 113, 179, 171, 196, 105, 179, 56}, 291 }, 292 293 Type: "REPRO", 294 }, 295 }, 296 }, 297 } 298 299 for _, tt := range tests { 300 t.Run(tt.in.filepath, func(t *testing.T) { 301 ops := Options{Fast: true} 302 file, err := New(tt.in.filepath, &ops) 303 if err != nil { 304 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 305 } 306 307 err = file.Parse() 308 if err != nil { 309 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 310 } 311 312 var va, size uint32 313 314 if file.Is64 { 315 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 316 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 317 va = dirEntry.VirtualAddress 318 size = dirEntry.Size 319 } else { 320 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 321 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 322 va = dirEntry.VirtualAddress 323 size = dirEntry.Size 324 } 325 326 err = file.parseDebugDirectory(va, size) 327 if err != nil { 328 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", 329 tt.in.filepath, err) 330 } 331 332 debugEntry := file.Debugs[tt.in.index] 333 if !reflect.DeepEqual(debugEntry, tt.out.debugEntry) { 334 t.Fatalf("debug entry assertion failed, got %v, want %v", 335 debugEntry, tt.out.debugEntry) 336 } 337 338 }) 339 } 340 } 341 342 func TestDebugDirectoryExDLLCharacteristics(t *testing.T) { 343 344 type TestExDLLCharacteristics struct { 345 debugEntry DebugEntry 346 exDLLCharacteristics string 347 } 348 349 tests := []struct { 350 in TestDebugIn 351 out TestExDLLCharacteristics 352 }{ 353 { 354 TestDebugIn{ 355 index: 3, 356 filepath: getAbsoluteFilePath("test/kernel32.dll"), 357 }, 358 TestExDLLCharacteristics{ 359 debugEntry: DebugEntry{ 360 Struct: ImageDebugDirectory{ 361 Characteristics: 0x0, 362 TimeDateStamp: 0x38b369c4, 363 MajorVersion: 0x0, 364 MinorVersion: 0x0, 365 Type: 0x14, 366 SizeOfData: 0x4, 367 AddressOfRawData: 0x938b0, 368 PointerToRawData: 0x922b0, 369 }, 370 Info: DllCharacteristicsExType(0x1), 371 Type: "Ex.DLL Characteristics", 372 }, 373 exDLLCharacteristics: "CET Compatible", 374 }, 375 }, 376 } 377 378 for _, tt := range tests { 379 t.Run(tt.in.filepath, func(t *testing.T) { 380 ops := Options{Fast: true} 381 file, err := New(tt.in.filepath, &ops) 382 if err != nil { 383 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 384 } 385 386 err = file.Parse() 387 if err != nil { 388 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 389 } 390 391 var va, size uint32 392 393 if file.Is64 { 394 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 395 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 396 va = dirEntry.VirtualAddress 397 size = dirEntry.Size 398 } else { 399 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 400 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 401 va = dirEntry.VirtualAddress 402 size = dirEntry.Size 403 } 404 405 err = file.parseDebugDirectory(va, size) 406 if err != nil { 407 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", 408 tt.in.filepath, err) 409 } 410 411 debugEntry := file.Debugs[tt.in.index] 412 if !reflect.DeepEqual(debugEntry, tt.out.debugEntry) { 413 t.Fatalf("debug entry assertion failed, got %v, want %v", 414 debugEntry, tt.out.debugEntry) 415 } 416 417 dllCharacteristicsExString := debugEntry.Info.(DllCharacteristicsExType).String() 418 if dllCharacteristicsExString != tt.out.exDLLCharacteristics { 419 t.Fatalf("debug entry DllCharacteristicsEx string assertion failed, got %v, want %v", 420 dllCharacteristicsExString, tt.out.exDLLCharacteristics) 421 } 422 }) 423 } 424 } 425 426 func TestDebugDirectoryVCFeature(t *testing.T) { 427 428 type TestVCFeature struct { 429 debugEntry DebugEntry 430 } 431 432 tests := []struct { 433 in TestDebugIn 434 out TestVCFeature 435 }{ 436 { 437 TestDebugIn{ 438 index: 1, 439 filepath: getAbsoluteFilePath("test/00da1a2a9d9ebf447508bf6550f05f466f8eabb4ed6c4f2a524c0769b2d75bc1"), 440 }, 441 TestVCFeature{ 442 debugEntry: DebugEntry{ 443 Struct: ImageDebugDirectory{ 444 Characteristics: 0x0, 445 TimeDateStamp: 0x5ef47ea0, 446 MajorVersion: 0x0, 447 MinorVersion: 0x0, 448 Type: 0xc, 449 SizeOfData: 0x14, 450 AddressOfRawData: 0x39d58, 451 PointerToRawData: 0x39158, 452 }, 453 Info: VCFeature{ 454 PreVC11: 0xa, 455 CCpp: 0x115, 456 Gs: 0xe4, 457 Sdl: 0x0, 458 GuardN: 0x115, 459 }, 460 Type: "VC Feature", 461 }, 462 }, 463 }, 464 } 465 466 for _, tt := range tests { 467 t.Run(tt.in.filepath, func(t *testing.T) { 468 ops := Options{Fast: true} 469 file, err := New(tt.in.filepath, &ops) 470 if err != nil { 471 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 472 } 473 474 err = file.Parse() 475 if err != nil { 476 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 477 } 478 479 var va, size uint32 480 481 if file.Is64 { 482 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 483 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 484 va = dirEntry.VirtualAddress 485 size = dirEntry.Size 486 } else { 487 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 488 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 489 va = dirEntry.VirtualAddress 490 size = dirEntry.Size 491 } 492 493 err = file.parseDebugDirectory(va, size) 494 if err != nil { 495 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", 496 tt.in.filepath, err) 497 } 498 499 debugEntry := file.Debugs[tt.in.index] 500 if !reflect.DeepEqual(debugEntry, tt.out.debugEntry) { 501 t.Fatalf("debug entry assertion failed, got %+v, want %+v", 502 debugEntry, tt.out.debugEntry) 503 } 504 505 }) 506 } 507 } 508 509 func TestDebugDirectoryFPO(t *testing.T) { 510 511 type TestFPO struct { 512 imgDebugEntry ImageDebugDirectory 513 entriesCount int 514 debugType string 515 FPODataIndex int 516 FPOData FPOData 517 FPOFrameType string 518 } 519 520 tests := []struct { 521 in TestDebugIn 522 out TestFPO 523 }{ 524 { 525 TestDebugIn{ 526 index: 1, 527 filepath: getAbsoluteFilePath("test/jobexec.dll"), 528 }, 529 TestFPO{ 530 imgDebugEntry: ImageDebugDirectory{ 531 Characteristics: 0x0, 532 TimeDateStamp: 0x355b8e5f, 533 MajorVersion: 0x0, 534 MinorVersion: 0x0, 535 Type: 0x3, 536 SizeOfData: 0x840, 537 AddressOfRawData: 0x0, 538 PointerToRawData: 0xb310, 539 }, 540 debugType: "FPO", 541 entriesCount: 131, 542 FPODataIndex: 0, 543 FPOData: FPOData{ 544 OffsetStart: 0x1bc0, 545 ProcSize: 0x22, 546 }, 547 FPOFrameType: "FPO", 548 }, 549 }, 550 { 551 TestDebugIn{ 552 index: 1, 553 filepath: getAbsoluteFilePath("test/jobexec.dll"), 554 }, 555 TestFPO{ 556 imgDebugEntry: ImageDebugDirectory{ 557 Characteristics: 0x0, 558 TimeDateStamp: 0x355b8e5f, 559 MajorVersion: 0x0, 560 MinorVersion: 0x0, 561 Type: 0x3, 562 SizeOfData: 0x840, 563 AddressOfRawData: 0x0, 564 PointerToRawData: 0xb310, 565 }, 566 debugType: "FPO", 567 entriesCount: 131, 568 FPODataIndex: 2, 569 FPOData: FPOData{ 570 OffsetStart: 0x1c26, 571 ProcSize: 0x267, 572 NumLocals: 0x104, 573 ParamsSize: 0x1, 574 PrologLength: 0x16, 575 SavedRegsCount: 0x3, 576 HasSEH: 0x0, 577 UseBP: 0x1, 578 Reserved: 0x0, 579 FrameType: 0x3, 580 }, 581 FPOFrameType: "Non FPO", 582 }, 583 }, 584 } 585 586 for _, tt := range tests { 587 t.Run(tt.in.filepath, func(t *testing.T) { 588 ops := Options{Fast: true} 589 file, err := New(tt.in.filepath, &ops) 590 if err != nil { 591 t.Fatalf("New(%s) failed, reason: %v", tt.in.filepath, err) 592 } 593 594 err = file.Parse() 595 if err != nil { 596 t.Fatalf("Parse(%s) failed, reason: %v", tt.in.filepath, err) 597 } 598 599 var va, size uint32 600 601 if file.Is64 { 602 oh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64) 603 dirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug] 604 va = dirEntry.VirtualAddress 605 size = dirEntry.Size 606 } else { 607 oh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32) 608 dirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug] 609 va = dirEntry.VirtualAddress 610 size = dirEntry.Size 611 } 612 613 err = file.parseDebugDirectory(va, size) 614 if err != nil { 615 t.Fatalf("parseExportDirectory(%s) failed, reason: %v", tt.in.filepath, err) 616 } 617 618 imgDebugEntry := file.Debugs[tt.in.index].Struct 619 if !reflect.DeepEqual(imgDebugEntry, tt.out.imgDebugEntry) { 620 t.Fatalf("debug entry assertion failed, got %v, want %v", 621 imgDebugEntry, tt.out.imgDebugEntry) 622 } 623 624 debugTypeString := file.Debugs[tt.in.index].Type 625 if debugTypeString != tt.out.debugType { 626 t.Fatalf("debug type assertion failed, got %v, want %v", 627 debugTypeString, tt.out.debugType) 628 } 629 630 fpo := file.Debugs[tt.in.index].Info.([]FPOData) 631 entriesCount := len(fpo) 632 if entriesCount != tt.out.entriesCount { 633 t.Fatalf("debug entry count failed, got %v, want %v", 634 entriesCount, tt.out.entriesCount) 635 } 636 637 fpoData := fpo[tt.out.FPODataIndex] 638 if !reflect.DeepEqual(fpoData, tt.out.FPOData) { 639 t.Fatalf("debug FPO data entry assertion failed, got %v, want %v", 640 fpoData, tt.out.FPOData) 641 } 642 643 frameType := fpoData.FrameType.String() 644 if frameType != tt.out.FPOFrameType { 645 t.Fatalf("debug FPO frame type string assertion failed, got %v, want %v", 646 frameType, tt.out.FPOFrameType) 647 } 648 }) 649 } 650 } 651 652 func TestDebugSectionAttributes(t *testing.T) { 653 654 tests := []struct { 655 in string 656 out string 657 }{ 658 { 659 660 ".00cfg", 661 "CFG Check Functions Pointers", 662 }, 663 { 664 "__undefined__", 665 "", 666 }, 667 } 668 669 for _, tt := range tests { 670 t.Run(tt.out, func(t *testing.T) { 671 672 secAttrString := SectionAttributeDescription(tt.in) 673 if secAttrString != tt.out { 674 t.Fatalf("debug section attributes description failed, got %v, want %v", 675 secAttrString, tt.out) 676 } 677 }) 678 } 679 }