2020-09-30 17:17:25 +02:00

1042 lines
26 KiB
C

/*
*
* dmmodule.c
*
* Get information about loaded modules and about memory
*
*/
#include "dmp.h"
/* We can't allow the Ex/Dm pool defines here */
#undef ExAllocatePool
#undef ExAllocatePoolWithTag
#undef ExFreePool
NTSTATUS
StLoadImage(
PCOSTR oszName,
PCOSTR oszFor,
PLDR_DATA_TABLE_ENTRY *ppldte
);
void
UnloadReferencedModules(
PLDR_DATA_TABLE_ENTRY pldte
);
RTL_CRITICAL_SECTION csLoader;
void InitLoader(void)
{
RtlInitializeCriticalSection(&csLoader);
}
struct {
PVOID pfnImport;
PVOID pfnSub;
} rgfsub[] = {
{ NULL, DmAllocatePool },
{ NULL, DmAllocatePoolWithTag },
{ NULL, DmFreePool },
};
/* In certain cases, we may want to call the functions we've overridden. Here
* are the stubs */
void DmExFreePool(PVOID p)
{
void (*pfnExFreePool)(PVOID) = rgfsub[2].pfnImport;
pfnExFreePool(p);
}
PLDR_DATA_TABLE_ENTRY PldteGetModule(LPCSTR sz, BOOL fMatchExt)
{
PLDR_DATA_TABLE_ENTRY pldte = NULL;
PLIST_ENTRY ple;
WCHAR *wz;
int cch;
const char *pch;
ple = g_dmi.LoadedModuleList->Flink;
while(ple != g_dmi.LoadedModuleList) {
pldte = CONTAINING_RECORD(ple, LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
ple = ple->Flink;
wz = pldte->BaseDllName.Buffer;
cch = pldte->BaseDllName.Length >> 1;
pch = sz;
while(cch) {
char ch1, ch2;
ch1 = (char )*wz++;
if(ch1 >= 'a' && ch1 <= 'z')
ch1 -= 'a' - 'A';
ch2 = *pch++;
if(ch2 >= 'a' && ch2 <= 'z')
ch2 -= 'a' - 'A';
if(ch1 != ch2)
break;
--cch;
}
if(cch == 0) {
if(*pch == 0)
/* We've matched the whole name */
break;
} else if(!fMatchExt && pch[-1] == 0 && wz[-1] == '.')
/* We've matched the basename */
break;
pldte = NULL;
}
return pldte;
}
HRESULT DmGetMemory(LPCVOID pb, DWORD cb, LPVOID lpbBuf, LPDWORD pcbRet)
{
DWORD cbRet;
BOOL fIsPageValid = TRUE;
DWORD dwPageBase = (DWORD) pb + 0x1000;
if (!lpbBuf)
return E_INVALIDARG;
for(cbRet = 0; cb-- && fIsPageValid; ++(DWORD)pb) {
if((dwPageBase ^ (DWORD)pb) & 0xfffff000) {
dwPageBase = (DWORD)pb & 0xfffff000;
fIsPageValid = MmIsAddressValid((BYTE *)pb);
}
if(fIsPageValid)
fIsPageValid = FGetMemory((BYTE *)pb, lpbBuf);
if(fIsPageValid)
++cbRet, ++(BYTE *)lpbBuf;
}
if(pcbRet)
*pcbRet = cbRet;
return !cb || pcbRet ? XBDM_NOERR : XBDM_MEMUNMAPPED;
}
HRESULT DmSetMemory(LPVOID pb, DWORD cb, LPCVOID lpbBuf, LPDWORD pcbRet)
{
DWORD cbRet;
BOOL fIsPageValid = TRUE;
DWORD dwPageBase = (DWORD) pb + 0x1000;
if (!lpbBuf)
return E_INVALIDARG;
for(cbRet = 0; cb-- && fIsPageValid; ++(DWORD)pb) {
if((dwPageBase ^ (DWORD)pb) & 0xfffff000) {
dwPageBase = (DWORD)pb & 0xfffff000;
fIsPageValid = MmIsAddressValid(pb);
}
if(fIsPageValid)
fIsPageValid = FSetMemory(pb, *(BYTE *)lpbBuf);
if(fIsPageValid)
++cbRet, ++(BYTE *)lpbBuf;
}
if(pcbRet)
*pcbRet = cbRet;
return !cb || pcbRet ? XBDM_NOERR : XBDM_MEMUNMAPPED;
}
void FixupBistroImage(void)
{
/* VTune Call Graph needs to be able to modify the code pages in the image,
* so we need to detect whether we're running an instrumented image. We
* look for a section marked .bistro. If we see one, we go through all of
* the section flags and mark them as writable */
PXBEIMAGE_SECTION pxsh;
int cxsh;
cxsh = XeImageHeader()->NumberOfSections;
pxsh = XeImageHeader()->SectionHeaders;
for(; cxsh--; ++pxsh) {
if(0 == memcmp(pxsh->SectionName, ".bistro", 8)) {
/* We found it */
cxsh = XeImageHeader()->NumberOfSections;
pxsh = XeImageHeader()->SectionHeaders;
for(; cxsh--; ++pxsh)
pxsh->SectionFlags |= XBEIMAGE_SECTION_WRITEABLE;
return;
}
}
}
DWORD DwXmhFlags(PDMN_MODLOAD pdmml)
{
DWORD dwFlags = DMN_MODFLAG_XBE;
PIMAGE_DOS_HEADER pdosh;
PIMAGE_NT_HEADERS pnth;
if (XeImageHeader()->TlsDirectory)
dwFlags |= DMN_MODFLAG_TLS;
/* See if we appear to have PE headers here */
do {
pdosh = pdmml->BaseAddress;
if(pdosh->e_magic != IMAGE_DOS_SIGNATURE)
break;
pnth = (PIMAGE_NT_HEADERS)((ULONG)pdosh + pdosh->e_lfanew);
if(pnth->Signature != IMAGE_NT_SIGNATURE)
break;
if(pnth->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
dwFlags |= DMN_MODFLAG_PEHEADER;
} while (0);
return dwFlags;
}
struct _DM_WALK_MODULES {
PLIST_ENTRY ple;
};
HRESULT DmWalkLoadedModules(PDM_WALK_MODULES *ppdmwm, DMN_MODLOAD *pdmml)
{
PDM_WALK_MODULES pdmwm;
PLDR_DATA_TABLE_ENTRY pldte;
PIMAGE_NT_HEADERS pnth;
if(!ppdmwm || !pdmml)
return E_INVALIDARG;
pdmwm = *ppdmwm;
if(!pdmwm) {
/* Set up our list */
pdmwm = DmAllocatePoolWithTag(sizeof *pdmwm, 'mwmd');
if(!pdmwm)
return E_OUTOFMEMORY;
pdmwm->ple = g_dmi.LoadedModuleList->Flink;
*ppdmwm = pdmwm;
}
for(;;) {
if(pdmwm->ple == g_dmi.LoadedModuleList)
break;
pldte = CONTAINING_RECORD(pdmwm->ple, LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
pdmwm->ple = pdmwm->ple->Flink;
FillSzFromWz(pdmml->Name, sizeof pdmml->Name,
pldte->BaseDllName.Buffer, pldte->BaseDllName.Length >> 1);
pdmml->BaseAddress = pldte->DllBase;
if(!(pldte->Flags & LDRP_ENTRY_XE_IMAGE)) {
pdmml->Size = pldte->SizeOfImage;
pnth = RtlImageNtHeader(pldte->DllBase);
if(pnth) {
pdmml->CheckSum = (ULONG)pnth->OptionalHeader.CheckSum;
pdmml->TimeStamp = (ULONG)pnth->FileHeader.TimeDateStamp;
} else {
pdmml->CheckSum = 0;
pdmml->TimeStamp = 0;
}
pdmml->Flags = 0;
return XBDM_NOERR;
} else {
pdmml->Size = XeImageHeader()->NtSizeOfImage;
pdmml->TimeStamp = XeImageHeader()->NtTimeDateStamp;
pdmml->CheckSum = XeImageHeader()->NtCheckSum;
pdmml->Flags = DwXmhFlags(pdmml);
return XBDM_NOERR;
}
}
/* We didn't find anything to return, so say end of list */
return XBDM_ENDOFLIST;
}
HRESULT DmCloseLoadedModules(PDM_WALK_MODULES pdmwm)
{
if (!pdmwm)
DmFreePool(pdmwm);
return XBDM_NOERR;
}
struct _DM_WALK_MODSECT {
PLDR_DATA_TABLE_ENTRY pldte;
union {
struct {
/* XE module */
PXBEIMAGE_SECTION pxsh;
int cxsh;
int ixsh;
};
struct {
/* kernel module */
int ish;
PIMAGE_NT_HEADERS pnth;
PIMAGE_SECTION_HEADER psh;
int csh;
};
};
};
void RewindDmws(PDM_WALK_MODSECT pdmws)
{
if(pdmws->pldte->Flags & LDRP_ENTRY_XE_IMAGE) {
pdmws->cxsh = XeImageHeader()->NumberOfSections;
pdmws->pxsh = XeImageHeader()->SectionHeaders;
pdmws->ixsh = 0;
} else {
pdmws->pnth = RtlImageNtHeader(pdmws->pldte->DllBase);
if(pdmws->pnth) {
pdmws->psh = IMAGE_FIRST_SECTION(pdmws->pnth);
pdmws->csh = pdmws->pnth->FileHeader.NumberOfSections;
pdmws->ish = 0;
}
}
}
void DmslFromXsh(PXBEIMAGE_SECTION pxsh, PDMN_SECTIONLOAD pdmsl)
{
strncpy(pdmsl->Name, pxsh->SectionName, MAX_PATH);
pdmsl->Name[MAX_PATH-1] = 0;
pdmsl->BaseAddress = (PVOID)pxsh->VirtualAddress;
pdmsl->Size = pxsh->VirtualSize;
pdmsl->Index = pxsh - XeImageHeader()->SectionHeaders;
pdmsl->Flags = pxsh->SectionReferenceCount ? DMN_SECFLAG_LOADED : 0;
}
HRESULT DmWalkModuleSections(PDM_WALK_MODSECT *ppdmws, LPCSTR szModule,
PDMN_SECTIONLOAD pdmsl)
{
PDM_WALK_MODSECT pdmws;
if (!ppdmws || !pdmsl)
return E_INVALIDARG;
pdmws = *ppdmws;
if(!pdmws) {
PLDR_DATA_TABLE_ENTRY pldte;
if (!szModule)
return E_INVALIDARG;
/* First find the module */
pldte = PldteGetModule(szModule, TRUE);
if(!pldte)
/* Never found a match */
return XBDM_NOMODULE;
/* Now we can set things up */
pdmws = DmAllocatePoolWithTag(sizeof *pdmws, 'swmd');
if(!pdmws)
return E_OUTOFMEMORY;
*ppdmws = pdmws;
pdmws->pldte = pldte;
RewindDmws(pdmws);
}
for(;;) {
if(pdmws->pldte->Flags & LDRP_ENTRY_XE_IMAGE) {
/* This is an XE section */
PXBEIMAGE_SECTION pxsh;
if(!pdmws->cxsh)
/* Nothing left */
break;
pxsh = pdmws->pxsh++;
--pdmws->cxsh;
/* We want to report this section */
DmslFromXsh(pxsh, pdmsl);
++pdmws->ixsh;
return XBDM_NOERR;
} else {
/* Make sure we actually have headers */
if(!pdmws->pnth)
return XBDM_NOMODULE;
if(pdmws->csh) {
/* We want to report this section */
strcpy(pdmsl->Name, pdmws->psh->Name);
pdmsl->Name[8] = 0;
pdmsl->BaseAddress = (PVOID)((PBYTE)pdmws->pldte->DllBase +
pdmws->psh->PointerToRawData);
pdmsl->Size = pdmws->psh->Misc.VirtualSize;
pdmsl->Index = ++pdmws->ish;
pdmsl->Flags = 0;
++pdmws->psh;
--pdmws->csh;
return XBDM_NOERR;
}
break;
}
}
return XBDM_ENDOFLIST;
}
HRESULT DmCloseModuleSections(PDM_WALK_MODSECT pdmws)
{
if (pdmws)
DmFreePool(pdmws);
return XBDM_NOERR;
}
HRESULT DmGetModuleLongName(LPCSTR szShort, LPSTR szLong, LPDWORD pcchLong)
{
PLDR_DATA_TABLE_ENTRY pldte;
LPCSTR sz;
DWORD cch;
DWORD cchMax;
if (!szShort || !szLong || !pcchLong)
return E_INVALIDARG;
/* First find the module */
pldte = PldteGetModule(szShort, TRUE);
if(!pldte)
return XBDM_NOMODULE;
/* No XE means no data */
if(!(pldte->Flags & LDRP_ENTRY_XE_IMAGE))
return E_FAIL;
/* Look for the long name */
sz = XeImageHeader()->DebugPathName;
if(!sz)
return E_FAIL;
cchMax = *pcchLong - 1;
for(cch = 0; *sz && cch < cchMax; ++cch)
*szLong++ = *sz++;
*szLong = 0;
*pcchLong = cch;
return XBDM_NOERR;
}
HRESULT DmGetXbeInfo(LPCSTR szName, PDM_XBE pxbe)
{
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
POBJECT_STRING objectName;
XBEIMAGE_HEADER xh;
PXBEIMAGE_HEADER pxh;
HRESULT hr;
if (!pxbe)
return E_INVALIDARG;
/* If we already have an XBE in memory, we'll use its information.
report the name it came from */
pxh = XeImageHeader();
if(MmDbgReadCheck(pxh) && !(g_dmi.Flags & DMIFLAG_RUNSHELL))
{
//
// Grab the image name out the LDR_CURRENT_IMAGE structure.
//
objectName = XeImageFileName;
FObNameToFileName(objectName, pxbe->LaunchPath, sizeof(pxbe->LaunchPath));
pxbe->TimeStamp = pxh->TimeDateStamp;
pxbe->CheckSum = 0;
pxbe->StackSize = pxh->SizeOfStackCommit;
return XBDM_NOERR;
}
st = FCreateFile(&h, GENERIC_READ | SYNCHRONIZE, szName, NULL, 0, 0,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(st))
return XBDM_NOSUCHFILE;
/* Make sure we can read an XE header out of this thing */
st = NtReadFile(h, NULL, NULL, NULL, &iosb, &xh, sizeof xh, NULL);
if(NT_SUCCESS(st) && xh.Signature == XBEIMAGE_SIGNATURE) {
int ich;
for(ich = 0; *szName && ich < sizeof pxbe->LaunchPath - 1; ++ich)
pxbe->LaunchPath[ich] = *szName++;
pxbe->LaunchPath[ich] = 0;
pxbe->TimeStamp = xh.TimeDateStamp;
pxbe->CheckSum = 0;
pxbe->StackSize = xh.SizeOfStackCommit;
hr = XBDM_NOERR;
} else
hr = XBDM_NOSUCHFILE;
NtClose(h);
return hr;
}
BOOL FResolveImport(PVOID pvBase, PIMAGE_EXPORT_DIRECTORY ped, ULONG cbExports,
LPCSTR szName, PVOID *ppvRet)
{
ULONG *rgichNames;
USHORT *rgwOrdinals;
ULONG *rgpvFunctions;
ULONG iMic, iMid, iMac;
ULONG iSym;
if(((ULONG)szName & 0xffff0000) == 0)
/* ordinal */
iSym = (ULONG)szName - ped->Base;
else {
rgichNames = (PVOID)((PBYTE)pvBase + ped->AddressOfNames);
rgwOrdinals = (PVOID)((PBYTE)pvBase + ped->AddressOfNameOrdinals);
//
// Lookup the import name in the name table using a binary search.
//
iMic = 0;
iMid = 0;
iMac = ped->NumberOfNames;
while (iMac > iMic) {
int sgn;
//
// Compute the next probe index and compare the import name
// with the export name entry.
//
iMid = (iMic + iMac) >> 1;
sgn = strcmp(szName, (PCHAR)((PCHAR)pvBase + rgichNames[iMid]));
if (sgn < 0) {
iMac = iMid;
} else if (sgn > 0) {
iMic = iMid + 1;
} else {
break;
}
}
//
// If the iMac index is less than the iMic index, then a matching
// table entry was not found. Otherwise, get the ordinal number
// from the ordinal table.
//
if (iMac <= iMic)
return FALSE;
else
iSym = rgwOrdinals[iMid];
}
if(iSym >= ped->NumberOfFunctions)
return FALSE;
rgpvFunctions = (PVOID)((PBYTE)pvBase + ped->AddressOfFunctions);
*ppvRet = (PBYTE)pvBase + rgpvFunctions[iSym];
if(*ppvRet > (PVOID)ped && *ppvRet < (PVOID)((PBYTE)ped + cbExports)) {
/* This is a forwarder */
int cchDll;
PSTR szSym;
char szDll[64];
PLDR_DATA_TABLE_ENTRY pldteF;
szSym = strchr(*ppvRet, '.');
if(!szSym)
return FALSE;
cchDll = szSym++ - (PCHAR)*ppvRet;
if(cchDll > sizeof szDll - 1)
return FALSE;
memcpy(szDll, *ppvRet, cchDll);
szDll[cchDll] = 0;
pldteF = PldteGetModule(szDll, FALSE);
if(!pldteF)
return FALSE;
return DmGetProcAddress((HANDLE)pldteF, szSym, ppvRet);
} else {
/* Check to see whether we need to do import substitution on this
* guy */
int ifsub = sizeof rgfsub / sizeof rgfsub[0];
while(ifsub--) {
if(*ppvRet == rgfsub[ifsub].pfnImport) {
*ppvRet = rgfsub[ifsub].pfnSub;
break;
}
}
}
return TRUE;
}
BOOL FResolveImageImports(PLDR_DATA_TABLE_ENTRY pldteImage, PCOSTR oszName,
PLIST_ENTRY LoadedModuleList)
{
PIMAGE_IMPORT_DESCRIPTOR pid;
PIMAGE_EXPORT_DIRECTORY ped;
ULONG cbImports;
ULONG cbExports;
PLDR_DATA_TABLE_ENTRY pldte;
PIMAGE_THUNK_DATA pthILT;
PIMAGE_THUNK_DATA pthIAT;
PBYTE pbImageBase = pldteImage->DllBase;
int ipldte;
BOOL fLoadFailed = FALSE;
/* Loop through all of the imports of this library and fix them up, loading
* the supporting libs as necessary */
pid = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(pbImageBase,
TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &cbImports);
if(!pid)
return FALSE;
if(!LoadedModuleList)
LoadedModuleList = g_dmi.LoadedModuleList;
if(g_dmi.LoadedModuleList) {
/* Build a list of imported libraries */
PIMAGE_IMPORT_DESCRIPTOR pidT = pid;
for(ipldte = 0; pidT->Name && pidT->FirstThunk; ++ipldte, ++pidT);
pldteImage->LoadedImports = DmAllocatePoolWithTag((ipldte + 1) *
sizeof(PVOID), 'TDmM');
if(!pldteImage)
return FALSE;
RtlZeroMemory(pldteImage->LoadedImports, (ipldte + 1) * sizeof(PVOID));
*(int *)pldteImage->LoadedImports = ipldte;
} else
/* This is not an unloadable module */
pldteImage->LoadedImports = NULL;
ipldte = 1;
while(pid->Name && pid->FirstThunk) {
/* Get the loader entry for this library */
if(g_dmi.LoadedModuleList) {
char *szName = pbImageBase + pid->Name;
RtlEnterCriticalSection(&csLoader);
/* See if this guy is already loaded */
_asm {
pushfd
cli
}
pldte = PldteGetModule(szName, TRUE);
if(pldte)
++pldte->LoadCount;
_asm popfd
if(!pldte) {
/* Not already loaded, so we need to load it */
if(!NT_SUCCESS(StLoadImage(szName, oszName, &pldte)))
pldte = NULL;
}
RtlLeaveCriticalSection(&csLoader);
} else {
/* We're relocating ourselves, so we need to look up the
* entry. But we should safely assume that the first entry in
* the list is the kernel */
pldte = CONTAINING_RECORD(LoadedModuleList->Flink,
LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
}
if(!pldte)
return FALSE;
if(pldteImage->LoadedImports)
((PVOID *)pldteImage->LoadedImports)[ipldte++] = pldte;
/* Get the export directory from the library we've referenced */
ped = (PIMAGE_EXPORT_DIRECTORY)
RtlImageDirectoryEntryToData(pldte->DllBase, TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT, &cbExports);
if(!ped) {
UnloadReferencedModules(pldteImage);
return FALSE;
}
/* Now loop over all of the import thunks and fix them up */
pthILT = (PIMAGE_THUNK_DATA)(pbImageBase + pid->OriginalFirstThunk);
pthIAT = (PIMAGE_THUNK_DATA)(pbImageBase + pid->FirstThunk);
while(pthILT->u1.AddressOfData) {
char *szName;
if(IMAGE_SNAP_BY_ORDINAL(pthILT->u1.Ordinal))
szName = (char *)IMAGE_ORDINAL(pthILT->u1.Ordinal);
else {
PIMAGE_IMPORT_BY_NAME pibn = (PIMAGE_IMPORT_BY_NAME)
(pbImageBase + pthILT->u1.AddressOfData);
szName = pibn->Name;
}
if(!FResolveImport(pldte->DllBase, ped, cbExports, szName,
(PVOID *)&pthIAT->u1.Function))
{
#if DBG
char szError[256];
char szFn[32];
STRING st, *pst;
if(((ULONG)szName & 0xffff0000) == 0) {
sprintf(szFn, "ordinal %d", szName);
szName = szFn;
}
sprintf(szError, "missing %s in %s\n", szName,
pbImageBase + pid->Name);
st.Buffer = szError;
st.MaximumLength = sizeof szError - 1;
st.Length = (USHORT)strlen(szError);
pst = &st;
_asm {
mov eax, BREAKPOINT_PRINT
mov ecx, pst
int 2dh
int 3
}
#endif // DBG
fLoadFailed = TRUE;
}
++pthILT;
++pthIAT;
}
if(fLoadFailed) {
UnloadReferencedModules(pldteImage);
return FALSE;
}
++pid;
}
return TRUE;
}
BOOL FFinishImageLoad(PLDR_DATA_TABLE_ENTRY pldteT, LPCSTR szName,
PLDR_DATA_TABLE_ENTRY *ppldteOut)
{
PLDR_DATA_TABLE_ENTRY pldte;
UNICODE_STRING ustName;
UNICODE_STRING ustFullName;
ANSI_STRING ast;
NTSTATUS st;
PBYTE pbBase = pldteT->DllBase;
PIMAGE_NT_HEADERS pnth;
const char *pch, *pchBase;
ULONG ul;
for(pch = pchBase = szName; *pch; ++pch)
if(*pch == '\\')
pchBase = pch + 1;
RtlInitAnsiString(&ast, pchBase);
ustName.MaximumLength = RtlAnsiStringToUnicodeSize(&ast);
ustName.Buffer = DmAllocatePool(ustName.MaximumLength);
if(!ustName.Buffer)
return FALSE;
st = RtlAnsiStringToUnicodeString(&ustName, &ast, FALSE);
if(!NT_SUCCESS(st))
goto RetFree1;
RtlInitAnsiString(&ast, szName);
ustFullName.MaximumLength = RtlAnsiStringToUnicodeSize(&ast);
ustFullName.Buffer = DmAllocatePool(ustFullName.MaximumLength);
if(!ustFullName.Buffer)
goto RetFree1;
st = RtlAnsiStringToUnicodeString(&ustFullName, &ast, FALSE);
if(!NT_SUCCESS(st))
goto RetFree2;
pldte = DmAllocatePoolWithTag(sizeof *pldte, 'dLmM');
if(!pldte) {
RetFree2:
DmFreePool(ustFullName.Buffer);
RetFree1:
DmFreePool(ustName.Buffer);
return FALSE;
}
RtlZeroMemory(pldte, sizeof *pldte);
pldte->BaseDllName = ustName;
pldte->FullDllName = ustFullName;
pldte->DllBase = pldteT->DllBase;
pnth = RtlImageNtHeader(pbBase);
pldte->EntryPoint = pbBase + pnth->OptionalHeader.AddressOfEntryPoint;
pldte->SizeOfImage = pnth->OptionalHeader.SizeOfImage;
pldte->CheckSum = pnth->OptionalHeader.CheckSum;
pldte->Flags = LDRP_ENTRY_PROCESSED | LDRP_SYSTEM_MAPPED;
pldte->LoadCount = 1;
pldte->LoadedImports = pldteT->LoadedImports;
ExInterlockedInsertTailList(g_dmi.LoadedModuleList, &pldte->InLoadOrderLinks);
if(RtlImageDirectoryEntryToData(pldte->DllBase, TRUE,
IMAGE_DIRECTORY_ENTRY_DEBUG, &ul))
{
DbgLoadImageSymbols(&ast, pldte->DllBase, (ULONG_PTR)-1);
pldte->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
}
if(ppldteOut)
*ppldteOut = pldte;
return TRUE;
}
BOOL FFixupXbdm(PVOID pvBase, PDMINIT pdmi)
{
LDR_DATA_TABLE_ENTRY ldte;
PVOID *rgppv[3];
int ifsub;
ldte.DllBase = pvBase;
/* Find our kernel imports */
if(!FResolveImageImports(&ldte, NULL, pdmi->LoadedModuleList))
return FALSE;
/* Initialize the pool */
InitPool();
ldte.LoadedImports = (PVOID)-1;
g_dmi.LoadedModuleList = pdmi->LoadedModuleList;
if(!FFinishImageLoad(&ldte, "xbdm.dll", NULL))
return FALSE;
/* With the module loaded, we now need to set up the kernel routine
* substitution list, and then thunk ourselves */
_asm {
lea eax, ExAllocatePool
lea ecx, ExAllocatePoolWithTag
lea edx, ExFreePool
mov rgppv, eax
mov rgppv+4, ecx
mov rgppv+8, edx
}
for(ifsub = 0; ifsub < 3; ++ifsub) {
rgfsub[ifsub].pfnImport = *rgppv[ifsub];
*rgppv[ifsub] = rgfsub[ifsub].pfnSub;
}
}
void UnloadReferencedModules(PLDR_DATA_TABLE_ENTRY pldte)
{
if(pldte->LoadedImports) {
PLDR_DATA_TABLE_ENTRY *rgpldte = pldte->LoadedImports;
int i;
int c = (int)rgpldte[0];
for(i = c; i; --i) {
HANDLE h = (HANDLE)rgpldte[i];
if(h)
DmUnloadExtension(h);
}
DmFreePool(rgpldte);
pldte->LoadedImports = NULL;
}
}
HRESULT DmUnloadExtension(HANDLE hModule)
{
PLDR_DATA_TABLE_ENTRY pldte = (PLDR_DATA_TABLE_ENTRY)hModule;
if(pldte->LoadedImports == (PVOID)-1)
/* This module can't be unloaded */
return XBDM_NOERR;
_asm {
pushfd
cli
}
if(--pldte->LoadCount == 0) {
RemoveEntryList(&pldte->InLoadOrderLinks);
} else
pldte = NULL;
_asm popfd
if(pldte) {
ANSI_STRING ast;
char sz[256];
ast.Length = 0;
ast.MaximumLength = sizeof sz;
ast.Buffer = sz;
RtlUnicodeStringToAnsiString(&ast, &pldte->FullDllName, FALSE);
DbgUnLoadImageSymbols(&ast, pldte->DllBase, (ULONG_PTR)-1);
UnloadReferencedModules(pldte);
MmDbgFreeMemory(pldte->DllBase, 0);
DmFreePool(pldte->BaseDllName.Buffer);
DmFreePool(pldte->FullDllName.Buffer);
DmFreePool(pldte);
}
return XBDM_NOERR;
}
NTSTATUS StLoadImage(PCOSTR oszName, PCOSTR oszFor,
PLDR_DATA_TABLE_ENTRY *ppldte)
{
NTSTATUS st;
HANDLE h;
IO_STATUS_BLOCK iosb;
OBJECT_ATTRIBUTES oa;
OBJECT_STRING ost;
FILE_NETWORK_OPEN_INFORMATION fni;
LDR_DATA_TABLE_ENTRY ldte;
*ppldte = NULL;
if(oszFor) {
const OCHAR *poch, *pochDir;
int cchBase;
int cchTot;
for(poch = pochDir = oszFor; *poch; ++poch)
if(*poch == '\\')
pochDir = poch;
cchBase = strlen(oszName);
cchTot = (pochDir - oszFor) + cchBase + 1;
poch = oszName;
oszName = DmAllocatePool(cchTot + 1);
if(!poch)
return STATUS_INSUFFICIENT_RESOURCES;
memcpy((POCHAR)oszName, oszFor, (pochDir - oszFor) + 1);
memcpy((POCHAR)oszName + (cchTot - cchBase), poch, cchBase);
((POCHAR)oszName)[cchTot] = 0;
}
RtlInitObjectString(&ost, oszName);
InitializeObjectAttributes(&oa, &ost, OBJ_CASE_INSENSITIVE, NULL,
NULL);
st = NtOpenFile(&h, FILE_EXECUTE, &oa, &iosb, 0,
FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(st))
goto ErrRet;
st = NtQueryInformationFile(h, &iosb, &fni, sizeof fni,
FileNetworkOpenInformation);
if(!NT_SUCCESS(st))
goto ErrClose;
ldte.DllBase = MmDbgAllocateMemory(fni.EndOfFile.LowPart, PAGE_READWRITE);
if(ldte.DllBase == NULL) {
st = STATUS_NO_MEMORY;
goto ErrClose;
}
st = NtReadFile(h, NULL, NULL, NULL, &iosb, ldte.DllBase,
fni.EndOfFile.LowPart, NULL);
if(!NT_SUCCESS(st))
goto ErrClose;
try {
/* Now that the image is loaded, we first relocate it */
st = LdrRelocateImage(ldte.DllBase, "xbdm", STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES, STATUS_INVALID_IMAGE_FORMAT);
if(NT_SUCCESS(st)) {
/* Now we resolve its imports */
st = FResolveImageImports(&ldte, oszName, NULL) ? STATUS_SUCCESS :
STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
if(NT_SUCCESS(st) && !FFinishImageLoad(&ldte, oszName, ppldte)) {
st = STATUS_INSUFFICIENT_RESOURCES;
UnloadReferencedModules(&ldte);
}
if(NT_SUCCESS(st)) {
BOOL fUnload = FALSE;
/* We need to call this function's entry point. Since we
* don't have any faith that the calling convention of the
* callee is sane, we do this via an asm thunk */
CallDxtEntry((*ppldte)->EntryPoint, &fUnload);
if(fUnload)
st = 0x80001000;
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
st = GetExceptionCode();
}
if(!NT_SUCCESS(st)) {
if(*ppldte) {
/* We failed after registering the module. Need to pull it from
* the list */
DmUnloadExtension((HANDLE)*ppldte);
*ppldte = NULL;
} else
MmDbgFreeMemory(ldte.DllBase, fni.EndOfFile.LowPart);
}
ErrClose:
NtClose(h);
ErrRet:
if(oszFor)
DmFreePool((PVOID)oszName);
return st;
}
HRESULT DmLoadExtension(LPCSTR szName, PHANDLE phModule, PVOID *ppvBase)
{
OCHAR oszName[256];
NTSTATUS st;
OBJECT_STRING ost;
const char *pch, *pchBase;
LDR_DATA_TABLE_ENTRY *pldte;
if (!szName)
return E_INVALIDARG;
st = FFileNameToObName(szName, oszName, sizeof(oszName)/sizeof(OCHAR)) ?
STATUS_SUCCESS : STATUS_OBJECT_PATH_INVALID;
RtlEnterCriticalSection(&csLoader);
/* See if this basename is already loaded */
for(pchBase = NULL, pch = szName; *pch; ++pch)
if(*pch == '\\')
pchBase = pch + 1;
_asm {
pushfd
cli
}
pldte = PldteGetModule(pchBase ? pchBase : szName, TRUE);
if(pldte) {
if(pchBase && NT_SUCCESS(st)) {
/* The basename is there; if the full filenames are different,
* then we'll generate an error */
WCHAR *wz;
int cch;
const OCHAR *poch;
wz = pldte->FullDllName.Buffer;
cch = pldte->FullDllName.Length >> 1;
poch = szName;
while(cch) {
char ch1, ch2;
ch1 = (char )*wz++;
if(ch1 >= 'a' && ch1 <= 'z')
ch1 -= 'a' - 'A';
ch2 = (char )*pch++;
if(ch2 >= 'a' && ch2 <= 'z')
ch2 -= 'a' - 'A';
if(ch1 != ch2)
break;
--cch;
}
if(cch || *pch)
/* The names differ */
st = STATUS_OBJECT_NAME_COLLISION;
}
if(NT_SUCCESS(st))
/* We've got a match; increment the refcount and we're done */
++pldte->LoadCount;
}
_asm popfd
if(!pldte && NT_SUCCESS(st))
/* No already-loaded module, so let's try to load it */
st = StLoadImage(oszName, NULL, &pldte);
RtlLeaveCriticalSection(&csLoader);
switch(st) {
case STATUS_OBJECT_NAME_NOT_FOUND:
case STATUS_OBJECT_PATH_NOT_FOUND:
case STATUS_OBJECT_PATH_INVALID:
case STATUS_OBJECT_NAME_INVALID:
return XBDM_NOSUCHFILE;
default:
if(!NT_SUCCESS(st))
return E_FAIL;
break;
}
if(phModule)
*phModule = (HANDLE)pldte;
if(ppvBase)
*ppvBase = pldte->DllBase;
return XBDM_NOERR;
}
HRESULT DmGetProcAddress(HANDLE hModule, LPCSTR szName, PVOID *ppvRet)
{
PLDR_DATA_TABLE_ENTRY pldte = (PLDR_DATA_TABLE_ENTRY)hModule;
PVOID pvBase = pldte->DllBase;
PIMAGE_EXPORT_DIRECTORY ped;
ULONG cbExports;
if (!szName || !ppvRet)
return E_INVALIDARG;
/* Find the module's export directory */
ped = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(pvBase, TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT, &cbExports);
if(!ped)
return XBDM_NOSUCHFILE;
return FResolveImport(pvBase, ped, cbExports, szName, ppvRet) ?
XBDM_NOERR : XBDM_NOSUCHFILE;
}