惡意軟件開發——內存相關API


一、前言

Windows操作系統的內存有三種屬性,分別為:可讀、可寫、可執行,並且操作系統將每個進程的內存都隔離開來,當進程運行時,創建一個虛擬的內存空間,系統的內存管理器將虛擬內存空間映射到物理內存上,所以每個進程的內存都是等大的。

操作系統給予每個進程申請內存的權利,使用不同的API,申請的內存具有不同的涵義。

在進程申請時,需要聲明這塊內存的基本信息:申請內存大小、申請內存起始內存基址、申請內存屬性、申請內存對外的權限等。

二、相關API介紹

1.VirtualAlloc
該函數的功能是在調用進程的虛地址空間,預定或者提交一部分頁,如果用於內存分配的話,並且分配類型未指定MEM_RESET,則系統將自動設置為0;其函數原型:

LPVOID VirtualAlloc{
  LPVOID lpAddress, // 要分配的內存區域的地址
  DWORD dwSize, // 分配的大小
  DWORD flAllocationType, // 分配的類型
  DWORD flProtect // 該內存的初始保護屬性
};

參數說明:
1.LPVOID lpAddress, 分配內存區域的地址。當你使用VirtualAlloc來提交一塊以前保留的內存塊的時候,lpAddress參數可以用來識別以前保留的內存塊。如果這個參數是NULL,系統將會決定分配內存區域的位置,並且按64-KB向上取整(roundup)。
2.SIZE_T dwSize, 要分配或者保留的區域的大小。這個參數以字節為單位,而不是頁,系統會根據這個大小一直分配到下頁的邊界DWORD
3.flAllocationType, 分配類型 ,你可以指定或者合並以下標志:MEM_COMMIT,MEM_RESERVE和MEM_TOP_DOWN。
4.DWORD flProtect 指定了被分配區域的訪問保護方式:PAGE_EXECUTE_READ,PAGE_EXECUTE_READWRITE
小結:
VirtualAlloc可以通過並行多次調用提交一個區域的部分或全部來保留一個大的內存區域。多重調用提交同一塊區域不會引起失敗。這使得一個應用程 序保留內存后可以隨意提交將被寫的頁。當這種方式不在有效的時候,它會釋放應用程序通過檢測被保留頁的狀態看它是否在提交調用之前已經被提交。
VirtualAlloc對應的釋放函數為VirtualFree。

2.HeapAlloc
該函數是從堆上分配一塊內存,且分配的內存是不可移動的(即如果沒有連續的空間能滿足分配的大小,程序不能將其他零散的空間利用起來,從而導致分配失敗)。該分配方法是從一指定地址開始分配,而不像GloabalAlloc是從全局堆上分配,這個有可能是全局,也有可能是局部,其函數原型:

LPVOID HeapAlloc(
  HANDLE hHeap, //要分配堆的句柄
  DWORD dwFlags, //堆分配時的可選參數
  SIZE_T dwBytes, //要分配堆的字節數
);

參數說明:
1.HANDLE hHeap,要分配堆的句柄,可以通過HeapCreate()函數或GetProcessHeap()函數獲得。
2.DWORD dwFlags,堆分配時的可選參數,其值可以為以下的一種或多種:HEAP_GENERATE_EXCEPTIONS(如果分配錯誤將會拋出異常,而不是返回NULL。異常值可能是STATUS_NO_MEMORY, 表示獲得的內存容量不足,或是STATUS_ACCESS_VIOLATION,表示存取不合法),HEAP_NO_SERIALIZE(不使用連續存取),HEAP_ZERO_MEMORY(將分配的內存全部清零)。
3.SIZE_T dwBytes,要分配堆的字節數。
小結:
hHeap是進程堆內存開始位置,dwFlags是分配堆內存的標志。包括HEAP_ZERO_MEMORY,即使分配的空間清零,dwBytes是分配堆內存的大小,其對應的釋放空間函數為HeapFree。

3.GlobalAlloc
該函數用於從全局堆中分配出內存供程序使用,函數原型為:

HGLOBALGlobalAlloc(
  UINTuFlags, // 分配屬性(方式)
  DWORDdwBytes, // 分配的字節數
);

參數說明:
1.UINTuFlags,指定如何分配內存,若指定為0,則是默認的GMEM_FIXED.這個值可以是下面其中一個或幾個位標識(那些指明不兼容的組合除外),標識:GHND(為GMEM_MOVEABLE 和 GMEM_ZEROINIT的組合),GMEM_FIXED(分配固定的內存,返回值是一個指針),GMEM_MOVEABLE(分配可移動的內存,在Win32中內存塊在物理內存中是不可移動的,但在缺省堆中可以. 返回值是該內存對象的句柄,可使用函數 GlobalLock 將該句柄轉換為一個指針,這個標識不能與 GMEM_FIXED 組合使用),GMEM_ZEROINIT(將所申請內存初始化為0),GPTR(為GMEM_FIXED和GMEM_ZEROINIT組合)。
2.DWORDdwBytes,指定要申請的字節數.若該參數為 0 且參數 uFlags 指定為 GMEM_MOVEABLE 則該函數返回一個內存對象的句柄,該內存對象被標識為discarded(可拋棄的)
小結:
使用此函數分配內存可以保證8字節的邊界.所有的內存均在執行訪問時創建;不需要特別的函數來動態執行所產生的代碼,若函數調用成功,將至少分配所需內存.若實際分配量超過所需,則內存仍然能夠充分利用之.可用函數 GlobalSize 來確定實際所分配的字節數,可使用 GlobalFree 來釋放內存。

4.LocalAlloc
該函數用於從局部堆中分配內存供程序使用,函數原型為:

HLOCAL LocalAlloc(
  UINT uFlags, //指定怎樣去分配內存
  UINT uBytes, //指定要分配的字節數
);

參數說明:
1.uFlags,指定怎樣去分配內存。如果zero被指定,默認的是LMEM_FIXED標志。此參數有三種標志:LMEM_FIXED(分配固定內存,返回值是指向一個內存對象的指針),LMEM_ZEROINIT(初始化內存內容為zero),LPTR(結合了LMEM_FIXED和LMEM_ZEROINIT這兩種標志),LMEM_MOVEABLE(分配可移動內存),LMEM_DISCARDABLE(分配可刪除的內存)。
2.uBytes,指定要分配的字節數。
小結:
在16位Windows中是有區別的,因為在16位windows用一個全局堆和局部堆來管理內存,每一個應用程序或dll裝入內存時,代碼段被裝入全局 堆,而系統又為每個實例從全局堆中分配了一個64kb的數據段作為該實例的局部堆,用來存放應用程序的堆棧和所有全局或靜態變量。而 LocalAlloc/GlobalAlloc就是分別用於在局部堆或全局堆中分配內存。
由於每個進程的局部堆很小,所以在局部堆中分配內存會受到空間的限制。但這個堆是每個進程私有的,相對而言分配數據較安全,數據訪問出錯不至於影響到整個系統。
而在全局堆中分配的內存是為各個進程共享的,每個進程只要擁有這個內存塊的句柄都可以訪問這塊內存,但是每個全局內存空間需要額外的內存開銷,造成分配浪費。而且一旦發生嚴重錯誤,可能會影響到整個系統的穩定。
不過在Win32中,每個進程都只擁有一個省缺的私有堆,它只能被當前進程訪問。應用程序也不可能直接訪問系統內存。所以在Win32中全局堆和局部堆都 指向進程的省缺堆。用LocalAlloc/GlobalAlloc分配內存沒有任何區別。甚至LocalAlloc分配的內存可以被 GlobalFree釋放掉。所以在Win32下編程,無需注意Local和Global的區別,一般的內存分配都等效於 HeapAlloc(GetProcessHeap(),...)。
LocalAlloc對應的釋放函數為LockFree。

5.Malloc
malloc與free是C++/C語言的標准庫函數,可用於申請動態內存和釋放內存。對於非內部數據類型的對象而言,光用 malloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是 庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。

6.New
是C++的運算符,主要用來新建類,與C++的構造函數和異常機制有關,與上述其它函數的使用環境大相庭徑。一般編譯器中的new都是用malloc來分配內存的。
New對應的釋放函數為delete。

三、拷貝/復制內存函數

1.memcpy
函數原型:
void *memcpy(void *destin, void *source, unsigned n);
參數:

  • destin-- 指向用於存儲復制內容的目標數組,類型強制轉換為 void* 指針。
  • source-- 指向要復制的數據源,類型強制轉換為 void* 指針。
  • n-- 要被復制的字節數。

2.CopyMemory
函數原型:

VOID CopyMemory(
  PVOID Destination,
  CONST VOID *Source,
  SIZE_T Length
);

參數:

  • Destination-- 要復制內存塊的目的地址。
  • Source-- 要復制內存塊的源地址。
  • Length-- 指定要復制內存塊的大小,單位為字節

3.RtlCopyMemory
CopyMemory一樣
函數原型:

void RtlCopyMemory(
   void*       Destination,
   const void* Source,
   size_t      Length
);

參數:

  • Destination-- 要復制內存塊的目的地址。
  • Source-- 要復制內存塊的源地址。
  • Length-- 指定要復制內存塊的大小,單位為字節

4.RtlMoveMemory
函數原型:

VOID RtlMoveMemory(
  VOID UNALIGNED *Destination,
  const VOID UNALIGNED *Source,
  SIZE_T Length
);

參數:
Destination-- 指向移動目的地址的指針。
Source-- 指向要復制的內存地址的指針。
Length-- 指定要復制的字節數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM