Commit local workspace.
- Split minidump (upload) and exception handling code - Smaller minidumps for faster uploads - Several new build flags (accessible from premake5 with --params) - BitMessage abstractions - Some other things I can't remember.
This commit is contained in:
parent
1d5927f093
commit
ebba2819d6
42
premake5.lua
42
premake5.lua
@ -43,6 +43,21 @@ newoption {
|
||||
description = "Always compile unit tests."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "force-exception-handler",
|
||||
description = "Install custom unhandled exception handler even for Debug builds."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "force-minidump-upload",
|
||||
description = "Upload minidumps even for Debug builds."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "disable-bitmessage",
|
||||
description = "Disable use of BitMessage completely."
|
||||
}
|
||||
|
||||
newaction {
|
||||
trigger = "version",
|
||||
description = "Returns the version string for the current commit of the source code.",
|
||||
@ -242,6 +257,9 @@ workspace "iw4x"
|
||||
resincludedirs {
|
||||
"$(ProjectDir)src" -- fix for VS IDE
|
||||
}
|
||||
removefiles {
|
||||
"./src/Components/Modules/Tor.*",
|
||||
}
|
||||
|
||||
-- Debug flags
|
||||
if _OPTIONS["ac-debug-detections"] then
|
||||
@ -253,6 +271,18 @@ workspace "iw4x"
|
||||
if _OPTIONS["force-unit-tests"] then
|
||||
defines { "FORCE_UNIT_TESTS" }
|
||||
end
|
||||
if _OPTIONS["force-minidump-upload"] then
|
||||
defines { "FORCE_MINIDUMP_UPLOAD" }
|
||||
end
|
||||
if _OPTIONS["force-exception-handler"] then
|
||||
defines { "FORCE_EXCEPTION_HANDLER" }
|
||||
end
|
||||
if _OPTIONS["disable-bitmessage"] then
|
||||
defines { "DISABLE_BITMESSAGE" }
|
||||
removefiles {
|
||||
"./src/Components/Modules/BitMessage.*",
|
||||
}
|
||||
end
|
||||
|
||||
-- Pre-compiled header
|
||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||
@ -260,7 +290,9 @@ workspace "iw4x"
|
||||
buildoptions { "/Zm200" }
|
||||
|
||||
-- Dependency libraries
|
||||
bitmrc.import()
|
||||
if not _OPTIONS["disable-bitmessage"] then
|
||||
bitmrc.import()
|
||||
end
|
||||
fmt.import()
|
||||
json11.import()
|
||||
libtomcrypt.import()
|
||||
@ -360,16 +392,18 @@ workspace "iw4x"
|
||||
}
|
||||
|
||||
group "External dependencies"
|
||||
bitmrc.project()
|
||||
if not _OPTIONS["disable-bitmessage"] then
|
||||
bitmrc.project()
|
||||
libcryptopp.project()
|
||||
sqlite3.project()
|
||||
end
|
||||
fmt.project()
|
||||
json11.project()
|
||||
libcryptopp.project()
|
||||
libtomcrypt.project()
|
||||
libtommath.project()
|
||||
mongoose.project()
|
||||
pdcurses.project()
|
||||
protobuf.project()
|
||||
sqlite3.project()
|
||||
winksignals.project()
|
||||
zlib.project()
|
||||
|
||||
|
@ -45,6 +45,7 @@ namespace Components
|
||||
Loader::Register(new Dedicated());
|
||||
Loader::Register(new Discovery());
|
||||
Loader::Register(new Exception());
|
||||
Loader::Register(new MinidumpUpload());
|
||||
Loader::Register(new FastFiles());
|
||||
Loader::Register(new Materials());
|
||||
Loader::Register(new FileSystem());
|
||||
|
@ -62,6 +62,7 @@ namespace Components
|
||||
#include "Modules\Dedicated.hpp"
|
||||
#include "Modules\Discovery.hpp"
|
||||
#include "Modules\Exception.hpp"
|
||||
#include "Modules\MinidumpUpload.hpp"
|
||||
#include "Modules\FastFiles.hpp"
|
||||
#include "Modules\Materials.hpp"
|
||||
#include "Modules\Singleton.hpp"
|
||||
|
@ -8,14 +8,13 @@ using namespace Utils;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
BitMessage* BitMessage::Singleton = nullptr;
|
||||
BitMessage* BitMessage::Singleton = NULL;
|
||||
|
||||
BitMessage::BitMessage()
|
||||
{
|
||||
if (Singleton != nullptr)
|
||||
{
|
||||
throw new std::runtime_error("Only 1 BitMessage instance allowed at the same time.");
|
||||
}
|
||||
if (Singleton != NULL)
|
||||
throw new std::runtime_error("Only one instance of BitMessage allowed at the same time.");
|
||||
|
||||
Singleton = this;
|
||||
|
||||
Logger::Print("Initializing BitMessage...\n");
|
||||
@ -160,6 +159,92 @@ namespace Components
|
||||
});
|
||||
}
|
||||
|
||||
BitMessage::~BitMessage()
|
||||
{
|
||||
Singleton = NULL;
|
||||
}
|
||||
|
||||
void BitMessage::SetDefaultTTL(time_t ttl)
|
||||
{
|
||||
this->BMClient->defaultTTL = ttl;
|
||||
}
|
||||
|
||||
bool BitMessage::RequestPublicKey(std::string targetAddress)
|
||||
{
|
||||
// Convert to ustring
|
||||
ustring targetAddressU;
|
||||
targetAddressU.fromString(targetAddress);
|
||||
|
||||
// Convert to PubAddr
|
||||
PubAddr pubAddr;
|
||||
if (!pubAddr.loadAddr(targetAddressU))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Request public key!
|
||||
this->BMClient->getPubKey(pubAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitMessage::Subscribe(std::string targetAddress)
|
||||
{
|
||||
// Convert to ustring
|
||||
ustring targetAddressU;
|
||||
targetAddressU.fromString(targetAddress);
|
||||
|
||||
// Convert to PubAddr
|
||||
PubAddr pubAddr;
|
||||
if (!pubAddr.loadAddr(targetAddressU))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subscribe!
|
||||
this->BMClient->addSubscription(pubAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitMessage::SendMsg(std::string targetAddress, std::string message, time_t ttl)
|
||||
{
|
||||
// Convert target address to ustring
|
||||
ustring targetAddressU;
|
||||
targetAddressU.fromString(targetAddress);
|
||||
|
||||
// Convert target address to PubAddr
|
||||
PubAddr pubAddr;
|
||||
if (!pubAddr.loadAddr(targetAddressU))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert message to ustring
|
||||
ustring messageU;
|
||||
messageU.fromString(message);
|
||||
|
||||
// Send the message
|
||||
// TODO - Set mutex on priv when accessing first private address
|
||||
if (ttl > 0)
|
||||
this->BMClient->sendMessage(messageU, pubAddr, this->BMClient->PrivAddresses[0], ttl);
|
||||
else
|
||||
this->BMClient->sendMessage(messageU, pubAddr, this->BMClient->PrivAddresses[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitMessage::SendBroadcast(std::string message, time_t ttl)
|
||||
{
|
||||
// Convert message to ustring
|
||||
ustring messageU;
|
||||
messageU.fromString(message);
|
||||
|
||||
// TODO - Set mutex on priv when accessing first private address
|
||||
if (ttl > 0)
|
||||
this->BMClient->sendBroadcast(messageU, this->BMClient->PrivAddresses[0], ttl);
|
||||
else
|
||||
this->BMClient->sendBroadcast(messageU, this->BMClient->PrivAddresses[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitMessage::InitAddr()
|
||||
{
|
||||
Logger::Print("Generating BM address...\n");
|
||||
|
@ -2,40 +2,27 @@
|
||||
|
||||
#ifndef DISABLE_BITMESSAGE
|
||||
|
||||
#define BITMESSAGE_KEYS_FILENAME std::string("players/bmkey.dat")
|
||||
#define BITMESSAGE_KEYS_FILENAME std::string("players/bmkeys.dat")
|
||||
#define BITMESSAGE_OBJECT_STORAGE_FILENAME std::string("players/bmstore.dat")
|
||||
|
||||
static const std::map<std::string, unsigned short> bitmessageKnownNodes = {
|
||||
// https://github.com/Bitmessage/PyBitmessage/blob/4622d952e47a7dbb3a90aa79f4d20163aa14b041/src/defaultKnownNodes.py#L15-L23
|
||||
|
||||
// Stream 1
|
||||
//{ "2604:2000:1380:9f:82e:148b:2746:d0c7", 8080 },
|
||||
{ "5.45.99.75", 8444 },
|
||||
{ "75.167.159.54", 8444 },
|
||||
{ "95.165.168.168", 8444 },
|
||||
{ "85.180.139.241", 8444 },
|
||||
{ "158.222.211.81", 8080 },
|
||||
{ "178.62.12.187", 8448 },
|
||||
{ "24.188.198.204", 8111 },
|
||||
{ "109.147.204.113", 1195 },
|
||||
{ "178.11.46.221", 8444 },
|
||||
|
||||
// Stream 2 has none yet
|
||||
|
||||
// Stream 3 has none yet
|
||||
};
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class BitMessage : public Component
|
||||
{
|
||||
public:
|
||||
BitMessage();
|
||||
~BitMessage();
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* GetName() { return "BitMessage"; };
|
||||
#endif
|
||||
|
||||
void SetDefaultTTL(time_t ttl);
|
||||
bool RequestPublicKey(std::string targetAddress);
|
||||
bool Subscribe(std::string targetAddress);
|
||||
bool SendMsg(std::string targetAddress, std::string message, time_t ttl = 0);
|
||||
bool SendBroadcast(std::string message, time_t ttl = 0);
|
||||
|
||||
static BitMessage* Singleton;
|
||||
BitMRC* BMClient;
|
||||
|
||||
|
@ -9,145 +9,53 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
// Fileupload to Webhost
|
||||
bool Exception::UploadMinidump(std::string filename)
|
||||
{
|
||||
Utils::WebIO webio("Firefucks", UPLOAD_URL);
|
||||
|
||||
if (Utils::IO::FileExists(filename))
|
||||
{
|
||||
std::string buffer = Utils::IO::ReadFile(filename);
|
||||
std::string result = webio.PostFile(buffer);
|
||||
|
||||
std::string errors;
|
||||
json11::Json object = json11::Json::parse(result, errors);
|
||||
|
||||
if (!object.is_object()) return false;
|
||||
|
||||
json11::Json success = object["success"];
|
||||
|
||||
if (!success.is_bool() || !success.bool_value()) return false;
|
||||
|
||||
json11::Json files = object["files"];
|
||||
|
||||
if (!files.is_array()) return false;
|
||||
|
||||
for (auto file : files.array_items())
|
||||
{
|
||||
json11::Json url = file["url"];
|
||||
json11::Json hash = file["hash"];
|
||||
|
||||
if (hash.is_string() && url.is_string())
|
||||
{
|
||||
if (Utils::String::ToLower(Utils::Cryptography::SHA1::Compute(buffer, true)) == Utils::String::ToLower(hash.string_value()))
|
||||
{
|
||||
MessageBoxA(0, url.string_value().data(), 0, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fileupload to Bitmessage
|
||||
bool Exception::UploadMinidump2BM(std::string filename)
|
||||
{
|
||||
if (Components::BitMessage::Singleton == nullptr)
|
||||
{
|
||||
//throw new std::runtime_error("BitMessage was already stopped.");
|
||||
Logger::Print("Bitmessage was already stopped.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
BitMessage* Singleton;
|
||||
Singleton = Components::BitMessage::Singleton;
|
||||
|
||||
if (Utils::IO::FileExists(filename))
|
||||
{
|
||||
// TODO: Validate filesize of minidump
|
||||
// TODO: Convert to base64
|
||||
// TODO: Split if filesize > xxxkB
|
||||
std::string buffer = Utils::String::encodeBase64(Utils::IO::ReadFile(filename));
|
||||
|
||||
ustring pubAddrString;
|
||||
pubAddrString.fromString(BITMESSAGE_UPLOAD_IDENTITY);
|
||||
PubAddr pubAddr;
|
||||
if (pubAddr.loadAddr(pubAddrString))
|
||||
{
|
||||
int g;
|
||||
ustring msg;
|
||||
|
||||
Logger::Print("Uploading Minidump (this may take a while)...\n");
|
||||
|
||||
for (size_t i = 0; i < buffer.size(); i=i+BITMESSAGE_SIZE_LIMIT)
|
||||
{
|
||||
if (buffer.size() > i + BITMESSAGE_SIZE_LIMIT)
|
||||
g = buffer.size();
|
||||
else
|
||||
g = i + BITMESSAGE_SIZE_LIMIT;
|
||||
std::string substring = buffer.substr(i, g);
|
||||
msg.fromString(substring);
|
||||
Singleton->BMClient->sendMessage(msg, pubAddr, Singleton->BMClient->PrivAddresses[0]);
|
||||
}
|
||||
|
||||
Logger::Print("Minidump uploaded.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("Address not correct!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LONG WINAPI Exception::ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
__time64_t time;
|
||||
tm ltime;
|
||||
|
||||
_time64(&time);
|
||||
_localtime64_s(<ime, &time);
|
||||
strftime(filename, sizeof(filename) - 1, "iw4x-" VERSION_STR "-%Y%m%d%H%M%S.dmp", <ime);
|
||||
|
||||
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hFile && hFile != INVALID_HANDLE_VALUE)
|
||||
// Pass on harmless errors
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_INTEGER_OVERFLOW ||
|
||||
ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_FLOAT_OVERFLOW)
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
|
||||
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ex, NULL, NULL);
|
||||
CloseHandle(hFile);
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
//Exception::UploadMinidump(filename);
|
||||
Exception::UploadMinidump2BM(filename);
|
||||
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
|
||||
auto minidump = MinidumpUpload::Singleton->CreateQueuedMinidump(ExceptionInfo);
|
||||
if (minidump == NULL)
|
||||
{
|
||||
OutputDebugStringA("Failed to create new minidump!");
|
||||
Utils::OutputDebugLastError();
|
||||
}
|
||||
|
||||
printf("Trying to print an error message from ExceptionFilter...");
|
||||
switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
Logger::Error("Termination because of a stack overflow.\n");
|
||||
TerminateProcess(GetCurrentProcess(), EXCEPTION_STACK_OVERFLOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
default:
|
||||
Logger::Error("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
break;
|
||||
}
|
||||
|
||||
//TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
||||
{
|
||||
SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
||||
return lpTopLevelExceptionFilter;
|
||||
return SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
||||
}
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER Exception::Hook()
|
||||
{
|
||||
return SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
||||
}
|
||||
|
||||
Exception::Exception()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Display DEBUG branding, so we know we're on a debug build
|
||||
Renderer::OnFrame([] ()
|
||||
Renderer::OnFrame([]()
|
||||
{
|
||||
Game::Font* font = Game::R_RegisterFont("fonts/normalFont");
|
||||
float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
@ -162,18 +70,19 @@ namespace Components
|
||||
|
||||
Game::R_AddCmdDrawText("DEBUG-BUILD", 0x7FFFFFFF, font, 15.0f, 10.0f + Game::R_TextHeight(font), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED);
|
||||
});
|
||||
#else
|
||||
#endif
|
||||
#if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER)
|
||||
Utils::Hook::Set(0x6D70AC, Exception::SetUnhandledExceptionFilterStub);
|
||||
SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
||||
#endif
|
||||
|
||||
Command::Add("mapTest", [] (Command::Params params)
|
||||
Command::Add("mapTest", [](Command::Params params)
|
||||
{
|
||||
std::string command;
|
||||
|
||||
int max = (params.Length() >= 2 ? atoi(params[1]) : 16), current = 0;
|
||||
|
||||
for (int i =0;;)
|
||||
for (int i = 0;;)
|
||||
{
|
||||
char* mapname = Game::mapnames[i];
|
||||
if (!*mapname)
|
||||
@ -182,7 +91,7 @@ namespace Components
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!(i % 2)) command.append(fmt::sprintf("wait 250;disconnect;wait 750;", mapname)); // Test a disconnect
|
||||
if (!(i % 2)) command.append(fmt::sprintf("wait 250;disconnect;wait 750;", mapname)); // Test a disconnect
|
||||
else command.append(fmt::sprintf("wait 500;", mapname)); // Test direct map switch
|
||||
command.append(fmt::sprintf("map %s;", mapname));
|
||||
|
||||
@ -193,6 +102,12 @@ namespace Components
|
||||
|
||||
Command::Execute(command, false);
|
||||
});
|
||||
Command::Add("debug_exceptionhandler", [](Command::Params)
|
||||
{
|
||||
Logger::Print("Rerunning SetUnhandledExceptionHandler...\n");
|
||||
auto oldHandler = Exception::Hook();
|
||||
Logger::Print("Old exception handler was 0x%010X.\n", oldHandler);
|
||||
});
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4740) // flow in or out of inline asm code suppresses global optimization
|
||||
Command::Add("debug_minidump", [](Command::Params)
|
||||
|
@ -1,6 +1,4 @@
|
||||
#define UPLOAD_URL "https://reich.io/upload.php"
|
||||
#define BITMESSAGE_UPLOAD_IDENTITY "BM-NBhqCHraxGyUT38cfhRQYWx29gJ7QePa"
|
||||
#define BITMESSAGE_SIZE_LIMIT 230000
|
||||
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -12,12 +10,10 @@ namespace Components
|
||||
#ifdef DEBUG
|
||||
const char* GetName() { return "Exception"; };
|
||||
#endif
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER Hook();
|
||||
|
||||
private:
|
||||
static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo);
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
|
||||
|
||||
static bool UploadMinidump(std::string filename);
|
||||
static bool UploadMinidump2BM(std::string filename);
|
||||
};
|
||||
}
|
||||
|
366
src/Components/Modules/MinidumpUpload.cpp
Normal file
366
src/Components/Modules/MinidumpUpload.cpp
Normal file
@ -0,0 +1,366 @@
|
||||
#include "STDInclude.hpp"
|
||||
#include "Shlwapi.h"
|
||||
|
||||
const int MiniDumpTiny = MiniDumpFilterMemory | MiniDumpWithoutAuxiliaryState | MiniDumpWithoutOptionalData | MiniDumpFilterModulePaths | MiniDumpIgnoreInaccessibleMemory;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
||||
#pragma region Minidump class implementation
|
||||
inline Minidump::Minidump() {}
|
||||
|
||||
Minidump::~Minidump()
|
||||
{
|
||||
if (this->mapFileHandle != NULL && this->mapFileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(this->mapFileHandle);
|
||||
this->mapFileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (this->fileHandle != NULL && this->fileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(this->fileHandle);
|
||||
this->fileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Minidump::ToString()
|
||||
{
|
||||
if (!Minidump::EnsureFileMapping()) return false;
|
||||
|
||||
auto pBuf = MapViewOfFile(this->mapFileHandle, FILE_MAP_READ, 0, 0, 0);
|
||||
if (pBuf == NULL)
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
throw new std::runtime_error("Could not read minidump.");
|
||||
}
|
||||
|
||||
size_t fileSize;
|
||||
DWORD fileSizeHi;
|
||||
fileSize = GetFileSize(this->fileHandle, &fileSizeHi);
|
||||
#ifdef _WIN64
|
||||
fileSize |= ((size_t)fileSizeHi << 32);
|
||||
#endif
|
||||
std::string retval = std::string((const char*)pBuf, fileSize * sizeof(const char));
|
||||
UnmapViewOfFile(pBuf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool Minidump::GetStream(MINIDUMP_STREAM_TYPE type, PMINIDUMP_DIRECTORY* directoryPtr, PVOID* streamBeginningPtr, ULONG* streamSizePtr)
|
||||
{
|
||||
if (!Minidump::EnsureFileMapping()) return false;
|
||||
|
||||
auto pBuf = MapViewOfFile(this->mapFileHandle, FILE_MAP_READ, 0, 0, 0);
|
||||
if (pBuf == NULL)
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
throw new std::runtime_error("Could not read minidump.");
|
||||
}
|
||||
|
||||
BOOL success = MiniDumpReadDumpStream(pBuf, type, directoryPtr, streamBeginningPtr, streamSizePtr);
|
||||
UnmapViewOfFile(pBuf);
|
||||
if (success != TRUE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Minidump::Check()
|
||||
{
|
||||
/*PMINIDUMP_DIRECTORY directory;
|
||||
PVOID stream;
|
||||
ULONG streamSize;
|
||||
return Minidump::GetStream(ExceptionStream, &directory, &stream, &streamSize);*/
|
||||
return Minidump::GetStream(ExceptionStream, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
Minidump* Minidump::Create(std::string path, LPEXCEPTION_POINTERS exceptionInfo, int type)
|
||||
{
|
||||
Minidump* minidump = Minidump::Initialize(path);
|
||||
if (minidump == NULL) return minidump;
|
||||
|
||||
// Do the dump generation
|
||||
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), exceptionInfo, FALSE };
|
||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), minidump->fileHandle, (MINIDUMP_TYPE)type, &ex, NULL, NULL))
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
delete minidump;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SetFilePointer(minidump->fileHandle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
delete minidump;
|
||||
DeleteFileA(path.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return minidump;
|
||||
}
|
||||
|
||||
Minidump* Minidump::Open(std::string path)
|
||||
{
|
||||
Minidump* minidump = Minidump::Initialize(path, FILE_SHARE_READ);
|
||||
return minidump;
|
||||
}
|
||||
|
||||
bool Minidump::EnsureFileMapping()
|
||||
{
|
||||
if (this->mapFileHandle == NULL || this->mapFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
this->mapFileHandle = CreateFileMappingA(this->fileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (this->mapFileHandle == NULL || this->mapFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Minidump* Minidump::Initialize(std::string path, DWORD fileShare)
|
||||
{
|
||||
Minidump* minidump = new Minidump();
|
||||
|
||||
minidump->fileHandle = CreateFileA(path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ, fileShare,
|
||||
NULL, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, NULL);
|
||||
if (minidump->fileHandle == NULL || minidump->fileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
delete minidump;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return minidump;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Minidump uploader class implementation
|
||||
MinidumpUpload* MinidumpUpload::Singleton = NULL;
|
||||
|
||||
const std::string MinidumpUpload::queuedMinidumpsFolder = "minidumps\\";
|
||||
#ifdef DISABLE_BITMESSAGE
|
||||
const std::vector<std::string> MinidumpUpload::targetUrls = {
|
||||
"https://reich.io/upload.php",
|
||||
"https://hitlers.kz/upload.php"
|
||||
};
|
||||
#else
|
||||
const std::string MinidumpUpload::targetAddress = "BM-2cSksR7gyyFcNK7MaFoxGCjRJWxtoGckdj";
|
||||
const unsigned int MinidumpUpload::maxSegmentSize = 200 * 1024; // 200 kB
|
||||
#endif
|
||||
|
||||
MinidumpUpload::MinidumpUpload()
|
||||
{
|
||||
if (Singleton != NULL)
|
||||
throw new std::runtime_error("Can only create one instance at a time.");
|
||||
|
||||
Singleton = this;
|
||||
|
||||
#if !defined(DEBUG) || defined(FORCE_MINIDUMP_UPLOAD)
|
||||
this->uploadThread = std::thread([&]() { this->UploadQueuedMinidumps(); });
|
||||
#endif
|
||||
}
|
||||
|
||||
MinidumpUpload::~MinidumpUpload()
|
||||
{
|
||||
Singleton = NULL;
|
||||
|
||||
if (this->uploadThread.joinable())
|
||||
{
|
||||
this->uploadThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool MinidumpUpload::EnsureQueuedMinidumpsFolderExists()
|
||||
{
|
||||
BOOL success = CreateDirectoryA(queuedMinidumpsFolder.c_str(), NULL);
|
||||
if (success != TRUE) success = GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
return success == TRUE;
|
||||
}
|
||||
|
||||
Minidump* MinidumpUpload::CreateQueuedMinidump(LPEXCEPTION_POINTERS exceptionInfo, int minidumpType)
|
||||
{
|
||||
// Note that most of the Path* functions are DEPRECATED and they have been replaced by Cch variants that only work on Windows 8+.
|
||||
// If you plan to drop support for Windows 7, please upgrade these calls to prevent accidental buffer overflows!
|
||||
|
||||
if (!EnsureQueuedMinidumpsFolderExists()) return NULL;
|
||||
|
||||
// Current executable name
|
||||
char exeFileName[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, exeFileName, MAX_PATH);
|
||||
PathStripPathA(exeFileName);
|
||||
PathRemoveExtensionA(exeFileName);
|
||||
|
||||
// Generate filename
|
||||
char filenameFriendlyTime[MAX_PATH];
|
||||
__time64_t time;
|
||||
tm ltime;
|
||||
_time64(&time);
|
||||
_localtime64_s(<ime, &time);
|
||||
strftime(filenameFriendlyTime, sizeof(filenameFriendlyTime) - 1, "%Y%m%d%H%M%S", <ime);
|
||||
|
||||
// Combine with queuedMinidumpsFolder
|
||||
char filename[MAX_PATH];
|
||||
PathCombineA(filename, queuedMinidumpsFolder.c_str(), Utils::String::VA("%s-" VERSION_STR "-%s.dmp", exeFileName, filenameFriendlyTime));
|
||||
|
||||
// Generate the dump
|
||||
return Minidump::Create(filename, exceptionInfo, minidumpType);
|
||||
}
|
||||
|
||||
bool MinidumpUpload::UploadQueuedMinidumps()
|
||||
{
|
||||
#ifndef DISABLE_BITMESSAGE
|
||||
// Preload public key for our target that will receive minidumps
|
||||
if (!BitMessage::Singleton->RequestPublicKey(MinidumpUpload::targetAddress))
|
||||
{
|
||||
Logger::Error("Failed to request public key for minidump collection address.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if folder exists
|
||||
if (!PathIsDirectoryA(queuedMinidumpsFolder.c_str()))
|
||||
{
|
||||
// Nothing to upload
|
||||
return PathFileExistsA(queuedMinidumpsFolder.c_str()) == FALSE;
|
||||
}
|
||||
|
||||
// Walk through directory and search for valid minidumps
|
||||
WIN32_FIND_DATAA ffd;
|
||||
HANDLE hFind = FindFirstFileA(Utils::String::VA("%s\\*.dmp", queuedMinidumpsFolder), &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0)
|
||||
continue; // ignore directory
|
||||
|
||||
char fullPath[MAX_PATH_SIZE];
|
||||
PathCombineA(fullPath, queuedMinidumpsFolder.c_str(), ffd.cFileName);
|
||||
|
||||
// Try to open this minidump
|
||||
auto minidump = Minidump::Open(fullPath);
|
||||
if (minidump == NULL)
|
||||
continue; // file can't be opened
|
||||
|
||||
// Upload!
|
||||
if (!MinidumpUpload::Upload(minidump))
|
||||
continue; // couldn't upload that minidump, keep for another attempt
|
||||
|
||||
delete minidump;
|
||||
|
||||
#ifndef KEEP_MINIDUMPS_AFTER_UPLOAD
|
||||
// Delete minidump if possible
|
||||
DeleteFileA(fullPath);
|
||||
#endif
|
||||
} while (FindNextFileA(hFind, &ffd) != 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpUpload::Upload(Minidump* minidump)
|
||||
{
|
||||
// Checking if we can extract any information from minidump first, just to be sure that this is valid.
|
||||
if (!minidump->Check())
|
||||
{
|
||||
Utils::OutputDebugLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> extraHeaders = {
|
||||
{"Hash-SHA512", Utils::Cryptography::SHA512::Compute(minidump->ToString(), true)},
|
||||
{"Compression", "deflate"},
|
||||
{"ID", Utils::String::GenerateUUIDString()},
|
||||
};
|
||||
|
||||
Logger::Print("Compressing minidump...\n");
|
||||
|
||||
std::string compressedMinidump = Utils::Compression::ZLib::Compress(minidump->ToString());
|
||||
|
||||
Logger::Print("Uploading minidump...\n");
|
||||
|
||||
#ifdef DISABLE_BITMESSAGE
|
||||
for (auto& targetUrl : targetUrls)
|
||||
{
|
||||
Utils::WebIO webio("Firefucks", targetUrl);
|
||||
|
||||
std::string buffer = MinidumpUpload::Encode(compressedMinidump, extraHeaders);
|
||||
std::string result = webio.PostFile(buffer, "files[]", "minidump.dmpx");
|
||||
|
||||
std::string errors;
|
||||
json11::Json object = json11::Json::parse(result, errors);
|
||||
|
||||
if (!object.is_object()) continue;
|
||||
|
||||
json11::Json success = object["success"];
|
||||
|
||||
if (!success.is_bool() || !success.bool_value()) return false;
|
||||
|
||||
json11::Json files = object["files"];
|
||||
|
||||
if (!files.is_array()) continue;
|
||||
|
||||
for (auto file : files.array_items())
|
||||
{
|
||||
json11::Json url = file["url"];
|
||||
json11::Json hash = file["hash"];
|
||||
|
||||
if (hash.is_string() && url.is_string())
|
||||
{
|
||||
if (Utils::String::ToLower(Utils::Cryptography::SHA1::Compute(buffer, true)) == Utils::String::ToLower(hash.string_value()))
|
||||
{
|
||||
MessageBoxA(0, url.string_value().data(), 0, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
// BitMessage has a max msg size that is somewhere around 220 KB, split it up as necessary!
|
||||
auto totalParts = compressedMinidump.size() / this->maxSegmentSize + 1;
|
||||
extraHeaders.insert({ "Parts", Utils::String::VA("%d",totalParts) });
|
||||
|
||||
for (size_t offset = 0; offset < compressedMinidump.size(); offset += this->maxSegmentSize)
|
||||
{
|
||||
auto extraPartHeaders = extraHeaders;
|
||||
|
||||
auto part = compressedMinidump.substr(offset, std::min(this->maxSegmentSize, compressedMinidump.size() - offset));
|
||||
auto partNum = offset / this->maxSegmentSize + 1;
|
||||
extraPartHeaders.insert({ "Part", Utils::String::VA("%d", partNum) });
|
||||
|
||||
Logger::Print("Uploading part %d out of %d (%d bytes)...\n", partNum, totalParts, part.size());
|
||||
BitMessage::Singleton->SendMsg(MinidumpUpload::targetAddress, MinidumpUpload::Encode(part, extraPartHeaders));
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MinidumpUpload::Encode(std::string data, std::map<std::string, std::string> extraHeaders)
|
||||
{
|
||||
std::string marker = "MINIDUMP";
|
||||
std::stringstream output;
|
||||
|
||||
extraHeaders["Encoding"] = "raw";
|
||||
extraHeaders["Version"] = VERSION_STR;
|
||||
|
||||
output << "-----BEGIN " << marker << "-----\n";
|
||||
|
||||
// Insert extra headers
|
||||
for (auto& header : extraHeaders)
|
||||
{
|
||||
output << header.first << ": " << header.second << "\n";
|
||||
}
|
||||
|
||||
output << "\n"
|
||||
<< data << "\n"
|
||||
<< "-----END " << marker << "-----\n";
|
||||
|
||||
return output.str();
|
||||
}
|
||||
#pragma endregion
|
||||
}
|
77
src/Components/Modules/MinidumpUpload.hpp
Normal file
77
src/Components/Modules/MinidumpUpload.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091)
|
||||
#include <dbghelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#pragma warning(pop)
|
||||
|
||||
extern const int MiniDumpTiny;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
// This class holds a Minidump and allows easy access to its aspects.
|
||||
class Minidump
|
||||
{
|
||||
public:
|
||||
~Minidump();
|
||||
|
||||
static Minidump* Create(std::string path, LPEXCEPTION_POINTERS exceptionInfo, int type = MiniDumpTiny);
|
||||
static Minidump* Open(std::string path);
|
||||
bool GetStream(MINIDUMP_STREAM_TYPE type, PMINIDUMP_DIRECTORY* directoryPtr, PVOID* streamPtr, ULONG* streamSizePtr);
|
||||
bool Check();
|
||||
std::string ToString();
|
||||
|
||||
private:
|
||||
Minidump();
|
||||
|
||||
bool EnsureFileMapping();
|
||||
|
||||
static Minidump* Initialize(std::string path, DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE);
|
||||
|
||||
HANDLE fileHandle;
|
||||
HANDLE mapFileHandle;
|
||||
};
|
||||
|
||||
class MinidumpUpload : public Component
|
||||
{
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
const char* GetName() { return "MinidumpUpload"; };
|
||||
#endif
|
||||
MinidumpUpload();
|
||||
~MinidumpUpload();
|
||||
|
||||
// Uploads the given minidump.
|
||||
bool Upload(Minidump* minidump);
|
||||
|
||||
// Generates a new minidump and saves it to the folder for queued minidumps.
|
||||
Minidump* CreateQueuedMinidump(LPEXCEPTION_POINTERS exceptionInfo, int minidumpType = MiniDumpTiny);
|
||||
|
||||
// Browses the folder for queued minidumps and uploads each queued minidump.
|
||||
// On Release builds this will also delete every successfully uploaded minidump.
|
||||
bool UploadQueuedMinidumps();
|
||||
|
||||
static MinidumpUpload* Singleton;
|
||||
|
||||
private:
|
||||
std::thread uploadThread;
|
||||
|
||||
// Encodes the given minidump so that it can be uploaded in a proper format.
|
||||
// Internally, this will compress the minidump and decorate it with proper markers and first-look headers.
|
||||
static std::string Encode(std::string data, std::map<std::string, std::string> extraHeaders = {});
|
||||
|
||||
// Ensures the queued minidumps folder exists. Will return false if the directory can't be created and does not exist.
|
||||
static bool EnsureQueuedMinidumpsFolderExists();
|
||||
|
||||
// Contains the path to the minidumps folder.
|
||||
static const std::string queuedMinidumpsFolder;
|
||||
|
||||
#ifdef DISABLE_BITMESSAGE
|
||||
static const std::vector<std::string> targetUrls;
|
||||
#else
|
||||
static const std::string targetAddress;
|
||||
static const unsigned int maxSegmentSize;
|
||||
#endif
|
||||
};
|
||||
}
|
@ -61,8 +61,9 @@
|
||||
#include <json11.hpp>
|
||||
#include <tomcrypt.h>
|
||||
#include <wink/signal.hpp>
|
||||
// Bitmessage
|
||||
#ifndef DISABLE_BITMESSAGE
|
||||
#include <BitMRC.h>
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
@ -112,6 +113,7 @@
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
#pragma comment(lib, "Urlmon.lib")
|
||||
#pragma comment(lib, "Advapi32.lib")
|
||||
#pragma comment(lib, "rpcrt4.lib")
|
||||
|
||||
// Enable additional literals
|
||||
using namespace std::literals;
|
||||
|
@ -131,14 +131,16 @@ namespace Utils
|
||||
return EncodeBase64(input.c_str(), input.size());
|
||||
}
|
||||
|
||||
// Decodes a given string from Base64
|
||||
std::string DecodeBase64(const std::string& input) {
|
||||
unsigned char* out = new unsigned char[input.size()];
|
||||
unsigned long outlen = input.size();
|
||||
base64_decode((unsigned char*)input.c_str(), input.size(), out, &outlen);
|
||||
std::string ret((char*)out, outlen);
|
||||
delete[] out;
|
||||
return ret;
|
||||
// Generates a UUID and returns the string representation of it
|
||||
std::string GenerateUUIDString() {
|
||||
// Generate UUID data
|
||||
UUID uuid;
|
||||
UuidCreate(&uuid);
|
||||
|
||||
// Convert to string representation
|
||||
char* strdata = NULL;
|
||||
UuidToStringA(&uuid, (RPC_CSTR*)&strdata);
|
||||
return std::string(strdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ namespace Utils
|
||||
|
||||
std::string EncodeBase64(const char* input, const unsigned long inputSize);
|
||||
std::string EncodeBase64(const std::string& input);
|
||||
std::string DecodeBase64(const std::string& input);
|
||||
|
||||
std::string GenerateUUIDString();
|
||||
}
|
||||
}
|
||||
|
@ -22,4 +22,17 @@ namespace Utils
|
||||
if (pos == std::string::npos) return data;
|
||||
return data.substr(0, pos).data();
|
||||
}
|
||||
|
||||
void OutputDebugLastError()
|
||||
{
|
||||
DWORD errorMessageID = ::GetLastError();
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
||||
std::string message(messageBuffer, size);
|
||||
|
||||
OutputDebugStringA(Utils::String::VA("Last error code: 0x%08X (%s)\n", errorMessageID, message));
|
||||
|
||||
LocalFree(messageBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ namespace Utils
|
||||
{
|
||||
std::string GetMimeType(std::string url);
|
||||
std::string ParseChallenge(std::string data);
|
||||
void OutputDebugLastError();
|
||||
|
||||
template <typename T> void Merge(std::vector<T>* target, T* source, size_t length)
|
||||
{
|
||||
|
@ -134,21 +134,30 @@ namespace Utils
|
||||
return body;
|
||||
}
|
||||
|
||||
std::string WebIO::PostFile(std::string url, std::string data)
|
||||
std::string WebIO::PostFile(std::string url, std::string data, std::string fieldName, std::string fileName)
|
||||
{
|
||||
WebIO::SetURL(url);
|
||||
return WebIO::PostFile(data);
|
||||
return WebIO::PostFile(data, fieldName, fileName);
|
||||
}
|
||||
|
||||
std::string WebIO::PostFile(std::string data)
|
||||
std::string WebIO::PostFile(std::string data, std::string fieldName, std::string fileName)
|
||||
{
|
||||
WebIO::Params headers;
|
||||
|
||||
std::string boundary = "----WebKitFormBoundaryHoLVocRsBxs71fU6";
|
||||
headers["Content-Type"] = "multipart/form-data, boundary=" + boundary;
|
||||
|
||||
Utils::String::Replace(fieldName, "\"", "\\\"");
|
||||
Utils::String::Replace(fieldName, "\\", "\\\\");
|
||||
Utils::String::Replace(fileName, "\"", "\\\"");
|
||||
Utils::String::Replace(fileName, "\\", "\\\\");
|
||||
|
||||
std::string body = "--" + boundary + "\r\n";
|
||||
body += "Content-Disposition: form-data; name=\"files[]\"; filename=\"minidump.dmp\"\r\n";
|
||||
body += "Content-Disposition: form-data; name=\"";
|
||||
body += fieldName;
|
||||
body += "\"; filename=\"";
|
||||
body += fileName;
|
||||
body += "\"\r\n";
|
||||
body += "Content-Type: application/octet-stream\r\n\r\n";
|
||||
body += data + "\r\n";
|
||||
body += "--" + boundary + "--\r\n";
|
||||
|
@ -26,8 +26,8 @@ namespace Utils
|
||||
void SetURL(std::string url);
|
||||
void SetCredentials(std::string username, std::string password);
|
||||
|
||||
std::string PostFile(std::string url, std::string data);
|
||||
std::string PostFile(std::string data);
|
||||
std::string PostFile(std::string url, std::string data, std::string fieldName, std::string fileName);
|
||||
std::string PostFile(std::string data, std::string fieldName, std::string fileName);
|
||||
|
||||
std::string Post(std::string url, WebIO::Params params);
|
||||
std::string Post(std::string url, std::string body);
|
||||
|
Loading…
Reference in New Issue
Block a user