/*
 *
 * 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;
}
