Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion CAPE/CAPE.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,33 @@ extern void UnpackerInit();
extern BOOL SetInitialBreakpoints(PVOID ImageBase);
extern BOOL BreakpointsSet, TraceRunning;
extern lookup_t g_dotnet_jit;

dotnet_module_cache_t g_dotnet_modules[1024] = {0};
int g_dotnet_modules_count = 0;

void CacheDotNetModule(ULONG_PTR ModuleBase, DWORD MetadataRVA, DWORD MetadataSize) {
if (g_dotnet_modules_count >= 1024) return;
// Prevent duplicate caching
for (int i = 0; i < g_dotnet_modules_count; i++) {
if (g_dotnet_modules[i].ModuleBase == ModuleBase) {
return;
}
}
g_dotnet_modules[g_dotnet_modules_count].ModuleBase = ModuleBase;
g_dotnet_modules[g_dotnet_modules_count].MetadataRVA = MetadataRVA;
g_dotnet_modules[g_dotnet_modules_count].MetadataSize = MetadataSize;
g_dotnet_modules_count++;
DebugOutput("CacheDotNetModule: Cached module base 0x%p (Metadata RVA 0x%x, Size 0x%x).\n", (PVOID)ModuleBase, MetadataRVA, MetadataSize);
}

dotnet_module_cache_t* FindCachedDotNetModule(ULONG_PTR ModuleBase) {
for (int i = 0; i < g_dotnet_modules_count; i++) {
if (g_dotnet_modules[i].ModuleBase == ModuleBase) {
return &g_dotnet_modules[i];
}
}
return NULL;
}
extern char* StringsFile;
extern HANDLE Strings;

Expand Down Expand Up @@ -824,7 +851,7 @@ PVOID GetFunctionAddress(HMODULE ModuleBase, PCHAR FunctionName)
}


if (!FunctionAddress && ModuleBase == GetModuleHandle("clr"))
if (!FunctionAddress && (ModuleBase == GetModuleHandle("clr") || ModuleBase == GetModuleHandle("mscorwks") || ModuleBase == GetModuleHandle("coreclr")))
return GetCLRAddress(ModuleBase, FunctionName);

if (!FunctionAddress && ModuleBase == GetModuleHandle("clrjit"))
Expand Down
12 changes: 12 additions & 0 deletions CAPE/CAPE.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ void DumpStrings(void);
BOOL ProcessDumped;
unsigned int DumpCount, DotNetCacheDumpCount;

typedef struct {
ULONG_PTR ModuleBase;
DWORD MetadataRVA;
DWORD MetadataSize;
} dotnet_module_cache_t;

extern dotnet_module_cache_t g_dotnet_modules[1024];
extern int g_dotnet_modules_count;

void CacheDotNetModule(ULONG_PTR ModuleBase, DWORD MetadataRVA, DWORD MetadataSize);
dotnet_module_cache_t* FindCachedDotNetModule(ULONG_PTR ModuleBase);

SYSTEM_INFO SystemInfo;
PVOID CallingModule;

Expand Down
70 changes: 70 additions & 0 deletions CAPE/ScyllaHarness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,73 @@ extern "C" int ScyllaDumpProcess(HANDLE hProcess, DWORD_PTR ModuleBase, DWORD_PT
return 0;
}

static void HealDotNetPEHeaders(DWORD_PTR Buffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)Buffer;

// 1. Heal DOS Signature ("MZ" = 0x5A4D)
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) {
pDos->e_magic = IMAGE_DOS_SIGNATURE;
pDos->e_lfanew = 0x80; // Standard NT header offset
}

PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((PBYTE)Buffer + pDos->e_lfanew);

// 2. Heal NT Signature ("PE\0\0" = 0x00004550)
if (pNt->Signature != IMAGE_NT_SIGNATURE) {
pNt->Signature = IMAGE_NT_SIGNATURE;
#ifdef _WIN64
pNt->FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64; // Set to standard 64-bit AMD64 machine target
pNt->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
#else
pNt->FileHeader.Machine = IMAGE_FILE_MACHINE_I386; // Set to standard 32-bit x86 machine target
pNt->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
#endif
pNt->FileHeader.NumberOfSections = 3; // Standard fallback section count
}

// 3. Heal CLR COM Descriptor Directory (index 14)
PIMAGE_DATA_DIRECTORY pClrDir = &pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
if (pClrDir->VirtualAddress == 0 || pClrDir->Size == 0) {
dotnet_module_cache_t* pCache = FindCachedDotNetModule((ULONG_PTR)Buffer);
DWORD metadataRVA = pCache ? pCache->MetadataRVA : 0;
DWORD metadataSize = pCache ? pCache->MetadataSize : 0;

// If no cached metadata RVA exists, dynamically scan the buffer for IMAGE_COR20_HEADER
// Standard IMAGE_COR20_HEADER has Size of 72 bytes (0x48) and MajorRuntimeVersion 2.
if (metadataRVA == 0) {
__try {
PBYTE pStart = (PBYTE)Buffer;

DWORD imageSize = pNt->OptionalHeader.SizeOfImage;
if (imageSize == 0 || imageSize > 0x2000000) {
imageSize = 0x2000000; // Cap at 32MB instead of blind hardcoded 2MB segment limit
}

PBYTE pEnd = pStart + imageSize;
for (PBYTE p = pStart + 0x200; p < pEnd - 8; p += 4) {
PDWORD pdw = (PDWORD)p;
// Match size=0x48, version=2.5 or 2.0
if (pdw[0] == 0x48 && (pdw[1] == 0x00050002 || pdw[1] == 0x00000002)) {
metadataRVA = (DWORD)(p - pStart);
metadataSize = 0x48; // Size of COR20 header
DebugOutput("HealDotNetPEHeaders: Successfully found IMAGE_COR20_HEADER natively in memory at offset 0x%x.\n", metadataRVA);
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DebugOutput("HealDotNetPEHeaders: Exception occurred scanning for IMAGE_COR20_HEADER magic.\n");
}
}

if (metadataRVA != 0) {
pClrDir->VirtualAddress = metadataRVA;
pClrDir->Size = metadataSize;
DebugOutput("HealDotNetPEHeaders: Successfully healed zeroed CLR Data Directory to RVA 0x%x (Size 0x%x).\n", metadataRVA, metadataSize);
}
}
}

//**************************************************************************************
extern "C" int ScyllaDumpPE(DWORD_PTR Buffer)
//**************************************************************************************
Expand All @@ -417,6 +484,9 @@ extern "C" int ScyllaDumpPE(DWORD_PTR Buffer)

ProcessAccessHelp::setCurrentProcessAsTarget();

// Surgically heal zeroed/mangled PE headers and CLR directories in-memory right before Scylla is called
HealDotNetPEHeaders(Buffer);

DebugOutput("DumpPE: Instantiating PeParser with address: 0x%p.\n", Buffer);

peFile = new PeParser((DWORD_PTR)Buffer, TRUE);
Expand Down
5 changes: 5 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,11 @@ void parse_config_line(char* line)
if (g_config.trace_all)
DebugOutput("Config: Trace all enabled.\n");
}
else if (!stricmp(key, "jit-trace-all")) {
g_config.jit_trace_all = value[0] == '1';
if (g_config.jit_trace_all)
DebugOutput("Config: JIT verbose tracing enabled.\n");
}
else if (!stricmp(key, "trace-into-api")) {
unsigned int x = 0;
char *p2;
Expand Down
1 change: 1 addition & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ struct _g_config {
char *str[MAX_PATH];

int trace_all;
int jit_trace_all;
int step_out;
int file_offsets;
int no_logs;
Expand Down
Loading