HyperDbg Debugger
Loading...
Searching...
No Matches
testcases.h File Reference

header for test cases More...

Go to the source code of this file.

Functions

BOOLEAN TestCommandParser ()
 Test command parser.
BOOLEAN TestPeParser ()
 Runs all PE parser unit tests and reports pass/fail results.
BOOLEAN TestCodeViewRsdsParser ()
 Runs a series of test cases to validate the behavior of the RSDS parser helper functions.
BOOLEAN TestSemanticScripts ()
 Test semantic scripts.

Detailed Description

header for test cases

Author
Sina Karvandi (sina@.nosp@m.hype.nosp@m.rdbg..nosp@m.org)
Version
0.11
Date
2024-08-11

Function Documentation

◆ TestCodeViewRsdsParser()

BOOLEAN TestCodeViewRsdsParser ( )

Runs a series of test cases to validate the behavior of the RSDS parser helper functions.

Returns
BOOLEAN TRUE if all test cases passed, FALSE if any test case failed
450{
451 BYTE Buffer[RsdsFixtureSize] = {0};
452 INT32 TestNum = 0;
453
454 RsdsBuildMinimalPe(Buffer, FALSE);
455 RsdsWriteValidDebugEntry(Buffer, RsdsGuid64, 7, "symbols\\valid64.pdb");
456 TestNum++;
457 if (RsdsExpectSuccess(Buffer, "valid64.pdb", RsdsGuid64, 7))
458 {
459 printf("[+] Test number %d Passed\n", TestNum);
460 }
461 else
462 {
463 printf("[-] Test number %d Failed\n", TestNum);
464 printf("[x] valid PE32+ RSDS entry was not parsed\n");
465 return FALSE;
466 }
467
468 RsdsBuildMinimalPe(Buffer, FALSE);
469 RsdsWriteValidDebugEntry(Buffer, RsdsGuid64, 7, "symbols\\unadvertised.pdb");
470 RsdsSetNumberOfRvaAndSizes(Buffer, IMAGE_DIRECTORY_ENTRY_DEBUG);
471 TestNum++;
472 if (RsdsExpectFailure(Buffer))
473 {
474 printf("[+] Test number %d Passed\n", TestNum);
475 }
476 else
477 {
478 printf("[-] Test number %d Failed\n", TestNum);
479 printf("[x] raw parser accepted an unadvertised debug directory\n");
480 return FALSE;
481 }
482
483 RsdsBuildMinimalPe(Buffer, TRUE);
484 RsdsWriteValidDebugEntry(Buffer, RsdsGuid32, 9, "symbols/valid32.pdb");
485 TestNum++;
486 if (RsdsExpectSuccess(Buffer, "valid32.pdb", RsdsGuid32, 9))
487 {
488 printf("[+] Test number %d Passed\n", TestNum);
489 }
490 else
491 {
492 printf("[-] Test number %d Failed\n", TestNum);
493 printf("[x] valid PE32 RSDS entry was not parsed\n");
494 return FALSE;
495 }
496
497 RsdsBuildMinimalPe(Buffer, FALSE);
498 RsdsWriteValidDebugEntry(Buffer, RsdsGuid64, 7, "symbols\\valid64.pdb");
499 RsdsSetDebugDirectory(Buffer, 0x3000, sizeof(IMAGE_DEBUG_DIRECTORY));
500 TestNum++;
501 if (RsdsExpectFailure(Buffer))
502 {
503 printf("[+] Test number %d Passed\n", TestNum);
504 }
505 else
506 {
507 printf("[-] Test number %d Failed\n", TestNum);
508 printf("[x] unmapped debug directory parsed successfully\n");
509 return FALSE;
510 }
511
512 RsdsBuildMinimalPe(Buffer, FALSE);
513 RsdsWriteValidDebugEntry(Buffer, RsdsGuid64, 7, "symbols\\invalid.pdb");
514 CopyMemory(Buffer + RsdsPayloadRaw, "ABCD", sizeof(DWORD));
515 TestNum++;
516 if (RsdsExpectFailure(Buffer))
517 {
518 printf("[+] Test number %d Passed\n", TestNum);
519 }
520 else
521 {
522 printf("[-] Test number %d Failed\n", TestNum);
523 printf("[x] unsupported CodeView signature parsed successfully\n");
524 return FALSE;
525 }
526
527 RsdsBuildMinimalPe(Buffer, FALSE);
528 RsdsWritePayload(Buffer, RsdsPayloadRaw, RsdsGuid64, 7, "symbols\\missing-nul.pdb", FALSE);
529 RsdsWriteDebugEntry(Buffer,
530 RsdsDebugDirectoryRaw,
531 IMAGE_DEBUG_TYPE_CODEVIEW,
532 RsdsPayloadRva,
533 RsdsPayloadRaw,
534 RsdsPayloadSize("symbols\\missing-nul.pdb", FALSE));
535 TestNum++;
536 if (RsdsExpectFailure(Buffer))
537 {
538 printf("[+] Test number %d Passed\n", TestNum);
539 }
540 else
541 {
542 printf("[-] Test number %d Failed\n", TestNum);
543 printf("[x] RSDS path without NUL terminator parsed successfully\n");
544 return FALSE;
545 }
546
547 RsdsBuildMinimalPe(Buffer, FALSE);
548 RsdsSetDebugDirectory(Buffer, RsdsDebugDirectoryRva, sizeof(IMAGE_DEBUG_DIRECTORY) * 2);
549 RsdsWriteDebugEntry(Buffer, RsdsDebugDirectoryRaw, IMAGE_DEBUG_TYPE_CODEVIEW, RsdsPayloadRva, RsdsPayloadRaw, sizeof(DWORD));
550 CopyMemory(Buffer + RsdsPayloadRaw, "NB10", sizeof(DWORD));
551 RsdsWritePayload(Buffer, 0x390, RsdsGuidMulti, 11, "alt/second-valid.pdb", TRUE);
552 RsdsWriteDebugEntry(Buffer,
553 RsdsDebugDirectoryRaw + sizeof(IMAGE_DEBUG_DIRECTORY),
554 IMAGE_DEBUG_TYPE_CODEVIEW,
555 0x1190,
556 0x390,
557 RsdsPayloadSize("alt/second-valid.pdb", TRUE));
558 TestNum++;
559 if (RsdsExpectSuccess(Buffer, "second-valid.pdb", RsdsGuidMulti, 11))
560 {
561 printf("[+] Test number %d Passed\n", TestNum);
562 }
563 else
564 {
565 printf("[-] Test number %d Failed\n", TestNum);
566 printf("[x] parser did not skip invalid first entry and parse second RSDS entry\n");
567 return FALSE;
568 }
569
570 RsdsBuildLoadedPe(Buffer, FALSE);
571 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid64, 0x21, "loaded\\loaded64.pdb");
572 TestNum++;
573 if (RsdsExpectLoadedSuccess(Buffer, "loaded64.pdb", RsdsGuid64, 0x21))
574 {
575 printf("[+] Test number %d Passed\n", TestNum);
576 }
577 else
578 {
579 printf("[-] Test number %d Failed\n", TestNum);
580 printf("[x] loaded PE32+ RSDS entry was not parsed\n");
581 return FALSE;
582 }
583
584 RsdsBuildLoadedPe(Buffer, FALSE);
585 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid64, 0x21, "loaded\\unadvertised.pdb");
586 RsdsSetNumberOfRvaAndSizes(Buffer, IMAGE_DIRECTORY_ENTRY_DEBUG);
587 TestNum++;
588 if (RsdsExpectLoadedFailure(Buffer))
589 {
590 printf("[+] Test number %d Passed\n", TestNum);
591 }
592 else
593 {
594 printf("[-] Test number %d Failed\n", TestNum);
595 printf("[x] loaded parser accepted an unadvertised debug directory\n");
596 return FALSE;
597 }
598
599 RsdsBuildLoadedPe(Buffer, TRUE);
600 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid32, 0x22, "loaded/loaded32.pdb");
601 TestNum++;
602 if (RsdsExpectLoadedSuccess(Buffer, "loaded32.pdb", RsdsGuid32, 0x22))
603 {
604 printf("[+] Test number %d Passed\n", TestNum);
605 }
606 else
607 {
608 printf("[-] Test number %d Failed\n", TestNum);
609 printf("[x] loaded PE32 RSDS entry was not parsed\n");
610 return FALSE;
611 }
612
613 RsdsBuildLoadedPe(Buffer, FALSE);
614 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid64, 0x23, "loaded\\bogus-raw-ignored.pdb");
615 TestNum++;
616 if (RsdsExpectLoadedSuccess(Buffer, "bogus-raw-ignored.pdb", RsdsGuid64, 0x23))
617 {
618 printf("[+] Test number %d Passed\n", TestNum);
619 }
620 else
621 {
622 printf("[-] Test number %d Failed\n", TestNum);
623 printf("[x] loaded parser used bogus PointerToRawData instead of loaded RVA\n");
624 return FALSE;
625 }
626
627 std::vector<BYTE> HighLoadedBuffer(RsdsHighLoadedSize);
628 RsdsBuildLoadedPe(HighLoadedBuffer.data(), FALSE);
629 RsdsSetSizeOfImage(HighLoadedBuffer.data(), (DWORD)HighLoadedBuffer.size());
630 RsdsSetDebugDirectory(HighLoadedBuffer.data(), RsdsHighLoadedDebugRva, sizeof(IMAGE_DEBUG_DIRECTORY));
631 RsdsWritePayload(HighLoadedBuffer.data(), RsdsHighLoadedPayloadRva, RsdsGuid64, 0x28, "loaded\\high-rva.pdb", TRUE);
632 RsdsWriteDebugEntry(HighLoadedBuffer.data(),
633 RsdsHighLoadedDebugRva,
634 IMAGE_DEBUG_TYPE_CODEVIEW,
635 RsdsHighLoadedPayloadRva,
636 RsdsBogusRawPointer,
637 RsdsPayloadSize("loaded\\high-rva.pdb", TRUE));
638 CHAR HighLoadedPdbFileName[MAX_PATH] = {0};
639 GUID HighLoadedGuid = {0};
640 DWORD HighLoadedAge = 0;
641 TestNum++;
642 if (SymExtractCodeViewRsdsInfoFromLoadedPeImage(HighLoadedBuffer.data(),
643 HighLoadedBuffer.size(),
644 HighLoadedPdbFileName,
645 sizeof(HighLoadedPdbFileName),
646 &HighLoadedGuid,
647 &HighLoadedAge) &&
648 strcmp(HighLoadedPdbFileName, "high-rva.pdb") == 0 && RsdsGuidEquals(HighLoadedGuid, RsdsGuid64) &&
649 HighLoadedAge == 0x28)
650 {
651 printf("[+] Test number %d Passed\n", TestNum);
652 }
653 else
654 {
655 printf("[-] Test number %d Failed\n", TestNum);
656 printf("[x] loaded parser did not parse high-RVA RSDS data\n");
657 return FALSE;
658 }
659
660 RsdsBuildLoadedPe(Buffer, FALSE);
661 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid64, 0x24, "loaded\\invalid-dir.pdb");
662 RsdsSetDebugDirectory(Buffer, RsdsFixtureSize - sizeof(IMAGE_DEBUG_DIRECTORY) / 2, sizeof(IMAGE_DEBUG_DIRECTORY));
663 TestNum++;
664 if (RsdsExpectLoadedFailure(Buffer))
665 {
666 printf("[+] Test number %d Passed\n", TestNum);
667 }
668 else
669 {
670 printf("[-] Test number %d Failed\n", TestNum);
671 printf("[x] loaded parser accepted malformed debug directory bounds\n");
672 return FALSE;
673 }
674
675 RsdsBuildLoadedPe(Buffer, FALSE);
676 RsdsWriteValidLoadedDebugEntry(Buffer, RsdsGuid64, 0x25, "loaded\\unsupported.pdb");
677 CopyMemory(Buffer + RsdsLoadedPayloadRva, "ABCD", sizeof(DWORD));
678 TestNum++;
679 if (RsdsExpectLoadedFailure(Buffer))
680 {
681 printf("[+] Test number %d Passed\n", TestNum);
682 }
683 else
684 {
685 printf("[-] Test number %d Failed\n", TestNum);
686 printf("[x] loaded parser accepted unsupported CodeView signature\n");
687 return FALSE;
688 }
689
690 RsdsBuildLoadedPe(Buffer, FALSE);
691 RsdsWritePayload(Buffer, RsdsLoadedPayloadRva, RsdsGuid64, 0x26, "loaded\\missing-nul.pdb", FALSE);
692 RsdsWriteDebugEntry(Buffer,
693 RsdsLoadedDebugRva,
694 IMAGE_DEBUG_TYPE_CODEVIEW,
695 RsdsLoadedPayloadRva,
696 RsdsBogusRawPointer,
697 RsdsPayloadSize("loaded\\missing-nul.pdb", FALSE));
698 TestNum++;
699 if (RsdsExpectLoadedFailure(Buffer))
700 {
701 printf("[+] Test number %d Passed\n", TestNum);
702 }
703 else
704 {
705 printf("[-] Test number %d Failed\n", TestNum);
706 printf("[x] loaded parser accepted RSDS path without NUL terminator\n");
707 return FALSE;
708 }
709
710 RsdsBuildLoadedPe(Buffer, FALSE);
711 RsdsSetDebugDirectory(Buffer, RsdsLoadedDebugRva, sizeof(IMAGE_DEBUG_DIRECTORY) * 2);
712 RsdsWriteDebugEntry(Buffer, RsdsLoadedDebugRva, IMAGE_DEBUG_TYPE_CODEVIEW, RsdsLoadedPayloadRva, RsdsBogusRawPointer, sizeof(DWORD));
713 CopyMemory(Buffer + RsdsLoadedPayloadRva, "NB10", sizeof(DWORD));
714 RsdsWritePayload(Buffer, 0x330, RsdsGuidMulti, 0x27, "loaded/second-loaded.pdb", TRUE);
715 RsdsWriteDebugEntry(Buffer,
716 RsdsLoadedDebugRva + sizeof(IMAGE_DEBUG_DIRECTORY),
717 IMAGE_DEBUG_TYPE_CODEVIEW,
718 0x330,
719 RsdsBogusRawPointer,
720 RsdsPayloadSize("loaded/second-loaded.pdb", TRUE));
721 TestNum++;
722 if (RsdsExpectLoadedSuccess(Buffer, "second-loaded.pdb", RsdsGuidMulti, 0x27))
723 {
724 printf("[+] Test number %d Passed\n", TestNum);
725 }
726 else
727 {
728 printf("[-] Test number %d Failed\n", TestNum);
729 printf("[x] loaded parser did not skip invalid first entry and parse second RSDS entry\n");
730 return FALSE;
731 }
732
733 CHAR SymbolServerRelativePath[MAX_PATH] = {0};
734 CHAR GuidAndAgeDetails[MAX_PATH] = {0};
735 CHAR SmallGuidAndAgeDetails[MAXIMUM_GUID_AND_AGE_SIZE] = {0};
736 const GUID Guid = {0x01234567, 0x89ab, 0xcdef, {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}};
737 TestNum++;
738 if (SymFormatPdbIdentity("valid32.pdb",
739 &Guid,
740 0x1a,
741 SymbolServerRelativePath,
742 sizeof(SymbolServerRelativePath),
743 GuidAndAgeDetails,
744 sizeof(GuidAndAgeDetails)) &&
745 strcmp(GuidAndAgeDetails, "0123456789abcdeffedcba98765432101a") == 0 &&
746 strcmp(SymbolServerRelativePath, "valid32.pdb/0123456789abcdeffedcba98765432101a/valid32.pdb") == 0)
747 {
748 printf("[+] Test number %d Passed\n", TestNum);
749 }
750 else
751 {
752 printf("[-] Test number %d Failed\n", TestNum);
753 printf("[x] PDB identity formatting did not match symbol server path and GUID+age expectations\n");
754 return FALSE;
755 }
756
757 TestNum++;
758 if (SymFormatPdbIdentity("valid32.pdb",
759 &Guid,
760 0x1a,
761 NULL,
762 0,
763 SmallGuidAndAgeDetails,
764 sizeof(SmallGuidAndAgeDetails)) &&
765 strcmp(SmallGuidAndAgeDetails, "0123456789abcdeffedcba98765432101a") == 0)
766 {
767 printf("[+] Test number %d Passed\n", TestNum);
768 }
769 else
770 {
771 printf("[-] Test number %d Failed\n", TestNum);
772 printf("[x] GUID+age identity did not fit the SDK-sized buffer\n");
773 return FALSE;
774 }
775
776 RsdsBuildMinimalPe(Buffer, FALSE);
777 RsdsWriteValidDebugEntry(Buffer, RsdsGuid64, 0x1c, "preferred\\preferred.pdb");
778 RSDS_FAKE_FALLBACK_CONTEXT FallbackContext = {0, TRUE};
779 CHAR PreferredPath[MAX_PATH] = {0};
780 TestNum++;
782 RsdsFixtureSize,
783 PreferredPath,
784 sizeof(PreferredPath),
785 NULL,
786 0,
787 NULL,
788 0,
789 RsdsFakeFallback,
790 &FallbackContext) &&
791 strcmp(PreferredPath, "preferred.pdb/67452301ab89efcd1032547698badcfe1c/preferred.pdb") == 0 &&
792 FallbackContext.CallCount == 0)
793 {
794 printf("[+] Test number %d Passed\n", TestNum);
795 }
796 else
797 {
798 printf("[-] Test number %d Failed\n", TestNum);
799 printf("[x] PE RSDS identity was not preferred over fallback identity\n");
800 return FALSE;
801 }
802
803 FallbackContext.CallCount = 0;
804 CHAR ZeroSizeOutput = 'x';
805 TestNum++;
807 RsdsFixtureSize,
808 &ZeroSizeOutput,
809 0,
810 NULL,
811 0,
812 NULL,
813 0,
814 RsdsFakeFallback,
815 &FallbackContext) &&
816 ZeroSizeOutput == 'x' && FallbackContext.CallCount == 0)
817 {
818 printf("[+] Test number %d Passed\n", TestNum);
819 }
820 else
821 {
822 printf("[-] Test number %d Failed\n", TestNum);
823 printf("[x] zero-sized output buffer was written or reported success\n");
824 return FALSE;
825 }
826
827 FallbackContext.CallCount = 0;
828 TestNum++;
830 RsdsFixtureSize,
831 NULL,
832 0,
833 NULL,
834 0,
835 NULL,
836 0,
837 RsdsFakeFallback,
838 &FallbackContext) &&
839 FallbackContext.CallCount == 0)
840 {
841 printf("[+] Test number %d Passed\n", TestNum);
842 }
843 else
844 {
845 printf("[-] Test number %d Failed\n", TestNum);
846 printf("[x] identity formatting without requested output reported success or used fallback\n");
847 return FALSE;
848 }
849
850 FallbackContext.CallCount = 0;
851 CHAR SmallPdbPath[4] = {'x'};
852 CHAR SmallFailureGuidAge[64] = {'x'};
853 TestNum++;
855 RsdsFixtureSize,
856 NULL,
857 0,
858 SmallPdbPath,
859 sizeof(SmallPdbPath),
860 SmallFailureGuidAge,
861 sizeof(SmallFailureGuidAge),
862 RsdsFakeFallback,
863 &FallbackContext) &&
864 SmallPdbPath[0] == '\0' && SmallFailureGuidAge[0] == '\0' && FallbackContext.CallCount == 0)
865 {
866 printf("[+] Test number %d Passed\n", TestNum);
867 }
868 else
869 {
870 printf("[-] Test number %d Failed\n", TestNum);
871 printf("[x] output formatting failure leaked partial identity data\n");
872 return FALSE;
873 }
874
875 ZeroMemory(Buffer, sizeof(Buffer));
876 FallbackContext.CallCount = 0;
877 FallbackContext.Succeed = TRUE;
878 CHAR FallbackPdb[MAX_PATH] = {0};
879 CHAR FallbackGuidAge[MAX_PATH] = {0};
880 TestNum++;
882 RsdsFixtureSize,
883 NULL,
884 0,
885 FallbackPdb,
886 sizeof(FallbackPdb),
887 FallbackGuidAge,
888 sizeof(FallbackGuidAge),
889 RsdsFakeFallback,
890 &FallbackContext) &&
891 strcmp(FallbackPdb, "fallback.pdb") == 0 &&
892 strcmp(FallbackGuidAge, "aabbccddeeff112233445566778899aa2b") == 0 &&
893 FallbackContext.CallCount == 1)
894 {
895 printf("[+] Test number %d Passed\n", TestNum);
896 }
897 else
898 {
899 printf("[-] Test number %d Failed\n", TestNum);
900 printf("[x] malformed PE bytes did not use fallback identity\n");
901 return FALSE;
902 }
903
904 FallbackContext.CallCount = 0;
905 FallbackContext.Succeed = FALSE;
906 CHAR FailedPath[MAX_PATH] = {'x'};
907 CHAR FailedPdb[MAX_PATH] = {'x'};
908 CHAR FailedGuidAge[MAX_PATH] = {'x'};
909 TestNum++;
911 RsdsFixtureSize,
912 FailedPath,
913 sizeof(FailedPath),
914 FailedPdb,
915 sizeof(FailedPdb),
916 FailedGuidAge,
917 sizeof(FailedGuidAge),
918 RsdsFakeFallback,
919 &FallbackContext) &&
920 FailedPath[0] == '\0' && FailedPdb[0] == '\0' && FailedGuidAge[0] == '\0' && FallbackContext.CallCount == 1)
921 {
922 printf("[+] Test number %d Passed\n", TestNum);
923 }
924 else
925 {
926 printf("[-] Test number %d Failed\n", TestNum);
927 printf("[x] fallback failure reported success or left success-looking output\n");
928 return FALSE;
929 }
930
931 return TRUE;
932}
signed int INT32
Definition BasicTypes.h:50
unsigned char BYTE
Definition BasicTypes.h:40
#define TRUE
Definition BasicTypes.h:114
#define FALSE
Definition BasicTypes.h:113
unsigned long DWORD
Definition BasicTypes.h:38
char CHAR
Definition BasicTypes.h:33
#define MAXIMUM_GUID_AND_AGE_SIZE
maximum size for GUID and Age of PE @detail It seems that 33 bytes is enough but let's have more spac...
Definition Constants.h:493
BOOLEAN SymExtractCodeViewRsdsInfoFromLoadedPeImage(const BYTE *ImageBase, SIZE_T ImageSize, CHAR *PdbFileName, SIZE_T PdbFileNameSize, GUID *Guid, DWORD *Age)
Extracts CodeView RSDS information from a PE image in memory, interpreting the image as it would be l...
Definition codeview-rsds.cpp:551
printf("ho")
BOOLEAN SymFormatPdbIdentity(const CHAR *PdbFile, const GUID *Guid, DWORD Age, CHAR *SymbolServerRelativePath, SIZE_T SymbolServerRelativePathSize, CHAR *GuidAndAgeDetails, SIZE_T GuidAndAgeDetailsSize)
Helper function to format the PDB identity information into the specified output buffers.
Definition pdb-identity.cpp:67
BOOLEAN SymFormatPdbIdentityFromPeImageOrFallback(const BYTE *PeImageBytes, SIZE_T PeImageSize, CHAR *SymbolServerRelativePath, SIZE_T SymbolServerRelativePathSize, CHAR *PdbFilePath, SIZE_T PdbFilePathSize, CHAR *GuidAndAgeDetails, SIZE_T GuidAndAgeDetailsSize, PSYM_PDB_IDENTITY_FALLBACK_CALLBACK FallbackCallback, PVOID FallbackContext)
Extracts PDB identity information from a PE image using the specified extractor callback,...
Definition pdb-identity.cpp:267
BOOLEAN Succeed
Definition test-codeview-rsds-parser.cpp:407
INT32 CallCount
Definition test-codeview-rsds-parser.cpp:406
struct _RSDS_FAKE_FALLBACK_CONTEXT RSDS_FAKE_FALLBACK_CONTEXT

◆ TestCommandParser()

BOOLEAN TestCommandParser ( )

Test command parser.

Returns
BOOLEAN
286{
287 BOOLEAN OverallResult = TRUE;
288 INT32 TestNum = 0;
289 CHAR FilePath[MAX_PATH] = {0};
290 UINT32 FailedTokenNum = 0;
291 UINT32 FailedTokenPosition = 0;
292
293 //
294 // Parse the test cases from the file
295 // Setup the path for the filename
296 //
298 {
299 //
300 // Error could not find the test case files
301 //
302 cout << "[-] Could not find the test case files" << endl;
303 return FALSE;
304 }
305
306 //
307 // Parse the test cases from the file
308 //
309 auto TestCases = ParseTestCases(FilePath);
310
311 //
312 // Perform testing test cases with parsed file
313 //
314 cout << "Perform testing test cases with parsed file:" << endl;
315
316 //
317 // Output the parsed test cases
318 //
319 for (const auto & TestCase : TestCases)
320 {
321 TestNum++;
322
323 //
324 // Create CHAR**
325 //
326 CHAR_PTR_PTR TestCaseArray = CreateTestCaseArray(TestCase.second);
327
328 //
329 // Check token with actual parser
330 //
331 if (hyperdbg_u_test_command_parser((CHAR *)TestCase.first.c_str(),
332 (UINT32)TestCase.second.size(),
333 TestCaseArray,
334 &FailedTokenNum,
335 &FailedTokenPosition))
336 {
337 cout << "[+] Test number " << TestNum << " Passed " << endl;
338 }
339 else
340 {
341 //
342 // Set overall result to FALSE since one of the test cases failed
343 //
344 OverallResult = FALSE;
345
346 //
347 // Show parsed command and tokens
348 //
349 cout << "\n============================================================" << endl;
350 cout << "\n********************* " << endl;
351 cout << "*** Error details *** " << endl;
352 cout << "********************* " << endl;
353 cout << "\nParsed tokens from HyperDbg main command parser:\n"
354 << endl;
355
356 //
357 // Show tokens
358 //
360
361 cout << "\n============================================================" << endl;
362
363 cout << "\nThe parsed command and tokens (From file):" << endl;
364 ShowParsedCommandAndTokens(TestCase, FailedTokenNum, FailedTokenPosition);
365
366 cout << "\n[-] Test number " << TestNum << " Failed " << endl;
367 cout << "============================================================\n"
368 << endl;
369
370 break;
371 }
372
373 //
374 // Clean up memory
375 //
376 FreeTestCaseArray(TestCaseArray, TestCase.second.size());
377 }
378
379 return OverallResult;
380}
#define COMMAND_PARSER_TEST_CASES_FILE
Test cases file name for command parser.
Definition Definition.h:70
UCHAR BOOLEAN
Definition BasicTypes.h:35
unsigned int UINT32
Definition BasicTypes.h:54
IMPORT_EXPORT_LIBHYPERDBG VOID hyperdbg_u_test_command_parser_show_tokens(CHAR *command)
Parse and show tokens for the command (used for testing purposes).
Definition export.cpp:237
IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_test_command_parser(CHAR *command, UINT32 number_of_tokens, CHAR **tokens_list, UINT32 *failed_token_num, UINT32 *failed_token_position)
Parse the command (used for testing purposes).
Definition export.cpp:220
IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_setup_path_for_filename(const CHAR *filename, CHAR *file_location, UINT32 buffer_len, BOOLEAN check_file_existence)
Setip the path for the filename.
Definition export.cpp:725
BOOLEAN TestCase(std::vector< std::string > &TestCase)
std::vector< std::pair< std::string, std::vector< std::string > > > ParseTestCases(const std::string &Filename)
Parse the test cases from the file.
Definition test-parser.cpp:78
CHAR ** CHAR_PTR_PTR
Definition test-parser.cpp:14
VOID FreeTestCaseArray(CHAR_PTR_PTR TestCaseArray, SIZE_T Size)
Free the memory allocated for the test case array.
Definition test-parser.cpp:55
CHAR_PTR_PTR CreateTestCaseArray(const std::vector< std::string > &TestCases)
Create an array of strings from a vector of strings.
Definition test-parser.cpp:23
VOID ShowParsedCommandAndTokens(const std::pair< std::string, std::vector< std::string > > &TestCase, UINT32 FailedTokenNum, UINT32 FailedTokenPosition)
Show parsed command and tokens.
Definition test-parser.cpp:214

◆ TestPeParser()

BOOLEAN TestPeParser ( )

Runs all PE parser unit tests and reports pass/fail results.

Each numbered test case exercises a distinct behaviour of the PE image reader:

  1. A valid PE32+ image initialises successfully and reports 64-bit.
  2. A valid PE32 image initialises successfully and reports 32-bit.
  3. A corrupt DOS magic causes initialisation to fail.
  4. An optional header that is one byte too small causes initialisation to fail.
  5. An e_lfanew value that points past the buffer causes initialisation to fail.
  6. A valid section RVA maps to the correct raw file offset.
  7. Header-range RVA resolution is enforced at SizeOfHeaders boundaries.
  8. An 8-byte section name is always returned null-terminated.
  9. An RVA whose raw mapping extends outside the file is rejected.
Returns
BOOLEAN TRUE if all tests pass, FALSE if any test fails
214{
215 BOOLEAN OverallResult = TRUE;
216 INT32 TestNum = 0;
217 BYTE Buffer[PeFixtureSize] = {0};
218
219 BuildMinimalPe64(Buffer);
220 TestNum++;
221 {
222 PE_IMAGE_READER Reader = {0};
223
224 if (PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader) && !PeImageReaderIs32Bit(&Reader))
225 {
226 printf("[+] Test number %d Passed\n", TestNum);
227 }
228 else
229 {
230 printf("[-] Test number %d Failed\n", TestNum);
231 printf("[x] valid PE64 did not initialize as PE32+\n");
232 return FALSE;
233 }
234 }
235
236 BuildMinimalPe32(Buffer);
237 TestNum++;
238 {
239 PE_IMAGE_READER Reader = {0};
240
241 if (PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader) && PeImageReaderIs32Bit(&Reader))
242 {
243 printf("[+] Test number %d Passed\n", TestNum);
244 }
245 else
246 {
247 printf("[-] Test number %d Failed\n", TestNum);
248 printf("[x] valid PE32 did not initialize as PE32\n");
249 return FALSE;
250 }
251 }
252
253 BuildMinimalPe64(Buffer);
254 WriteWord(Buffer, 0, 0);
255 TestNum++;
256 {
257 PE_IMAGE_READER Reader = {0};
258
259 if (!PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader))
260 {
261 printf("[+] Test number %d Passed\n", TestNum);
262 }
263 else
264 {
265 printf("[-] Test number %d Failed\n", TestNum);
266 printf("[x] invalid DOS magic initialized successfully\n");
267 return FALSE;
268 }
269 }
270
271 BuildMinimalPe64(Buffer);
272 WriteWord(Buffer,
273 PeHeaderOffset + sizeof(DWORD) + offsetof(IMAGE_FILE_HEADER, SizeOfOptionalHeader),
274 sizeof(IMAGE_OPTIONAL_HEADER64) - 1);
275 TestNum++;
276 {
277 PE_IMAGE_READER Reader = {0};
278
279 if (!PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader))
280 {
281 printf("[+] Test number %d Passed\n", TestNum);
282 }
283 else
284 {
285 printf("[-] Test number %d Failed\n", TestNum);
286 printf("[x] truncated optional header initialized successfully\n");
287 return FALSE;
288 }
289 }
290
291 BuildMinimalPe64(Buffer);
292 WriteDword(Buffer, offsetof(IMAGE_DOS_HEADER, e_lfanew), PeFixtureSize);
293 TestNum++;
294 {
295 PE_IMAGE_READER Reader = {0};
296
297 if (!PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader))
298 {
299 printf("[+] Test number %d Passed\n", TestNum);
300 }
301 else
302 {
303 printf("[-] Test number %d Failed\n", TestNum);
304 printf("[x] invalid e_lfanew initialized successfully\n");
305 return FALSE;
306 }
307 }
308
309 BuildMinimalPe64(Buffer);
310 SetPe64OptionalHeaderSizeOfHeaders(Buffer, 0x1c0);
311 ConfigureTextSection(Buffer, sizeof(IMAGE_OPTIONAL_HEADER64), 0x1000, 0x50, 0x1c0, 0x40);
312 TestNum++;
313 {
314 PE_IMAGE_READER Reader = {0};
315 SIZE_T FileOffset = 0;
316
317 if (PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader) &&
318 PeImageReaderRvaToFileOffset(&Reader, 0x1010, 4, &FileOffset) && FileOffset == 0x1d0)
319 {
320 printf("[+] Test number %d Passed\n", TestNum);
321 }
322 else
323 {
324 printf("[-] Test number %d Failed\n", TestNum);
325 printf("[x] valid section RVA did not map to raw file offset\n");
326 return FALSE;
327 }
328 }
329
330 BuildMinimalPe64(Buffer);
331 SetPe64OptionalHeaderSizeOfHeaders(Buffer, 0x1c0);
332 TestNum++;
333 {
334 PE_IMAGE_READER Reader = {0};
335 SIZE_T FileOffset = 0;
336
337 if (PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader) &&
338 PeImageReaderRvaToFileOffset(&Reader, 0x20, 4, &FileOffset) && FileOffset == 0x20 &&
339 !PeImageReaderRvaToFileOffset(&Reader, 0x1be, 4, &FileOffset))
340 {
341 printf("[+] Test number %d Passed\n", TestNum);
342 }
343 else
344 {
345 printf("[-] Test number %d Failed\n", TestNum);
346 printf("[x] header RVA bounds were not enforced\n");
347 return FALSE;
348 }
349 }
350
351 TestNum++;
352 {
353 IMAGE_SECTION_HEADER SectionHeader = {0};
354 CHAR Name[9];
355
356 FillMemory(Name, sizeof(Name), 'X');
357 CopyMemory(SectionHeader.Name, "ABCDEFGH", IMAGE_SIZEOF_SHORT_NAME);
358 if (PeImageReaderGetSectionName(&SectionHeader, Name, sizeof(Name)) &&
359 strcmp(Name, "ABCDEFGH") == 0 && Name[IMAGE_SIZEOF_SHORT_NAME] == '\0')
360 {
361 printf("[+] Test number %d Passed\n", TestNum);
362 }
363 else
364 {
365 printf("[-] Test number %d Failed\n", TestNum);
366 printf("[x] 8-byte section name was not null-terminated\n");
367 return FALSE;
368 }
369 }
370
371 BuildMinimalPe64(Buffer);
372 SetPe64OptionalHeaderSizeOfHeaders(Buffer, 0x1c0);
373 ConfigureTextSection(Buffer, sizeof(IMAGE_OPTIONAL_HEADER64), 0x1000, 0x40, 0x300, 0x20);
374 TestNum++;
375 {
376 PE_IMAGE_READER Reader = {0};
377 SIZE_T FileOffset = 0;
378
379 if (PeImageReaderInitialize(Buffer, sizeof(Buffer), &Reader) &&
380 !PeImageReaderRvaToFileOffset(&Reader, 0x1000, 1, &FileOffset))
381 {
382 printf("[+] Test number %d Passed\n", TestNum);
383 }
384 else
385 {
386 printf("[-] Test number %d Failed\n", TestNum);
387 printf("[x] RVA mapping accepted raw pointer outside file\n");
388 OverallResult = FALSE;
389 }
390 }
391
392 return OverallResult;
393}
BOOLEAN PeImageReaderIs32Bit(PPE_IMAGE_READER Reader)
Returns whether the PE image is a 32-bit (PE32) image.
Definition pe-image-reader.cpp:194
BOOLEAN PeImageReaderRvaToFileOffset(PPE_IMAGE_READER Reader, DWORD Rva, DWORD Length, PSIZE_T FileOffset)
Translates a relative virtual address (RVA) to a raw file offset.
Definition pe-image-reader.cpp:287
BOOLEAN PeImageReaderGetSectionName(const IMAGE_SECTION_HEADER *SectionHeader, CHAR *NameBuffer, SIZE_T NameBufferSize)
Copies the section name from a section header into a null-terminated buffer.
Definition pe-image-reader.cpp:244
BOOLEAN PeImageReaderInitialize(const BYTE *ImageBase, SIZE_T ImageSize, PPE_IMAGE_READER Reader)
Parses and validates all PE headers in an in-memory image buffer.
Definition pe-image-reader.cpp:94
struct _PE_IMAGE_READER PE_IMAGE_READER
struct _IMAGE_FILE_HEADER IMAGE_FILE_HEADER
struct _IMAGE_DOS_HEADER IMAGE_DOS_HEADER
#define IMAGE_SIZEOF_SHORT_NAME
Definition pe-image-reader.h:51
struct _IMAGE_SECTION_HEADER IMAGE_SECTION_HEADER
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]
Definition pe-image-reader.h:55

◆ TestSemanticScripts()

BOOLEAN TestSemanticScripts ( )

Test semantic scripts.

Returns
BOOLEAN
96{
97 INT32 TestNum = 0;
98 CHAR dirPath[MAX_PATH] = {0};
99
100 //
101 // Parse the semantic script test cases from the file
102 // Setup the path for the filenames
103 //
105 {
106 //
107 // Error could not find the test case files
108 //
109 cout << "[-] Could not find the test case files" << endl;
110 return FALSE;
111 }
112
113 //
114 // Connect to the debugger
115 //
117 {
118 cout << "[-] Could not connect to the debugger" << endl;
119 return FALSE;
120 }
121
122 //
123 // Run test cases
124 //
126
127 //
128 // Close the connection
129 //
131
132 return TRUE;
133}
#define TEST_DEFAULT_NAMED_PIPE
Default named pipe name for communication between debugger and debuggee process.
Definition Definition.h:55
#define SCRIPT_SEMANTIC_TEST_CASE_DIRECTORY
Test cases directory name for script semantic tests.
Definition Definition.h:105
IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_debug_close_remote_debugger()
Close the remote debugger.
Definition export.cpp:631
IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_connect_remote_debugger_using_named_pipe(const CHAR *named_pipe, BOOLEAN pause_after_connection)
Connect to the remote debugger using named pipe.
Definition export.cpp:620
VOID ReadDirectoryAndTestSemanticTestCases(const CHAR *ScriptSemanticPath)
Read directory of semantic test cases and run each of them.
Definition test-semantic-scripts.cpp:24