Skip to content

Commit

Permalink
Merge pull request #7 from buzzer-re/dev
Browse files Browse the repository at this point in the history
Improvements
  • Loading branch information
buzzer-re authored Aug 2, 2023
2 parents 77d0866 + 941442e commit eed53ea
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Shinigami/Ichigo/Mem.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <Windows.h>

#define PAGE_SIZE 0x1000
// Unpack structures
struct Memory
{
Expand Down Expand Up @@ -30,4 +31,5 @@ struct Memory
bool safe;
DWORD ProcessID;
bool cAlloc;
bool Visited;
};
24 changes: 20 additions & 4 deletions Shinigami/Ichigo/PEDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ PIMAGE_DOS_HEADER PEDumper::FindPE(Memory* Mem)
if (!VirtualQuery((LPCVOID)pNtHeader, &mbi, 0x1000))
continue;

if (pNtHeader->Signature == IMAGE_NT_SIGNATURE)
if (IsValidNT(pNtHeader))
{

return pDosHeader;
}

}
}
}
Expand All @@ -95,6 +99,7 @@ PIMAGE_DOS_HEADER PEDumper::HeuristicSearch(Memory* Mem)
// If found, validated sections offset and if it has code
PIMAGE_NT_HEADERS pNtHeader;
MEMORY_BASIC_INFORMATION mbi;
uint8_t* PossibleBegin = nullptr;

for (uint8_t* Curr = reinterpret_cast<uint8_t*>(Mem->Addr); (ULONG_PTR)Curr < Mem->End - sizeof(IMAGE_NT_HEADERS); Curr++)
{
Expand Down Expand Up @@ -131,7 +136,7 @@ PIMAGE_DOS_HEADER PEDumper::HeuristicSearch(Memory* Mem)
// Save the current NT address and proceed to the brute-force part of this code, if the brute-force fail
// use this saved NT address and tell the user
IMAGE_SECTION_HEADER* sectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
bool Invalid = true;
bool Invalid = false;
bool IPInBetween = false;
ULONG_PTR VirtualMemAddr;
for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, sectionHeader++) {
Expand All @@ -140,7 +145,7 @@ PIMAGE_DOS_HEADER PEDumper::HeuristicSearch(Memory* Mem)
Invalid = true;
break;
}

VirtualMemAddr = sectionHeader->VirtualAddress + (ULONG_PTR)Mem->Addr;
// Check if there is any overflow here
if (sectionHeader->PointerToRawData + sectionHeader->SizeOfRawData + (ULONG_PTR) Mem->Addr >= Mem->End ||
Expand All @@ -159,8 +164,9 @@ PIMAGE_DOS_HEADER PEDumper::HeuristicSearch(Memory* Mem)
if (Invalid)
continue;

if (!Invalid && IPInBetween)
if (!Invalid)
{
PossibleBegin = Curr;
PipeLogger::LogInfo(L"Possible NT found at 0x%p! Trying to rebuild...", pNtHeader);
PIMAGE_DOS_HEADER DosHdr = RebuildDOSHeader(Mem, (ULONG_PTR)Curr);
if (DosHdr != nullptr)
Expand All @@ -173,6 +179,16 @@ PIMAGE_DOS_HEADER PEDumper::HeuristicSearch(Memory* Mem)
}
}

if (PossibleBegin != nullptr)
{
PipeLogger::LogInfo(L"Found a possible injected PE file, trying to rebuild it...");
PIMAGE_DOS_HEADER DosHdr = RebuildDOSHeader(Mem, (ULONG_PTR)PossibleBegin);
if (DosHdr != nullptr)
{
PipeLogger::LogInfo(L"DOS header rebuilded! Please notice that this image could be damaged!");
return DosHdr;
}
} else PipeLogger::LogInfo(L"Failed to find an malicious implant");

// We failed to search detached DOS headers
// Search for common sections names such: .text, .data, .rdata
Expand Down
3 changes: 3 additions & 0 deletions Shinigami/Ichigo/ProcessUnhollow.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ namespace Unhollow

// Place our hooks
BOOL InitUnhollowHooks(HookManager& hkManager, Ichigo::Arguments& Options);


// Clean
VOID Shutdown();


// Hold the current config state
static Ichigo::Arguments* IchigoOptions;
}
Expand Down
68 changes: 60 additions & 8 deletions Shinigami/Ichigo/Unpacker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ NTSTATUS WINAPI GenericUnpacker::hkNtAllocateVirtualMemory(HANDLE ProcessHandle,
memory.End = reinterpret_cast<ULONG_PTR>(memory.Addr + AllocatedSize);
memory.Size = AllocatedSize;
memory.prot = Protect;
memory.Visited = false;
PipeLogger::LogInfo(L"Tracking newly allocated memory 0x%p with protections 0x%x", *BaseAddress, Protect);
}

Expand Down Expand Up @@ -131,6 +132,7 @@ LONG WINAPI GenericUnpacker::VEHandler(EXCEPTION_POINTERS* pExceptionPointers)
// Verify if it's being monitored and executing
//
GuardedAddress = ExceptionRecord->ExceptionInformation[1];

if (GenericUnpacker::cUnpacker.IsBeingMonitored((ULONG_PTR)pExceptionPointers->ContextRecord->XIP))
{
PipeLogger::LogInfo(L"STATUS_GUARD_PAGE_VIOLATION: Attempt to execute a monitored memory area at address 0x%p, starting dumping...", ExceptionRecord->ExceptionAddress);
Expand All @@ -141,9 +143,10 @@ LONG WINAPI GenericUnpacker::VEHandler(EXCEPTION_POINTERS* pExceptionPointers)
if (GenericUnpacker::cUnpacker.Dump(Mem))
{
PipeLogger::Log(L"Saved stage %d as %s ", GenericUnpacker::cUnpacker.StagesPath.size(), GenericUnpacker::cUnpacker.StagesPath.back().c_str());
GenericUnpacker::cUnpacker.RemoveMonitor(Mem);
GenericUnpacker::cUnpacker.RemoveMonitor(Mem);
}


LastValidExceptionAddress = NULL;
}
// An exception happened, but we are not monitoring this code and this code is operating inside our monitored memory
// like an shellcode decryption process, we need to save this address to place the page_guard bit again
Expand All @@ -157,7 +160,7 @@ LONG WINAPI GenericUnpacker::VEHandler(EXCEPTION_POINTERS* pExceptionPointers)

case STATUS_SINGLE_STEP:
// Add the PAGE_GUARD again
if (GenericUnpacker::cUnpacker.IsBeingMonitored(LastValidExceptionAddress))
if (LastValidExceptionAddress && GenericUnpacker::cUnpacker.IsBeingMonitored(LastValidExceptionAddress))
{
VirtualQuery((LPCVOID) LastValidExceptionAddress, &mbi, PAGE_SIZE);
mbi.Protect |= PAGE_GUARD;
Expand All @@ -169,6 +172,23 @@ LONG WINAPI GenericUnpacker::VEHandler(EXCEPTION_POINTERS* pExceptionPointers)
return EXCEPTION_CONTINUE_SEARCH;
}

LONG WINAPI GenericUnpacker::VEHLastHandler(EXCEPTION_POINTERS* pExceptionPointers)
{
if (!GenericUnpacker::cUnpacker.ExecutionAborted)
{
// Force exit to detach our DLL safely, this will trigger the last scan
PEXCEPTION_RECORD ExceptionRecord = pExceptionPointers->ExceptionRecord;
PipeLogger::LogInfo(L"ERROR: An exception with code (0x%lx) was raised! Performing one last scan and aborting execution...", ExceptionRecord->ExceptionCode);

GenericUnpacker::FinalScan();
GenericUnpacker::cUnpacker.ExecutionAborted = true;
}


return EXCEPTION_CONTINUE_SEARCH;
}


VOID GenericUnpacker::RemoveGuard(ULONG_PTR Address)
{
DWORD dwOldProt;
Expand All @@ -191,6 +211,7 @@ BOOL GenericUnpacker::InitUnpackerHooks(HookManager& hkManager, Ichigo::Argument
if (NTDLL == NULL)
return FALSE;


BYTE* NtAllocateVirtualMemoryPointer = reinterpret_cast<BYTE*>(GetProcAddress(NTDLL, "NtAllocateVirtualMemory"));
BYTE* NtWriteVirtualMemoryPointer = reinterpret_cast<BYTE*>(GetProcAddress(NTDLL, "NtWriteVirtualMemory"));
BYTE* NtProtectVirtualMemoryPointer = reinterpret_cast<BYTE*>(GetProcAddress(NTDLL, "NtProtectVirtualMemory"));
Expand All @@ -207,14 +228,19 @@ BOOL GenericUnpacker::InitUnpackerHooks(HookManager& hkManager, Ichigo::Argument
// Register the VEH handler
//
AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)GenericUnpacker::VEHandler);
//
// Handler to detect when process suddenly exits and invoke our memory scan
//
AddVectoredExceptionHandler(false, (PVECTORED_EXCEPTION_HANDLER)GenericUnpacker::VEHLastHandler);

PipeLogger::LogInfo(L"Unpacker: -- Hooked functions and added the VEH callback --");
PipeLogger::LogInfo(L"Unpacker: -- Hooked functions and added the exception handlers callbacks --");
GenericUnpacker::Ready = TRUE;
GenericUnpacker::IchigoOptions = &Arguments;

return TRUE;
}


//
// Save the raw dump of the suspicious code
// Also scan searching for MZ/PE headers
Expand All @@ -226,20 +252,24 @@ BOOL GenericUnpacker::Unpacker::Dump(Memory* Mem)

PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(Mem->Addr);
PIMAGE_NT_HEADERS NTHeaders;
Mem->Visited = true; // Mark this memory region as visited

if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE)
{

// Check NT
NTHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>((ULONG_PTR) dosHeader + dosHeader->e_lfanew);
if (NTHeaders->Signature == IMAGE_NT_SIGNATURE)

if (PEDumper::IsValidNT(NTHeaders))
{
PEDumper::FixPESections(Mem);
suffix = L"_stage_" + std::to_wstring(StagesPath.size() + 1);
suffix += (NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) ? L".dll" : L".exe";
goto save;
}
}
else
{
{
// Search a PE file within the region
PIMAGE_DOS_HEADER pDosHeader = PEDumper::FindPE(Mem);
if (pDosHeader != nullptr)
Expand All @@ -261,8 +291,7 @@ BOOL GenericUnpacker::Unpacker::Dump(Memory* Mem)

if (Utils::SaveToFile(SaveName.c_str(), &PeMem, TRUE))
{
PipeLogger::Log(L"Found a embedded PE file inside the newly executed memory are, saved as %s!", SaveName.c_str());

PipeLogger::Log(L"Found a embedded PE file inside the newly executed memory area, saved as %s!", SaveName.c_str());
StagesPath.push_back(SaveName);
return TRUE;
}
Expand All @@ -271,6 +300,7 @@ BOOL GenericUnpacker::Unpacker::Dump(Memory* Mem)

if (Ichigo::Options.OnlyPE) return FALSE;

save:
std::wstring FileName = Utils::BuildFilenameFromProcessName(suffix.c_str());
std::wstring SaveName = Utils::PathJoin(GenericUnpacker::IchigoOptions->WorkDirectory, FileName);

Expand All @@ -283,6 +313,28 @@ BOOL GenericUnpacker::Unpacker::Dump(Memory* Mem)
return FALSE;
}

//
// Perform one last memory scan in all unvisited memory areas, this can be called before the process crashes or exit normally
//
VOID GenericUnpacker::FinalScan()
{
if (GenericUnpacker::cUnpacker.ExecutionAborted) return;
PipeLogger::Log(L"Performing one last scan in unvisited memory regions...");
Ichigo::Options.OnlyPE = true;

for (auto& Mem : cUnpacker.Watcher)
{
if (!Mem.Visited)
{
PipeLogger::LogInfo(L"Visiting %p-%p...", Mem.Addr, Mem.End);
if (cUnpacker.Dump(&Mem))
{
PipeLogger::Log(L"Saved missed implant as %s!", cUnpacker.StagesPath.back().c_str());
}
}
}

}

//
// Verify if the exception happened in one of our monitered addresses
Expand Down
5 changes: 5 additions & 0 deletions Shinigami/Ichigo/Unpacker.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace GenericUnpacker
static class Unpacker
{
public:
Unpacker() : ExecutionAborted(false) {}
Memory* IsBeingMonitored(ULONG_PTR Address);
BOOL Dump(Memory* StartAddress);
VOID RemoveMonitor(Memory* Mem);
Expand All @@ -56,12 +57,16 @@ namespace GenericUnpacker
WinAPIPointers Win32Pointers;
std::list<Memory> Watcher;
std::vector<std::wstring> StagesPath;
bool ExecutionAborted;
} cUnpacker;


LONG WINAPI VEHandler(EXCEPTION_POINTERS* pExceptionPointers);
LONG WINAPI VEHLastHandler(EXCEPTION_POINTERS* pExceptionPointers);

BOOL InitUnpackerHooks(HookManager& hkManager, Ichigo::Arguments& Arguments);
// Perform an scan in all unvisited memory regions
VOID FinalScan();

VOID RemoveGuard(ULONG_PTR Address);
VOID Shutdown();
Expand Down
22 changes: 19 additions & 3 deletions Shinigami/Ichigo/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,28 @@ BOOL Utils::SaveToFile(const wchar_t* filename, Memory* data, BOOL Paginate)

DWORD BytesWritten;
DWORD OldProt;

// Paginate write
SIZE_T BytesToRead = PAGE_SIZE;
SIZE_T Remaining = data->Size;
SIZE_T Diff;

while (Remaining)
{
if (Remaining < PAGE_SIZE)
{
BytesToRead = Remaining;
}

if (!(success = VirtualProtect(data->Addr, BytesToRead, PAGE_READONLY, &OldProt))) break;

VirtualProtect(data->Addr, data->Size, PAGE_READWRITE, &OldProt);
if (!(success = WriteFile(hFile, data->Addr, BytesToRead, &BytesWritten, NULL) && (BytesWritten == BytesToRead))) break;

if (!(success = VirtualProtect(data->Addr, BytesToRead, OldProt, &OldProt))) break;

success = WriteFile(hFile, data->Addr, data->Size, &BytesWritten, NULL) && (BytesWritten == data->Size);
Remaining -= BytesToRead;

VirtualProtect(data->Addr, data->Size, OldProt, &OldProt);
}

CloseHandle(hFile);

Expand Down
5 changes: 5 additions & 0 deletions Shinigami/Ichigo/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,15 @@ typedef NTSTATUS (WINAPI NtProtectVirtualMemory) (
);


typedef LONG (WINAPI mPTOP_LEVEL_EXCEPTION_FILTER)(
EXCEPTION_POINTERS* ExceptionInfo
);

struct WinAPIPointers {
NtAllocateVirtualMemory* NtAllocateVirtualMemory;
NtWriteVirtualMemory* NtWriteVirtualMemory;
NtCreateUserProcess* NtCreateUserProcess;
NtResumeThread* NtResumeThread;
NtProtectVirtualMemory* NtProtectVirtualMemory;
mPTOP_LEVEL_EXCEPTION_FILTER* PTOP_LEVEL_EXCEPTION_FILTER;
};
3 changes: 3 additions & 0 deletions Shinigami/Ichigo/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ BOOL APIENTRY DllMain( HMODULE hModule,
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:

GenericUnpacker::FinalScan();

GenericUnpacker::Shutdown();
Unhollow::Shutdown();
PipeLogger::LogInfo(L"Exiting...");
Expand Down
4 changes: 2 additions & 2 deletions Shinigami/Shinigami/Injector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ BOOL Injector::InjectSuspended(_In_ const std::wstring& DLLPath, _In_ const Ichi
Logger::LogInfo(L"Injection finished, waiting connection...");
if (!PipeLogger::InitPipe())
{
Logger::LogInfo(L"Unable to initialize log pipes! Error: %d\n", GetLastError());
Logger::LogInfo(L"Unable to initialize log pipes! Error: %llx\n", GetLastError());
TerminateProcess(pi.hProcess, 0);
goto quit;
}
Expand All @@ -82,7 +82,7 @@ BOOL Injector::InjectSuspended(_In_ const std::wstring& DLLPath, _In_ const Ichi
WaitForSingleObject(pi.hThread, INFINITE);
GetExitCodeProcess(pi.hProcess, &ExitCode);

Logger::LogInfo(L"Child process exited with code %d, closing...", ExitCode);
Logger::LogInfo(L"Child process exited with code %llx, closing...", ExitCode);
quit:
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Expand Down

0 comments on commit eed53ea

Please sign in to comment.