Skip to content

Commit 849190a

Browse files
committed
File version information option added
1 parent 45ab684 commit 849190a

File tree

2 files changed

+116
-7
lines changed

2 files changed

+116
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ In addition, the tool allows you to specify a module of the process in order to
3434
.\WindowsMemoryExtractor_x64.exe --pid 1234 --protections "PAGE_READONLY PAGE_EXECUTE_READ" --module user32.dll
3535
```
3636

37-
By default, if a module is provided but no memory protections are indicated, all the memory regions of that module whose protections match the supported ones will be extracted. Finally, for additional help, execute the command below:
37+
By default, if a module is provided but no memory protections are indicated, all the memory regions of that module whose protections match the supported ones will be extracted. The tool also has the --join option, in order to obtain the solicited memory regions of a module in one file. Additionally, if the user wants to get the version information about the file corresponding to a module, there is the --file-version-info option. Finally, for additional help, execute the command below:
3838

3939
```bash
4040
.\WindowsMemoryExtractor_x64.exe --help

windows_memory_extractor.cpp

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@
2424
#include <cryptopp/files.h>
2525
#include <cryptopp/sha.h>
2626
#include <psapi.h>
27+
#pragma comment( lib, "Version.lib" )
2728

2829

2930
struct ArgumentManager {
3031

3132
void validateArguments(int argc, char* argv[]) {
3233

3334
namespace po = boost::program_options;
34-
std::string version = "v1.0.5";
35+
std::string version = "v1.0.6";
3536
po::options_description description("Windows memory extractor " + version + "\nUsage");
3637

3738
description.add_options()
3839
("help,h", "Display this help message")
40+
("file-version-info,i", "Retrieve version information about the file corresponding to a module")
3941
("join,j", "Generate an additional .dmp file with the contents of the other .dmp files joined")
40-
("output-directory,o", po::value<std::string>(), "Directory where the output will be stored")
4142
("module,m", po::value<std::string>(), "Module of the process")
43+
("output-directory,o", po::value<std::string>(), "Directory where the output will be stored")
4244
("pid,p", po::value<int>()->required(), "Process ID")
4345
("protections,s", po::value<std::string>(), "Memory protections")
4446
("version,v", "Version")
@@ -79,6 +81,15 @@ struct ArgumentManager {
7981
}
8082
}
8183

84+
if (vm.count("file-version-info")) {
85+
isFileVersionInfoOptionSupplied = true;
86+
if (!isModuleOptionSupplied) {
87+
// As with the --join option, the --file-version-info option is implemented to work alongside the --module option
88+
// If the --file-version-info option is supplied without the --module option, the tool interprets that the user is asking for the version information of the file corresponding to the main module
89+
isModuleOptionSupplied = true;
90+
}
91+
}
92+
8293
if (vm.count("pid")) {
8394
pid = vm["pid"].as<int>();
8495
}
@@ -135,6 +146,10 @@ struct ArgumentManager {
135146
return isOutputDirectoryOptionSupplied;
136147
}
137148

149+
bool getIsFileVersionInfoOptionSupplied() {
150+
return isFileVersionInfoOptionSupplied;
151+
}
152+
138153
private:
139154

140155
void validateProtections(std::string suppliedProtectionsAsString) {
@@ -193,6 +208,7 @@ struct ArgumentManager {
193208
bool isProtectionsOptionSupplied;
194209
bool isJoinOptionSupplied;
195210
bool isOutputDirectoryOptionSupplied;
211+
bool isFileVersionInfoOptionSupplied;
196212

197213
};
198214

@@ -229,7 +245,7 @@ struct MemoryExtractionManager {
229245
}
230246

231247
if (argumentManager.getIsModuleOptionSupplied() && argumentManager.getModule().length() == 0) {
232-
// The user is asking for the contents of the main module
248+
// The user is asking for data about the main module
233249
char mainModulePathAsCharArray[MAX_PATH];
234250
if (GetProcessImageFileNameA(processHandle, mainModulePathAsCharArray, MAX_PATH) != 0) {
235251
std::string mainModulePath(mainModulePathAsCharArray);
@@ -242,6 +258,8 @@ struct MemoryExtractionManager {
242258
}
243259
}
244260

261+
directoryName = createDirectory();
262+
245263
BYTE* memoryPointer = NULL; // Virtual address 0x0000000000000000
246264

247265
// Module option related variables
@@ -257,9 +275,25 @@ struct MemoryExtractionManager {
257275
moduleBaseAddress = moduleInformation.modBaseAddr;
258276
moduleSize = moduleInformation.modBaseSize;
259277
moduleBaseAddressAsNumber = reinterpret_cast<size_t>(moduleInformation.modBaseAddr);
260-
}
261278

262-
directoryName = createDirectory();
279+
if (argumentManager.getIsFileVersionInfoOptionSupplied()) {
280+
std::wstring modulePathW{ moduleInformation.szExePath };
281+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
282+
std::string modulePath = converter.to_bytes(modulePathW);
283+
DWORD dwHandle;
284+
DWORD fileVersionInfoSize = GetFileVersionInfoSizeA(modulePath.c_str(), &dwHandle);
285+
if (fileVersionInfoSize == 0) {
286+
throw std::exception{ "An error has occurred trying to get the version information at function GetFileVersionInfoSizeA" };
287+
}
288+
std::vector<unsigned char> fileVersionInfoBuffer(fileVersionInfoSize);
289+
if (!GetFileVersionInfoA(modulePath.c_str(), dwHandle, fileVersionInfoSize, &fileVersionInfoBuffer[0]))
290+
{
291+
throw std::exception{ "An error has occurred trying to get the version information at function GetFileVersionInfoA" };
292+
}
293+
294+
retrieveFileVersionInformation(&fileVersionInfoBuffer[0]);
295+
}
296+
}
263297

264298
std::ofstream resultsFile(directoryName + "/results.txt", std::ofstream::out);
265299
resultsFile << "List of .dmp files generated:\n";
@@ -304,7 +338,35 @@ struct MemoryExtractionManager {
304338

305339
joinedModuleContentsStream.close();
306340
}
307-
resultsFile << "Number of .dmp files generated: " << dmpFilesGeneratedCount << std::endl;
341+
342+
resultsFile << "Number of .dmp files generated: " << dmpFilesGeneratedCount << "\n";
343+
344+
if (argumentManager.getIsFileVersionInfoOptionSupplied()) {
345+
resultsFile << "\nAdditional files generated:\n";
346+
347+
using namespace CryptoPP;
348+
dmpFilesGeneratedCount++;
349+
350+
// Calculate the SHA-256 of the file moduleFileVersionInfo.fileinfo
351+
std::ifstream moduleFileVersionInfoStream(directoryName + "/moduleFileVersionInfo.fileinfo", std::ios::in | std::ios::binary);
352+
std::string contents((std::istreambuf_iterator<char>(moduleFileVersionInfoStream)),
353+
(std::istreambuf_iterator<char>()));
354+
HexEncoder hexEncoder(new FileSink(resultsFile), false);
355+
std::string sha256Digest;
356+
SHA256 hash;
357+
hash.Update((const byte*)contents.c_str(), contents.length());
358+
sha256Digest.resize(hash.DigestSize());
359+
hash.Final((byte*)&sha256Digest[0]);
360+
361+
// Create an entry in the results file for the moduleFileVersionInfo.fileinfo file
362+
resultsFile << "Filename: " << "moduleFileVersionInfo.fileinfo" << ", SHA-256: ";
363+
StringSource(sha256Digest, true, new Redirector(hexEncoder));
364+
resultsFile << "\n";
365+
366+
moduleFileVersionInfoStream.close();
367+
}
368+
369+
resultsFile << std::endl;
308370
resultsFile.close();
309371
CloseHandle(processHandle);
310372
}
@@ -539,6 +601,53 @@ struct MemoryExtractionManager {
539601
return TRUE;
540602
}
541603

604+
void retrieveFileVersionInformation(LPCVOID fileVersionInfoBufferPointer) {
605+
std::vector<std::string> versionInfoKeys{
606+
"Comments",
607+
"CompanyName",
608+
"FileDescription",
609+
"FileVersion",
610+
"InternalName",
611+
"LegalCopyright",
612+
"LegalTrademarks",
613+
"OriginalFilename",
614+
"ProductName",
615+
"ProductVersion",
616+
"PrivateBuild",
617+
"SpecialBuild"
618+
};
619+
620+
struct LANGANDCODEPAGE {
621+
WORD wLanguage;
622+
WORD wCodePage;
623+
} *lpTranslate;
624+
625+
UINT cbTranslate = 0;
626+
if (!VerQueryValueA(fileVersionInfoBufferPointer, "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate)) {
627+
throw std::exception{ "An error has occurred trying to get the version information at function VerQueryValueA" };
628+
}
629+
630+
std::ofstream moduleVersionInfoFile(directoryName + "/moduleFileVersionInfo.fileinfo", std::ofstream::out);
631+
632+
for (unsigned int i = 0; i < (cbTranslate / sizeof(LANGANDCODEPAGE)); i++) {
633+
BOOST_FOREACH(const std::string & versionInfoKey, versionInfoKeys) {
634+
std::string versionInfoKeyFormat = "\\StringFileInfo\\%04x%04x\\" + versionInfoKey;
635+
char versionInfoKeyWithLanguage[256];
636+
sprintf_s(versionInfoKeyWithLanguage, versionInfoKeyFormat.c_str(), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
637+
LPSTR versionInfoValuePointer = NULL;
638+
UINT versionInfoValueSize = 0;
639+
if (VerQueryValueA(fileVersionInfoBufferPointer, versionInfoKeyWithLanguage, (LPVOID*)&versionInfoValuePointer, &versionInfoValueSize)) {
640+
moduleVersionInfoFile << versionInfoKey << "," << std::string(versionInfoValuePointer) << "\n";
641+
}
642+
else {
643+
// The value for the key is empty
644+
moduleVersionInfoFile << versionInfoKey << ",\n";
645+
}
646+
}
647+
}
648+
moduleVersionInfoFile.close();
649+
}
650+
542651
ArgumentManager& argumentManager;
543652
std::string directoryName; // The directory where the memory data files will be placed
544653
unsigned int dmpFilesGeneratedCount;

0 commit comments

Comments
 (0)