1. 程式人生 > >從記憶體中載入DLL (修正版)

從記憶體中載入DLL (修正版)

轉載地址: http://www.freshbug.com/archives/39 ======================================================

程式使用動態庫DLL一般分為隱式載入和顯式載入兩種,分別對應兩種連結情況。本文主要討論顯式載入的技術問題。我們知道,要顯式載入一個DLL,並取得其中匯出的函式地址一般是通過如下步驟: (1) 用LoadLibrary載入dll檔案,獲得該dll的模組控制代碼; (2) 定義一個函式指標型別,並宣告一個變數; (3) 用GetProcAddress取得該dll中目標函式的地址,賦值給函式指標變數; (4) 呼叫函式指標變數。

這 個方法要求dll檔案位於硬碟上面。現在假設我們的dll已經位於記憶體中,比如通過脫殼、解密或者解壓縮得到,能不能不把它寫入硬碟檔案,而直接從記憶體加 載呢?答案是肯定的。經過多天的研究,非法操作了N次,修改了M個BUG,死亡了若干腦細胞後,終於有了初步的結果,下面做個總結與大家共享。

一、載入的步驟

由 於沒有相關的資料說明,只能憑藉感覺來寫。首先LoadLibrary是把dll的程式碼對映到exe程序的虛擬地址空間中,我們要實現的也是這個。所以先 要弄清楚dll的檔案結構。好在這個比較簡單,它和exe一樣也是PE檔案結構,關於PE檔案的資料很多,閱讀一番後,基本上知道了必須做的幾個工作: (1)

判斷記憶體資料是否是一個有效的DLL。這個功能通過函式CheckDataValide完成。原型是: BOOL CMemLoadDll::CheckDataValide(void* lpFileData, int DataLength); (2)計算載入該DLL所需的虛擬記憶體大小。這個功能通過函式CalcTotalImageSize完成。原型是: int CMemLoadDll::CalcTotalImageSize(); (3)將DLL資料複製到所分配的虛擬記憶體塊中。該功能通過函式CopyDllDatas完成。要注意段對齊。 void CMemLoadDll::CopyDllDatas(void* pDest, void* pSrc); (4)
修正基地重定位資料。這個功能通過函式DoRelocation完成。原型是: void CMemLoadDll::DoRelocation( void *NewBase); (5)填充該DLL的引入地址表。這個功能由函式FillRavAddress完成。原型是: BOOL CMemLoadDll::FillRavAddress(void *pImageBase); (6)根據DLL每個節的屬性設定其對應記憶體頁的讀寫屬性。我這裡做了簡化,所有記憶體區域都設定成一樣的讀寫屬性。 (7)呼叫入口函式DllMain,完成初始化工作。這一步我一開始忽略了,所以總是發現自己載入的dll和LoadLibrary載入的dll有些不同(我把整塊記憶體區域儲存到兩個檔案中進行比較,夠暈的)。只是最近猜想到還需要這一步。 (8)儲存dll的基地址(即分配的記憶體塊起始地址),用於查詢dll的匯出函式。從現在開始這個dll已經完全對映到了程序的虛擬地址空間,可以使用它了。 (9)不需要dll的時候,釋放所分配的虛擬記憶體。

二、要說明的幾個問題

(1)目前CMemLoadDll僅僅針對win32 動態庫,沒有考慮mfc常規和擴充套件dll。 (2) 只考慮使用dll中的函式,對於匯出類的dll,由於通常都是隱式連結,所以也沒有考慮。匯出變數的dll雖然也是隱式連結,但是通過查詢函式的方法也可 以找到該變數,不過在取值的時候一定要符合dll中對變數的定義,比如dll中匯出的是一個int變數,則得到該變數在dll中的地址後,需要強制轉換成 int*指標,然後取值。 (3)查詢函式的功能通過函式 FARPROC CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName); 實現,引數是dll匯出的函式(或者變數)的名字。這裡必須注意函式名修飾,通常不加extern”C”的函式,編譯以後在dll中匯出的都是修飾名,比如:在dll標頭檔案中: extern __declspec(dllexport) int nTestDll; 在.dll中的匯出符號變成 [email protected]@3HA 所以,為了能夠找到我們需要的函式,必須在.h中新增extern “C”修飾。最好是給dll加一個def檔案,裡面明確給出每個函式的匯出名字。 (4)PE中的內容比較多,有些細節沒有考慮。比如CheckDataValide函式中沒有考慮dll對作業系統版本的要求。 (5)PE檔案中的節有很多種。可以從節表(或者叫做區塊表)中一一找到。而且每個節的屬性都不同。例如:.text, .data, .rsrc, .crt等等。由於這個程式碼基於手頭已有的pe檔案資料,對於不熟悉的節,在對映dll資料的時候沒有考慮是否需要處理。 (6) 一開始把dll對映到程序的地址空間以後,我試圖直接使用GetProcAddress查詢函式。最初我認為LoadLibrary返回的 HINSTANCE值是0×10000000,把它傳遞給GetProcAddress可以找到目標函式,而我也把dll對映到0×10000000這個 地址,但是當我把這個值傳遞給GetProcAddress的時候,發現無法找到函式,用GetLastError得到錯誤碼一看是無效控制代碼的錯誤,這才 明白原來LoadLibrary在載入dll的時候,同時建立了一個控制代碼放入程序的控制代碼表,而我們要做這個工作是比較麻煩的,所以只能自己寫一個查詢函 數。 (7)釋放dll所佔據的虛擬記憶體,原來我使用 VirtualFree((LPVOID)pImageBase, 0,MEM_FREE); 後來發現有問題,應該使用 VirtualFree((LPVOID)pImageBase, 0, MEM_RELEASE); (8)MemGetProcAddress不僅支援通過函式名查詢,還支援通過匯出序號查詢函式。例如下面的用法: DLLFUNCTION fDll = (DLLFUNCTION)a.MemGetProcAddress((LPCTSTR)1);

三、建立測試用的DLL,工程的名字取”TestDll”

用VC嚮導建立一個WIN32 DLL工程,裡面選擇“匯出一些符號”,為了測試需要,對原始碼進行如下修改:(1)標頭檔案 // This class is exported from the TestDll.dll class TESTDLL_API CTestDll {     public:     CTestDll(void); }; extern TESTDLL_API int nTestDll; //要修改的地方,添加了extern “C” 和 char *引數: extern “C” TESTDLL_API int fnTestDll(char *); (2)cpp檔案 a. 新增 #include “stdlib.h” b. DllMain中 case DLL_PROCESS_DETACH: nTestDll = 12345; break; c. 初始化變數 TESTDLL_API int nTestDll=654321; d. 修改函式 TESTDLL_API int fnTestDll(char *p) {     if(p == NULL)         return nTestDll;     else         return atoi(p); }

四、建立測試工程。使用一個dlg工程,測試程式碼如下:

假設 DllNameBuffer裡面儲存有dll檔案的路徑 CFile f; if(f.Open(DllNameBuffer,CFile::modeRead)) {     int FileLength = f.GetLength();     void *lpBuf = new char[FileLength];     f.Read(lpBuf, FileLength);     f.Close();

    CMemLoadDll a;     if(a.MemLoadLibrary(lpBuf, FileLength)) //載入dll到當前程序的地址空間     {         typedef int (*DLLFUNCTION)(char *);         DLLFUNCTION fDll = (DLLFUNCTION)a.MemGetProcAddress(”fnTestDll”);         if(fDll != NULL)         {             MessageBox(”找到函式!!”);             CString str;             str.Format(”Result is: %d & %d”,fDll(NULL), fDll(”100″));             MessageBox(str);         }         else         {             DWORD err = GetLastError();             CString str;             str.Format(”Error: %d”,err);             MessageBox(str);          }     }     delete[] lpBuf; }

五、載入類原始碼。 typedef BOOL (__stdcall *ProcDllMain)(HINSTANCE, DWORD, LPVOID ); class CMemLoadDll {    public:          CMemLoadDll();          ~CMemLoadDll();          BOOL MemLoadLibrary( void* lpFileData , int DataLength); // Dll file data buffer          FARPROC MemGetProcAddress(LPCSTR lpProcName);   private:          BOOL isLoadOk;          BOOL CheckDataValide(void* lpFileData, int DataLength);          int CalcTotalImageSize();          void CopyDllDatas(void* pDest, void* pSrc);          BOOL FillRavAddress(void* pBase);          void DoRelocation(void* pNewBase);          int GetAlignedSize(int Origin, int Alignment);     private:          ProcDllMain pDllMain;     private:          DWORD pImageBase;          PIMAGE_DOS_HEADER pDosHeader;          PIMAGE_NT_HEADERS pNTHeader;          PIMAGE_SECTION_HEADER pSectionHeader; }; CMemLoadDll::CMemLoadDll() {     isLoadOk = FALSE;    pImageBase = NULL;    pDllMain = NULL; } CMemLoadDll::~CMemLoadDll() {     if(isLoadOk)    {       ASSERT(pImageBase != NULL);          ASSERT(pDllMain != NULL);         //脫鉤,準備解除安裝dll          pDllMain((HINSTANCE)pImageBase,DLL_PROCESS_DETACH,0);          VirtualFree((LPVOID)pImageBase, 0, MEM_RELEASE);    } } //MemLoadLibrary函式從記憶體緩衝區資料中載入一個dll到當前程序的地址空間,預設位置0×10000000 //返回值: 成功返回TRUE , 失敗返回FALSE //lpFileData: 存放dll檔案資料的緩衝區 //DataLength: 緩衝區中資料的總長度 BOOL CMemLoadDll::MemLoadLibrary(void* lpFileData, int DataLength) {     if(pImageBase != NULL)     {         return FALSE; //已經載入一個dll,還沒有釋放,不能載入新的dll     } //檢查資料有效性,並初始化    if(!CheckDataValide(lpFileData, DataLength))return FALSE;    //計算所需的載入空間    int ImageSize = CalcTotalImageSize();    if(ImageSize == 0) return FALSE;     // 分配虛擬記憶體     void *pMemoryAddress = VirtualAlloc((LPVOID)0×10000000, ImageSize,             MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);     if(pMemoryAddress == NULL)       return FALSE;    else    {         CopyDllDatas(pMemoryAddress, lpFileData); //複製dll資料,並對齊每個段         //重定位資訊         if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress >0                 && pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size>0)         {             DoRelocation(pMemoryAddress);         }         //填充引入地址表         if(!FillRavAddress(pMemoryAddress)) //修正引入地址表失敗          {              VirtualFree(pMemoryAddress,0,MEM_RELEASE);             return FALSE;          }          //修改頁屬性。應該根據每個頁的屬性單獨設定其對應記憶體頁的屬性。這裡簡化一下。          //統一設定成一個屬性PAGE_EXECUTE_READWRITE          unsigned long old;          VirtualProtect(pMemoryAddress, ImageSize, PAGE_EXECUTE_READWRITE,&old);    }    //修正基地址    pNTHeader->OptionalHeader.ImageBase = (DWORD)pMemoryAddress;    //接下來要呼叫一下dll的入口函式,做初始化工作。     pDllMain = (ProcDllMain)(pNTHeader->OptionalHeader.AddressOfEntryPoint +(DWORD) pMemoryAddress);     BOOL InitResult = pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_ATTACH,0);    if(!InitResult) //初始化失敗    {         pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_DETACH,0);          VirtualFree(pMemoryAddress,0,MEM_RELEASE);          pDllMain = NULL;          return FALSE;    }     isLoadOk = TRUE;    pImageBase = (DWORD)pMemoryAddress;    return TRUE; } //MemGetProcAddress函式從dll中獲取指定函式的地址 //返回值: 成功返回函式地址 , 失敗返回NULL //lpProcName: 要查詢函式的名字或者序號 FARPROC CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName) {     if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 ||             pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)         return NULL;     if(!isLoadOk) return NULL;     DWORD OffsetStart = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;     DWORD Size = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;     PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pImageBase + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);     int iBase = pExport->Base;    int iNumberOfFunctions = pExport->NumberOfFunctions;    int iNumberOfNames = pExport->NumberOfNames; //<= iNumberOfFunctions     LPDWORD pAddressOfFunctions = (LPDWORD)(pExport->AddressOfFunctions + pImageBase);    LPWORD pAddressOfOrdinals = (LPWORD)(pExport->AddressOfNameOrdinals + pImageBase);    LPDWORD pAddressOfNames = (LPDWORD)(pExport->AddressOfNames + pImageBase);     int iOrdinal = -1;     if(((DWORD)lpProcName & 0xFFFF0000) == 0) //IT IS A ORDINAL!     {         iOrdinal = (DWORD)lpProcName & 0×0000FFFF - iBase;    }    else //use name {         int iFound = -1;         for(int i=0;i<iNumberOfNames;i++){ // 原文此處為 for(int i=0;i             char* pName= (char* )(pAddressOfNames[i] + pImageBase);             if(strcmp(pName, lpProcName) == 0){                 iFound = i; break;             }         }         if(iFound >= 0)          {             iOrdinal = (int)(pAddressOfOrdinals[iFound]);          }    }     if(iOrdinal < 0 || iOrdinal >= iNumberOfFunctions )       return NULL;    else    {       DWORD pFunctionOffset = pAddressOfFunctions[iOrdinal];         if(pFunctionOffset > OffsetStart && pFunctionOffset < (OffsetStart+Size))//maybe Export Forwarding             return NULL;         else             return (FARPROC)(pFunctionOffset + pImageBase);     } }

// 重定向PE用到的地址 void CMemLoadDll::DoRelocation( void *NewBase) {    /* 重定位表的結構:      // DWORD sectionAddress, DWORD size (包括本節需要重定位的資料)      // 例如 1000節需要修正5個重定位資料的話,重定位表的資料是      // 00 10 00 00 14 00 00 00 xxxx xxxx xxxx xxxx xxxx 0000      // ———– ———– —-      // 給出節的偏移 總尺寸=8+6*2 需要修正的地址 用於對齊4位元組      // 重定位表是若干個相連,如果address 和 size都是0 表示結束      // 需要修正的地址是12位的,高4位是形態字,intel cpu下是3      */      //假設NewBase是0×600000,而檔案中設定的預設ImageBase是0×400000,則修正偏移量就是0×200000      DWORD Delta = (DWORD)NewBase - pNTHeader->OptionalHeader.ImageBase;      //注意重定位表的位置可能和硬碟檔案中的偏移地址不同,應該使用載入後的地址      PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)((unsigned long)NewBase             + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);      while((pLoc->VirtualAddress + pLoc->SizeOfBlock) != 0) //開始掃描重定位表     {         WORD *pLocData = (WORD *)((int)pLoc + sizeof(IMAGE_BASE_RELOCATION));         //計算本節需要修正的重定位項(地址)的數目          int NumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))/sizeof(WORD);         for( int i=0 ; i < NumberOfReloc; i++)         {             if( (DWORD)(pLocData[i] & 0xF000) == 0×00003000) //這是一個需要修正的地址             {                  // 舉例:                  // pLoc->VirtualAddress = 0×1000;                  // pLocData[i] = 0×313E; 表示本節偏移地址0×13E處需要修正                  // 因此 pAddress = 基地址 + 0×113E                  // 裡面的內容是 A1 ( 0c d4 02 10) 彙編程式碼是: mov eax , [1002d40c]                  // 需要修正1002d40c這個地址                  DWORD * pAddress = (DWORD *)((unsigned long)NewBase + pLoc->VirtualAddress + (pLocData[i] & 0×0FFF));                  *pAddress += Delta;             }         }         //轉移到下一個節進行處理         pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock);     } } //填充引入地址表 BOOL CMemLoadDll::FillRavAddress(void *pImageBase) {     // 引入表實際上是一個 IMAGE_IMPORT_DESCRIPTOR 結構陣列,全部是0表示結束      // 陣列定義如下:      //      // DWORD OriginalFirstThunk; // 0表示結束,否則指向未繫結的IAT結構陣列      // DWORD TimeDateStamp;      // DWORD ForwarderChain; // -1 if no forwarders      // DWORD Name; // 給出dll的名字      // DWORD FirstThunk; // 指向IAT結構陣列的地址(繫結後,這些IAT裡面就是實際的函式地址) unsigned long Offset = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress ; if(Offset == 0) return TRUE; //No Import Table     PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned long) pImageBase + Offset);     while(pID->Characteristics != 0 )     {       PIMAGE_THUNK_DATA pRealIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->FirstThunk);         PIMAGE_THUNK_DATA pOriginalIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->OriginalFirstThunk);         //獲取dll的名字         char buf[256]; //dll name;         BYTE* pName = (BYTE*)((unsigned long)pImageBase + pID->Name);          for(int i=0;i<256;i++)          {              if(pName[i] == 0)break;              buf[i] = pName[i];          }          HMODULE hDll = GetModuleHandle(buf);          if(hDll == NULL) {             hDll = LoadLibrary (buf);             if (hDll == NULL)                  return FALSE; //NOT FOUND DLL          }          //獲取DLL中每個匯出函式的地址,填入IAT          //每個IAT結構是 :          // union { PBYTE ForwarderString;          // PDWORD Function;          // DWORD Ordinal;          // PIMAGE_IMPORT_BY_NAME AddressOfData;          // } u1;          // 長度是一個DWORD ,正好容納一個地址。         for(i=0; ;i++)         {              if(pOriginalIAT[i].u1.Function == 0)break;              FARPROC lpFunction = NULL;              if(pOriginalIAT[i].u1.Ordinal & IMAGE_ORDINAL_FLAG) //這裡的值給出的是匯出序號              {                  lpFunction = GetProcAddress(hDll, (LPCSTR)(pOriginalIAT[i].u1.Ordinal & 0×0000FFFF));              }              else //按照名字匯入              {                  //獲取此IAT項所描述的函式名稱                  PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)                  ((DWORD)pImageBase + (DWORD)(pOriginalIAT[i].u1.AddressOfData));                  // if(pByName->Hint !=0)                  // lpFunction = GetProcAddress(hDll, (LPCSTR)pByName->Hint);                  // else                  lpFunction = GetProcAddress(hDll, (char *)pByName->Name);              }             if(lpFunction != NULL) //找到了!                  pRealIAT[i].u1.Function = (PDWORD) lpFunction;            else                  return FALSE;          }         //move to next          pID = (PIMAGE_IMPORT_DESCRIPTOR)( (DWORD)pID + sizeof(IMAGE_IMPORT_DESCRIPTOR));     }     return TRUE; } //CheckDataValide函式用於檢查緩衝區中的資料是否有效的dll檔案 //返回值: 是一個可執行的dll則返回TRUE,否則返回FALSE。 //lpFileData: 存放dll資料的記憶體緩衝區 //DataLength: dll檔案的長度 BOOL CMemLoadDll::CheckDataValide(void* lpFileData, int DataLength) {    //檢查長度    if(DataLength < sizeof(IMAGE_DOS_HEADER)) return FALSE;     pDosHeader = (PIMAGE_DOS_HEADER)lpFileData; // DOS頭    //檢查dos頭的標記    if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; //0×5A4D : MZ     //檢查長度     if((DWORD)DataLength < (pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)) ) return FALSE;     //取得pe頭     pNTHeader = (PIMAGE_NT_HEADERS)( (unsigned long)lpFileData + pDosHeader->e_lfanew); // PE頭     //檢查pe頭的合法性    if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return FALSE; //0×00004550: PE00     if((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) //0×2000: File is a DLL          return FALSE;      if((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0) //0×0002: 指出檔案可以執行          return FALSE;      if(pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER))          return FALSE;     //取得節表(段表)     pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));    //驗證每個節表的空間     for(int i=0; i< pNTHeader->FileHeader.NumberOfSections; i++)    {       if((pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData) > (DWORD)DataLength)return FALSE;     }     return TRUE; } //計算對齊邊界 int CMemLoadDll::GetAlignedSize(int Origin, int Alignment) {     return (Origin + Alignment - 1) / Alignment * Alignment; } //計算整個dll映像檔案的尺寸 int CMemLoadDll::CalcTotalImageSize() {    int Size;    if(pNTHeader == NULL)return 0;    int nAlign = pNTHeader->OptionalHeader.SectionAlignment; //段對齊位元組數     // 計算所有頭的尺寸。包括dos, coff, pe頭 和 段表的大小    Size = GetAlignedSize(pNTHeader->OptionalHeader.SizeOfHeaders, nAlign);    // 計算所有節的大小    for(int i=0; i < pNTHeader->FileHeader.NumberOfSections; ++i)    {         //得到該節的大小         int CodeSize = pSectionHeader[i].Misc.VirtualSize ;          int LoadSize = pSectionHeader[i].SizeOfRawData;          int MaxSize = (LoadSize > CodeSize)?(LoadSize):(CodeSize);         int SectionSize = GetAlignedSize(pSectionHeader[i].VirtualAddress + MaxSize, nAlign);          if(Size < SectionSize)              Size = SectionSize; //Use the Max;     }    return Size; }

//CopyDllDatas函式將dll資料複製到指定記憶體區域,並對齊所有節 //pSrc: 存放dll資料的原始緩衝區 //pDest:目標記憶體地址 void CMemLoadDll::CopyDllDatas(void* pDest, void* pSrc) {     // 計算需要複製的PE頭+段表字節數     int HeaderSize = pNTHeader->OptionalHeader.SizeOfHeaders;    int SectionSize = pNTHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);    int MoveSize = HeaderSize + SectionSize;    //複製頭和段資訊    memmove(pDest, pSrc, MoveSize);     //複製每個節     for(int i=0; i < pNTHeader->FileHeader.NumberOfSections; ++i)    {       if(pSectionHeader[i].VirtualAddress == 0 || pSectionHeader[i].SizeOfRawData == 0)continue;         // 定位該節在記憶體中的位置         void *pSectionAddress = (void *)((unsigned long)pDest + pSectionHeader[i].VirtualAddress);         // 複製段資料到虛擬記憶體         memmove((void *)pSectionAddress,                 (void *)((DWORD)pSrc + pSectionHeader[i].PointerToRawData),                 pSectionHeader[i].SizeOfRawData);     }     //修正指標,指向新分配的記憶體    //新的dos頭    pDosHeader = (PIMAGE_DOS_HEADER)pDest;    //新的pe頭地址    pNTHeader = (PIMAGE_NT_HEADERS)((int)pDest + (pDosHeader->e_lfanew));    //新的節表地址    pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));    return ; }

相關推薦

記憶體載入DLL (正版)

轉載地址: http://www.freshbug.com/archives/39 ====================================================== 程式使用動態庫DLL一般分為隱式載入和顯式載入兩種,分別對應兩種連

Windows注入與攔截(6) -- 記憶體載入DLL

Windows提供的API(LoadLibrary, LoadLibraryEx)只支援從檔案系統上載入DLL檔案,我們無法使用這些API從記憶體中載入DLL。 但是有些時候,我們的確需要從記憶體中載入DLL,比如: 對釋出的檔案數量有限制。我們可以將DL

cocos2dx載入圖片資源的方法,和記憶體獲取已經載入的圖片資源的方法 以及釋放問題

遊戲中通常需要將常用的資源如:聲音,圖片,plist檔案,提前載入進記憶體,以加快遊戲的流暢度 1.預載入聲音: SimpleAudioEngine::getInstance()->preloadBackgroundMusic("boom.mp3"); 載入之後就可以

啟動伺服器時將配置引數資料庫載入到快取

最近做專案,碰到這樣的需求:在伺服器啟動的時候從資料庫讀取引數,將引數儲存到記憶體快取中 由於使用的是spring的自動注入方式,一開始用@component註解在啟動的時候載入查詢配置引數的bean,由於bean中要用到其他bean來查詢,但此時都為null 查詢相關資料,發現@PostC

Springboot 類不能使用@Value註解yml載入

對於下面的類,使用了@Value,但是不能從yml中讀取值,怎麼辦? 帶有@Value標籤類: package com.itmuch.cloud; import org.springframework.beans.factory.annotation.Value; import org

OllyDbg IDA pro強強聯合 OllyDbg載入IDA Pro輸出的map資訊檔案,帶符號資訊除錯

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

HiveHDFS載入資料

建表         以手機流量資訊為例插入30w行資料   create table flow(id string,phonenum string,mac string,ip string,num1 int,num2 int,up in

Win32資源載入PNG圖片,然後建立GDI+的Image物件

void LoadPNGFromStaticRes(HMODULE hModule, UINT nResId, Image** ppImg) { HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(nResId), TEXT("PNG"))

Opengl如何磁碟載入圖片生成紋理物件

IplImage* imgBGR = cvLoadImage(szPathName); IplImage* imgRGB = cvCreateImage(cvGetSize(imgBGR), 8, 3); cvCvtColor(imgBGR,imgRGB,CV_BGR2RG

TinyXml記憶體解析,並儲存到記憶體

 網上介紹TinyXml的例子很多,所以不用再介紹了。最近也需要解析XML,從網上找了一下,發現TinyXml是最合適的。 TinyXml同時支援Windows和Linux平臺,而且它很小巧,功能也全面,包括對XML各種特性的操作。 不過美中不足,雖然網上的應用例項很多,

iOS-設定UITableViewCell之間的間距, xib載入另一個xib

重新設定的UITableViewCellframe。 程式碼如下: #import "MyViewCell.h" @implementation MyViewCell - (void)awakeFromNib { [super

ffmpeg 記憶體讀取資料(或將資料輸出到記憶體

原文見雷大神部落格:http://blog.csdn.net/leixiaohua1020/article/details/12980423 更新記錄(2014.7.24): 1.為了使本文更通俗易懂,更新了部分內容,將例子改為從記憶體中開啟。 2.增加了將資料輸出

FFMPEG記憶體操作(二)記憶體讀取數及資料格式的轉換

   相關部落格列表:     在雷神的《最簡單的基於FFmpeg的記憶體讀寫例子(記憶體播放器)》中,它是設計回撥函式從輸入檔案中讀取資料。與FFMPEG 官方給出的avio_reading.c不同的是,雷神給的例子是當需要資料的時候,回撥函式才去從輸入檔案讀取資料,而av

【Qt OpenGL教程】25:變形和檔案載入3D物體

第25課:變形和從檔案中載入3D物體 (參照NeHe) 這次教程中,我們將學會如何從檔案中載入3D模型,並且平滑的從一個模型變形為另一個模型。在這一課裡,我們將介紹如何實現模型的變形過程,這將會是效果很棒的一課! 程式執行時效果如下: 下面進入教程: 我們這次將在第

資源載入jpg, png到GDI+ Image

從資源中載入jpg和png檔案, 貌似不應該是個大問題, 一google結果一大堆, 卻有兩個陷阱,trap啊1, 是Bitmap(RT_BITMAP)型別的圖片無法載入, RT_BITMAP是預定義型別, 資源裡面沒有bmp檔案的頭, SizeofResource 的返

關於在struts2利用jquery如何動態資料庫載入圖片並顯示

本來我一開始總是在想,我該如何利用jquery的$ajax()方法來動態獲得圖片然後在改變現有img標籤圖片的內容呢? 查了好久,但一直沒發現滿意的,突然我從最基本的獲得圖片得到靈感。 首先,我是直接把圖片以blob型別存到資料庫中的,利用hibernate配置好的。 只

delphi -- GDi+ Delphi如何讓 TGPImage 直接載入圖片

方法一: Delphi 的 TBitmap 類可以方便地操作流, 如果能讓 TGPImage 和 TBitmap 溝通起來就好了; TGPImage 有一個子類 TGPBitmap 可以和 TBitmap 溝通... ok 了! 程式碼如下: uses GDIPOBJ, GDIPAPI; procedure

CImage 記憶體讀取影象

CImage 的CImage::Load( IStream* pStream) 從記憶體中讀取影象時,需要提供實現了IStream的物件。一般都是採用CreateStreamOnHGlobal建立IStream物件,但這需要重新分配記憶體,再將記憶體中影象複製到

hive 自定定義函式 hdfs載入jar

1、編寫函式 package com.example.hive.udf;   import org.apache.hadoop.hive.ql.exec.UDF;   import org.apache.hadoop.io.Text;   public final cl

openssl記憶體讀取RSA公鑰

背景:近期需要在專案中進行RSA簽名驗證,廠商會給出pem格式的RSA公鑰。在以往專案中使用openssl讀取RSA公鑰時基本都是從pem檔案中讀取,基本沒什麼問題,可最近由於專案需要需要從資料庫中讀取RSA公鑰,經查資料發現openssl提供了bio介面以支援各種形式的祕