1+ #include " codecave_dumper.h"
2+ #include < iostream>
3+ #include < fstream>
4+ #include < regex>
5+ #include < algorithm>
6+
7+ const DWORD CodecaveDumper::READABLE_PROTECTIONS[] = {
8+ MEM_PAGE_READONLY,
9+ MEM_PAGE_READWRITE,
10+ MEM_PAGE_WRITECOPY,
11+ MEM_PAGE_EXECUTE_READ,
12+ MEM_PAGE_EXECUTE_READWRITE
13+ };
14+
15+ HANDLE CodecaveDumper::getHandle (DWORD pid) {
16+ return OpenProcess (PROCESS_ALL_ACCESS, FALSE , pid);
17+ }
18+
19+ std::vector<char > CodecaveDumper::readMemory (HANDLE handle, LPVOID address, SIZE_T size) {
20+ std::vector<char > buffer (size);
21+ SIZE_T bytesRead;
22+
23+ if (!ReadProcessMemory (handle, address, buffer.data (), size, &bytesRead)) {
24+ throw std::runtime_error (" Failed to read process memory" );
25+ }
26+
27+ buffer.resize (bytesRead);
28+ return buffer;
29+ }
30+
31+ bool CodecaveDumper::isReadable (DWORD protect) {
32+ const size_t size = sizeof (READABLE_PROTECTIONS) / sizeof (READABLE_PROTECTIONS[0 ]);
33+ return std::find (READABLE_PROTECTIONS,
34+ READABLE_PROTECTIONS + size,
35+ protect) != READABLE_PROTECTIONS + size;
36+ }
37+
38+ std::vector<std::string> CodecaveDumper::extractData (const std::vector<char >& data, size_t minLength) {
39+ static const auto junkPattern = std::regex (R"( ^[0-9A-F]+$|^[A-Z]{1,3}\d+$|^\W+$|^.{1,3}$|(.)\1{3,})" ,
40+ std::regex::optimize);
41+
42+ std::vector<std::string> strings;
43+ strings.reserve (1000 );
44+
45+ constexpr size_t CHUNK_SIZE = 4096 ;
46+ std::string current;
47+ current.reserve (256 );
48+
49+ for (size_t i = 0 ; i < data.size (); i += CHUNK_SIZE) {
50+ const size_t chunk_end = (i + CHUNK_SIZE < data.size ()) ? i + CHUNK_SIZE : data.size ();
51+ for (char byte : data) {
52+ if (isprint (byte)) {
53+ current += byte;
54+ } else if (!current.empty ()) {
55+ if (current.length () >= minLength &&
56+ !std::regex_match (current, junkPattern) &&
57+ std::any_of (current.begin (), current.end (), ::isalnum)) {
58+ strings.push_back (std::move (current));
59+ }
60+ current.clear ();
61+ current.reserve (256 );
62+ }
63+ }
64+ }
65+
66+ if (current.length () >= minLength &&
67+ !std::regex_match (current, junkPattern) &&
68+ std::any_of (current.begin (), current.end (), ::isalnum)) {
69+ strings.push_back (std::move (current));
70+ }
71+
72+ return strings;
73+ }
74+
75+ void CodecaveDumper::dump (DWORD pid) {
76+ try {
77+ HANDLE handle = getHandle (pid);
78+ if (handle == NULL ) {
79+ throw std::runtime_error (" Failed to open process" );
80+ }
81+
82+ std::cout << " \n Starting codecave dump for PID: " << pid << std::endl;
83+
84+ std::string outputFile = std::to_string (pid) + " _dump.txt" ;
85+ LPVOID address = nullptr ;
86+ MEMORY_BASIC_INFORMATION memInfo;
87+ std::set<std::string> uniqueStrings;
88+ size_t regionsAnalyzed = 0 ;
89+ size_t totalRegions = 0 ;
90+
91+ while (VirtualQueryEx (handle, address, &memInfo, sizeof (memInfo))) {
92+ totalRegions++;
93+ if (isReadable (memInfo.Protect )) {
94+ try {
95+ auto chunk = readMemory (handle, memInfo.BaseAddress , memInfo.RegionSize );
96+ auto strings = extractData (chunk);
97+ uniqueStrings.insert (std::make_move_iterator (strings.begin ()),
98+ std::make_move_iterator (strings.end ()));
99+ regionsAnalyzed++;
100+ }
101+ catch (const std::exception&) {
102+ // Skip failed regions
103+ }
104+ }
105+ address = (LPVOID)((DWORD_PTR)memInfo.BaseAddress + memInfo.RegionSize );
106+ }
107+
108+ std::ofstream outFile (outputFile, std::ios::binary);
109+ for (const auto & str : uniqueStrings) {
110+ outFile << str << ' \n ' ;
111+ }
112+
113+ std::cout << " \n Dump complete!" << std::endl;
114+ std::cout << " Analyzed " << regionsAnalyzed << " /" << totalRegions << " regions" << std::endl;
115+ std::cout << " Found " << uniqueStrings.size () << " unique strings" << std::endl;
116+ std::cout << " Results saved to: " << outputFile << std::endl;
117+
118+ CloseHandle (handle);
119+ }
120+ catch (const std::exception& e) {
121+ std::cerr << " Error: " << e.what () << std::endl;
122+ }
123+ }
0 commit comments