從記憶體中載入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)
二、要說明的幾個問題
(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 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Hive從HDFS中載入資料
建表 以手機流量資訊為例插入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介面以支援各種形式的祕