// LoadRemoteDLL.cpp: implementation of the CLoadRemoteDLL class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "LoadRemoteDLL.h"
#include "NTWriteProcessMemory.h"
#include "NtGetExitCodeThread.h"

#pragma warning( disable : 4996 4127 4090 )

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CLoadRemoteDLL::CLoadRemoteDLL(LPCWSTR swDllName, HANDLE hTargetProcess)
: m_pCopyOf_idata(NULL)
, m_pRemote_idata(NULL)
, m_dw_idataSize(0)
, m_hProcessHandle(hTargetProcess)
, m_pRemoteModuleBase(NULL)
, m_pRemotePEHeaders(NULL)
, m_nViewSize(0)
, m_objModule32HLP(hTargetProcess)
, m_bProcessNotStartUp(false)
, m_pCopyImpDirectoty(NULL)
, m_dw_idataRVA(0)
, m_pLocalModuleBase(NULL)
{
    errno_t nErrno;
    RtlZeroMemory(
        &m_stPEImageHeaders,
        sizeof(m_stPEImageHeaders)
        );
    nErrno = wcscpy_s(
        m_swDllName,
        swDllName
        );
    m_bProcessNotStartUp = (m_objModule32HLP.m_pModuleListTable == NULL);
}

CLoadRemoteDLL::~CLoadRemoteDLL( )
{
}

#pragma warning(disable: 4702)
bool CLoadRemoteDLL::OpenAndMapDLLFile(
    PCWSTR pwsDLLFileName,
    PSECURITY_DESCRIPTOR pDLLFileSD,
    PSECURITY_DESCRIPTOR pDLLSectionSD
                                      )
{
    WCHAR               wsNTFilePath[260];
    UNICODE_STRING      usNTFilePath;
    NTSTATUS            dwNTStatus, dwNTStatus1;
    OBJECT_ATTRIBUTES   stObjectAttributes;
    IO_STATUS_BLOCK     stIOStatusBlock;
    HANDLE              hDLLFileHandle;
    HANDLE              hDLLFileMapRP;
    LARGE_INTEGER       llSectionOffset = { 0, 0 };
    PBYTE               pPointToPEHeaders;
    PBYTE               pRemotePEHeaders;
    DWORD               dwPEHeadersRVA;
    DWORD               dwPEHeadersSize;
    DWORD               dwSignature;
    if (m_hProcessHandle == NULL)
    {
        return false;
    }
    if ((pwsDLLFileName == NULL) || (*pwsDLLFileName == 0))
    {
        return false;
    }
    if (pwsDLLFileName[1] == L':')
    {
        wcscpy( wsNTFilePath, L"\\??\\" );
        wcscat( wsNTFilePath, pwsDLLFileName );
    }
    else if (wcsncmp( pwsDLLFileName, L"\\\\", 2) == 0)
    {
        wcscpy( wsNTFilePath, L"\\??\\UNC" );
        wcscat( wsNTFilePath, &pwsDLLFileName[1] );
    }
    else
    {
        wcscpy( wsNTFilePath, pwsDLLFileName );
    }
    RtlInitUnicodeString(
        &usNTFilePath,
        wsNTFilePath
        );
    InitializeObjectAttributes(
        &stObjectAttributes,
        &usNTFilePath,
        OBJ_CASE_INSENSITIVE,
        NULL,
        pDLLFileSD
        );
    dwNTStatus = NtOpenFile(
        &hDLLFileHandle,
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
        &stObjectAttributes,
        &stIOStatusBlock,
        FILE_SHARE_READ | FILE_SHARE_DELETE,
        0
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    RtlZeroMemory(
        &stObjectAttributes,
        sizeof(stObjectAttributes)
        );
    InitializeObjectAttributes(
        &stObjectAttributes,
        NULL,
        OBJ_INHERIT,
        NULL,
        pDLLSectionSD
        );
    m_pLocalModuleBase = NULL;
    dwNTStatus = NtCreateSection(
        &hDLLFileMapRP,
        SECTION_ALL_ACCESS, // SECTION_MAP_EXECUTE | SECTION_MAP_READ | SECTION_QUERY
        &stObjectAttributes,
        NULL,
        PAGE_EXECUTE_WRITECOPY,  // PAGE_EXECUTE_WRITECOPY PAGE_READWRITE PAGE_EXECUTE_READWRITE PAGE_READONLY
        SEC_IMAGE, // SEC_IMAGE SEC_COMMIT
        hDLLFileHandle
        );
    dwNTStatus1 = NtClose(hDLLFileHandle);
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    RtlZeroMemory(
        &llSectionOffset,
        sizeof(llSectionOffset)
        );
    m_pRemoteModuleBase = NULL;
    m_nViewSize = 0;
    dwNTStatus = NtMapViewOfSection(
        hDLLFileMapRP,                  // IN HANDLE  SectionHandle
        m_hProcessHandle,               // IN HANDLE  ProcessHandle
        (PVOID*) &m_pRemoteModuleBase,  // IN OUT PVOID  *BaseAddress (PVOID*) &m_pRemoteModuleBase
        0,                              // IN ULONG  ZeroBits
        0,                              // IN ULONG  CommitSize
        &llSectionOffset,               // IN OUT PLARGE_INTEGER  SectionOffset  OPTIONAL
        &m_nViewSize,                   // IN OUT PSIZE_T  ViewSize
        ViewUnmap,                      // IN SECTION_INHERIT InheritDisposition
        0,                              // IN ULONG  AllocationType
        PAGE_EXECUTE_WRITECOPY          // IN ULONG  Protect PAGE_EXECUTE_WRITECOPY PAGE_READWRITE PAGE_EXECUTE_READWRITE
        );
    dwNTStatus1 = NtClose(hDLLFileMapRP);
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pPointToPEHeaders = m_pRemoteModuleBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof dwPEHeadersRVA,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    if ((dwPEHeadersRVA == 0) || (dwPEHeadersRVA >= m_nViewSize))
    {
        return false;
    }
    pRemotePEHeaders = m_pRemoteModuleBase + dwPEHeadersRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &dwSignature,
        sizeof dwSignature,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    if (dwSignature != IMAGE_NT_SIGNATURE)
    {
        return false;
    }
    dwPEHeadersSize = DWORD((dwPEHeadersRVA + sizeof m_stPEImageHeaders > m_nViewSize)
        ? m_nViewSize - dwPEHeadersRVA : sizeof m_stPEImageHeaders);
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &m_stPEImageHeaders,
        dwPEHeadersSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    m_pRemotePEHeaders = pRemotePEHeaders;
    return true;
}

#pragma warning(push)
#pragma warning(disable: 4701)
bool CLoadRemoteDLL::ElaborateAll(
    PSECURITY_DESCRIPTOR pDLLFileSD,
    PSECURITY_DESCRIPTOR pDLLSectionSD
                                 )
{
    bool                    bRets;
    WCHAR                   wsDLLFileName[260];
    errno_t                 nErrno;
    PWCHAR                  pcFindPosion;
    LDR_DATA_TABLE_ENTRY    stInjDllModuleInfo;
    PEB_LDR_DATA            stModuleListTable;
    PLDR_DATA_TABLE_ENTRY   pInjDllModuleInfo;
    PLDR_DATA_TABLE_ENTRY   pProcessModuleInfo;
    NTSTATUS                dwNTStatus;
    IMAGE_NT_HEADERS        stPEImageHeaders;
    PBYTE                   pPointToPEHeaders;
    DWORD                   dwPEHeadersRVA;
    DWORD                   dwSignature;
    PBYTE                   pRemotePEHeaders;
    PWSTR                   pwsInjDllName;
    UNICODE_STRING          usInjDllName;
    UNICODE_STRING          usInjDllShortName;
    BYTE                    baSaveStartIns[sizeof(JumpIndInstAddress)];
    PBYTE                   pbaSaveStartIns;
#ifdef _AMD64_
    JumpIndInstAddress      stJumpIndInstAddress = { 0x25FF, 0, NULL };
#elif defined(_X86_)
    JumpIndInstAddress      stJumpIndInstAddress = { 0x25FF, NULL };
#endif
    PBYTE                   pfInjectDllStartup;
    PBYTE                   pInjectDllStartup;
    PBYTE                   pEntryPoint;
    nErrno = wcscpy_s(
        wsDLLFileName,
        m_swDllName
        );
    pcFindPosion  = wcsrchr( wsDLLFileName, L'\\' ) + 1;
    *pcFindPosion = L'\0';
    nErrno = wcscat_s(
        wsDLLFileName,
        L"injDllStartHLP.dll"
        );
    bRets = OpenAndMapDLLFile(
        wsDLLFileName,
        pDLLFileSD,
        pDLLSectionSD
        );
    if (!bRets)
    {
        return false;
    }
    bRets = RelocationsFixUp( );
    if (!bRets)
    {
        return false;
    }
    assert(!m_bProcessNotStartUp);
    RtlZeroMemory(
        &stInjDllModuleInfo,
        sizeof(stInjDllModuleInfo)
        );
    RtlZeroMemory(
        &stModuleListTable,
        sizeof(stModuleListTable)
        );
    RtlZeroMemory(
        &stPEImageHeaders,
        sizeof(stPEImageHeaders)
        );
    pInjDllModuleInfo = (PLDR_DATA_TABLE_ENTRY) FindExportByName(
        "g_stInjDllModuleInfo",
        (PBYTE) m_pRemoteModuleBase
        );
    if (pInjDllModuleInfo == NULL)
    {
        return false;
    }
    pwsInjDllName = (PWSTR) FindExportByName(
        "g_wsInjDllName",
        (PBYTE) m_pRemoteModuleBase
        );
    if (pwsInjDllName == NULL)
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pwsInjDllName,
        PVOID(m_swDllName),
        ULONG(wcslen(m_swDllName)*sizeof(WCHAR) + sizeof(WCHAR)),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pwsInjDllName = (PWSTR) FindExportByName(
        "g_wsInjHelpDllName",
        (PBYTE) m_pRemoteModuleBase
        );
    if (pwsInjDllName == NULL)
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pwsInjDllName,
        PVOID(wsDLLFileName),
        ULONG(wcslen(wsDLLFileName)*sizeof(WCHAR) + sizeof(WCHAR)),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    RtlInitUnicodeString(
        &usInjDllName,
        wsDLLFileName
        );
    stInjDllModuleInfo.FullDllName.Length = usInjDllName.Length;
    stInjDllModuleInfo.FullDllName.MaximumLength = usInjDllName.MaximumLength;
    stInjDllModuleInfo.FullDllName.Buffer = pwsInjDllName;
    pcFindPosion = wcsrchr(
        usInjDllName.Buffer,
        L'\\') + 1;
    RtlInitUnicodeString(
        &usInjDllShortName,
        pcFindPosion
        );
    stInjDllModuleInfo.ShortDllName.Length = usInjDllShortName.Length;
    stInjDllModuleInfo.ShortDllName.MaximumLength = usInjDllShortName.MaximumLength;
    stInjDllModuleInfo.ShortDllName.Buffer = pwsInjDllName + (pcFindPosion - wsDLLFileName);
    stInjDllModuleInfo.DllBase = m_pRemoteModuleBase;
    pPointToPEHeaders = m_pRemoteModuleBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof dwPEHeadersRVA,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    if (dwPEHeadersRVA == 0)
    {
        return false;
    }
    pRemotePEHeaders = m_pRemoteModuleBase + dwPEHeadersRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &dwSignature,
        sizeof(dwSignature),
        NULL
        );
    if (dwNTStatus < 0)
    {
        return false;
    }
    if (dwSignature != IMAGE_NT_SIGNATURE)
    {
        return false;
    }
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &stPEImageHeaders,
        sizeof(stPEImageHeaders),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    stInjDllModuleInfo.dwImageSize = stPEImageHeaders.OptionalHeader.SizeOfImage;
    stInjDllModuleInfo.TimeDateStamp = stPEImageHeaders.FileHeader.TimeDateStamp;
    stInjDllModuleInfo.pStartRoutine = (PVOID) (PBYTE(m_pRemoteModuleBase)
        + stPEImageHeaders.OptionalHeader.AddressOfEntryPoint);
    bRets = ImportsTableFixUp();
    if (!bRets) {
        return false;
    }
    pProcessModuleInfo = m_objModule32HLP.GetFirstModuleInfoEntry();
    pEntryPoint = (PBYTE) FindExportByName(
        "g_pEntryPoint",
        (PBYTE)m_pRemoteModuleBase
        );
    if (pEntryPoint == NULL)
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pEntryPoint,
        PVOID(&pProcessModuleInfo->pStartRoutine),
        sizeof(pProcessModuleInfo->pStartRoutine),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pEntryPoint = (PBYTE) FindExportByName(
        "g_pfStartupRoutine",
        (PBYTE)m_pRemoteModuleBase
        );
    if (pEntryPoint == NULL)
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pEntryPoint,
        PVOID(&pProcessModuleInfo->pStartRoutine),
        sizeof(pProcessModuleInfo->pStartRoutine),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pbaSaveStartIns = (PBYTE) FindExportByName(
        "g_baSaveStartIns",
        (PBYTE)m_pRemoteModuleBase
        );
    if (pbaSaveStartIns == NULL)
    {
        return false;
    }
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pProcessModuleInfo->pStartRoutine,
        PVOID(baSaveStartIns),
        sizeof(baSaveStartIns),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pbaSaveStartIns,
        PVOID(baSaveStartIns),
        sizeof(baSaveStartIns),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pfInjectDllStartup = (PBYTE) FindExportByName(
        "g_pfInjectDllStartup",
        (PBYTE)m_pRemoteModuleBase
        );
    if (pfInjectDllStartup == NULL)
    {
        return false;
    }
    pInjectDllStartup = (PBYTE) FindExportByName(
        "injectDllStartup",
        (PBYTE) m_pRemoteModuleBase
        );
    if (pInjectDllStartup == NULL)
    {
        return false;
    }
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pfInjectDllStartup,
        PVOID(&pInjectDllStartup),
        sizeof(pInjectDllStartup),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    stJumpIndInstAddress.pJumpAddress = pInjectDllStartup;
    dwNTStatus = NTWriteProcessMemory(
        m_hProcessHandle,
        pProcessModuleInfo->pStartRoutine,
        PVOID(&stJumpIndInstAddress),
        sizeof(stJumpIndInstAddress),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    return true;
}
#pragma warning(pop)

bool CLoadRemoteDLL::ImportsTableFixUp( )
{
    NTSTATUS                    dwNTStatus;
    bool                        bRets;
    PIMAGE_IMPORT_DESCRIPTOR    pImportDesc;
    PLDR_DATA_TABLE_ENTRY       pExporterInfo;
    PIMAGE_SECTION_HEADER       pRemoteSectionsTable;
    PIMAGE_SECTION_HEADER       pCopyOfSectionsTable;
    DWORD                       dwSectionsTableSize;
    DWORD                       dwNumberOfSections;
    BYTE                        _idataName[8] = ".idata\000";
    DWORD                       j;
    DWORD_PTR                   dwUpdateValue;
    PCSTR                       pModuleName;
    if (m_hProcessHandle == NULL)
    {
        return false;
    }
    if (m_stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_IMPORT)
    {
        // Image not have Import Section
        return true;
    }
    m_dw_idataSize = m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
    if (m_dw_idataSize == 0)
    {
        // Image not have Import Endtries
        return true;
    }
    pRemoteSectionsTable = PIMAGE_SECTION_HEADER(
        ULONG_PTR(m_pRemotePEHeaders) +
        FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) +
        m_stPEImageHeaders.FileHeader.SizeOfOptionalHeader
        );
    dwNumberOfSections  = m_stPEImageHeaders.FileHeader.NumberOfSections;
    dwSectionsTableSize = dwNumberOfSections*sizeof(IMAGE_SECTION_HEADER);
    pCopyOfSectionsTable = new IMAGE_SECTION_HEADER[dwNumberOfSections];
    if (pCopyOfSectionsTable == NULL)
    {
        return false;
    }
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteSectionsTable,
        pCopyOfSectionsTable,
        dwSectionsTableSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOfSectionsTable;
        return false;
    }
    m_dw_idataSize = 0;
    for (j=0;j<dwNumberOfSections;j++)
    {
        if (memcmp( _idataName, pCopyOfSectionsTable[j].Name, 8 ) == 0)
        {
            if(pCopyOfSectionsTable[j].Misc.VirtualSize
                > pCopyOfSectionsTable[j].SizeOfRawData)
            {
                m_dw_idataSize = pCopyOfSectionsTable[j].SizeOfRawData;
            }
            else
            {
                m_dw_idataSize = pCopyOfSectionsTable[j].Misc.VirtualSize;
            }
            m_dw_idataRVA = pCopyOfSectionsTable[j].VirtualAddress;
            m_pRemote_idata = m_pRemoteModuleBase + m_dw_idataRVA;
            break;
        }
    }
    if (m_dw_idataSize == 0)
    {
        for(j=0;j<dwNumberOfSections;j++)
        {
            DWORD   dwSectSize;
            if (pCopyOfSectionsTable[j].Misc.VirtualSize
                > pCopyOfSectionsTable[j].SizeOfRawData)
            {
                dwSectSize = pCopyOfSectionsTable[j].SizeOfRawData;
            }
            else
            {
                dwSectSize = pCopyOfSectionsTable[j].Misc.VirtualSize;
            }
            if(
                (m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress >=
                pCopyOfSectionsTable[j].VirtualAddress) &&
                (m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress <
                pCopyOfSectionsTable[j].VirtualAddress + dwSectSize)
              )
            {
                m_dw_idataSize = dwSectSize;
                m_dw_idataRVA = pCopyOfSectionsTable[j].VirtualAddress;
                m_pRemote_idata = m_pRemoteModuleBase + m_dw_idataRVA;
                break;
            }
        }
    }
    delete pCopyOfSectionsTable;
    if (m_dw_idataSize == 0)
    {
        return false;
    }
    m_pCopyOf_idata = new BYTE[m_dw_idataSize];
    if(m_pCopyOf_idata == NULL)
    {
        return false;
    }
    m_pCopyImpDirectoty = PIMAGE_IMPORT_DESCRIPTOR(
        m_pCopyOf_idata + (m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
            - m_dw_idataRVA)
        );
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        m_pRemote_idata,
        m_pCopyOf_idata,
        m_dw_idataSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete m_pCopyOf_idata;
        m_pCopyOf_idata = NULL;
        return false;
    }
    dwUpdateValue = DWORD_PTR(m_pCopyOf_idata - PBYTE(m_dw_idataRVA));
    pImportDesc = m_pCopyImpDirectoty;
    for (; pImportDesc->Characteristics != 0; pImportDesc++)
    {
        pModuleName = PCSTR(dwUpdateValue + pImportDesc->Name);
        pExporterInfo = m_objModule32HLP.FindModule(
            pModuleName
            );
        if (pExporterInfo == NULL)
        {
            delete m_pCopyOf_idata;
            m_pCopyOf_idata = NULL;
            return false;
        }
        bRets = ParseExportTable(
            HMODULE(pExporterInfo->DllBase),
            pExporterInfo->dwImageSize,
            pImportDesc,
            dwUpdateValue
            );
        if (!bRets)
        {
            delete m_pCopyOf_idata;
            m_pCopyOf_idata = NULL;
            return false;
        }
    }
    delete m_pCopyOf_idata;
    m_pCopyOf_idata = NULL;
    return true;
}


bool CLoadRemoteDLL::ParseExportTable(
    HMODULE     hExporter,
    DWORD_PTR   dwImageSize,
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc,
    DWORD_PTR   dwImportUpdateAddr
    )
{
    NTSTATUS                dwNTStatus;
    PBYTE                   pPointToPEHeaders;
    PBYTE                   pRemotePEHeaders;
    DWORD                   dwPEHeadersRVA;
    DWORD                   dwPEHeadersSize;
    PBYTE                   pImageBase = PBYTE(hExporter);
    IMAGE_NT_HEADERS        stPEImageHeaders;
    PBYTE                   pCopyOf_edata;
    DWORD                   dw_edataSize;
    PBYTE                   pRemoteAddr1;
    DWORD                   dwExportTableRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory;
    PDWORD                  pAddressOfFunctions;
    DWORD                   j;
    DWORD_PTR               dwUpdateAddr;
    DWORD                   dwOrdBase;
    DWORD_PTR               dwThunk;
    DWORD                   dwOrdIndx;
    DWORD                   nFunctions;
    PBYTE                   pFunctionAddr;
    DWORD                   dwFunctionAddr;
    PCSTR                   psForwardRef;
    PBYTE                   pRemoteTarget;
    PDWORD_PTR              pOrdinalArray, pThunkArray;
    pPointToPEHeaders = pImageBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof(dwPEHeadersRVA),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    pRemotePEHeaders = pImageBase + dwPEHeadersRVA;
    dwPEHeadersSize = (dwPEHeadersRVA + sizeof(stPEImageHeaders) > dwImageSize)
        ? DWORD(dwImageSize - dwPEHeadersRVA) : DWORD(sizeof(stPEImageHeaders));
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &stPEImageHeaders,
        dwPEHeadersSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return false;
    }
    if (stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes == 0)
    {
        // Image no have Export Section
        return false;
    }
    dw_edataSize = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    if (dw_edataSize == 0)
    {
        // Image not have Export Endtries
        return false;
    }
    pCopyOf_edata = new BYTE[dw_edataSize];
    if (pCopyOf_edata == NULL)
    {
        return false;
    }
    dwExportTableRVA = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    pRemoteAddr1 = pImageBase + dwExportTableRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteAddr1,
        pCopyOf_edata,
        dw_edataSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOf_edata;
        return false;
    }
    pExportDirectory = PIMAGE_EXPORT_DIRECTORY(pCopyOf_edata);
    dwUpdateAddr = DWORD_PTR(pCopyOf_edata - dwExportTableRVA);
    pAddressOfFunctions = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfFunctions);
    dwOrdBase  = pExportDirectory->Base;
    nFunctions = pExportDirectory->NumberOfFunctions;
    pOrdinalArray = PDWORD_PTR(dwImportUpdateAddr + pImportDesc->OriginalFirstThunk);
    pThunkArray = PDWORD_PTR(dwImportUpdateAddr + pImportDesc->FirstThunk);
    for (j=0;pOrdinalArray[j]!=0;j++)
    {
        dwThunk = pOrdinalArray[j];
        if (IMAGE_SNAP_BY_ORDINAL(dwThunk))
        {
            dwThunk = IMAGE_ORDINAL(dwThunk);
            dwOrdIndx = DWORD(dwThunk - dwOrdBase);
            if(dwOrdIndx >= nFunctions) {
                delete pCopyOf_edata;
                return false;
            }
            dwFunctionAddr = pAddressOfFunctions[dwOrdIndx];
            if ((dwFunctionAddr < dwExportTableRVA)
                || (dwFunctionAddr >= dwExportTableRVA + dw_edataSize))
            {
                pFunctionAddr = pImageBase + dwFunctionAddr;
            }
            else
            {
                psForwardRef = PCSTR(dwFunctionAddr + dwUpdateAddr);
                pFunctionAddr = EvaluteForwardExport( psForwardRef );
                if (pFunctionAddr == NULL)
                {
                    delete pCopyOf_edata;
                    return false;
                }
            }
        }
        else
        {
            pFunctionAddr = FindExportByName(
                PIMAGE_IMPORT_BY_NAME(dwImportUpdateAddr + pOrdinalArray[j]),
                pExportDirectory,
                pImageBase,
                dwExportTableRVA,
                dw_edataSize,
                dwUpdateAddr
                );
            if (pFunctionAddr == NULL)
            {
                delete pCopyOf_edata;
                return false;
            }
        }
        pRemoteTarget = m_pRemoteModuleBase + DWORD_PTR(&pThunkArray[j]) - dwImportUpdateAddr;
        dwNTStatus = NTWriteProcessMemory(
            m_hProcessHandle,
            pRemoteTarget,
            PVOID(&pFunctionAddr),
            sizeof(pFunctionAddr),
            NULL
            );
        if (!NT_SUCCESS(dwNTStatus))
        {
            delete pCopyOf_edata;
            return false;
        }
    }
    delete pCopyOf_edata;
    return true;
}

PBYTE CLoadRemoteDLL::EvaluteForwardExport( PCSTR psForwardRef )
{
    CHAR                    sDLLName[260];
    PCSTR                   psFunctionName;
    DWORD                   dwFindPos;
    PLDR_DATA_TABLE_ENTRY   pExporterInfo;
    PBYTE                   pImageBase;
    NTSTATUS                dwNTStatus;
    PBYTE                   pPointToPEHeaders;
    PBYTE                   pRemotePEHeaders;
    DWORD                   dwPEHeadersRVA;
    DWORD                   dwPEHeadersSize;
    IMAGE_NT_HEADERS        stPEImageHeaders;
    DWORD_PTR               dwImageSize;
    PBYTE                   pCopyOf_edata;
    DWORD                   dw_edataSize;
    PBYTE                   pRemoteAddr1;
    DWORD                   dwExportTableRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory;
    PDWORD                  pAddressOfFunctions;
    PDWORD                  pAddressOfNames;
    PWORD                   pAddressOfNameOrdinals;
    DWORD_PTR               dwUpdateAddr;
    DWORD                   dwOrdBase;
    DWORD                   nFunctions;
    DWORD                   dwThunk;
    PCSTR                   psForwardNest;
    DWORD                   dwOrdIndx;
    DWORD                   dwFunctionAddr;
    PBYTE                   pFunctionAddr;
    DWORD                   nNames;
    DWORD                   dwNameIndx;
    DWORD                   dwMin;
    DWORD                   dwMax;
    int                     nCompRet;
    PCSTR                   pNameString;
    psFunctionName = strrchr(psForwardRef, '.') + 1;
    dwFindPos = DWORD(psFunctionName - psForwardRef) - 1;
    strncpy( sDLLName, psForwardRef, dwFindPos );
    sDLLName[dwFindPos] = '\000';
    pExporterInfo = m_objModule32HLP.FindModule(sDLLName);
    if(pExporterInfo == NULL) {
        return NULL;
    }
    pImageBase = PBYTE(pExporterInfo->DllBase);
    dwImageSize = pExporterInfo->dwImageSize;
    pPointToPEHeaders  = pImageBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof(dwPEHeadersRVA),
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    pRemotePEHeaders = pImageBase + dwPEHeadersRVA;
    dwPEHeadersSize = (dwPEHeadersRVA + sizeof(stPEImageHeaders) > dwImageSize)
        ? DWORD(dwImageSize - dwPEHeadersRVA) : DWORD(sizeof(stPEImageHeaders));
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &stPEImageHeaders,
        dwPEHeadersSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    if (stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes == 0)
    {
        // Image not have Export Section
        return NULL;
    }
    dw_edataSize = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    if (dw_edataSize == 0)
    {
        // Image not have Export Endtries
        return NULL;
    }
    pCopyOf_edata = new BYTE[dw_edataSize];
    if(pCopyOf_edata == NULL) {
        return NULL;
    }
    dwExportTableRVA = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    pRemoteAddr1 = pImageBase + dwExportTableRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteAddr1,
        pCopyOf_edata,
        dw_edataSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOf_edata;
        return NULL;
    }
    pExportDirectory = PIMAGE_EXPORT_DIRECTORY(pCopyOf_edata);
    dwUpdateAddr = DWORD_PTR(pCopyOf_edata - dwExportTableRVA);
    pAddressOfFunctions = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfFunctions);
    pAddressOfNames = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfNames);
    pAddressOfNameOrdinals = PWORD(dwUpdateAddr + pExportDirectory->AddressOfNameOrdinals);
    dwOrdBase = pExportDirectory->Base;
    nFunctions = pExportDirectory->NumberOfFunctions;
    nNames     = pExportDirectory->NumberOfNames;
    if(psFunctionName[0] == '#')
    {
        psFunctionName += 1;
        dwThunk = strtoul( psFunctionName, NULL, 10 );
        dwOrdIndx = dwThunk - dwOrdBase;
        if (dwOrdIndx >= nFunctions)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwFunctionAddr = pAddressOfFunctions[dwOrdIndx];
        if ((dwFunctionAddr < dwExportTableRVA)
            || (dwFunctionAddr >= dwExportTableRVA + dw_edataSize))
        {
            pFunctionAddr = pImageBase + dwFunctionAddr;
        }
        else
        {
            psForwardNest = PCSTR(dwUpdateAddr + dwFunctionAddr);
            pFunctionAddr = EvaluteForwardExport( psForwardNest );
        }
    }
    else
    {
        if (nNames < 16)
        {
            // linear search
            for (dwNameIndx=0;dwNameIndx<nNames;dwNameIndx++)
            {
                pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
                nCompRet = strcmp(psFunctionName, pNameString);
                if (nCompRet == 0)
                {
                    goto mFindSuccess;
                }
                if (nCompRet < 0)
                {
                    delete pCopyOf_edata;
                    return NULL;
                }
            }
        }
        // binary search
        dwMin = 0;
        dwMax = nNames - 1;
        do
        {
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMin]);
            nCompRet = strcmp(psFunctionName, pNameString);
            if (nCompRet == 0)
            {
                goto mFindSuccess;
            }
            if (nCompRet < 0)
            {
                delete pCopyOf_edata;
                return NULL;
            }
            dwMin++;
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMax]);
            nCompRet = strcmp(psFunctionName, pNameString);
            if (nCompRet == 0)
            {
                goto mFindSuccess;
            }
            if (nCompRet > 0)
            {
                delete pCopyOf_edata;
                return NULL;
            }
            dwMax--;
            if (dwMax < dwMin)
            {
                delete pCopyOf_edata;
                return NULL;
            }
            dwNameIndx = ((dwMax - dwMin + 1)>>1) + dwMin;
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
            nCompRet = strcmp(psFunctionName, pNameString);
            if (nCompRet == 0)
            {
                goto mFindSuccess;
            }
            if(dwMax == dwMin) {
                delete pCopyOf_edata;
                return NULL;
            }
            if (nCompRet > 0)
            {
                dwMin = dwNameIndx + 1;
            }
            else
            {
                dwMax = dwNameIndx - 1;
            }
        } while(true);
mFindSuccess:
        dwOrdIndx      = pAddressOfNameOrdinals[dwNameIndx];
        dwFunctionAddr = pAddressOfFunctions[dwOrdIndx];
        if((dwFunctionAddr < dwExportTableRVA)
            || (dwFunctionAddr >= dwExportTableRVA + dw_edataSize))
        {
            pFunctionAddr = pImageBase + dwFunctionAddr;
        } else {
            psForwardNest = PCSTR(dwUpdateAddr + dwFunctionAddr);
            pFunctionAddr = EvaluteForwardExport( psForwardNest );
        }
    }
    delete pCopyOf_edata;
    return pFunctionAddr;
}


PBYTE CLoadRemoteDLL::FindExportByName(
    PIMAGE_IMPORT_BY_NAME   pImportStruct,
    PIMAGE_EXPORT_DIRECTORY pExportDirectory,
    PBYTE                   pImageBase,
    DWORD                   dwExportTableRVA,
    DWORD                   dw_edataSize,
    DWORD_PTR               dwUpdateAddr
    )
{
    PDWORD  pAddressOfFunctions;
    PDWORD  pAddressOfNames;
    PWORD   pAddressOfNameOrdinals;
    DWORD   dwOrdBase;
    DWORD   nFunctions;
    DWORD   nNames;
    DWORD   dwHint;
    DWORD   dwNameIndx;
    DWORD   dwMin;
    DWORD   dwMax;
    PCSTR   psFunctionName;
    int     nCompRet;
    DWORD   dwOrdIndx;
    DWORD   dwFunctionAddr;
    PBYTE   pFunctionAddr;
    PCSTR   psForwardRef;
    PCSTR   pNameString;
    pAddressOfFunctions = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfFunctions);
    pAddressOfNames = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfNames);
    pAddressOfNameOrdinals = PWORD(dwUpdateAddr + pExportDirectory->AddressOfNameOrdinals);
    dwOrdBase  = pExportDirectory->Base;
    nFunctions = pExportDirectory->NumberOfFunctions;
    nNames     = pExportDirectory->NumberOfNames;
    dwHint     = DWORD(pImportStruct->Hint);
    psFunctionName = PCSTR(&pImportStruct->Name[0]);
    if(dwHint < nNames)
    {
        // try search by Hint
        dwNameIndx = dwHint;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
        if (strcmp(psFunctionName, pNameString) == 0)
        {
            goto mFindSuccess;
        }
    }
    if (nNames < 16)
    {
        // linear search
        for (dwNameIndx=0;dwNameIndx<nNames;dwNameIndx++)
        {
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
            nCompRet = strcmp(psFunctionName, pNameString);
            if(nCompRet == 0)
            {
                goto mFindSuccess;
            }
            if(nCompRet < 0)
            {
                return NULL;
            }
        }
    }
    // binary search
    dwMin = 0;
    dwMax = nNames - 1;
    do
    {
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMin]);
        nCompRet = strcmp(psFunctionName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (nCompRet < 0)
        {
            return NULL;
        }
        dwMin++;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMax]);
        nCompRet = strcmp(psFunctionName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (nCompRet > 0)
        {
            return NULL;
        }
        dwMax--;
        if (dwMax < dwMin)
        {
            return NULL;
        }
        dwNameIndx = ((dwMax - dwMin + 1)>>1) + dwMin;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
        nCompRet = strcmp(psFunctionName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (dwMax == dwMin)
        {
            return NULL;
        }
        if (nCompRet > 0)
        {
            dwMin = dwNameIndx + 1;
        }
        else
        {
            dwMax = dwNameIndx - 1;
        }
    } while(true);
mFindSuccess:
    dwOrdIndx      = pAddressOfNameOrdinals[dwNameIndx];
    dwFunctionAddr = pAddressOfFunctions[dwOrdIndx];
    if ((dwFunctionAddr < dwExportTableRVA)
        || (dwFunctionAddr >= dwExportTableRVA + dw_edataSize))
    {
        pFunctionAddr = pImageBase + dwFunctionAddr;
    }
    else
    {
        psForwardRef  = PCSTR(dwFunctionAddr + dwUpdateAddr);
        pFunctionAddr = EvaluteForwardExport( psForwardRef );
    }
    return pFunctionAddr;
}

bool CLoadRemoteDLL::RelocationsFixUp( )
{
    DWORD                   dwRelocTableSize;
    PBYTE                   pRemoteRelocTable;
    PBYTE                   pCopyOfRelocTable;
    PBYTE                   pStandardModuleBase;
    DWORD                   dwDelta;
    NTSTATUS                dwNTStatus;
    PBYTE                   pCurrentBlock;
    PBYTE                   pRelocLimit;
    PIMAGE_BASE_RELOCATION  pRelocHeader;
    DWORD                   nRelocations;
    PWORD                   pRelocArray;
    DWORD                   j;
    WORD                    wCReloc;
    PBYTE                   pRemoteAddr;
    PBYTE                   pFixUpValue;
    WORD                    wRelocType;
    bool                    bBlockSkipped;
    if (m_hProcessHandle == NULL)
    {
        return false;
    }
    pStandardModuleBase = PBYTE(m_stPEImageHeaders.OptionalHeader.ImageBase);
    dwDelta = DWORD(m_pRemoteModuleBase - pStandardModuleBase);
    if (dwDelta == 0)
    {
        // not need relocation fix-up
        return true;
    }
    if (m_stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes < 6)
    {
        // Image not have Relocation Table
        return true;
    }
    dwRelocTableSize = m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    if(dwRelocTableSize == 0) {
        // Image no have Relocation Table
        return true;
    }
    pRemoteRelocTable = m_pRemoteModuleBase +
        m_stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    pCopyOfRelocTable = new BYTE[dwRelocTableSize];
    if (pCopyOfRelocTable == NULL)
    {
        return false;
    }
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteRelocTable,
        pCopyOfRelocTable,
        dwRelocTableSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOfRelocTable;
        return false;
    }
    pCurrentBlock = pCopyOfRelocTable;
    pRelocLimit   = pCopyOfRelocTable + dwRelocTableSize;
    do
    {
        pRelocHeader = PIMAGE_BASE_RELOCATION(pCurrentBlock);
        pRelocArray  = PWORD(pCurrentBlock + sizeof(IMAGE_BASE_RELOCATION));
        pCurrentBlock += pRelocHeader->SizeOfBlock;
        nRelocations = (pRelocHeader->SizeOfBlock - sizeof IMAGE_BASE_RELOCATION)/sizeof(WORD);
        for (j=0;j<nRelocations;j++)
        {
            wCReloc = pRelocArray[j];
            wRelocType = (wCReloc >> 12);
            wCReloc &= 0x0FFF;
            bBlockSkipped = false;
            switch (wRelocType)
            {
#if defined(_M_AMD64)
            case IMAGE_REL_BASED_DIR64:
#elif defined(_M_IX86)
            case IMAGE_REL_BASED_HIGHLOW:
#endif
                pRemoteAddr = m_pRemoteModuleBase + pRelocHeader->VirtualAddress + wCReloc;
                dwNTStatus = NtReadVirtualMemory(
                    m_hProcessHandle,
                    pRemoteAddr,
                    PVOID(&pFixUpValue),
                    sizeof(pFixUpValue),
                    NULL
                    );
                if (!NT_SUCCESS(dwNTStatus))
                {
                    delete pCopyOfRelocTable;
                    return false;
                }
                pFixUpValue += dwDelta;
                dwNTStatus = NTWriteProcessMemory(
                    m_hProcessHandle,
                    pRemoteAddr,
                    PVOID(&pFixUpValue),
                    sizeof(pFixUpValue),
                    NULL
                    );
                if (!NT_SUCCESS(dwNTStatus))
                {
                    delete pCopyOfRelocTable;
                    return false;
                }
                break;
            case IMAGE_REL_BASED_ABSOLUTE:
                bBlockSkipped = true;
                break;
            default:
                assert(false);
                bBlockSkipped = true;
                break;
            }
            if (bBlockSkipped)
            {
                break;
            }
        }
    } while(pCurrentBlock < pRelocLimit);
    delete pCopyOfRelocTable;
    return true;
}

PBYTE CLoadRemoteDLL::FindExportByName(PCSTR psEntryName, PBYTE pRemoteModuleBase)
{
    NTSTATUS                dwNTStatus;
    PBYTE                   pPointToPEHeaders;
    PBYTE                   pRemotePEHeaders;
    DWORD                   dwPEHeadersRVA;
    DWORD                   dwPEHeadersSize;
    IMAGE_NT_HEADERS        stPEImageHeaders;
    PBYTE                   pCopyOf_edata;
    DWORD                   dw_edataSize;
    PBYTE                   pRemoteAddr1;
    DWORD                   dwExportTableRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory;
    PDWORD                  pAddressOfFunctions;
    PDWORD                  pAddressOfNames;
    PWORD                   pAddressOfNameOrdinals;
    DWORD_PTR               dwUpdateAddr;
    DWORD                   nFunctions;
    DWORD                   nNames;
    DWORD                   dwNameIndx;
    DWORD                   dwMin;
    DWORD                   dwMax;
    int                     nCompRet;
    DWORD                   dwOrdIndx;
    DWORD                   dwFunctionAddr;
    PCSTR                   psForwardRef;
    PBYTE                   pResult;
    PCSTR                   pNameString;
    if (m_hProcessHandle == NULL)
    {
        return NULL;
    }
    pPointToPEHeaders  = pRemoteModuleBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof dwPEHeadersRVA,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    pRemotePEHeaders = pRemoteModuleBase + dwPEHeadersRVA;
    dwPEHeadersSize = sizeof(stPEImageHeaders);
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &stPEImageHeaders,
        dwPEHeadersSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    if (stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes == 0)
    {
        // Image not have Export Section
        return NULL;
    }
    dw_edataSize = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    if (dw_edataSize == 0)
    {
        // Image not have Export Endtries
        return NULL;
    }
    pCopyOf_edata = new BYTE[dw_edataSize];
    if (pCopyOf_edata == NULL)
    {
        return NULL;
    }
    dwExportTableRVA = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    pRemoteAddr1 = pRemoteModuleBase + dwExportTableRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteAddr1,
        pCopyOf_edata,
        dw_edataSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOf_edata;
        return NULL;
    }
    pExportDirectory = PIMAGE_EXPORT_DIRECTORY(pCopyOf_edata);
    dwUpdateAddr = DWORD_PTR(pCopyOf_edata - dwExportTableRVA);
    pAddressOfFunctions = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfFunctions);
    pAddressOfNames = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfNames);
    pAddressOfNameOrdinals = PWORD(dwUpdateAddr + pExportDirectory->AddressOfNameOrdinals);
    nFunctions = pExportDirectory->NumberOfFunctions;
    nNames     = pExportDirectory->NumberOfNames;
    if (nNames < 16)
    {
        // linear search
        for(dwNameIndx=0;dwNameIndx<nNames;dwNameIndx++)
        {
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
            nCompRet = strcmp(psEntryName, pNameString);
            if (nCompRet == 0)
            {
                goto mFindSuccess;
            }
            if (nCompRet < 0)
            {
                delete pCopyOf_edata;
                return NULL;
            }
        }
    }
    // binary search
    dwMin = 0;
    dwMax = nNames - 1;
    do
    {
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMin]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (nCompRet < 0)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwMin++;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMax]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0) {
            goto mFindSuccess;
        }
        if (nCompRet > 0)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwMax--;
        if (dwMax < dwMin)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwNameIndx = ((dwMax - dwMin + 1)>>1) + dwMin;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if(dwMax == dwMin)
        {
            return NULL;
        }
        if(nCompRet > 0)
        {
            dwMin = dwNameIndx + 1;
        }
        else
        {
            dwMax = dwNameIndx - 1;
        }
    } while(true);
mFindSuccess:
    dwOrdIndx      = pAddressOfNameOrdinals[dwNameIndx];
    dwFunctionAddr = pAddressOfFunctions[dwOrdIndx];
    if ((dwFunctionAddr < dwExportTableRVA)
        || (dwFunctionAddr >= dwExportTableRVA + dw_edataSize))
    {
        pResult      = pRemoteModuleBase + dwFunctionAddr;
    } else {
        psForwardRef = PCSTR(dwUpdateAddr + dwFunctionAddr);
        pResult      = EvaluteForwardExport( psForwardRef );
    }
    delete pCopyOf_edata;
    return pResult;
}

PBYTE CLoadRemoteDLL::GetExportEntryAddress(
    PCSTR psEntryName,
    PBYTE pRemoteModuleBase
                                           )
{
    NTSTATUS                dwNTStatus;
    PBYTE                   pPointToPEHeaders;
    PBYTE                   pRemotePEHeaders;
    DWORD                   dwPEHeadersRVA;
    DWORD                   dwPEHeadersSize;
    IMAGE_NT_HEADERS        stPEImageHeaders;
    PBYTE                   pCopyOf_edata;
    DWORD                   dw_edataSize;
    PBYTE                   pRemoteAddr1;
    DWORD                   dwExportTableRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory;
    PDWORD                  pAddressOfNames;
    PWORD                   pAddressOfNameOrdinals;
    DWORD_PTR               dwUpdateAddr;
    DWORD                   nFunctions;
    DWORD                   nNames;
    DWORD                   dwNameIndx;
    DWORD                   dwMin;
    DWORD                   dwMax;
    int                     nCompRet;
    DWORD                   dwOrdIndx;
    PDWORD                  pResult;
    PCSTR                   pNameString;
    if (m_hProcessHandle == NULL)
    {
        return NULL;
    }
    pPointToPEHeaders  = pRemoteModuleBase + 0x3C;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pPointToPEHeaders,
        &dwPEHeadersRVA,
        sizeof dwPEHeadersRVA,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    pRemotePEHeaders = pRemoteModuleBase + dwPEHeadersRVA;
    dwPEHeadersSize = sizeof(stPEImageHeaders);
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemotePEHeaders,
        &stPEImageHeaders,
        dwPEHeadersSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        return NULL;
    }
    if (stPEImageHeaders.OptionalHeader.NumberOfRvaAndSizes == 0)
    {
        // Image not have Export Section
        return NULL;
    }
    dw_edataSize = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    if (dw_edataSize == 0)
    {
        // Image not have Export Endtries
        return NULL;
    }
    pCopyOf_edata = new BYTE[dw_edataSize];
    if (pCopyOf_edata == NULL)
    {
        return NULL;
    }
    dwExportTableRVA = stPEImageHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    pRemoteAddr1 = pRemoteModuleBase + dwExportTableRVA;
    dwNTStatus = NtReadVirtualMemory(
        m_hProcessHandle,
        pRemoteAddr1,
        pCopyOf_edata,
        dw_edataSize,
        NULL
        );
    if (!NT_SUCCESS(dwNTStatus))
    {
        delete pCopyOf_edata;
        return NULL;
    }
    pExportDirectory = PIMAGE_EXPORT_DIRECTORY(pCopyOf_edata);
    dwUpdateAddr = DWORD_PTR(pCopyOf_edata - dwExportTableRVA);
    pResult = PDWORD(pExportDirectory->AddressOfFunctions);
    pAddressOfNames = PDWORD(dwUpdateAddr + pExportDirectory->AddressOfNames);
    pAddressOfNameOrdinals = PWORD(dwUpdateAddr + pExportDirectory->AddressOfNameOrdinals);
    nFunctions = pExportDirectory->NumberOfFunctions;
    nNames     = pExportDirectory->NumberOfNames;
    if (nNames < 16)
    {
        // linear search
        for (dwNameIndx=0;dwNameIndx<nNames;dwNameIndx++)
        {
            pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
            nCompRet = strcmp(psEntryName, pNameString);
            if (nCompRet == 0) {
                goto mFindSuccess;
            }
            if (nCompRet < 0) {
                delete pCopyOf_edata;
                return NULL;
            }
        }
    }
    // binary search
    dwMin = 0;
    dwMax = nNames - 1;
    do
    {
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMin]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0) {
            goto mFindSuccess;
        }
        if (nCompRet < 0)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwMin++;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx = dwMax]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (nCompRet > 0)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwMax--;
        if (dwMax < dwMin)
        {
            delete pCopyOf_edata;
            return NULL;
        }
        dwNameIndx = ((dwMax - dwMin + 1)>>1) + dwMin;
        pNameString = PCSTR(dwUpdateAddr + pAddressOfNames[dwNameIndx]);
        nCompRet = strcmp(psEntryName, pNameString);
        if (nCompRet == 0)
        {
            goto mFindSuccess;
        }
        if (dwMax == dwMin)
        {
            return NULL;
        }
        if (nCompRet > 0)
        {
            dwMin = dwNameIndx + 1;
        }
        else
        {
            dwMax = dwNameIndx - 1;
        }
    } while(true);
mFindSuccess:
    dwOrdIndx =  DWORD(pAddressOfNameOrdinals[dwNameIndx]);
    pResult   += dwOrdIndx;
    delete pCopyOf_edata;
    return pRemoteModuleBase + DWORD(pResult);
}
