恢复迂回的库函数

Recovering Detoured Library Functions

问题很简单,我想做的是恢复我的进程绕行的功能。

当我说绕行时,我指的是通常的 jmp 到未知位置的指示。

比如ntdll.dll导出NtOpenProcess()不绕路时,函数指令的前5个字节是沿着mov eax, *的行。

(* 偏移量取决于 OS 版本。)

绕路的时候,那个mov eax, *就变成了jmp.

我想做的是将它们的字节恢复到任何内存修改之前的原始状态。

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

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

我终于弄明白了。

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。

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

recovered/reconstructed 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;
}