1042 lines
26 KiB
C
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;
|
|
}
|