中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

windows中怎么從內存加載DLL

發布時間:2021-11-03 17:45:12 來源:億速云 閱讀:189 作者:iii 欄目:編程語言

這篇文章主要講解了“windows中怎么從內存加載DLL”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“windows中怎么從內存加載DLL”吧!

Windows可執行文件– PE格式

首先我們先看看pe的結構

DOS headerDOS stub
PE header
Section header
Section 1
Section 2
. . .
Section n

下面給出的所有結構都可以在頭文件winnt.h中找到。

DOS header

DOS header 僅用于向后兼容。它位于DOS stub 之前。

Microsoft定義DOS標頭如下:

typedef struct _IMAGE_DOS_HEADER {// DOS .EXE標頭
    WORD e_magic; //Magic number
    字e_cblp; //文件最后一頁上的字節
    字e_cp; //文件中的頁面
    WORD e_crlc; //Relocations
    字e_cparhdr; //段落中header的大小
    字e_minalloc; //所需的最少額外段落
    字e_maxalloc; //所需的最大額外段落數
    WORD e_ss; //初始(相對)SS值
    WORD e_sp; //初始SP值
    WORD e_csum; //校驗和
    WORD e_ip; //初始IP值
    WORD e_cs; //初始(相對)CS值
    字e_lfarlc; //重定位表的文件地址
    WORD e_ovno; //覆蓋數
    WORD e_res [4]; //保留字
    WORD e_oemid; // OEM標識符(用于e_oeminfo)
    WORD e_oeminfo; // OEM信息;特定于e_oemid
    字e_res2 [10]; //保留字
    LONG e_lfanew; //新的exe標頭的文件地址
  } IMAGE_DOS_HEADER,* PIMAGE_DOS_HEADER;

PE header

PE 頭包含有關可執行文件內不同部分的信息,這些信息用于存儲代碼和數據或定義從其他庫導入或此庫提供的導出。

它的定義如下:

typedef struct _IMAGE_NT_HEADERS {
    DWORD簽名;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32可選標題;
} IMAGE_NT_HEADERS32,* PIMAGE_NT_HEADERS32;

該FileHeader里描述的physical 文件的格式,如目錄符號等信息:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD NumberOfSections;
    DWORD TimeDateStamp;
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER,* PIMAGE_FILE_HEADER;

該OptionalHeader里包含的信息邏輯庫的格式,包括需要的操作系統版本,內存需求和切入點:

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    //標準字段。
    //
WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    //
    // NT其他字段。
    //
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32,* PIMAGE_OPTIONAL_HEADER32;

所述DataDirectory目錄包含16(IMAGE_NUMBEROF_DIRECTORY_ENTRIES定義庫的邏輯組件)條目:

Index描述
0導出功能
1導入功能
2資源資源
3異常信息
4安全信息
5基地搬遷表
6調試信息
7特定于架構的數據
8全局指針
9線程本地存儲
10加載配置
11綁定進口
12導入地址表
13延遲加載導入
14COM運行時描述符

對于導入DLL,我們僅需要描述導入和基本重定位表的條目。為了提供對導出功能的訪問,需要導出條目。

Section header

段頭存儲在PE頭中的OptionalHeader結構之后。Microsoft提供了宏IMAGE_FIRST_SECTION以基于PE標頭獲得起始地址。

實際上,節頭是文件中每個節的信息列表:

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

一個部分可以包含代碼,數據,重定位信息,資源,導出或導入定義等。

加載庫

要模擬PE加載程序,我們必須首先了解,將文件加載到內存并準備結構以便從其他程序中調用它們是必需的。

在發出API調用LoadLibrary時,Windows基本上執行以下任務:

1.打開給定的文件并檢查DOS和PE標頭。

2.嘗試在位置PEHeader.OptionalHeader.ImageBase處分配PEHeader.OptionalHeader.SizeOfImage字節的內存塊。

3.解析節標題并將節復制到其地址。相對于已分配內存塊的基址的每個節的目標地址存儲在IMAGE_SECTION_HEADER結構的VirtualAddress屬性中。

4.如果分配的內存塊與ImageBase不同,則必須調整代碼和/或數據部分中的各種引用。這稱為Base relocation.。

5.必須通過加載相應的庫來解決所需的庫導入。

6.必須根據部分的特性來保護不同部分的存儲區域。有些部分標記為可丟棄,因此此時可以安全釋放。這些部分通常包含僅在導入期間需要的臨時數據,例如基本重定位的信息。

7.現在,庫已完全加載。必須通過使用標志DLL_PROCESS_ATTACH調用入口點來對此進行通知。

在以下段落中,將描述每個步驟。

分配內存

該庫所需的所有內存必須使用VirtualAlloc保留/分配,因為Windows提供了保護這些內存塊的功能。這是限制訪問存儲器所必需的,例如阻止對代碼或常量數據的寫訪問。

OptionalHeader結構定義該庫所需的內存塊的大小。如果可能,必須在ImageBase指定的地址處保留它:

memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase),
    PEHeader->OptionalHeader.SizeOfImage,
    MEM_RESERVE,
    PAGE_READWRITE);

如果保留的內存與ImageBase中指定的地址不同,則必須執行下面的基本重定位。

復制sections

保留內存后,即可將文件內容復制到系統中。必須對section header 進行評估,以確定文件中的位置和內存中的目標區域。

在復制數據之前,必須先提交內存塊:

dest = VirtualAlloc(baseAddress + section->VirtualAddress,
    section->SizeOfRawData,
    MEM_COMMIT,
    PAGE_READWRITE);

Base relocation

庫的代碼/數據部分中的所有內存地址都相對于ImageBase在OptionalHeader中定義的地址進行存儲。如果無法將庫導入到該內存地址,則必須對引用進行調整=> relocated。文件格式通過在基本重定位表中存儲有關所有這些引用的信息來幫助實現此目的,這些信息可在OptionalHeader中的DataDirectory的目錄條目5中找到。

該表由一系列這種結構組成

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION;

它包含(SizeOfBlock – IMAGE_SIZEOF_BASE_RELOCATION)/ 2個條目,每個條目16位。高4位定義重定位的類型,低12位定義相對于VirtualAddress的偏移量。

似乎在DLL中使用的唯一類型是

IMAGE_REL_BASED_ABSOLUTE

用于填充。

IMAGE_REL_BASED_HIGHLOW

將ImageBase和分配的內存塊之間的增量添加到在偏移處找到的32位。

解決導入部分

OptionalHeader中DataDirectory的目錄條目1指定要從中導入符號的庫列表。此列表中的每個條目定義如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)
    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;

該名條目描述偏移到庫的名稱(例如的空值終止字符串KERNEL32.DLL)。該OriginalFirstThunk條目指向的函數名的引用列表從外部庫中導入。FirstThunk指向地址列表,該地址列表中包含指向導入符號的指針。

解決導入問題時,我們瀏覽兩個列表,將名稱定義的函數導入第一個列表,并將指向符號的指針存儲在第二個列表中:

nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk);
symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk);
for (; *nameRef; nameRef++, symbolRef++)
{
    PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef);
    *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name);
    if (*funcRef == 0)
    {
        handleImportError();
        return;
    }
}

內存保護

每個部分的“ 特征”條目中都指定了權限標志。這些標志可以是以下之一或組合

IMAGE_SCN_MEM_EXECUTE

本節包含可以執行的數據。

IMAGE_SCN_MEM_READ

本節包含可讀數據。

IMAGE_SCN_MEM_WRITE

本節包含可寫的數據。

這些標志必須映射到保護標志

PAGE_NOACCESS

PAGE_WRITECOPY

PAGE_READONLY

PAGE_READWRITE

PAGE_EXECUTE

PAGE_EXECUTE_WRITECOPY

PAGE_EXECUTE_READ

PAGE_EXECUTE_READWRITE

現在,可以使用函數VirtualProtect限制對內存的訪問。如果程序嘗試以未經授權的方式訪問它,則Windows會引發異常。

除了上面的部分標志之外,還可以添加以下內容:

IMAGE_SCN_MEM_DISCARDABLE

導入后可以釋放此部分中的數據。通常,這是為重定位數據指定的。

IMAGE_SCN_MEM_NOT_CACHED

Windows不得緩存此部分中的數據。將位標志PAGE_NOCACHE添加到上面的保護標志中。

Notify library

最后要做的是調用DLL入口點(由AddressOfEntryPoint定義),并因此通知庫有關附加到進程的信息。

入口點的功能定義為

typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

所以我們最后需要執行的代碼是

DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint);
(*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);

之后,我們可以像使用任何普通庫一樣使用導出的函數。

導出功能

如果要訪問庫導出的函數,則需要找到符號的入口點,即要調用的函數的名稱。

OptionalHeader中DataDirectory的目錄條目0包含有關導出函數的信息。它的定義如下:

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

首先要做的是將函數名稱映射到導出符號的序號。因此,只需并行遍歷AddressOfNames和AddressOfNameOrdinals定義的數組,直到找到所需的名稱。

現在,您可以使用序號通過評估AddressOfFunctions數組的第n個元素來讀取地址。

釋放

要釋放自定義加載的庫,請執行以下步驟

調用入口點以通知庫有關分離的信息:

DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint);
(*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);

用于解析導入的免費外部庫。

釋放已分配的內存。

內存模塊

MemoryModule是一個C庫,可用于從內存加載DLL。

該接口與加載庫的標準方法非常相似:

typedef void *HMEMORYMODULE;
HMEMORYMODULE MemoryLoadLibrary(const void *);
FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *);
void MemoryFreeLibrary(HMEMORYMODULE);

感謝各位的閱讀,以上就是“windows中怎么從內存加載DLL”的內容了,經過本文的學習后,相信大家對windows中怎么從內存加載DLL這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

华安县| 石台县| 和平县| 阳泉市| 苏州市| 策勒县| 尉犁县| 四川省| 罗源县| 阿巴嘎旗| 宣化县| 牟定县| 准格尔旗| 天柱县| 南宫市| 揭东县| 论坛| 福清市| 自治县| 大城县| 罗山县| 平谷区| 调兵山市| 封丘县| 稻城县| 阆中市| 万山特区| 华池县| 大兴区| 大关县| 咸丰县| 扶沟县| 准格尔旗| 车险| 临高县| 泽库县| 仁化县| 汾西县| 德昌县| 旌德县| 门源|