5

我很想清楚地解释一下,在 Windows 环境(PE 可执行文件)中,CALL XXXXXXXXXXXXXXX 指令是如何工作的。我一直在研究 PE 格式,但我对 CALL ADDRESS 指令、从 dll 中导入函数以及 CALL ADDRESS 如何访问 DLL 中的代码之间的关系感到非常困惑。除了 ASLR 和其他安全功能可能会在 DLL 中移动,可执行文件如何应对呢?

4

3 回答 3

10

它(即直接通过正常的相对调用调用导入)不起作用,这就是为什么它不是这样做的原因。

要调用导入的函数,您需要通过称为导入地址表 (IAT) 的东西。简而言之,IAT 中的条目首先指向函数名称(即,它从导入名称表的副本开始),然后这些指针被加载程序更改为指向实际函数。

IAT 位于固定地址,但如果图像已重新定位,则可以重新定位,因此通过它调用仅涉及单个间接 - 因此call r/m与内存操作数(只是一个简单的常量)一起使用以调用导入的函数,例如例子call [0x40206C]

于 2013-01-19T16:41:24.887 回答
4

2013 年 1 月 22 日:添加了更多更简单的具体示例和讨论,因为 (A) 选择了一个不正确的答案作为解决方案,并且 (B) 包括 OP 在内的一些读者显然不理解我的原始答案。对不起,我的罪过。我只是匆忙发布了一个答案,添加了一个我手头已经有的代码示例。


我如何解释这个问题。

你问,

“我一直在研究 PE 格式,但我对 CALL ADDRESS 指令、从 dll 中导入函数以及 CALL ADDRESS 如何访问 DLL 中的代码之间的关系感到非常困惑。”

CALL ADDRESS 一词在 C++ 级别没有多大意义,所以我假设您的意思是汇编语言或机器代码级别的 CALL ADDRESS。

那么问题是,当一个 DLL 被加载到某个地址而不是首选地址时,call指令是如何连接到 DLL 函数的?


它的短处。

  • 在机器代码级别,call具有指定地址的 a 通过调用由单个jmp指令组成的最小转发例程来工作。该jmp指令通过查表调用 DLL 函数。通常,DLL 的导入库会导出带有名称前缀的 DLL 函数本身和__imp__不带名称前缀的包装例程,例如__imp__MessageBoxA@16_MessageBoxA@16

即,除了我发明了以下名称外,汇编程序通常会翻译

    call MessageBox

进入

    call MessageBox_forwarder
     ; 不管在这里
    MessageBox_forwarder: jmp ds:[MessageBox_tableEntry]

加载 DLL 时,加载程序将相关地址放入表中。

  • 在汇编语言级别call,将例程指定为标识符的 a 可以映射到 acall到转发器,或call通过表查找直接映射到 DLL 函数,具体取决于为标识符声明的类型。

  • 可以有多个 DLL 函数地址表,即使是从同一个 DLL 导入。但总的来说,它们被认为是一张大表,然后称为“导入地址表” ,或简称IAT。IAT 表(或更准确地说是表)每个都位于图像中的固定位置,即当代码加载到不喜欢的地方而不是固定地址时,它们会随代码一起移动。

当前选择的解决方案答案在以下方面不正确

  • 答案是“它不起作用,这就是为什么它不是这样做的。”,其中“它”可能是指一个呼叫地址。但是在汇编或机器代码级别使用 CALL ADDRESS 可以很好地调用 DLL 函数。前提是正确完成。

  • 答案认为 IAT 位于固定地址。但事实并非如此。


呼叫地址工作得很好。

让我们考虑一个具体的CALL ADDRESS 指令,其中地址是一个众所周知的 DLL 函数,即MessageBoxA从 [user32.dll] DLL 调用 Windows API 函数:

call MessageBoxA

使用这个指令没有问题。

正如您将在下面看到的,在机器代码级别,该call指令本身仅包含一个偏移量,该偏移量导致调用执行一条jmp指令,该指令在函数指针的导入地址表中查找 DLL 例程地址,该表通常由加载程序在加载有问题的 DLL 时。

为了能够检查机器代码,这里有一个使用该具体示例指令的完整 32 位 x86 汇编语言程序:

.model flat, stdcall
option casemap :none        ; Case sensitive identifiers, please.
_as32bit        textequ <DWord ptr>

public  start

ExitProcess                     proto stdcall :DWord

MessageBoxA_t                   typedef proto stdcall :DWord, :DWord, :DWord, :DWord
extern MessageBoxA              : MessageBoxA_t
extern _imp__MessageBoxA@16     : ptr MessageBoxA_t

MB_ICONINFORMATION      equ     0040h
MB_SETFOREGROUND        equ     00010000h
infoBoxOptions          equ     MB_ICONINFORMATION or MB_SETFOREGROUND

    .const
boxtitle_1  db  "Just FYI 1 (of 3):", 0
boxtitle_2  db  "Just FYI 2 (of 3):", 0
boxtitle_3  db  "Just FYI 3 (of 3):", 0
boxtext     db  "There’s intelligence somewhere in the universe", 0

    .code
start:
    push infoBoxOptions
    push offset boxtitle_1
    push offset boxtext
    push 0
    call MessageBoxA                    ; Call #1 - to jmp to DLL-func.

    push infoBoxOptions
    push offset boxtitle_2
    push offset boxtext
    push 0
    call ds:[_imp__MessageBoxA@16]      ; Call #2 - directly to DLL-func.

    push infoBoxOptions
    push offset boxtitle_3
    push offset boxtext
    push 0
    call _imp__MessageBoxA@16           ; Call #3 - same as #2, due to type of identifier.

    push 0  ; Exit code, 0 indicates success.
    call ExitProcess
end

使用 Microsoft 的工具链进行组装和链接,其中/debug链接器选项要求链接器生成 PDB 调试信息文件以与 Visual Studio 调试器一起使用:

[d:\dev\test\call]
> ml /nologo /c asm_call.asm
 组装:asm_call.asm

[d:\dev\test\call]
>链接 /nologo asm_call.obj kernel32.lib user32.lib /entry:start /subsystem:windows /debug

[d:\dev\test\call]
>目录 asm* /b
asm_call.asm
asm_call.exe
asm_call.ilk
asm_call.obj
asm_call.pdb

[d:\dev\test\call]
> _

现在,一种简单的调试方法是启动 Visual Studio([devenv.exe] 程序)并在 Visual Studio 中单击 [调试步入],或者直接按 F11:

[d:\dev\test\call]
> devenv asm_call.exe

[d:\dev\test\call]
> _

在此处输入图像描述

在上图中,显示了运行中的 Visual Studio 2012 调试器,最左边的大红色箭头显示了机器代码指令中的地址信息,即0000004E十六进制(注意:最低有效字节位于最低地址,在内存中首先),以及另一个大红色箭头向您显示,尽管看起来令人难以置信,但这个相当小的幻数以某种方式指定了_MessageBoxA@16就调试器所知,驻留在地址01161064h十六进制的函数。

  • CALL ADDRESS 指令中的地址数据是一个偏移量,它是相对于下一条指令的地址的,因此它不需要对更改的 DLL 位置进行任何修复。

  • 调用的地址只包含一个jmp ds:[IAT_entry_for_MessageBoxA].

  • 此转发器代码来自导入库,而不是来自 DLL,因此它也不需要修复(但显然它确实得到了一些特殊处理,DLL 函数地址也是如此)。

第二条调用指令直接jmp执行第一条的操作,即在 IAT 表中查找 DLL 函数地址。

现在可以看到第三条调用指令在机器代码级别与第二条相同。declspec( dllimport )显然,如何在汇编中模拟 Visual C++ 并不为人所知。上述声明是一种方式,可能与文本 equ 结合使用。


IAT 不在固定地址。

下面的 C++ 程序报告它被加载的地址、它从哪些模块导入的 DLL 函数以及各种 IAT 表所在的位置。

当它使用现代版本的 Microsoft 工具链构建时,仅使用默认值,它通常在每次运行时加载到不同的地址。

您可以使用链接器选项来防止这种行为/dynamicbase:no

#include <assert.h>         // assert
#include <stddef.h>         // ptrdiff_t
#include <sstream>
using std::ostringstream;

#undef UNICODE
#define UNICODE
#include <windows.h>

template< class Result, class SomeType >
Result as( SomeType const p ) { return reinterpret_cast<Result>( p ); }

template< class Type >
class OffsetTo
{
private:
    ptrdiff_t offset_;
public:
    ptrdiff_t asInteger() const { return offset_; }
    explicit OffsetTo( ptrdiff_t const offset ): offset_( offset ) {}
};

template< class ResultPointee, class SourcePointee >
ResultPointee* operator+(
    SourcePointee* const            p,
    OffsetTo<ResultPointee> const   offset
    )
{
    return as<ResultPointee*>( as<char const*>( p ) + offset.asInteger() );
}

int main()
{
    auto const pImage =
        as<IMAGE_DOS_HEADER const*>( ::GetModuleHandle( nullptr ) );
    assert( pImage->e_magic == IMAGE_DOS_SIGNATURE );

    auto const pNTHeaders =
        pImage + OffsetTo<IMAGE_NT_HEADERS const>( pImage->e_lfanew );
    assert( pNTHeaders->Signature == IMAGE_NT_SIGNATURE );

    auto const& importDir =
        pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

    auto const pImportDescriptors = pImage + OffsetTo<IMAGE_IMPORT_DESCRIPTOR const>(
        importDir.VirtualAddress //+ importSectionHeader.PointerToRawData
        );

    ostringstream stream;
    stream << "I'm loaded at " << pImage << ", and I'm using...\n";
    for( int i = 0;  pImportDescriptors[i].Name != 0;  ++i )
    {
        auto const pModuleName = pImage + OffsetTo<char const>( pImportDescriptors[i].Name );

        DWORD const offsetNameTable = pImportDescriptors[i].OriginalFirstThunk;
        DWORD const offsetAddressTable = pImportDescriptors[i].FirstThunk;  // The module "IAT"

        auto const pNameTable = pImage + OffsetTo<IMAGE_THUNK_DATA const>( offsetNameTable );
        auto const pAddressTable = pImage + OffsetTo<IMAGE_THUNK_DATA const>( offsetAddressTable );

        stream << "\n* '" << pModuleName << "'";
        stream << " with IAT at " << pAddressTable << "\n";
        stream << "\t";
        for( int j = 0; pNameTable[j].u1.AddressOfData != 0; ++j )
        {
            auto const pFuncName =
                pImage + OffsetTo<char const>( 2 + pNameTable[j].u1.AddressOfData );
            stream << pFuncName << " ";
        }
        stream << "\n";
    }

    MessageBoxA(
        0,
        stream.str().c_str(),
        "FYI:",
        MB_ICONINFORMATION | MB_SETFOREGROUND
        );
}

在此处输入图像描述


一个自我复制的 Windows 机器代码程序。

最后,根据我的原始答案,这是我为另一个目的制作的 Microsoft 汇编程序 (MASM) 程序,它说明了一些问题,因为就其性质而言(它作为输出源代码生成,在组装和运行时会生成相同的源代码,并且依此类推)它必须是完全可重定位的代码,并且只需要普通程序加载器的最少量帮助:

.model flat, stdcall
option casemap :none        ; Case sensitive identifiers, please.
dword_aligned textequ <4>   ; Just for readability.

    ; Windows API functions:
    extern  ExitProcess@4: proc         ; from [kernel32.dll]
    extern  GetStdHandle@4: proc        ; from [kernel32.dll]
    extern  WriteFile@20: proc          ; from [kernel32.dll]
    extern  wsprintfA: proc             ; from [user32.dll]

    STD_OUTPUT_HANDLE       equ     -11

        ; The main code.
GlobalsStruct   struct  dword_aligned
    codeStart               dword   ?
    outputStreamHandle      dword   ?
GlobalsStruct   ends
globals         textequ     <(GlobalsStruct ptr [edi])>

    .code
startup:
    jmp     code_start

    ; Trampolines to add references to these functions.
myExitProcess:    jmp ExitProcess@4
myGetStdHandle:   jmp GetStdHandle@4     
myWriteFile:      jmp WriteFile@20
mywsprintfA:      jmp wsprintfA


;------------------------------------------------------------------
;
;               The code below is reproduced, so it's all relative.

code_start:
    jmp     main

prologue:
byte    ".model flat, stdcall", 13, 10
byte    "option casemap :none", 13, 10
byte    13, 10
byte    "    extern  ExitProcess@4: proc", 13, 10
byte    "    extern  GetStdHandle@4: proc", 13, 10
byte    "    extern  WriteFile@20: proc", 13, 10
byte    "    extern  wsprintfA: proc", 13, 10
byte    13, 10
byte    "    .code", 13, 10
byte    "startup:", 13, 10
byte    "    jmp     code_start", 13, 10
byte    13, 10
byte    "jmp ExitProcess@4", 13, 10
byte    "jmp GetStdHandle@4", 13, 10
byte    "jmp WriteFile@20", 13, 10
byte    "jmp wsprintfA", 13, 10
byte    13, 10
byte    "code_start:", 13, 10
prologue_nBytes         equ     $ - prologue

epilogue:
byte    "code_end:", 13, 10
byte    "    end startup", 13, 10
epilogue_nBytes         equ     $ - epilogue

dbDirective             byte    4 dup( ' ' ), "byte       "
dbDirective_nBytes      equ     $ - dbDirective

numberFormat            byte    " 0%02Xh", 0
numberFormat_nBytes     equ     $ - numberFormat

comma                   byte    ","
windowsNewline          byte    13, 10

write:
    push    0           ; space for nBytesWritten
    mov     ecx, esp    ; &nBytesWritten

    push    0           ; lpOverlapped
    push    ecx         ; &nBytesWritten
    push    ebx         ; nBytes
    push    eax         ; &s[0]
    push    globals.outputStreamHandle
    call    myWriteFile

    pop     eax         ; nBytesWritten
    ret

displayMachineCode:
    dmc_LocalsStruct    struct  dword_aligned
        numberStringLen     dword   ?
        numberString        byte    16*4 DUP( ? )
        fileHandle          dword   ?
        nBytesWritten       dword   ?
        byteIndex           dword   ?
    dmc_LocalsStruct    ends
    dmc_locals          textequ     <[ebp - sizeof dmc_LocalsStruct].dmc_LocalsStruct>

    mov     ebp, esp
    sub     esp, sizeof dmc_LocalsStruct

    ; Output prologue that makes MASM happy (placing machine code data in context):
    ; lea     eax, prologue
        mov     eax, globals.codeStart
        add     eax, prologue - code_start
    mov     ebx, prologue_nBytes
    call    write

    ; Output the machine code bytes.
    mov     dmc_locals.byteIndex, 0

dmc_lineLoop:
    ; loop start
            ; Output a db directive
        ;lea     eax, dbDirective
            mov     eax, globals.codeStart
            add     eax, dbDirective - code_start
        mov     ebx, dbDirective_nBytes
        call    write

    dmc_byteIndexingLoop:
        ; loop start
                ; Create string representation of a number
            mov     ecx, dmc_locals.byteIndex
            mov     eax, 0
            ;mov     al, byte ptr [code_start + ecx]
                mov     ebx, globals.codeStart
                mov     al, [ebx + ecx]
            push    eax
            ;push    offset numberFormat
                mov     eax, globals.codeStart
                add     eax, numberFormat - code_start
                push    eax
            lea     eax, dmc_locals.numberString
            push    eax
            call    mywsprintfA
            add     esp, 3*(sizeof dword)
            mov     dmc_locals.numberStringLen, eax

                ; Output string representation of number
            lea     eax, dmc_locals.numberString
            mov     ebx, dmc_locals.numberStringLen
            call    write

                ; Are we finished looping yet?
            inc     dmc_locals.byteIndex
            mov     ecx, dmc_locals.byteIndex
            cmp     ecx, code_end - code_start
            je      dmc_finalNewline
            and     ecx, 07h
            jz      dmc_after_byteIndexingLoop

                ; Output a comma
            ; lea     eax, comma
                mov     eax, globals.codeStart
                add     eax, comma - code_start
            mov     ebx, 1
            call    write
            jmp dmc_byteIndexingLoop
        ; loop end

    dmc_after_byteIndexingLoop:
            ; New line
        ; lea     eax, windowsNewline
            mov     eax, globals.codeStart
            add     eax, windowsNewline - code_start
        mov     ebx, 2
        call    write
        jmp     dmc_lineLoop;
    ; loop end

dmc_finalNewline:
        ; New line
    ; lea     eax, windowsNewline
        mov     eax, globals.codeStart
        add     eax, windowsNewline - code_start
    mov     ebx, 2
    call    write

    ; Output epilogue that makes MASM happy:
    ; lea     eax, epilogue
        mov     eax, globals.codeStart
        add     eax, epilogue - code_start
    mov     ebx, epilogue_nBytes
    call    write

    mov     esp, ebp
    ret

main:
    sub esp, sizeof GlobalsStruct
    mov edi, esp

    call    main_knownAddress
main_knownAddress:
    pop     eax
    sub     eax, main_knownAddress - code_start
    mov     globals.codeStart, eax

    push    STD_OUTPUT_HANDLE
    call    myGetStdHandle
    mov     globals.outputStreamHandle, eax

    call displayMachineCode

    ; Well behaved process exit:
    push 0                          ; Process exit code, 0 indicates success.
    call myExitProcess

code_end:
    end startup

这是自我复制的输出:

.model flat, stdcall
option casemap :none

    extern  ExitProcess@4: proc
    extern  GetStdHandle@4: proc
    extern  WriteFile@20: proc
    extern  wsprintfA: proc

    .code
startup:
    jmp     code_start

jmp ExitProcess@4
jmp GetStdHandle@4
jmp WriteFile@20
jmp wsprintfA

code_start:
    byte        0E9h, 03Bh, 002h, 000h, 000h, 02Eh, 06Dh, 06Fh
    byte        064h, 065h, 06Ch, 020h, 066h, 06Ch, 061h, 074h
    byte        02Ch, 020h, 073h, 074h, 064h, 063h, 061h, 06Ch
    byte        06Ch, 00Dh, 00Ah, 06Fh, 070h, 074h, 069h, 06Fh
    byte        06Eh, 020h, 063h, 061h, 073h, 065h, 06Dh, 061h
    byte        070h, 020h, 03Ah, 06Eh, 06Fh, 06Eh, 065h, 00Dh
    byte        00Ah, 00Dh, 00Ah, 020h, 020h, 020h, 020h, 065h
    byte        078h, 074h, 065h, 072h, 06Eh, 020h, 020h, 045h
    byte        078h, 069h, 074h, 050h, 072h, 06Fh, 063h, 065h
    byte        073h, 073h, 040h, 034h, 03Ah, 020h, 070h, 072h
    byte        06Fh, 063h, 00Dh, 00Ah, 020h, 020h, 020h, 020h
    byte        065h, 078h, 074h, 065h, 072h, 06Eh, 020h, 020h
    byte        047h, 065h, 074h, 053h, 074h, 064h, 048h, 061h
    byte        06Eh, 064h, 06Ch, 065h, 040h, 034h, 03Ah, 020h
    byte        070h, 072h, 06Fh, 063h, 00Dh, 00Ah, 020h, 020h
    byte        020h, 020h, 065h, 078h, 074h, 065h, 072h, 06Eh
    byte        020h, 020h, 057h, 072h, 069h, 074h, 065h, 046h
    byte        069h, 06Ch, 065h, 040h, 032h, 030h, 03Ah, 020h
    byte        070h, 072h, 06Fh, 063h, 00Dh, 00Ah, 020h, 020h
    byte        020h, 020h, 065h, 078h, 074h, 065h, 072h, 06Eh
    byte        020h, 020h, 077h, 073h, 070h, 072h, 069h, 06Eh
    byte        074h, 066h, 041h, 03Ah, 020h, 070h, 072h, 06Fh
    byte        063h, 00Dh, 00Ah, 00Dh, 00Ah, 020h, 020h, 020h
    byte        020h, 02Eh, 063h, 06Fh, 064h, 065h, 00Dh, 00Ah
    byte        073h, 074h, 061h, 072h, 074h, 075h, 070h, 03Ah
    byte        00Dh, 00Ah, 020h, 020h, 020h, 020h, 06Ah, 06Dh
    byte        070h, 020h, 020h, 020h, 020h, 020h, 063h, 06Fh
    byte        064h, 065h, 05Fh, 073h, 074h, 061h, 072h, 074h
    byte        00Dh, 00Ah, 00Dh, 00Ah, 06Ah, 06Dh, 070h, 020h
    byte        045h, 078h, 069h, 074h, 050h, 072h, 06Fh, 063h
    byte        065h, 073h, 073h, 040h, 034h, 00Dh, 00Ah, 06Ah
    byte        06Dh, 070h, 020h, 047h, 065h, 074h, 053h, 074h
    byte        064h, 048h, 061h, 06Eh, 064h, 06Ch, 065h, 040h
    byte        034h, 00Dh, 00Ah, 06Ah, 06Dh, 070h, 020h, 057h
    byte        072h, 069h, 074h, 065h, 046h, 069h, 06Ch, 065h
    byte        040h, 032h, 030h, 00Dh, 00Ah, 06Ah, 06Dh, 070h
    byte        020h, 077h, 073h, 070h, 072h, 069h, 06Eh, 074h
    byte        066h, 041h, 00Dh, 00Ah, 00Dh, 00Ah, 063h, 06Fh
    byte        064h, 065h, 05Fh, 073h, 074h, 061h, 072h, 074h
    byte        03Ah, 00Dh, 00Ah, 063h, 06Fh, 064h, 065h, 05Fh
    byte        065h, 06Eh, 064h, 03Ah, 00Dh, 00Ah, 020h, 020h
    byte        020h, 020h, 065h, 06Eh, 064h, 020h, 073h, 074h
    byte        061h, 072h, 074h, 075h, 070h, 00Dh, 00Ah, 020h
    byte        020h, 020h, 020h, 062h, 079h, 074h, 065h, 020h
    byte        020h, 020h, 020h, 020h, 020h, 020h, 020h, 030h
    byte        025h, 030h, 032h, 058h, 068h, 000h, 02Ch, 00Dh
    byte        00Ah, 06Ah, 000h, 08Bh, 0CCh, 06Ah, 000h, 051h
    byte        053h, 050h, 0FFh, 077h, 004h, 0E8h, 074h, 0FEh
    byte        0FFh, 0FFh, 058h, 0C3h, 08Bh, 0ECh, 083h, 0ECh
    byte        050h, 08Bh, 007h, 005h, 005h, 000h, 000h, 000h
    byte        0BBh, 036h, 001h, 000h, 000h, 0E8h, 0D7h, 0FFh
    byte        0FFh, 0FFh, 0C7h, 045h, 0FCh, 000h, 000h, 000h
    byte        000h, 08Bh, 007h, 005h, 057h, 001h, 000h, 000h
    byte        0BBh, 00Fh, 000h, 000h, 000h, 0E8h, 0BFh, 0FFh
    byte        0FFh, 0FFh, 08Bh, 04Dh, 0FCh, 0B8h, 000h, 000h
    byte        000h, 000h, 08Bh, 01Fh, 08Ah, 004h, 019h, 050h
    byte        08Bh, 007h, 005h, 066h, 001h, 000h, 000h, 050h
    byte        08Dh, 045h, 0B4h, 050h, 0E8h, 02Ah, 0FEh, 0FFh
    byte        0FFh, 083h, 0C4h, 00Ch, 089h, 045h, 0B0h, 08Dh
    byte        045h, 0B4h, 08Bh, 05Dh, 0B0h, 0E8h, 08Fh, 0FFh
    byte        0FFh, 0FFh, 0FFh, 045h, 0FCh, 08Bh, 04Dh, 0FCh
    byte        081h, 0F9h, 068h, 002h, 000h, 000h, 074h, 02Bh
    byte        083h, 0E1h, 007h, 074h, 013h, 08Bh, 007h, 005h
    byte        06Eh, 001h, 000h, 000h, 0BBh, 001h, 000h, 000h
    byte        000h, 0E8h, 06Bh, 0FFh, 0FFh, 0FFh, 0EBh, 0AAh
    byte        08Bh, 007h, 005h, 06Fh, 001h, 000h, 000h, 0BBh
    byte        002h, 000h, 000h, 000h, 0E8h, 058h, 0FFh, 0FFh
    byte        0FFh, 0EBh, 086h, 08Bh, 007h, 005h, 06Fh, 001h
    byte        000h, 000h, 0BBh, 002h, 000h, 000h, 000h, 0E8h
    byte        045h, 0FFh, 0FFh, 0FFh, 08Bh, 007h, 005h, 03Bh
    byte        001h, 000h, 000h, 0BBh, 01Ch, 000h, 000h, 000h
    byte        0E8h, 034h, 0FFh, 0FFh, 0FFh, 08Bh, 0E5h, 0C3h
    byte        083h, 0ECh, 008h, 08Bh, 0FCh, 0E8h, 000h, 000h
    byte        000h, 000h, 058h, 02Dh, 04Ah, 002h, 000h, 000h
    byte        089h, 007h, 06Ah, 0F5h, 0E8h, 098h, 0FDh, 0FFh
    byte        0FFh, 089h, 047h, 004h, 0E8h, 023h, 0FFh, 0FFh
    byte        0FFh, 06Ah, 000h, 0E8h, 084h, 0FDh, 0FFh, 0FFh
code_end:
    end startup
于 2013-01-19T17:54:31.150 回答
0

链接器。当您的可执行文件被链接时,链接器将替换所有 DLL 地址并将其作为基础。由于虚拟内存,所有进程都加载到相同的基地址,从而使寻址更容易。因为 DLL 是 PIL(位置无关代码),加载程序可以为应用程序重新设置 DLL。因为代码引用了liker 可以重定位的符号,所以它永远不需要关心它的位置。

编辑:刚刚意识到其中一些是关闭的——Linux 动态库是 PIL,Windows 不是(这就是我们必须重新设置基准的原因)。

于 2013-01-19T16:22:28.790 回答