關于《加密與解密》的讀后感----對dump脫殼的一點思考

偶然翻了一下手機日歷,原來今天是夏至啊,時間過的真快。ISCC的比賽已經持續了2個多月了,我也跟著比賽的那些題目學了2個月.......雖然過程很辛苦,但感覺還是很幸運的,能在大三的時候遇到ISCC,不管怎樣,對我來說都會是一個很好的鍛煉機會。

在做綜合關的逆向破解的題目,遇到了很多蛋疼的問題,磕磕碰碰把《加密與解密》看完了。還是老習慣,把這1個多星期以后學到的,想到的做一個總結,希望能有一個思想上的提高,也希望能對其他人有一個借鑒的效果吧。這里想起lenus大神的一句話:

Hacker的精神里面除了學習(learn)的第一精神以外,還應該是共享(share)的精神。

好,廢話不多說,開始吧。

1. 逆向?破解?

一開始接觸逆向,我直接是使用的一些加殼脫殼工具,都是鼠標點一下就出來了,這段時間深入了解了逆向的原理之后,我感覺逆向是一個綜合性很強的技術,中間還需要windows編程,PE結果,匯編等知識。

對于脫殼,我的理解是:脫殼不意味著它字面上的意思,把外殼直接拿掉,然后就扔掉(像吃水果一樣),脫殼本質上是一個高級的crack技術,也就是繞過破解。

這是一張關于殼的圖示:

而我們不管用lordPE, procDump, 還是手工脫殼也好,其實本質上就是要改變程序的執行流,讓程序跳過頭部(實際中不一定是頭部,這里只是邏輯上的)的一段殼代碼,而直接來到原程序的入口代碼處,也就是常說的OEP,那問題就來了,那脫殼就這么簡單?

當然不是,我個人感覺,脫殼并不僅僅是改變程序執行流這么簡單,因為被加過殼的程序往往IAT,重定位表,節區都被加密,壓縮處理過了。如果你直接強行跳轉過來,程序也是不能執行的。所以,網上常說的找OEP的原理就在這里。為什么要找OEP呢?其實本質上來說,這并不是最重要的,不要被這些形式上的東西框住了思維,我們從原理上思考,一個程序要運行,無非需要幾種東西:

1. 可以執行的機器碼

2. IAT

3. 重定位表(DLL)

4. 資源代碼(不太重要就是了,因為并不影響逆向的結果)

轉換了思維之后,我就豁然開朗了,我們要做的就是找到代碼中的某一行,恰好已經全部完成了代碼的解壓縮和解密,IAT的重寫,重定位表的重寫,做好這些事后,我們就可以dump下來了,至于是不是OEP其實不是很重要,所謂的找OEP是因為,一般情況下在OEP的時候恰好都滿足了上面的條件,而且也比較好找到,所以,OEP是dump的首選,如果你理解了原理,這么做也沒有錯。

?

第二點就是:為什么要用ImportREC來重建IAT表呢?它的原理又是什么呢?

這里首先要從加殼的原理說起,不管你是UPX壓縮殼,ASProtect,穿山甲加密殼,還是VMP虛擬機殼...基本上來說都會破壞原本的IAT,將原本的IAT移到一個新的節區里面并加密等處理,然后構建自己的IAT表,并修改PE頭。為什么要這么做呢?

因為殼必須保證它能調用到自己需要的函數,但是原程序并不能保證這點,所以殼必須自己構造自己的IAT三劍客:

調用LoadLibrary將dll文件映射到調用進程的地址空間中
調用GetModualHandle獲得dll模塊句柄
調用GetProcAddress獲取輸入函數的地址

一般殼程序都會構造這三個API,然后重建自己的IAT表,這樣殼程序就能保證調用到自己需要的所有winAPI了。

而我們脫殼的是內存中的映像,即使這時候原程序的IAT已經恢復過來了(解壓縮,解密),但是位置已經變了,原程序無法直接調用了。而ImportREC的作用就是找到這里API對用RVA,然后在一段空的地方重建這些結構,逆向構造出一個IAT出來,然后修改PE頭,完成IAT的修復。明白了原理,自己手工修復IAT也是一樣的。

修復重定位表和資源表的原理也是一樣的,就不在詳述了。

?

下面,我們通過一個dump實例的編程過程來深刻的理解一個dump的思想。

接下來的程序是我們對看雪上的一篇帖子的學習筆記,是lenus大神的脫殼教程,我這里就當是做一個學習筆記。

?

一:dump小程序的目標

對于dump來說,他的英文翻譯就是“轉存”。也就是說把內存中或者其他的輸入轉存到另一個位置,當然對于我們現在說的dump就是把內存中運行的PE進程的數據,從內存中抓取出來,然后在用文件的形式保存下來。

根據上面的分析我們基本上得到了一個這樣的思維。Dump程序要做的事分幾個基本的步驟:

1.??在系統中找到目標進程
2.??在進程中確定進程的大小imagesize
3.??把進程中的數據保存到文件

?

Code:

resource.h

#define ID_FLESH??????????????????????? 2
#define IDD_DIALOG1???????????????????? 101
#define ICO_MAIN??????????????????????? 103
#define IDC_PROCESS???????????????????? 1028
#define IDC_CORRECT???????????????????? 1033

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE??????? 105
#define _APS_NEXT_COMMAND_VALUE???????? 40001
#define _APS_NEXT_CONTROL_VALUE???????? 1034
#define _APS_NEXT_SYMED_VALUE?????????? 101
#endif
#endif

?

#include <windows.h>
#include <tlhelp32.h>
#include "resource.h"

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL GetProcessListFunc(HWND hDlg,HWND hWindList); // 列出各個進程的函數,刷新時也用到
LPCTSTR SaveAsFunc(HWND hDlg);???????????????????? //通用對話框函數
BOOL DumpFunc(HWND hDlg,HWND hWindList);?????????? // 主要的dump函數,調用其他的小功能函數實現其功能
BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem);??? //生成dump文件函數,把dump的東西寫到磁盤上
HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess);????? //讀取目標進程空間,放置到本空間申請的堆中
int GetSizeOfImage(HWND hDlg,DWORD IDProcess);?????? // 獲取pe文件的SizeOfImage
BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess);??????? //檢查pe文件的完整性
BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList);?????????????????? //糾正文件的大小
LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess);//獲取目標exe的絕對路徑
BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name);//修改文件的節表使其RA=RVA ,RS=RVS
BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name);?? //把原來PE文件的頭部復制到dump文件中

全局變量/
int sizeoffile=0;
int sizeofimage=0;????????????? //當使用了CorrectSizeFunc后這個有了具體數值,就不需要再次獲取了
int BaseAddress=0x400000;???? ?
DWORD ID=0;?????????????????? //這個是用來控制進程的切換的
///

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
?????????????????? ??? ??? ??? ??? ??? ??? ?PSTR szCmdLine, int iCmdShow)
{
?? ?DialogBoxParam (hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc,(LPARAM)hInstance);
?? ?return TRUE;
}

BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{
?? ???? static HINSTANCE hInstance;
?? ??? ?static HWND hWindList;

??? ??? ?switch (message)
??? ??? ?{
?? ??? ?case??? WM_CLOSE:
?? ??? ??? ???? EndDialog (hDlg, 0) ;
????????????? ??? ?return TRUE ;
??? ??? ?case ?? ?WM_INITDIALOG :
?? ??? ??? ???? hInstance = (HINSTANCE) lParam;
?? ??? ??? ???? SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM)LoadIcon(hInstance,MAKEINTRESOURCE(ICO_MAIN)));
?? ??? ??? ??? ?hWindList = GetDlgItem(hDlg,IDC_PROCESS);
?? ??? ??? ??? ?if(!GetProcessListFunc(hDlg,hWindList))
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
?? ??? ??? ??? ??? ?EndDialog(hDlg,0);?? ??? ??? ?
?? ??? ??? ??? ?}
?? ??? ??? ??? ?return TRUE ;
?? ??? ??? ??? ?
??? ??? ?case ?? ?WM_COMMAND :
???????? ??? ??? ?switch (LOWORD (wParam))
???????? ??? ??? ?{
???????? ??? ??? ??? ?case ?? ?IDOK :
????????????? ??? ??? ??? ??? ??? ?DumpFunc(hDlg,hWindList);
????????????? ??? ??? ??? ??? ??? ?return TRUE ;
?? ??? ??? ??? ??? ?case?? ?ID_FLESH:
?? ??? ??? ??? ??? ??? ??? ??? ?if(!GetProcessListFunc(hDlg,hWindList))
?? ??? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
?? ??? ??? ??? ??? ??? ??? ??? ??? ?EndDialog(hDlg,0);?? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ??? ??? ?return TRUE;

?? ??? ??? ??? ??? ?case?? ?IDC_CORRECT:
?? ??? ??? ??? ??? ??? ???????? if(!CorrectSizeFunc(hDlg,hWindList))
?? ??? ??? ??? ??? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("The correct size function is faile..."),TEXT("Fail..."),MB_OK | MB_ICONWARNING);
?? ??? ??? ??? ??? ??? ???????? return TRUE;


?? ??? ??? ??? ?}


???????? ??? ??? ?break ;
???? }
??? ?return FALSE ;
}


BOOL GetProcessListFunc(HWND hDlg,HWND hWindList)
{
?? ?//此函數是進程列表的作用,在此不作過多介紹
?? ?HANDLE hProcessSnap = NULL;
?? ?PROCESSENTRY32 pe32?? = {0};
?? ?SendMessage(hWindList,LB_RESETCONTENT,0,0);
?? ?pe32.dwSize = sizeof(PROCESSENTRY32);


?? ?hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
?? ?if (hProcessSnap == INVALID_HANDLE_VALUE) ?
?? ?return FALSE;
?? ?

?? ?if (!Process32First(hProcessSnap, &pe32))
?? ?{
?? ??? CloseHandle (hProcessSnap);
?? ??? return FALSE;
?? ?}
?? ?do
?? ?{
?? ??? ?WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_ADDSTRING,0,(LPARAM)pe32.szExeFile);
?? ??? ?SendMessage(hWindList,LB_SETITEMDATA,tmp,(LPARAM)pe32.th32ProcessID);
?? ?}
?? ?while (Process32Next(hProcessSnap, &pe32));
?? ?CloseHandle (hProcessSnap);
?? ?
??? return TRUE;

}

BOOL DumpFunc(HWND hDlg,HWND hWindList)
{
?? ?HGLOBAL hMem;
??? LPCTSTR Dump_Name=NULL;??????????????????? //感覺有點問題,這個只是指針沒有開辟足夠的空間。
????????????????????????????????????????????? //從數組(規定好的)到指針(未規定好的)就可以,
?? ?????????????????????????????????????????? //從指針到數組就不可以
??? WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
?? ?if (tmp==LB_ERR)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //獲得此列單里面的進程ID
?? ?ID=IDProcess;
?? ?hMem=ReadProcess(hDlg,IDProcess);
?? ?if(hMem)?????????????????????????????????? //如果返回的hMen不正確說明沒有正確的申請到空間
?? ?{
?? ??? ?if(sizeoffile!=0)???????????????????? //沒有大小的dump 是沒有意義的
?? ??? ?{
?? ??? ??? ?Dump_Name=SaveAsFunc(hDlg);?????? //要保存的文件名
?? ??? ??? ?if(Dump_Name)???????????????????? //如果得到的文件名是空就不繼續執行
?? ??? ??? ?{
?? ??? ??? ??? ?CreateDumpFile(hDlg,Dump_Name,hMem); //把數據寫入文件中
?? ??? ??? ??? ?GlobalFree(hMem);??????????????????? //資源是可貴的,釋放空間
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ??? ?
?? ??? ?return TRUE;
}

LPCTSTR SaveAsFunc(HWND hDlg)
{
?? ?//獲取你要保存的文件名,默認為dumped.exe
?? ?HANDLE hFile;
??? static?? ?char szFileName[MAX_PATH]="dumped";
?? ?OPENFILENAME stOF={0};
??? stOF.hwndOwner=hDlg;
?? ?stOF.lStructSize=sizeof(stOF);
?? ?stOF.lpstrFilter="*.*";
?? ?stOF.lpstrDefExt="exe";
?? ?stOF.nMaxFile=MAX_PATH;?? ?
?? ?stOF.lpstrFile=szFileName;
?? ?if(!GetSaveFileName(&stOF))
?? ??? ??? ?return FALSE;
??? char szBuffer[100];
?? ?char szMsg[]="%s 已存在。要替換它嗎?";
?? ?wsprintf(szBuffer,szMsg,szFileName);
?? ?hFile=CreateFile(szFileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile != INVALID_HANDLE_VALUE)
?? ?{
?? ??? if(IDNO==MessageBox(hDlg,szBuffer,TEXT("另存為"),MB_YESNO | MB_ICONWARNING))
?? ??? {
?? ??? ??? CloseHandle(hFile);
?? ??? ??? return FALSE;
?? ??? }
?? ?}

?? ?CloseHandle(hFile);
?? ?return szFileName;
}

BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem)
{
?? ?//創建一個新的dump文件
?? ?HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{

?? ??? ?MessageBox(hDlg,TEXT("Maybe you have alreadly had a this name file:("),
?? ??? ??? ??????? TEXT("Can't create a file"),MB_OK | MB_ICONWARNING);
?? ??? ?GlobalFree(hMem);
?? ??? ?return FALSE;
?? ?}
?? ?int NumberOfBytesWritten;
?? ?WriteFile(hFile,hMem,sizeoffile,(LPDWORD)&NumberOfBytesWritten,NULL);??? //注意這個函數第三個參數是必要的!
??? CloseHandle(hFile);
?? ?if(!CopyThePEHead(hDlg,Dump_Name))
?? ?{
?? ??? ?//復制PE頭
?? ??? ?MessageBox(hDlg,TEXT("復制PE頭失敗了"),TEXT("失敗了"),MB_OK | MB_ICONWARNING);
?? ?}?? ?
?? ?if(!ModifySectionFunc(hDlg,Dump_Name))
?? ?{
?? ??? ?//節表對齊
?? ??? ?MessageBox(hDlg,TEXT("修改節表失敗了"),TEXT("失敗了"),MB_OK | MB_ICONWARNING);
?? ?}
?? ?MessageBox(hDlg,TEXT("文件已經dump成功"),TEXT("Lenus'ExeDump"),MB_OK | MB_ICONINFORMATION);//勝利的號角!?? ?
?? ?return TRUE;
}




HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess)
{
?? ?//此函數是讀取目標進程的空間,并把他寫入到自己內存空間里面的一個內存塊中
?? ?
?? ?HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,IDProcess);//使用上面獲得的進程id
?? ?if(!hProcess)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't open the process:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?if(sizeofimage==0 || ID!=IDProcess)??????????????? ?
?? ?//當更換當前的進程或者沒有使用修正的功能的時候需要重新的獲取
?? ?//由于不知道在更換選項的時候會發出什么消息,所以只能這么干 so foolish!!
?? ?{
?? ??? ??? ?sizeofimage=GetSizeOfImage(hDlg,IDProcess);
?? ?}
?? ?
?? ?if(!sizeofimage)
?? ?{
?? ??? ?return FALSE;
?? ?}
??? //為了以防萬一,讓sizeofimage增加一個文件對齊度。

?? ?if(!(sizeofimage%0x1000))????????????????????????? //如果是文件對齊度的整數倍的時候就不處理
?? ??? ?sizeoffile=sizeofimage;
?? ?else
?? ??? ?sizeoffile=(sizeofimage/0x1000+1)*0x1000;???? //如果不是就增加一個文件對齊度
?? ?
??? //申請一個文件空間的內存塊
?? ?static HGLOBAL hMem=0;
?? ?hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeoffile);
?? ?if(!hMem)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I think i have enough space to get!:("),TEXT("Wrong!!!"),MB_OK | MB_ICONSTOP);
?? ??? ?return FALSE;
?? ?}
?? ?//將這個pe文件在內存中的大小全部讀到申請的塊中
?? ?
?? ?DWORD NumberOfBytesReadorWrite;
?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,hMem,sizeofimage,&NumberOfBytesReadorWrite))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the process:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}

?? ?CloseHandle(hProcess);?? //有開始就,有關閉
?? ?Sleep(200);?????????????? //等待一會
?? ?return hMem;

}

int GetSizeOfImage(HWND hDlg,DWORD IDProcess)
{
?? ?//這個函數的作用是獲取SizeOfImage的數值
?? ?//當函數執行失敗返回的是0
?? ?//成功返回的是非0
?? ?HANDLE hModuleSnap = NULL;
?? ?MODULEENTRY32 stModE? = {0};
?? ?stModE.dwSize = sizeof(MODULEENTRY32);
?? ?hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);? //快照,對本進程中所有的模塊進行snap

?? ?if (hModuleSnap == INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ???? return FALSE;??? //返回0
?? ?}
?? ?if (!Module32First(hModuleSnap, &stModE))
?? ?{
?? ??? MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ??? CloseHandle (hModuleSnap);
?? ??? return FALSE;
?? ?}
?? ?CloseHandle (hModuleSnap);
?? ?return stModE.modBaseSize;//初始化為0
}

BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess)
{
?? ?//檢查pe文件,檢查兩個標志
?? ?//如果不是pe文件那么會迫使GetSizeOfImage直接返回
?? ?//下面不是重點,所以不介紹了
?? ?int BaseAddress=0x400000;
?? ?IMAGE_DOS_HEADER DosHead;
?? ?_IMAGE_NT_HEADERS NtHead;

?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,&DosHead.e_magic,2,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the IMAGE_DOS_SIGNATURE:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}

?? ?if(DosHead.e_magic != IMAGE_DOS_SIGNATURE)
?? ?{
?? ??? ?return FALSE;
?? ?}


?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+0x3c),&DosHead.e_lfanew,4,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}

?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+DosHead.e_lfanew),&NtHead.Signature,4,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}

?? ?if(NtHead.Signature != IMAGE_NT_SIGNATURE)
?? ?{
?? ??? ?return FALSE;
?? ?}
?? ?return TRUE;?? ?

}


BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList)
{
?? ?//函數能獲取文件的PE頭部的SizeOfImage,作為正確的SizeOfImage
??? LPCTSTR File_Name=NULL;????????????????? ?
??? WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
?? ?if (tmp==LB_ERR)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //獲得此列單里面的進程ID
?? ?ID=IDProcess;//全局變量ID的作用是控制在不同的進程的切換
?? ?File_Name=GetFilePath(hDlg,IDProcess);
?? ?if(!File_Name)
?? ??? ?return FALSE;
??? //打開文件
?? ?HANDLE? hFile;
?? ?hFile=CreateFile(File_Name,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

?? ?if (hFile == INVALID_HANDLE_VALUE )
?? ?{
?? ??? ?return FALSE ;
?? ?}

?? ?//創建文件映射內核對象
?? ?HANDLE hMapping;
?? ?hMapping =?? ?CreateFileMapping (hFile, NULL, PAGE_READONLY,0,0,NULL);
?? ?if (hMapping == NULL )
?? ?{
?? ??? ?CloseHandle (hFile ) ;
?? ??? ?return FALSE;
?? ?}
?? ?//創建文件視圖
?? ?LPVOID ImageBase ;
?? ?ImageBase =MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0) ;
?? ?if (ImageBase == NULL)
?? ?{
?? ??? ?CloseHandle (hMapping) ;
?? ??? ?return FALSE;
?? ?}
??? //下面的代碼就是從文件的PE頭找到SizeOfImage的
?? ?PIMAGE_DOS_HEADER DosHead = NULL ;
?? ?PIMAGE_NT_HEADERS32 pNtHeader = NULL ;
??? PIMAGE_FILE_HEADER pFileHeader = NULL ;
??? PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL ;
??? PIMAGE_SECTION_HEADER pSectionHeader = NULL ;
?? ?DosHead=(PIMAGE_DOS_HEADER)ImageBase;
?? ?pNtHeader = ( PIMAGE_NT_HEADERS32 ) ((DWORD)ImageBase + DosHead->e_lfanew ) ;
?? ?pOptionalHeader = &pNtHeader->OptionalHeader;?? ?
??? sizeofimage=(int)pOptionalHeader->SizeOfImage;
??? //找到了以后,輸出結果
?? ?char szBuffer[100];
?? ?char szMsg[]="原來的image size是:%08X\n修整的image size是:%08X";
?? ?wsprintf(szBuffer,szMsg,GetSizeOfImage(hDlg,IDProcess),sizeofimage);
?? ?MessageBox(hDlg,szBuffer,TEXT("糾正結果"),MB_OK );
??? CloseHandle (hMapping);
??? CloseHandle (hFile) ;
?? ?Sleep(200);
?? ?return TRUE;
}

LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess)
{
?? ?//此函數獲得目標進程的絕對路徑
?? ?//如果獲取失敗返回NULL
?? ?HANDLE hModuleSnap = NULL;
?? ?MODULEENTRY32 a?? = {0};
?? ?a.dwSize = sizeof(MODULEENTRY32);
?? ?hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);? //快照,對本進程中所有的模塊進行snap

?? ?if (hModuleSnap == INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ???? return FALSE;??? //返回0
?? ?}
?? ?if (!Module32First(hModuleSnap, &a))
?? ?{
?? ??? MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ??? CloseHandle (hModuleSnap);
?? ??? return FALSE;
?? ?}
?? ?CloseHandle (hModuleSnap);
?? ?return a.szExePath;

}

BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name)
{
?? ?//此函數的將修改dump下來的exe,使其RA=RVA ,RS=RVS
?? ?//首先是打開dump文件
?? ?HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{

?? ??? ?MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
??? //下面移動到節表前面
?? ?IMAGE_DOS_HEADER myDosHeader;
?? ?DWORD NumberOfBytesReadorWrite;?? ?
?? ?ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
??? IMAGE_FILE_HEADER myNtHeader;
?? ?ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?int nSectionCount;
?? ?nSectionCount = myNtHeader.NumberOfSections;???????????? // 保存Section個數
?? ?// 過了IMAGE_NT_HEADERS結構就是IMAGE_SECTION_HEADER結構數組了,注意是結構數組,有幾個Section該結構就有幾個元素
?? ?// 這里動態開辟NumberOfSections個內存來存儲不同的Section信息
?? ?IMAGE_SECTION_HEADER *pmySectionHeader = (IMAGE_SECTION_HEADER *)calloc(nSectionCount, sizeof(IMAGE_SECTION_HEADER));
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
??? ReadFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER)*nSectionCount,
?? ??? ????? &NumberOfBytesReadorWrite,NULL);
?? ?//移動回到節表的開始,準備寫入
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
?? ?for (int i = 0; i < nSectionCount; i++, pmySectionHeader++)
?? ?{
?? ??? ?//將RA=RVA ,RS=RVS
?? ??? ?pmySectionHeader->SizeOfRawData=pmySectionHeader->Misc.VirtualSize;
?? ??? ?pmySectionHeader->PointerToRawData=pmySectionHeader->VirtualAddress;
?? ??? ?//將修改好的數值寫回
?? ??? ?WriteFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?}
?? ?// 恢復指針
?? ?pmySectionHeader -=nSectionCount;

?? ?if (pmySectionHeader != NULL)????????? // 釋放內存
?? ?{
?? ?? free(pmySectionHeader);
?? ?? pmySectionHeader = NULL;
?? ?}

?? ?// 最后不要忘記關閉文件
?? ?CloseHandle(hFile);
?? ?return TRUE;
}

BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name)
{
??? //此函數的作用是將原來PE文件的PE頭部完整的copy到dump文件中
?? ?HANDLE hFile=CreateFile(GetFilePath(hDlg,ID),GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{

?? ??? ?MessageBox(hDlg,TEXT("I can open the object file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
??? //下面移動到節表前面
?? ?IMAGE_DOS_HEADER myDosHeader;
?? ?DWORD NumberOfBytesReadorWrite;?? ?
?? ?ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
??? IMAGE_FILE_HEADER myNtHeader;
?? ?ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?IMAGE_SECTION_HEADER mySectionHeader;
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
??? ReadFile(hFile,(LPVOID)&mySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
??? SetFilePointer(hFile,NULL,NULL,FILE_BEGIN);
?? ?HGLOBAL hMem=0;
?? ?//讀出節表的第一個文件位置,以確PE頭的大小
?? ?//申請同樣大小的空間
?? ?hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,mySectionHeader.PointerToRawData);
?? ?if(!hMem)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't get the Memory space!"),TEXT("Error!!!"),MB_OK | MB_ICONSTOP);
?? ??? ?return FALSE;
?? ?}
?? ?//將文件中的PE頭部讀取到申請的空間中
?? ?ReadFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
?? ?CloseHandle(hFile);
?? ?//上面是讀///
??? //下面是寫///
?? ?hFile=CreateFile(Dump_Name,GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
?? ?//下面是將空間中的數據寫到dump文件的頭部
?? ?WriteFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
??? CloseHandle(hFile);
?? ?GlobalFree(hMem);
?? ?return TRUE;
}

?

這里:

1. 獲取當前進程列表使用的是:CreateToolhelp32Snapshot,Process32First,?Process32Next 這三個函數,即創建一個進程快照,然后遍歷它,之前做ring3的進程注入的時候用的也是這個技術,原理不是很難理解,本質上就是一個鏈表的操作。

2. 在進程中確定進程的大小imagesize 使用的是從磁盤的文件PE頭中讀取SizeOfImage這個字段來獲取。這里就有一個很有趣的知識點了,我們先從原始的方法開始講。

2.1 用ReadProcessMemory函數從從當前內存的進程映像工具PE格式的偏移獲取到SizeOfImage的值的,這種方法不太可靠,要是加殼程序修改了映像中的PE頭信息,就要出錯了。

2.2 LordPE使用的方法,采用了對Module32Next來獲取dump的進程的基本信息的。

BOOL?WINAPI?Module32First(?
HANDLE?hSnapshot,?//這是先前的CreateToolhelp32Snapshot函數返回的快照
??LPMODULEENTRY32?lpme?//這個是指向MODULEENTRY32結構的指針
);

下面是MUDULEENTRY32結構:
typedef?struct?tagMODULEENTRY32?{?
??DWORD?dwSize;?
??DWORD?th32ModuleID;?
??DWORD?th32ProcessID;?
??DWORD?GlblcntUsage;?
??DWORD?ProccntUsage;?
??BYTE?*modBaseAddr;?
??DWORD?modBaseSize;????//這個是是我們要獲取的關鍵?
??HMODULE?hModule;?
??TCHAR?szModule[MAX_PATH];?
??TCHAR?szExePath[MAX_PATH];?
??DWORD?dwFlags;
}?MODULEENTRY32,?*PMODULEENTRY32,?*LPMODULEENTRY32;

這種方法會受到anti-dump技術的影響。

因為MUDULEENTRY32是從內核中PEB(進程環境塊)獲取的數據。

下面是PEB的結構。
struct???_PEB?(sizeof=488)?
+000?byte?????InheritedAddressSpace?
+001?byte?????ReadImageFileExecOptions?
+002?byte?????BeingDebugged?
+003?byte?????SpareBool?
+004?void?????*Mutant?
+008?void?????*ImageBaseAddress?
+00c?struct???_PEB_LDR_DATA?*Ldr?
+010?struct???_RTL_USER_PROCESS_PARAMETERS?*ProcessParameters?
+014?void?????*SubSystemData?
+018?void?????*ProcessHeap?
+01c?void?????*FastPebLock?
+020?void?????*FastPebLockRoutine?
+024?void?????*FastPebUnlockRoutine?
+028?uint32???EnvironmentUpdateCount?
+02c?void?????*KernelCallbackTable?
+030?uint32???SystemReserved[2]?
+038?struct???_PEB_FREE_BLOCK?*FreeList?
+03c?uint32???TlsExpansionCounter?
+040?void?????*TlsBitmap?
+044?uint32???TlsBitmapBits[2]?
+04c?void?????*ReadOnlySharedMemoryBase?
+050?void?????*ReadOnlySharedMemoryHeap?
+054?void?????**ReadOnlyStaticServerData?
+058?void?????*AnsiCodePageData?
+05c?void?????*OemCodePageData?
+060?void?????*UnicodeCaseTableData
+064?uint32???NumberOfProcessors?
+068?uint32???NtGlobalFlag?
+070?union????_LARGE_INTEGER?CriticalSectionTimeout?
+070????uint32???LowPart?
+074????int32????HighPart?
+070????struct???__unnamed3?u?
+070???????uint32???LowPart?
+074???????int32????HighPart?
+070????int64????QuadPart?
+078?uint32???HeapSegmentReserve?
+07c?uint32???HeapSegmentCommit?
+080?uint32???HeapDeCommitTotalFreeThreshold?
+084?uint32???HeapDeCommitFreeBlockThreshold?
+088?uint32???NumberOfHeaps?
+08c?uint32???MaximumNumberOfHeaps?
+090?void?????**ProcessHeaps?
+094?void?????*GdiSharedHandleTable?
+098?void?????*ProcessStarterHelper?
+09c?uint32???GdiDCAttributeList?
+0a0?void?????*LoaderLock?
+0a4?uint32???OSMajorVersion?
+0a8?uint32???OSMinorVersion?
+0ac?uint16???OSBuildNumber?
+0ae?uint16???OSCSDVersion?
+0b0?uint32???OSPlatformId?
+0b4?uint32???ImageSubsystem?
+0b8?uint32???ImageSubsystemMajorVersion?
+0bc?uint32???ImageSubsystemMinorVersion?
+0c0?uint32???ImageProcessAffinityMask?
+0c4?uint32???GdiHandleBuffer[34]?
+14c?function?*PostProcessInitRoutine?
+150?void?????*TlsExpansionBitmap?
+154?uint32???TlsExpansionBitmapBits[32]?
+1d4?uint32???SessionId?
+1d8?void?????*AppCompatInfo?
+1dc?struct???_UNICODE_STRING?CSDVersion?
+1dc????uint16???Length?
+1de????uint16???MaximumLength?
+1e0????uint16???*Buffer

我們從FS:[30]就可以獲得這個PEB的首地址。然后在0C處的_PEB_LDR_DATA?*Ldr是一個關鍵通過它,我們能訪問到

typedef?struct?_PEB_LDR_DATA?{
ULONG?Length;
BOOLEAN?Initialized;
PVOID?SsHandle;
LIST_ENTRY?InLoadOrderModuleList;
LIST_ENTRY?InMemoryOrderModuleList;
LIST_ENTRY?InInitializationOrderModuleList;
}?PEB_LDR_DATA,?*PPEB_LDR_DATA;

該結構的后三個成員是指向LDR_MODULE鏈表結構中相應三條雙向鏈表頭的指針,分別是按照加載順序、在內存中的地址順序和初始化順序排列的模塊信息結構的指針。于是通過它,我們能訪問到_LDR_MODULE結構,而這里面包括了本進程的SizeOfImage。
_LDR_MODULE結構如下:
typedef?struct?_LDR_MODULE?{
LIST_ENTRY?InLoadOrderModuleList;
LIST_ENTRY?InMemoryOrderModuleList;
LIST_ENTRY?InInitializationOrderModuleList;
PVOID?BaseAddress;
PVOID?EntryPoint;
ULONG?SizeOfImage;???????????????????//進程的image?size
UNICODE_STRING?FullDllName;
UNICODE_STRING?BaseDllName;
ULONG?Flags;
SHORT?LoadCount;
SHORT?TlsIndex;
LIST_ENTRY?HashTableEntry;
ULONG?TimeDateStamp;
}?LDR_MODULE,?*PLDR_MODULE;

所以,我們得到關鍵的代碼就是:
??//這里的幾個代碼是修改PEB的關鍵
??__asm
??{
????mov?eax,fs:[30h]????????????????//獲得PEB地址
????mov?eax,[eax+0ch]???????????????//?+00c?struct???_PEB_LDR_DATA?*Ldr?
????mov?eax,[eax+0ch]???????????????//?_LDR_MODULE的首地址
????mov?dword?ptr?[eax+20h],1000h???//eax+20是保存image?size的地方

??}
上面的代碼的作用就是把image?size的大小改為了1000h,這樣我們用MODULEENTRY32得到的大小是不準確的。

?

2.3 針對上面的問題,必須使用LordPE的corect image size 技術了,它的原理很簡單,就是從磁盤PE文件頭中讀取真實的image?size來修正獲取到的數據,從而避免了anti-dump的影響,當然,anti-dump技術還有很多,不止這一種,我也要慢慢去摸索。

?

3. 對齊節表問題

學過PE結構的朋友都指導,FileAlignment(磁盤對齊值)和SectionAlignment(內存對齊值)是不一樣的。

FileAlignment:0x200h

SectionAlignment:0x1000h

在PE加載器加載文件映像的時候,就會在對齊值之間的差值中填0,當然我們dump的時候連著這些0也一并dump下來了。這也就是為什么脫殼的程序普遍都比源程序大很多的原理(當然實際情況不止這些,IAT重建也會造成大小擴大)。

另外,還造成了一個問題,就是 RA!=RVA?,RS!=RVS的問題:

因為我們是從內從中直接dump出來的數據,所以這時正常的情況應該是RA=RVA?,RS=RVS,這里可以手動修改,或者編程實現即可,原理上之前學PE的時候寫的PEInfo差不多,主要考察的PE結構的知識。

這是修改后的。

到這里,dump就完成了,還差IAT沒修復。

可以采取手工的方法在文件中找一塊空位,逆向的重構IAT,在修改PE頭。

或者用ImportREC來自動修復。

?

修復完成后,脫殼成功,至此,一個簡單的脫殼工具就出來了。

?

這個學習筆記就當這段時間學習逆向脫殼的總結了,留下了幾個問題沒解決:

1. DLL脫殼后的重定位問題

2. VMP虛擬機殼尋找OEP問題。

留待以后解決了.............

信安的路果然還很長,希望以后能繼續多多學習,思考,總結,分享,向看雪上那些大神看齊。

下一階段準備研究一些緩沖區溢出的內核的知識點,繼續看《0DAY》。離ISCC結束還有10天,繼續加油啦,暑假一定要到北京ISCC決賽去。

?

?

-------------------------------分割線-----------------------

不知道怎么結尾,就這樣了吧,? 跑步去。

轉載于:https://www.cnblogs.com/LittleHann/p/3148349.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/378232.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/378232.shtml
英文地址,請注明出處:http://en.pswp.cn/news/378232.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java vector_Java Vector elements()方法與示例

java vector向量類elements()方法 (Vector Class elements() method) elements() method is available in java.util package. elements()方法在java.util包中可用。 elements() method is used to get an enumeration of the elements that exist in this Vector. elements()方…

【數據結構基礎筆記】【鏈表】

代碼參考《妙趣橫生的算法.C語言實現》 文章目錄前言1、鏈表基礎2、創建一個鏈表3、插入結點4、刪除結點5、銷毀鏈表6、實例分析前言 本章總結&#xff1a;鏈表的定義、創建、銷毀&#xff0c;結點的插入與刪除 1、鏈表基礎 鏈表的物理存儲結構是用一組地址任意的存儲單元存儲…

動態添加,刪除行之心理測試系統

動態添加&#xff0c;刪除行之考試系統 數據庫設計&#xff1a; xl_option 題目選項 20090105134755404(編號) 20090105134904421(外鍵) 比較符合(選項內容) ②(選項標號) 2&#xff08;選項分值&#xff09; xl_subject 題目信息 20090105134943608&#xff08;編號&#xff…

android bitmap裁剪中間,Android裁剪中心位圖

雖然上面的大多數答案提供了一種方法來實現這一點&#xff0c;但已經有一種內置的方法來實現這一點&#xff0c;它是一行代碼(ThumbnailUtils.extractThumbnail())int dimension getSquareCropDimensionForBitmap(bitmap);bitmap ThumbnailUtils.extractThumbnail(bitmap, di…

二、request請求庫

一、requests介紹與安裝 1&#xff0c;requests介紹 答&#xff1a;requests是一個優雅且簡單的Python HTTP請求庫 2&#xff0c;requests作用 答&#xff1a;requests的作用是發送請求獲取響應數據 3&#xff0c;requests安裝 答&#xff1a;pip install requests 二、…

Java Vector Capacity()方法與示例

向量類的Capacity()方法 (Vector Class capacity() method) capacity() method is available in java.util package. Capacity()方法在java.util包中可用。 capacity() method is used to return the current capacity (i.e. initially, how many object exists) of this Vecto…

MFC和GTK的區別

關鍵技術 http://blog.csdn.net/master_max/article/details/1540204 MFC和GTK的區別&#xff1f;&#xff1f; 1.  兩者都是基于面向對象設計的。盡管MFC是用C寫的&#xff0c;而GTK是用C寫的&#xff0c;但思想都是面向對象的。GTK使用glib的對象機制&#xff0c;由于用C寫…

視頻圖像質量評價

目錄1、人眼視覺特性1、眼的適應性2、對比靈敏度3、空間分辨率和時間分辨率4、馬赫效應5、可見度閾值2、圖像質量測度3、圖像評價方法4、圖像評價方法的優劣1、人眼視覺特性 1、眼的適應性 暗適應性&#xff1a;從亮環境到暗環境&#xff0c;適應暗環境的特性 亮適應性&#…

鴻蒙科技與文化,數字閱讀 | “華為鴻蒙”:當現代科技遇到古典文化

華為事件愈演愈烈。海思芯片 20 年 " 備胎 " 終轉正&#xff0c;那么操作系統呢&#xff1f;最近&#xff0c;華為為自主研發的操作系統注冊商標—— " 鴻蒙 "&#xff0c;引發了關于華為注冊整本《山海經》的熱烈討論&#xff0c;很多人的朋友圈&#xff…

三、Beautiful Soup解析庫

一、Beautiful Soup介紹與安裝 1&#xff0c;Beautiful Soup介紹 答&#xff1a;Beautiful Soup是一個可以從HTML或XML文件中提取數據的Python庫 2&#xff0c;Beautiful Soup安裝 答&#xff1a;安裝Beautiful Soup 4&#xff1a;pip install bs4 安裝lxml&#xff1a;pip…

strictmath_Java StrictMath sqrt()方法與示例

strictmathStrictMath類sqrt()方法 (StrictMath Class sqrt() method) sqrt() Method is available in java.lang package. sqrt()方法在java.lang包中可用。 sqrt() Method is used to find the square root of the given parameter in the method. Here, "sqrt" st…

recovery編譯問題匯總

1、修改支持USB大容量存儲 &#xff08;1&#xff09;、首先需要查看手機lun位置 手機鏈接電腦&#xff0c;打開cmd命令行&#xff0c;依次輸入以下命令: adb shell find /sys -name "lun" 輸出以下結果&#xff1a; 發現手機輸出結果有兩個&#xff0c;需要進一步查…

言語理解每日學習及精解20110831

【例題】天氣預報一般要考慮氣溫、氣壓、溫度、風力等因素&#xff0c;這些都是大氣層本身變化的結果&#xff0c;只要掌握這些因素&#xff0c;通過計算機的計算就能準確地預報天氣變化的趨勢。沙塵暴作為一種特殊的天氣現象&#xff0c;同樣要考慮上述氣象因素。據氣象學家分…

【數據結構基礎筆記】【棧】

代碼參考《妙趣橫生的算法.C語言實現》 文章目錄前言1、棧的定義2、創建一個棧3、入棧和出棧操作4、棧的清空、銷毀、計算棧的當前容量5、實例分析前言 本章總結&#xff1a;棧的定義、創建棧&#xff0c;銷毀棧&#xff0c;入棧出棧操作等操作。 1、棧的定義 棧是一種重要的…

四、正則表達式

一、正則表達式的概念和作用 正則表達式概念&#xff1a;一種字符串匹配的模式 正則表達式作用&#xff1a; 可以檢查一個字符串中是否包含某種字串替換匹配的字串提取某個字符串中匹配的字串 二、正則表達式中常見的語法 字符描述原樣字符匹配字符一般字符匹配自身beyondb…

用HTML語言制作list標記,html5 datalist標簽的用法是什么?這里有datalist標簽的用法實例...

本篇文章主要為大家講述了關于html5 datalist標簽的用法及html5 datalist標簽的用法實例。本文說了兩個常用的選項框的實例供大家選擇觀看&#xff0c;下面就讓我們一起來看這篇文章吧我們先來看看html5 datalist標簽的用法&#xff1a;標簽定義選項列表。請與input元素配合使用…

java treemap_Java TreeMap lastKey()方法與示例

java treemapTreeMap類lastKey()方法 (TreeMap Class lastKey() method) lastKey() method is available in java.util package. lastKey()方法在java.util包中可用。 lastKey() method is used to return the last highest key element value exists in this TreeMap. lastKey…

網上看來的

http://blog.163.com/dong_xiao_yang/blog/static/216138205201321114659430/ http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20compile%20FFmpeg%20for%20Raspberry%20Pi%20%28Raspbian%29#FFmpegwithlibaacpluslibx264andalsa-lib 編譯環境 Ubuntu 12.04 w64-mingw32下載lib…

閱讀iPhone.3D.Programming(O'Reilly.2010-05) 英文版 第一感覺

最近開始閱讀iPhone.3D.Programming(OReilly.2010-05)&#xff0c;英文版此書&#xff0c;我閱讀到P21了&#xff0c;中間講了一個樣例&#xff0c;HelloArrow在這個過程中&#xff0c;我想簡單點&#xff0c;少打點字&#xff0c;直接拿書中配套來學習&#xff0c;發現一個問題…

【數據結構基礎筆記】【隊列】

代碼參考《妙趣橫生的算法.C語言實現》 文章目錄前言1、隊列定義2、創建一個隊列3、入隊列4、出隊列5、銷毀一個隊列6、循環隊列的概念7、循環隊列的實現8、實例分析前言 本章總結&#xff1a;鏈隊列定義&#xff0c;創建&#xff0c;出隊入隊操作&#xff0c;銷毀操作&#x…