恢复Detoured Library函数

时间:2016-06-20 15:39:47

标签: c++ visual-studio x86 inline-assembly detours

问题是相当直接的,我正在努力做的是恢复我的过程'绕道功能。

当我说绕道时,我的意思是指向未知位置的jmp指令。

例如,当ntdll.dll导出NtOpenProcess()没有绕道时,该函数指令的前5个字节沿mov eax, *行。

(*偏移取决于操作系统版本。)

绕道时,mov eax, *变为jmp.

我要做的就是在修改任何内存之前将其字节恢复到原来的状态。

我的想法是尝试从磁盘而不是从内存中读取我需要的信息,但是我不知道如何做到这一点,因为我只是一个初学者。

非常欢迎任何帮助或解释,如果我没有正确解释我的问题,请告诉我!

1 个答案:

答案 0 :(得分:0)

我最终搞清楚了。

NtOpenProcess的示例。 我没有恢复字节,而是决定跳过它们。

首先我们要定义ntdll的基础。

/* locate ntdll */
#define NTDLL _GetModuleHandleA("ntdll.dll")

一旦我们完成了这项工作,我们就会很高兴。 GetOffsetFromRva将根据传递给它的地址和模块头计算文件的偏移量。

DWORD GetOffsetFromRva(IMAGE_NT_HEADERS * nth, DWORD RVA)
{
    PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(nth);

    for (unsigned i = 0, sections = nth->FileHeader.NumberOfSections; i < sections; i++, sectionHeader++)
    {
        if (sectionHeader->VirtualAddress <= RVA)
        {
            if ((sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) > RVA)
            {
                RVA -= sectionHeader->VirtualAddress;
                RVA += sectionHeader->PointerToRawData;
                return RVA;
            }
        }
    }
    return 0;
}

我们调用它来获取我们需要的文件偏移量,以便找到函数的原始字节。

DWORD GetExportPhysicalAddress(HMODULE hmModule, char* szExportName)
{
    if (!hmModule)
    {
        return 0;
    }

    DWORD dwModuleBaseAddress = (DWORD)hmModule;

    IMAGE_DOS_HEADER* pHeaderDOS = (IMAGE_DOS_HEADER *)hmModule;
    if (pHeaderDOS->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return 0;
    }

    IMAGE_NT_HEADERS * pHeaderNT = (IMAGE_NT_HEADERS *)(dwModuleBaseAddress + pHeaderDOS->e_lfanew);
    if (pHeaderNT->Signature != IMAGE_NT_SIGNATURE)
    {
        return 0;
    }

    /* get the export virtual address through a custom GetProcAddress function. */
    void* pExportRVA = GetProcedureAddress(hmModule, szExportName);

    if (pExportRVA)
    {
        /* convert the VA to RVA... */
        DWORD dwExportRVA = (DWORD)pExportRVA - dwModuleBaseAddress;

        /* get the file offset and return */
        return GetOffsetFromRva(pHeaderNT, dwExportRVA);
    }

    return 0;
}

使用获取文件偏移量的函数,我们现在可以读取原始导出字节。

size_t ReadExportFunctionBytes(HMODULE hmModule, char* szExportName, BYTE* lpBuffer, size_t t_Count)
{
    /* get the offset */
    DWORD dwFileOffset = GetExportPhysicalAddress(hmModule, szExportName);
    if (!dwFileOffset)
    {
        return 0;
    }

    /* get the path of the targetted module */
    char szModuleFilePath[MAX_PATH];
    GetModuleFileNameA(hmModule, szModuleFilePath, MAX_PATH);
    if (strnull(szModuleFilePath))
    {
        return 0;
    }

    /* try to open the file off the disk */
    FILE *fModule = fopen(szModuleFilePath, "rb");
    if (!fModule)
    {
        /* we couldn't open the file */
        return 0;
    }

    /* go to the offset and read it */
    fseek(fModule, dwFileOffset, SEEK_SET);
    size_t t_Read = 0;

    if ((t_Read = fread(lpBuffer, t_Count, 1, fModule)) == 0)
    {
        /* we didn't read anything */
        return 0;
    }

    /* close file and return */
    fclose(fModule);

    return t_Read;
}

我们可以从最初放在x86导出的前5个字节中的mov指令中检索系统调用索引。

DWORD GetSyscallIndex(char* szFunctionName)
{
    BYTE buffer[5];
    ReadExportFunctionBytes(NTDLL, szFunctionName, buffer, 5);
    if (!buffer)
    {
        return 0;
    }

    return BytesToDword(buffer + 1);
}

获取NtOpenProcess地址并在其上添加5到trampoline。

DWORD _ptrNtOpenProcess = (DWORD) GetProcAddress(NTDLL, "NtOpenProcess") + 5;
DWORD _oNtOpenProcess = GetSyscallIndex("NtOpenProcess");

恢复/重建的NtOpenProcess。

__declspec(naked) NTSTATUS NTAPI _NtOpenProcess
(
    _Out_    PHANDLE            ProcessHandle,
    _In_     ACCESS_MASK        DesiredAccess,
    _In_     POBJECT_ATTRIBUTES ObjectAttributes,
    _In_opt_ PCLIENT_ID         ClientId
) {
    __asm
    {
        mov eax, [_oNtOpenProcess]
        jmp    dword ptr ds : [_ptrNtOpenProcess]
    }
}

我们称之为。

int main()
{
    printf("NtOpenProcess %x index: %x\n", _ptrNtOpenProcess, _oNtOpenProcess);

    uint32_t pId = 0;
    do 
    {
        pId = GetProcessByName("notepad.exe");
        Sleep(200);

    } while (pId == 0);

    OBJECT_ATTRIBUTES oa;
    CLIENT_ID cid;
    cid.UniqueProcess = (HANDLE)pId;
    cid.UniqueThread = 0;
    InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);

    HANDLE hProcess;
    NTSTATUS ntStat;

    ntStat = _NtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, &cid);

    if (!NT_SUCCESS(ntStat))
    {
        printf("Couldn't open the process. NTSTATUS: %d", ntStat);
        return 0;
    }

    printf("Successfully opened the process.");

    /* clean up. */
    NtClose(hProcess);

    getchar();
    return 0;
}