Windows 将延迟加载函数绑定正确写入映像可执行文件(dlltool)

Windows 将延迟加载函数绑定正确写入映像可执行文件(dlltool),windows,visual-studio,llvm,binutils,dlltool,Windows,Visual Studio,Llvm,Binutils,Dlltool,我一直在研究延迟加载(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; } 为了修改可执行映像,Microsof

我一直在研究延迟加载(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;
}
为了修改可执行映像,Microsoft开发了一些奇特的函数,可以临时向相应的内存区域添加写入权限

现在的问题是:要修改的代码在跳转表存根中,跳转表存根进入“.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:文件格式pe-x86-64
第节的分解。正文:
0000000000000000 :
0:ff 25 00 jmpq*0x0(%rip)#6
6:48 8d 05 00 lea 0x0(%rip),%rax#d
d:e9 00 jmpq 12
        ...

那么您使用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
。这在使用MSVC风格的导入库时有效,但不幸的是在使用GNU风格的导入库(由GNU dlltool或GNU ld生成的导入库)时无效.

完全正确。我正在尝试将MS link.exe与dlltool生成的导入库相结合,因为我希望延迟加载未为延迟加载而初始编译的库。也就是说,我需要提供新的导入库。我希望在不接触链接器逻辑的情况下完成此操作,因为导入库自然是一个自包含的entity.您能告诉我LLVM链接器在哪里准备“.idata”节以便可写吗?GNU dlltool只需制作
INIT_secu_数据(IDATA5,.idata$5”,secu有_内容,2)
,它不使其可写。LLD不使
.idata
部分可写,它将相应的数据放在
.data
中。因此,对于您的情况,您可能需要将
.idata$5
更改为
.data$5
,对于
.idata$4
也可以这样做。但对于MS link.exe delayloa来说,这可能是可行的d使用GNU ld创建的库,最简单的方法可能是让GNU ld在链接时创建一个def文件(
-Wl,--output def,mylib.def
),然后使用MS lib.exe(
lib.exe-machine:x64-def:mylib.def-out:mylib.lib
)从中创建一个导入库,然后使用MS link.exe链接此库,但传递
-delayload
选项,例如
link.exe[其他选项]mylib.lib-delayload:mylib.dll
.MS link.exe不需要为延迟导入案例使用单独的导入库,只要它是MSVC风格的导入库。将.idata$4和.idata$5转换为.data$4和.data$5会在此处导致新的访问冲突:
//计算导入地址表中IAT项的索引//注意NT条目的顺序与IAT条目的顺序相同,因此//计算可以在IAT端完成。//const unsigned iIAT=indexfromPimgtunkData(PCImgThunkData(ppfnientry),idd.pIAT);const unsigned iINT=iIAT;PCImgThunkData pitd=&(idd.pINT[iINT]);dli.dlp.fImportByName=!IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);
然后尝试将其他部分也更改为.data;
.text$2
.idata$2
-除了第一个实际包含可执行代码的
.text
部分之外的所有部分。
$ 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>
        ...