《Windows PE》4.1.3 IAT函数地址表

news2024/10/4 23:22:40

IAT(Import Address Table)表又称为函数地址表,是Windows可执行文件中的一个重要数据结构,用于存储导入函数的实际入口地址。

在可执行文件中,当一个模块需要调用另一个模块中的函数时,通常会使用导入函数的方式进行。导入函数的入口地址在程序加载时并不确定,需要在运行时进行动态链接,以获取实际的函数入口地址。IAT表就是用来存储这些实际的函数入口地址。

IAT表是一个由函数指针构成的表格,每个函数指针对应一个导入函数。在Windows可执行文件中,IAT表是一个以NULL结尾的函数指针数组,每个函数指针对应一个导入函数的实际入口地址。

IAT表通常是通过导入描述符(Import Descriptor)中的FirstThunk字段指向的地址来访问的。FirstThunk指向一个由IMAGE_THUNK_DATA结构体构成的表,每个IMAGE_THUNK_DATA结构体中的Function字段存储着导入函数的实际入口地址。

在程序加载时,操作系统会根据导入描述符中的INT表和IAT表进行动态链接,将导入函数的实际入口地址填充到IAT表中的函数指针对应的位置。这样,在程序运行时,就可以直接通过IAT表中的函数指针调用导入函数,而无需进行额外的解析和跳转。

 注意

IAT函数地址表中的函数指针是在程序加载和链接时被填充的,而导入表描述符中的IAT表中的函数指针在未绑定的状态下与INT表完全相同。如果导入表描述符的TimeDateStamp和ForwarderChain字段是一个特殊的标志值,如0xFFFFFFFF,表示导入函数已绑定,则IAT函数地址表中存储的是真实的导入函数地址。

实验二十五:如何定位IAT表?

我们以32位汇编版HelloWorld.exe为例,将其拖入WinHex,如下所示:

00000140   00 00 00 00 00 00 00 00  DC 20 00 00 3C 00 00 00   ........?..<...

00000150   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000160   00 00 00 00 00 00 00 00  00 40 00 00 10 00 00 00   .........@......

00000170   10 20 00 00 1C 00 00 00  00 00 00 00 00 00 00 00   . ..............

00000180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001A0   00 20 00 00 10 00 00 00  00 00 00 00 00 00 00 00   . ..............

000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001C0   2E 74 65 78 74 00 00 00  26 00 00 00 00 10 00 00   .text...&.......

000001D0   00 02 00 00 00 04 00 00  00 00 00 00 00 00 00 00   ................

000001E0   00 00 00 00 20 00 00 60  2E 72 64 61 74 61 00 00   .... ..`.rdata..

000001F0   5E 01 00 00 00 20 00 00  00 02 00 00 00 06 00 00   ^.... ..........

00000200   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

00000210   2E 64 61 74 61 00 00 00  1B 00 00 00 00 30 00 00   .data........0..

00000220   00 02 00 00 00 08 00 00  00 00 00 00 00 00 00 00   ................

00000230   00 00 00 00 40 00 00 C0  2E 72 65 6C 6F 63 00 00   ....@..?reloc..

00000240   10 00 00 00 00 40 00 00  00 02 00 00 00 0A 00 00   .....@..........

00000250   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 42   ............@..B

首先我们查找数据目录项的第12项,即IAT表项。位于00000220H地址处的RVA值为00002010H,大小为0000001CH。

此RVA地址同样位于.rdata节区。

IAT表的FOA地址=00002000H-00002000H+600H=600H。

00000600   42 21 00 00 00 00 00 00  28 21 00 00 00 00 00 00   B!......(!......

00000610   00 00 00 00 E4 68 03 66  00 00 00 00 0D 00 00 00   ....鋒.f........

00000620   B0 00 00 00 2C 20 00 00  2C 06 00 00 00 00 00 00   ?.., ..,.......

00000630   00 10 00 00 26 00 00 00  2E 74 65 78 74 00 00 00   ....&....text...

00000640   00 20 00 00 10 00 00 00  2E 69 64 61 74 61 24 35   . .......idata$5

00000650   00 00 00 00 10 20 00 00  1C 00 00 00 2E 72 64 61   ..... .......rda

00000660   74 61 00 00 2C 20 00 00  B0 00 00 00 2E 72 64 61   ta.., ..?...rda

00000670   74 61 24 7A 7A 7A 64 62  67 00 00 00 DC 20 00 00   ta$zzzdbg...?..

00000680   28 00 00 00 2E 69 64 61  74 61 24 32 00 00 00 00   (....idata$2....

00000690   04 21 00 00 14 00 00 00  2E 69 64 61 74 61 24 33   .!.......idata$3

000006A0   00 00 00 00 18 21 00 00  10 00 00 00 2E 69 64 61   .....!.......ida

000006B0   74 61 24 34 00 00 00 00  28 21 00 00 36 00 00 00   ta$4....(!..6...

000006C0   2E 69 64 61 74 61 24 36  00 00 00 00 00 30 00 00   .idata$6.....0..

000006D0   1B 00 00 00 2E 64 61 74  61 00 00 00 20 21 00 00   .....data... !..

000006E0   00 00 00 00 00 00 00 00  36 21 00 00 08 20 00 00   ........6!... ..

000006F0   18 21 00 00 00 00 00 00  00 00 00 00 50 21 00 00   .!..........P!..

00000700   00 20 00 00 00 00 00 00  00 00 00 00 00 00 00 00   . ..............

00000710   00 00 00 00 00 00 00 00  42 21 00 00 00 00 00 00   ........B!......

00000720   28 21 00 00 00 00 00 00  B1 01 4D 65 73 73 61 67   (!......?Messag

00000730   65 42 6F 78 41 00 75 73  65 72 33 32 2E 64 6C 6C   eBoxA.user32.dll

00000740   00 00 9B 00 45 78 69 74  50 72 6F 63 65 73 73 00   ..?ExitProcess.

00000750   6B 65 72 6E 65 6C 33 32  2E 64 6C 6C 00 00 00 00   kernel32.dll....

       IAT表内包含两个函数名的RVA地址:

       第一个:00002142H转为FOA地址为742H,是kernel32.dll中的ExitProcess函数。

       第二个:00002128H转为FOA地址为728H,是user32.dll中的MessagBoxA函数。

       我们再来看一下导入表描述符中的桥2(FirstThunk字段),指向IAT表。

       第一个导入表描述符FirstThunk字段的值为00002008H,转为FOA地址为608H,其RVA值为00002128H,即user32.dll中的MessagBoxA函数。

       第二个导入表描述符FirstThunk字段的值为00002000H,转为FOA地址为600H,其RVA值为00002142H,即kernel32.dll中的ExitProcess函数。

 

总结

       我们会发现,在未绑定导入函数的情况下,实验二十五中给出的IAT函数地址表中数据与INT表中的数据完全相同。如果已绑定导入函数,则IAT表直接存储真实的函数入口地址。我们将在下一节中详细讲述。

实验二十六:遍历IAT函数地址表。

我们以32位和64位记事本程序为例:

/*------------------------------------------------------------------------

 FileName:PrintImportDescriptor.c

 实验26:遍历IAT表(支持32位和64位PE)

 (c) bcdaren, 2024

-----------------------------------------------------------------------*/

#include <stdio.h>

#include <windows.h>

#define WIN64

PBYTE loadPE(LPCWSTR szFile);

VOID iat32(PBYTE lpvResult);

VOID iat64(PBYTE lpvResult);

DWORD RvaToFoa(PIMAGE_NT_HEADERS ntHeaders, DWORD rva);

int main(int argc, char* argv[])

{

    LPCWSTR szFileName = TEXT("c:\\notepad64.exe");

    //PCWSTR szFileName = TEXT("c:\\HelloWorld.exe");

    PBYTE lpAddress = NULL; //PE文件内存映射文件地址

    lpAddress = loadPE(szFileName);

    if (lpAddress)

    {

        printf("%ls\n", szFileName);

#ifdef WIN64

        iat64(lpAddress);

#else

        iat32(lpAddress);

#endif

    }

    system("pause");

    return 0;

}

//创建PE文件映射对象

PBYTE loadPE(LPCWSTR szFile)

{

    HANDLE hFile;

    LPVOID lpvResult, lpvResult2;

    char buffer[16] = { 0 };

    DWORD dwPageSize;

    DWORD dwBytesRead = 0;

    BOOL bReadFile;

    PIMAGE_DOS_HEADER psImageDOSHeader;

#ifdef WIN64

    PIMAGE_NT_HEADERS64 psImageNTHeader;

#else

    PIMAGE_NT_HEADERS32 psImageNTHeader;

#endif

    PIMAGE_SECTION_HEADER sImageSecctionHeader[20]; //节表项

    hFile = CreateFile(szFile,

        GENERIC_READ,          // 只读打开

        FILE_SHARE_READ,       // 允许其他进程以读取方式打开文件

        NULL,                  // 默认安全属性

        OPEN_EXISTING,         // 打开已存在的文件

        FILE_ATTRIBUTE_NORMAL, // 普通文件

        NULL);

    dwPageSize = GetFileSize(hFile, 0);//获得文件大小可通过结构体获取

    lpvResult = VirtualAlloc(// 给文件分配内存空间

        NULL,               // 系统自动选择起始地址

        dwPageSize,         // 指定要分配的内存大小,以字节为单位

        MEM_COMMIT,         // 将申请到的虚拟内存提交到物理内存

        PAGE_READWRITE);    // read/write 属性

//将文件读至分配的内存

    bReadFile = ReadFile(hFile, lpvResult, dwPageSize, &dwBytesRead, 0);

    //实现将磁盘文件地址偏移至内存地址

#ifdef WIN64   

    psImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    psImageNTHeader = (PIMAGE_NT_HEADERS64)

        ((BYTE*)lpvResult + psImageDOSHeader->e_lfanew);

    DWORD num = psImageNTHeader->FileHeader.NumberOfSections;

    sImageSecctionHeader[0] = (PIMAGE_SECTION_HEADER)

        ((BYTE*)psImageNTHeader + sizeof(IMAGE_NT_HEADERS64));

    for(DWORD i = 1; i < num;i++)

    {

        sImageSecctionHeader[i] = (PIMAGE_SECTION_HEADER)

            ((BYTE*)sImageSecctionHeader[0] + sizeof(IMAGE_SECTION_HEADER));

    }

#else

    psImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    psImageNTHeader = (PIMAGE_NT_HEADERS32)

        ((BYTE*)lpvResult + psImageDOSHeader->e_lfanew);

    DWORD num = psImageNTHeader->FileHeader.NumberOfSections;

    sImageSecctionHeader[0] = (PIMAGE_SECTION_HEADER)

        ((BYTE*)psImageNTHeader + sizeof(IMAGE_NT_HEADERS32));

    for (DWORD i = 1; i < num; i++)

    {

        sImageSecctionHeader[i] = (PIMAGE_SECTION_HEADER)

            ((BYTE*)sImageSecctionHeader[i-1] + sizeof(IMAGE_SECTION_HEADER));

    }

#endif

    //以4KB为单位分配内存空间

    dwPageSize = sImageSecctionHeader[num-1]->Misc.VirtualSize +

sImageSecctionHeader[num-1]->VirtualAddress;

    dwPageSize = (dwPageSize / 0x1000 + 1) * 0x1000;//已4KB为单位对齐

    lpvResult2 = VirtualAlloc(

        NULL,

        dwPageSize,         // 内存大小, 以字节为单位

        MEM_COMMIT,         // 将申请到的虚拟内存提交到物理内存

        PAGE_READWRITE);    // read/write 属性  

    //拷贝PE文件头

#ifdef WIN64

    CopyMemory(lpvResult2, lpvResult, psImageDOSHeader->e_lfanew +

sizeof(IMAGE_NT_HEADERS64) + 3 * sizeof(IMAGE_SECTION_HEADER));

#else

    CopyMemory(lpvResult2, lpvResult, psImageDOSHeader->e_lfanew +

sizeof(IMAGE_NT_HEADERS32) + 3 * sizeof(IMAGE_SECTION_HEADER));

#endif

    //拷贝第0个节区数据

    for (DWORD i = 0;i < num;i++)

    {

        CopyMemory((BYTE*)lpvResult2 + sImageSecctionHeader[i]->VirtualAddress,

            (BYTE*)lpvResult + sImageSecctionHeader[i]->PointerToRawData,

            sImageSecctionHeader[i]->Misc.VirtualSize);

    }

    return (PBYTE)lpvResult2;

}

//32位PE文件

VOID iat32(PBYTE lpvResult)

{

    DWORD dwTemp = 0;

    PIMAGE_DOS_HEADER pImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    PIMAGE_NT_HEADERS32 psImageNTHeader = (PIMAGE_NT_HEADERS32)(lpvResult +

pImageDOSHeader->e_lfanew);

   

    //导出表VA地址

    dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[12].VirtualAddress +

(DWORD)pImageDOSHeader;

    PIMAGE_THUNK_DATA32 IatAddress = (PIMAGE_THUNK_DATA32)dwTemp;

    printf("IAT函数地址表的VA地址为%08x\n", (DWORD)IatAddress);

    //导出表VA地址

    dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[1].VirtualAddress +

(DWORD)pImageDOSHeader;

    PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor =

(PIMAGE_IMPORT_DESCRIPTOR)dwTemp;

    //遍历导入表描述符

    for (DWORD i = 0; *(DWORD*)(pImageImportDescriptor + i) != 0; i++)

    {

        //打印模块名

        DWORD NameAddress = (pImageImportDescriptor + i)->Name +

(DWORD)pImageDOSHeader;

        printf("%s:\n", (PBYTE)NameAddress);

        //获取桥1 RVA地址

        DWORD OriginalFirstThunkRVA = (pImageImportDescriptor +

i)->OriginalFirstThunk;

        DWORD TimeDate = (pImageImportDescriptor + i)->TimeDateStamp;

        if (TimeDate == -1)

        {

            printf("已绑定导入函数。\n");

            //获取桥2 VA地址

            PIMAGE_THUNK_DATA32 FirstThunkData =

(PIMAGE_THUNK_DATA32)(pImageImportDescriptor + i)->FirstThunk;

            dwTemp = (DWORD)pImageDOSHeader + (DWORD)FirstThunkData;

            FirstThunkData = (PIMAGE_THUNK_DATA32)dwTemp;

            for (DWORD j = 0; *((DWORD*)FirstThunkData + j) != 0; j++)

            {

                //INT

                DWORD AddressOfData = (FirstThunkData + j)->u1.AddressOfData;

                //输出函数名

                printf("函数地址:0x%04X\n", AddressOfData);

            }

            printf("\n");

        }

        else

        {

            //获取桥2 VA地址

            PIMAGE_THUNK_DATA32 FirstThunkData =

(PIMAGE_THUNK_DATA32)(pImageImportDescriptor + i)->FirstThunk;

            dwTemp = (DWORD)pImageDOSHeader + (DWORD)FirstThunkData;

            FirstThunkData = (PIMAGE_THUNK_DATA32)dwTemp;

            for (DWORD j = 0; *((DWORD*)FirstThunkData + j) != 0; j++)

            {

                //INT

                DWORD AddressOfDataRVA = (FirstThunkData +

j)->u1.AddressOfData;

                dwTemp = (DWORD)pImageDOSHeader + (DWORD)AddressOfDataRVA;

                PIMAGE_IMPORT_BY_NAME pImageImportByName =

(PIMAGE_IMPORT_BY_NAME)dwTemp;

                //输出函数名

                printf("0x%04X : %s\n", pImageImportByName->Hint,

pImageImportByName->Name);

            }

            printf("\n");

        }

    }

}

//64位PE文件

VOID iat64(PBYTE lpvResult)

{

    ULONGLONG dwTemp = 0;

    PIMAGE_DOS_HEADER pImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    PIMAGE_NT_HEADERS64 psImageNTHeader = (PIMAGE_NT_HEADERS64)(lpvResult +

pImageDOSHeader->e_lfanew);

    //导出表VA地址

    dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[12].VirtualAddress +

(ULONGLONG)pImageDOSHeader;

    PIMAGE_THUNK_DATA64 IatAddress = (PIMAGE_THUNK_DATA64)dwTemp;

    printf("IAT函数地址表的VA地址为%08lx\n", (unsigned long)IatAddress);

    //导出表VA地址

    dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[1].VirtualAddress +

(ULONGLONG)pImageDOSHeader;

    PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor =

(PIMAGE_IMPORT_DESCRIPTOR)dwTemp;

    //遍历导入表描述符

    for (DWORD i = 0; *(DWORD*)(pImageImportDescriptor + i) != 0; i++)

    {

        //打印模块名

        LONGLONG NameAddress = (pImageImportDescriptor + i)->Name +

(ULONGLONG)pImageDOSHeader;

        printf("%s:\n", (PBYTE)NameAddress);

        //获取桥1 RVA地址

        DWORD OriginalFirstThunkRVA = (pImageImportDescriptor +

i)->OriginalFirstThunk;

        DWORD TimeDate = (pImageImportDescriptor + i)->TimeDateStamp;

        if (TimeDate == -1)

        {

            printf("已绑定导入函数。\n");

            //获取桥2 VA地址

            PIMAGE_THUNK_DATA64 FirstThunkData =

(PIMAGE_THUNK_DATA64)(pImageImportDescriptor + i)->FirstThunk;

            dwTemp = (ULONGLONG)pImageDOSHeader + (ULONGLONG)FirstThunkData;

            FirstThunkData = (PIMAGE_THUNK_DATA64)dwTemp;

            for (DWORD j = 0; *((DWORD*)FirstThunkData + j) != 0; j++)

            {

                //INT

                ULONGLONG AddressOfData = (FirstThunkData +

j)->u1.AddressOfData;

                //输出函数名

                printf("函数地址:0x%08lx\n", (unsigned long)AddressOfData);

            }

            printf("\n");

        }

        else

        {

            //获取桥2 VA地址

            PIMAGE_THUNK_DATA64 FirstThunkData =

(PIMAGE_THUNK_DATA64)(pImageImportDescriptor + i)->FirstThunk;

            dwTemp = (ULONGLONG)pImageDOSHeader + (ULONGLONG)FirstThunkData;

            FirstThunkData = (PIMAGE_THUNK_DATA64)dwTemp;

            for (ULONGLONG j = 0; *((ULONGLONG*)FirstThunkData + j) != 0; j++)

            {

                //INT

                ULONGLONG AddressOfDataRVA = (FirstThunkData +

j)->u1.AddressOfData;

                dwTemp = (ULONGLONG)pImageDOSHeader +

(ULONGLONG)AddressOfDataRVA;

                PIMAGE_IMPORT_BY_NAME pImageImportByName =

(PIMAGE_IMPORT_BY_NAME)dwTemp;

                //输出函数名

                printf("0x%04X : %s\n", pImageImportByName->Hint,

pImageImportByName->Name);

            }

            printf("\n");

        }

    }

}

//RVA转FOA

DWORD RvaToFoa(PIMAGE_NT_HEADERS ntHeaders, DWORD rva) {

    //ntHeaders+4+sizeof(IMAGE_FILE_HEADER)+FileHeader.SizeOfOptionalHeader(32或64位PE)

    PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);

    WORD numberOfSections = ntHeaders->FileHeader.NumberOfSections;

    for (WORD i = 0; i < numberOfSections; i++) {

        DWORD sectionStartRva = sectionHeader->VirtualAddress;

        DWORD sectionEndRva = sectionStartRva + sectionHeader->SizeOfRawData;

        if (rva >= sectionStartRva && rva < sectionEndRva) {

            DWORD foa = sectionHeader->PointerToRawData + (rva

sectionStartRva);

            return foa;

        }

        sectionHeader++;

    }

    return 0;  // RVA not found

}

 

总结

       1.示例程序以PE内存映像格式遍历IAT函数地址表。

       2.遍历时,根据导入表描述符的TimeDateStamp字段考虑两种情况:

       已绑定导入表,直接输出导入函数的实际地址,如notepad32.exe。

       未绑定导入表,则输出导入函数名。

       3.遍历IAT函数地址表的流程:

       第一步:创建PE内存映像文件,参见实验19。

       第二步:遍历IAT函数地址表。首先找到数据目录项的第12项,获取IAT函数地址表的RVA地址,加上基址后,得到并输出加载到内存中的IAT函数地址表的VA地址。

       第三步:根据数据目录项的第1项,找到导入表的RVA地址,加上基址后得到内存映像文件中的导入表VA地址。

       第四步:根据导入表描述符,输出DLL模块名。

第五步:判断是否已绑定导入表。如果已绑定,根据FirstThunk字段遍历导入函数地址表。如果未绑定,则根据FirstThunk字段遍历导入函数名。遍历的过程如图4-2所示。

遍历FirstThunk

图4-2 遍历IAT表

 注意

       IAT表中RVA地址后面4个字节0作为每个DLL模块的分隔符。示例中包含user32.dll和kernel32.dl两个DLL模块,每个模块各有一个导入函数。

实验二十七:在DTDebug调试中观察IAT

我们以32位汇编版HelloWorld.exe为例,将其拖入DTDebug调试器,按Ctrl+F9进入程序入口地址,然后在内存窗口按Ctrl+G,输入0x00042000,将地址跳转到.rdata节区。如图4-3所示:

图4-3 加载内存中的映像文件

 

总结

       仔细观察调试器反汇编窗口中的.text节区(代码段)。HelloWorld.exe程序非常简单,调用了user32.dll中的MessageBoxA函数和kernel32.dll中的ExitProcess函数。

       1.打开内存映射窗口

Memory map

Address    Size       Owner      Section    Contains        Type   Access      Initial   Mapped as

00040000  00001000   HelloWor              PE header    Imag   R         RWE

00041000  00001000  HelloWor   .text            code         Imag   R         RWE

00042000  00001000  HelloWor   .rdata                     Imag   R          RWE

00043000  00001000   HelloWor   .data       data          Imag   R         RWE

00044000  00001000   HelloWor   .reloc        relocations    Imag   R          RWE

74570000  00001000   user32                   PE header    Imag   R         RWE

74571000     00082000     user32     .text        code,exports  Imag   R         RWE

745F3000      00002000   user32     .data        data        Imag   R         RWE

745F5000      00008000   user32     .idata                  Imag   R         RWE

745FD000     00001000   user32     .didat                  Imag   R          RWE

745FE000      000E2000   user32     .rsrc        resources     Imag   R          RWE

746E0000      00006000   user32     .reloc       relocations    Imag   R          RWE

77170000       00001000       KERNEL32      PE header                Imag      R        RWE

77180000     0005F000     KERNEL32      .text       code          Imag   R E       RWE

771E0000       00027000      KERNEL32      .rdata     exports         Imag   R          RWE

77210000       00001000   KERNEL32      .data     data            Imag   RW         RWE

77220000   00001000      KERNEL32   .rsrc      resources     Imag   R            RWE

77230000   00005000      KERNEL32   .reloc     relocations   Imag   R          RWE

       我们需要关注以下几个地址区间:

00041000  00001000  HelloWor    .text      code         Imag   R         RWE

00042000  00001000  HelloWor    .rdata               Imag   R         RWE

74571000  00082000   user32     .text   code,exports  Imag   R         RWE

77180000  0005F000  KERNEL32   .text     code          Imag   R E       RWE

2.MessageBoxA函数

       MessageBoxA函数首先将4个参数push入栈,然后是call指令。对应的硬编码是E8 00000007,跳转7个字节,跳到地址0x0004101A,对应的汇编指令为JMP NEAR DWORD PTR DS:[42008]。注意0x00042008这个地址=0x00040000(基址)+ 2008(RVA地址)。

3.ExitProcess函数

ExitProcess函数push一个参数0,然后是call指令,硬编码为E8 00000006,跳转6个字节,跳到地址0x00041020,对应的汇编指令为JMP NEAR DWORD PTR DS:[42000]。注意0x00042000这个地址=0x00040000(基址)+ 2000(RVA地址)。

4.再观察一下内存窗口,.rdata节区地址0x00042000处的数据为0x77183BE0,就是ExitProcess函数的真实地址,刚好位于KERNEL32.dll的.text节区内。.rdata节区地址0x00042008处的数据为0x745DFDB0,就是MessageBoxA函数的真实地址,刚好位于user32.dll的.text节区内。

00042000  E0 3B 18 77 00 00 00 00 B0 FD 5D 74 00 00 00 00  ?w....褒]t....

0004101A       $- FF25 08200400  JMP NEAR DWORD PTR DS:[42008];  user32.MessageBoxA

00041020       .- FF25 00200400   JMP NEAR DWORD PTR DS:[42000];  KERNEL32.ExitProcess

因此上面的两条反汇编语句跳转的地址就是IAT函数地址表中替换后的真实导入函数地址。请读者单步执行上述两条JMP指令,观察一下跳转的函数入口地址。

       5.对照一下WinHex内HelloWorld.exe程序.rdata节区内的IAT表

00000600   42 21 00 00 00 00 00 00  28 21 00 00 00 00 00 00   B!......(!......

FOA文件偏移地址00000600对应的VA地址就是0x00042000,ExitProcess函数的RVA地址00002142H在加载到内存时被替换为真实的导入函数地址0x77183BE0。

FOA文件偏移地址00000608对应的VA地址就是0x00042008,MessageBoxA函数的RVA地址00002128H在加载到内存时被替换为真实的导入函数地址0x745DFDB0。

6.PE文件加载前后的变化如下图所示:

图4-4PE文件加载前

图4-5 PE文件加载后

练习

       请读者分别使用静态分析和动态分析的方法,分析32位和64位记事本程序的导入表和IAT表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2188891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十、敌人锁定

方法&#xff1a;通过寻找最近的敌人&#xff0c;使玩家的面朝向始终朝向敌人&#xff0c;进行攻击 1、代码 在这个方法中使用的是局部变量&#xff0c;作为临时声明和引用 public void SetActorAttackRotation() {Enemys GameObject.FindGameObjectsWithTag("Enemy&qu…

机器学习-树模型算法

机器学习-树模型算法 一、Bagging1.1 RF1.2 ET 二、Boosting2.1 GBDT2.2 XGB2.3 LGBM 仅个人笔记使用&#xff0c;感谢点赞关注 一、Bagging 1.1 RF 1.2 ET 二、Boosting 2.1 GBDT 2.2 XGB 2.3 LGBM LightGBM&#xff08;Light Gradient Boosting Machine) 基本算法原理…

2024企业网盘排行榜,十大企业网盘深度评测【part 2】

在当今数字化时代&#xff0c;企业网盘已成为提升工作效率、保障数据安全的重要工具。从Box到腾讯企业网盘&#xff0c;再到Egnyte、Amazon Drive、金山文档&#xff08;WPS&#xff09;和Huddle&#xff0c;每款产品都有其独特的功能和应用场景。然而&#xff0c;在众多选择中…

Spring Boot新闻推荐:实时数据处理

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

简单二叉树的构建及遍历

1.主要函数 #include <stdio.h> #include <stdlib.h> #include <string.h>//创建节点结构体 typedef struct node{char data[16];//节点数据struct node *L;//左节点struct node *R;//右结点}tree,*treeptr;//先序方式创建节点 treeptr create() {char buf[…

idea创建springboot模块

1.点击file->新建->model server url&#xff1a;如果倒数第二个java选项没有11&#xff0c;就把这里改为阿里云的 name&#xff1a;模块名字 location&#xff1a;文件存放的位置 其他的根据图片自行填写 2. 3.验证 如果没有iml文件(不影响&#xff0c;可以不弄)&#…

MongoDB聚合操作及索引底层原理

目录 链接:https://note.youdao.com/ynoteshare/index.html?id=50fdb657a9b06950fa255a82555b44a6&type=note&_time=1727951783296 本节课的内容: 聚合操作: 聚合管道操作: ​编辑 $match 进行文档筛选 ​编辑 将筛选和投影结合使用: ​编辑 多条件匹配: …

20241004给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Android12时永不休眠的步骤

20241004给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Android12时永不休眠的步骤 2024/10/4 19:22 1、 Z:\rk3588s4_3588a12\device\rockchip\common\device.mk ifeq ($(strip $(BOARD_HAVE_BLUETOOTH_RTK)), true) include hardware/realtek/rtkbt/rtkbt.mk endif ifeq ($(str…

YouTube音视频合并批处理基于 FFmpeg的

专门针对YouTube高品质分享处理的&#xff0c;将音频和视频合并。 首先下载ffmpeg.exe网上随便下载。 echo off title YouTube 音视频合并 20241004 echo 作者&#xff1a;xiaoshen echo 网站&#xff1a;http://www.xiaoshen.cn/ echo. set /p audio请将【音频】文件拖拽到此…

⌈ 传知代码 ⌋ 将一致性正则化用于弱监督学习

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

什么是 NVIDIA 机密计算?( 上篇 )

什么是机密计算? 文章目录 前言1. 机密计算定义2. 机密计算有何独特之处?3. 机密计算是如何得名的4. 机密计算的工作原理是什么?5. 缩小安全边界6. 机密计算的使用案例7. 机密计算如何发展8. 加速机密计算9. 机密计算的下一步前言 机密计算是一种在计算机处理器的受保护区域…

全网最详细kubernetes中的资源

1、资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务。 所谓的部署服务&#xff0c;其实就是在kubernetes集群中运行一个个的…

csp-j模拟三补题报告

前言 今天题难&#xff0c;排名没进前十 &#xff08;“关于二进制中一的个数的研究与规律”这篇文章正在写&#xff09; 第一题 三个&#xff08;three&#xff09; 我的代码&#xff08;AC&#xff09; #include<bits/stdc.h> #define ll long long using namespac…

快停止这种使用U盘的行为!

前言 现在各行各业的小伙伴基本上都需要用电脑来办公了&#xff0c;你敢说你不需要用电脑办公&#xff1f; 啊哈哈哈&#xff0c;用iPad或者手机办公的也算。 有些小伙伴可能经常996&#xff0c;甚至有时候都是007。有时候到了下班时间&#xff0c;工作还没做完&#xff0c;…

Python技巧:如何处理未完成的函数

一、问题的提出 写代码的时候&#xff0c;我们有时候会给某些未完成的函数预留一个空位&#xff0c;等以后有时间再写具体内容。通常&#xff0c;大家会用 pass 或者 ... &#xff08;省略号&#xff09;来占位。这种方法虽然能让代码暂时不报错&#xff0c;但可能在调试的时候…

精准翻译神器:英汉互译软件的卓越表现

英文作为目前世界上使用最广的一种语言&#xff0c;是的很多先进的科学文献或者一些大厂产品的说明书都有英文的版本。为了方便我们的阅读和学习&#xff0c;现在有不少支持翻译英汉互译的工具&#xff0c;今天我们就一起来讨论一下吧。 1.福昕中英在线翻译 链接直达>>…

二叉树的前序遍历——非递归版本

1.题目解析 题目来源&#xff1a;144.二叉树的前序遍历——力扣 测试用例 2.算法原理 前序遍历&#xff1a; 按照根节点->左子树->右子树的顺序遍历二叉树 二叉树的前序遍历递归版本十分简单&#xff0c;但是如果树的深度很深会有栈溢出的风险&#xff0c;这里的非递归…

【论文笔记】DKTNet: Dual-Key Transformer Network for small object detection

【引用格式】&#xff1a;Xu S, Gu J, Hua Y, et al. Dktnet: dual-key transformer network for small object detection[J]. Neurocomputing, 2023, 525: 29-41. 【网址】&#xff1a;https://cczuyiliu.github.io/pdf/DKTNet%20Dual-Key%20Transformer%20Network%20for%20s…

vue3实现打字机的效果,可以换行

之前看了很多文章,效果是实现了,就是没有自动换行的效果,参考了文章写了一个,先上个效果图,卡顿是因为模仿了卡顿的效果,还是很丝滑的 目录 效果图:代码如下 效果图: ![请添加图片描述](https://i-blog.csdnimg.cn/direct/d8ef33d83dd3441a87d6d033d9e7cafa.gif 代码如下 原…

jmeter学习(8)结果查看

1&#xff09;查看结果树 查看结果树&#xff0c;显示取样器请求和响应的细节以及请求结果&#xff0c;包括消息头&#xff0c;请求的数据&#xff0c;响应的数据。 2&#xff09;汇总报告 汇总报告&#xff0c;为测试中的每个不同命名的请求创建一个表行。这与聚合报告类似&…