0

通过以下示例,我一直在研究延迟加载(delayimp)管道作为 Windows 上缺少的 RPATH 功能的可能后端:

#include <stdio.h>

int __declspec(dllimport) foo(int arg);

int main(int argc, char* argv[])
{
    printf("foo() = %d\n", foo(foo(argc)));
    return 0;
}

GNU 和 LLVM 都使用“dlltool”类似地实现延迟加载(然而,LLVM 的 dlltool 似乎已合并到“ld-link”中)。本质上,在 LLVMlld/COFF/DLL.cpp或 BinUtil中执行的任务dlltool.c有两个:

  1. 为延迟加载函数生成跳转表存根(参见下面的示例)
  2. 生成应部署 __delayLoadHelper2 代码的蹦床(请参见下面的示例)

成功绑定后, __delayLoadHelper2似乎将解析的函数地址直接写入可执行代码部分:

extern "C"
FARPROC WINAPI
__delayLoadHelper2(
    PCImgDelayDescr     pidd,
    FARPROC *           ppfnIATEntry
    ) {
...
SetEntryHookBypass:
    *ppfnIATEntry = pfnRet; // access violation
...
}

为了对可执行映像进行修改,微软开发了一些花哨的功能,可以临时将写入权限添加到相应的内存区域。

现在的问题是:要修改的代码在进入“.idata”部分的跳转表存根中,并且无法获得写入权限

        if ((Characteristics & IMAGE_SCN_MEM_WRITE) == 0) {

            //
            // This delay load helper module does not support merging the delay
            // load section to a read only section because memory management
            // would not guarantee that there is commit available - and thus a
            // low memory failure path where the delay load failure hook could
            // not be safely invoked (the delay load section would still be
            // read only) might be encountered.
            //
            // It is a build time configuration problem to produce such a
            // binary so abort here and now so that the problem can be
            // identified & fixed.
            //

/* Exception thrown at 0x000000013F3B3F3F in dlltool_test_executable.exe: 0xC0000005: Access violation reading */
            __fastfail(FAST_FAIL_DLOAD_PROTECTION_FAILURE);
        }

因此,目前硬绑定不起作用,并给出“写访问冲突”。我想知道我在这里缺少哪种二进制配置?

我的测试配置:来自 github 的 LLVM 上游,来自 git 的 BinUtils,MSVC2019,Windows 7。

$ cat trampoline.s
# Import trampoline
        .section        .text
        .global __tailMerge_C__Users_marcusmae_dlltool_build_import_test_lib
__tailMerge_C__Users_marcusmae_dlltool_build_import_test_lib:
        pushq %rcx
        pushq %rdx
        pushq %r8
        pushq %r9
        subq  $40, %rsp
        movq  %rax, %rdx
        leaq  __DELAY_IMPORT_DESCRIPTOR_C__Users_marcusmae_dlltool_build_import_test_lib(%rip), %rcx
        call __delayLoadHelper2
        addq  $40, %rsp
        popq %r9
        popq %r8
        popq %rdx
        popq %rcx
        jmp *%rax

# DELAY_IMPORT_DESCRIPTOR
.section        .text$2
.global __DELAY_IMPORT_DESCRIPTOR_C__Users_marcusmae_dlltool_build_import_test_lib
__DELAY_IMPORT_DESCRIPTOR_C__Users_marcusmae_dlltool_build_import_test_lib:
        .long 1 # grAttrs
        .rva    __C__Users_marcusmae_dlltool_build_import_test_lib_iname        # rvaDLLName
        .rva    __DLL_HANDLE_C__Users_marcusmae_dlltool_build_import_test_lib   # rvaHmod
        .rva    __IAT_C__Users_marcusmae_dlltool_build_import_test_lib  # rvaIAT
        .rva    __INT_C__Users_marcusmae_dlltool_build_import_test_lib  # rvaINT
        .long   0       # rvaBoundIAT
        .long   0       # rvaUnloadIAT
        .long   0       # dwTimeStamp

.section .data
__DLL_HANDLE_C__Users_marcusmae_dlltool_build_import_test_lib:
        .long   0       # Handle
        .long   0

#Stuff for compatibility
        .section        .idata$5
        .long   0
        .long   0
__IAT_C__Users_marcusmae_dlltool_build_import_test_lib:
        .section        .idata$4
        .long   0
        .long   0
        .section        .idata$4
__INT_C__Users_marcusmae_dlltool_build_import_test_lib:
        .section        .idata$2
$ objdump -d dorks00000.o

dorks00000.o:     file format pe-x86-64


Disassembly of section .text:

0000000000000000 <foo>:
   0:   ff 25 00 00 00 00       jmpq   *0x0(%rip)        # 6 <foo+0x6>
   6:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # d <foo+0xd>
   d:   e9 00 00 00 00          jmpq   12 <foo+0x12>
        ...
4

1 回答 1

1

因此,您正在使用 GNU dlltool 生成延迟导入结构,但使用 LLD 或 MS link.exe 链接它?

我认为这里的区别在于 GNU dlltool 将在运行时更新的地址放置在 中.idata,而 GNU ld 通常链接.idata为可写,而 LLD 和 MS link.exe 通常具有只读的.idata(并将地址放置在.data而是通过延迟加载机制在运行时更新)。

LLD 恰好有一些额外的代码来从 GNU 导入库中获取读写.idata部分并将它们合并到 LLD 的其余只读部分中.idata——这使得普通的 GNU 导入库可以工作,但不幸的是,它与 GNU dlltool delayimport 一起使用会中断图书馆。

因此,对于 LLD,只需使用 LLD 的内置延迟导入机制,通过-delayload:user32.dll在链接时传递 eg。这在使用 MSVC 样式导入库时有效,但不幸的是在使用 GNU 样式导入库(由 GNU dlltool 或 GNU ld 生成的导入库)时不起作用。

于 2020-08-24T11:14:19.927 回答