日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

基于VC++实现PE的修改编程

發布時間:2023/12/15 综合教程 26 生活家
生活随笔 收集整理的這篇文章主要介紹了 基于VC++实现PE的修改编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在Windows系統下的可執行文件的一種(還有NE、LE),是微軟設計、TIS(Tool Interface Standard,工具接口標準)委員會批準的一種可執行文件格式。PE的意思是Portable Executable(可移植可執行)。所有Windows下的32位或64位可執行文件都是PE文件格式,其中包括DLL、EXE、FON、OCX、LIB和部分SYS文件。

DOS-stub(DOS-頭)

  DOS-根的概念很早從16位windows的可執行文件(當時是“NE”格式)時就廣為人知了。根原來是用于OS/2系統的可執行文件的,也用于自解壓檔案文件和其它的應用程序。對于PE文件來說,它是一個總是由大約100個字節所組成的和MS-DOS 2.0兼容的可執行體,用來輸出象“This program needs windows NT”之類的錯誤信息。   你可以通過確認DOS-頭部分是否為一個IMAGE_DOS_HEADER(DOS頭)結構來認出DOS-根,它的前兩個字節必須為連續的兩個字母“MZ”(有一個#define IMAGE_DOS_SIGNATURE的定義是針對這個WORD單元的)。   你可以通過跟在后面的簽名來將一個PE二進制文件和其它含有根的二進制文件區分開來,跟在后面的簽名可由頭成員'e_lfanew'(它是從字節偏移地址60處開始的,有32字節長)所設定的偏移地址找到。對于OS/2系統和Windows系統的二進制文件來說,簽名是一個16位的word單元;對于PE文件來說,它是一個按照8位字節邊界對齊的32位的longword單元,并且IMAGE_NT_SIGNATURE(NT簽名)的值已由#defined定義為0x00004550(即字母“PE/0/0”)。

file-header(文件頭)

  要到達IMAGE_FILE_HEADER(文件頭)結構,請先確認DOS-頭“MZ”(起始的2個字節),然后找出DOS-根的頭部的成員“e_lfanew”,并從文件開始處跳過那么多的字節。在核實你在那里找到的簽名后,IMAGE_FILE_HEADER(文件頭)結構的文件頭就緊跟其后開始了。

optional header(可選頭)

  緊跟在文件頭后面的就是IMAGE_OPTIONAL_HEADER(盡管它名叫“可選頭”,它卻一直都在那里)。它包含有怎樣去準確處理PE文件的信息。

data directories(數據目錄)

  IMAGE_NUMBEROF_DIRECTORY_ENTRIES (16)(映象文件目錄項數目)個IMAGE_DATA_DIRECTORY(映象文件數據目錄)數組。這些目錄中的每一個目錄都描述了一個特定的、位于目錄項后面的某一節中的信息的位置(32位的RVA,叫“VirtualAddress(虛擬地址)”)和大小(也是32位,叫“Size(大小)”)。   例如,安全目錄能在索引4中給定的RVA處發現并具有索引4中給定的大小。

section headers(節頭)

  節由兩個主要部分組成:首先,是一個節描述(IMAGE_SECTION_HEADER[意為“節頭”]類型的),然后是原始的節數據。因此,我們會在數據目錄后發現一“NumberOfSections”個節頭組成的數組,它們按照各節的RVA排序。

sections(節數據)

  所有的節在載入內存后都按“SectionAlignment”(節對齊)對齊,在文件中則以“FileAlignment”(文件對齊)對齊。節由節頭中的相關項來描述:在文件中你可通過“PointerToRawData”(原始數據指針)來找到,在內存中你可通過“VirtualAddress”(虛擬地址)來找到;長度由“SizeOfRawData”(原始數據長度)決定。

  根據節中包含的內容,可分為好幾種節。大多數(并非所有)情況下,節中至少由一個數據目錄,并在可選頭的數據目錄數組中有一個指針指向它。

下面我們實現編程修改PE

// Pe.cpp: 實現 CPe類.
//
#include "stdafx.h"
#include "Pe.h"

CPe::CPe()
{
}

CPe::~CPe()
{
}

void CPe::ModifyPe(CString strFileName,CString strMsg)
{
	CString strErrMsg;

	HANDLE hFile, hMapping;
	void *basepointer;
	
	// 打開要修改的文件.
	if ((hFile = CreateFile(strFileName, GENERIC_READ|GENERIC_WRITE, 
		FILE_SHARE_READ|FILE_SHARE_WRITE, 0, 
		OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Could not open file.");
		return;
	}

	// 創建一個映射文件.
	if (!(hMapping = CreateFileMapping(hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
	{
		AfxMessageBox("Mapping failed.");
		CloseHandle(hFile);
		return;
	}

	// 把文件頭映象存入baseointer.
	if (!(basepointer = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)))
	{
		AfxMessageBox("View failed.");
		CloseHandle(hMapping);
		CloseHandle(hFile);
		return;
	}

	CloseHandle(hMapping);
	CloseHandle(hFile);

	CalcAddress(basepointer); // 得到相關地址.
	UnmapViewOfFile(basepointer);
	
	if(dwSpace<50)
	{
		AfxMessageBox("No room to write the data!");
	}
	else
	{
		WriteFile(strFileName,strMsg); // 寫文件.
	}
	
	if ((hFile = CreateFile(strFileName, GENERIC_READ|GENERIC_WRITE, 
		FILE_SHARE_READ|FILE_SHARE_WRITE, 0, 
		OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Could not open file.");
		return;
	}
	
	CloseHandle(hFile);
}

void CPe::CalcAddress(const void *base)
{
	IMAGE_DOS_HEADER * dos_head =(IMAGE_DOS_HEADER *)base;

	if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)
	{
		AfxMessageBox("Unknown type of file.");
		return;
	}
	
	peHeader * header;

	// 得到PE文件頭.
	header = (peHeader *)((char *)dos_head + dos_head->e_lfanew);

	if(IsBadReadPtr(header, sizeof(*header)))
	{
		AfxMessageBox("No PE header, probably DOS executable.");
		return;
	}

	DWORD mods;
	char tmpstr[4]={0};
	if(strstr((const char *)header->section_header[0].Name,".text")!=NULL)
	{
		// 此段的真實長度.
		dwVirtSize=header->section_header[0].Misc.VirtualSize;

		// 此段的物理偏移.
		dwPhysAddress=header->section_header[0].PointerToRawData;

		// 此段的物理長度.
		dwPhysSize=header->section_header[0].SizeOfRawData;
		
		// 得到PE文件頭的開始偏移.
		dwPeAddress=dos_head->e_lfanew; 
		
		// 得到代碼段的可用空間,用以判斷可不可以寫入我們的代碼
		// 用此段的物理長度減去此段的真實長度就可以得到.
		dwSpace=dwPhysSize-dwVirtSize;

		// 得到程序的裝載地址,一般為0x400000.
		dwProgRAV=header->opt_head.ImageBase; 

		// 得到代碼偏移,用代碼段起始RVA減去此段的物理偏移
		// 應為程序的入口計算公式是一個相對的偏移地址,計算公式為:
		// 代碼的寫入地址+dwCodeOffset.
		dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;
		
		// 代碼寫入的物理偏移.
		dwEntryWrite=header->section_header[0].PointerToRawData+header->
			section_header[0].Misc.VirtualSize;

		//對齊邊界.
		mods=dwEntryWrite%16;

		if(mods!=0)
		{
			dwEntryWrite+=(16-mods);
		}
		
		// 保存舊的程序入口地址.
		dwOldEntryAddress=header->opt_head.AddressOfEntryPoint;

		// 計算新的程序入口地址.        
		dwNewEntryAddress=dwEntryWrite+dwCodeOffset;
		return;
	}
}	

CString CPe::StrOfDWord(DWORD dwAddress)
{
	unsigned char waddress[4]={0};
	
	waddress[3]=(char)(dwAddress>>24)&0xFF;
	waddress[2]=(char)(dwAddress>>16)&0xFF;
	waddress[1]=(char)(dwAddress>>8 )&0xFF;
	waddress[0]=(char)(dwAddress    )&0xFF;
   
	return waddress;
}

BOOL CPe::WriteNewEntry(int ret,long offset, DWORD dwAddress)
{
	CString strErrMsg;
	long retf;
	unsigned char waddress[4]={0};

	retf=_lseek(ret,offset,SEEK_SET);
	if(retf==-1)
	{
		AfxMessageBox("Error seek.");
		return FALSE;
	}

    memcpy(waddress,StrOfDWord(dwAddress),4);
	retf=_write(ret,waddress,4);
	
	if(retf==-1)
	{
		strErrMsg.Format("error write: %d",GetLastError());
		AfxMessageBox(strErrMsg);
		return FALSE;
	}

	return TRUE;
}

BOOL CPe::WriteMessageBox(int ret,long offset,CString strCap,CString strTxt)
{
	CString strAddress1,strAddress2;
	unsigned char waddress[4]={0};
	DWORD dwAddress;

	// 獲取MessageBox在內存中的地址.
	HINSTANCE gLibMsg=LoadLibrary("user32.dll"); 
	dwMessageBoxAadaddress=(DWORD)GetProcAddress(gLibMsg,"MessageBoxA");

    // 計算校驗位. 
	int nLenCap1 =strCap.GetLength()+1;   // 加上字符串后面的結束位. 
	int nLenTxt1 =strTxt.GetLength()+1;   // 加上字符串后面的結束位. 
	int nTotLen=nLenCap1+nLenTxt1+24;

    // 重新計算MessageBox函數的地址.
	dwAddress=dwMessageBoxAadaddress-(dwProgRAV+dwNewEntryAddress+nTotLen-5);
   	strAddress1=StrOfDWord(dwAddress);

	// 計算返回地址.
	dwAddress=0-(dwNewEntryAddress-dwOldEntryAddress+nTotLen);
	strAddress2=StrOfDWord(dwAddress);

    // 對話框頭代碼(固定).
	unsigned char cHeader[2]={0x6a,0x40};
    
	// 標題定義.	
	unsigned char cDesCap[5]={0xe8,nLenCap1,0x00,0x00,0x00};
    
	// 內容定義.
	unsigned char cDesTxt[5]={0xe8,nLenTxt1,0x00,0x00,0x00};
    
	// 對話框后部分的代碼段. 
	unsigned char cFix[12]
		 ={0x6a,0x00,0xe8,0x00,0x00,0x00,0x00,0xe9,0x00,0x00,0x00,0x00};
    
	// 修改對話框后部分的代碼段. 
	for(int i=0;i<4;i++)
		cFix[3+i]=strAddress1.GetAt(i);

	for(i=0;i<4;i++)
		cFix[8+i]=strAddress2.GetAt(i);

	char* cMessageBox=new char[nTotLen];
	char* cMsg; 

	// 生成對話框命令字符串.
	memcpy((cMsg  = cMessageBox),(char*)cHeader,2);
	memcpy((cMsg += 2),cDesCap,5);
	memcpy((cMsg += 5),strCap,nLenCap1);
	memcpy((cMsg += nLenCap1),cDesTxt,5);
	memcpy((cMsg += 5),strTxt,nLenTxt1);
	memcpy((cMsg += nLenTxt1),cFix,12);

    // 向應用程序寫入對話框代碼.
	CString strErrMsg;
	long retf;
	retf=_lseek(ret,(long)dwEntryWrite,SEEK_SET);
	if(retf==-1)
	{
		delete[] cMessageBox;
		AfxMessageBox("Error seek.");
		return FALSE;
	}

	retf=_write(ret,cMessageBox,nTotLen);
	if(retf==-1)
	{
		delete[] cMessageBox;
		strErrMsg.Format("error write: %d",GetLastError());
		AfxMessageBox(strErrMsg);
		return FALSE;
	}
	delete[] cMessageBox;

	return TRUE;
}

void CPe::WriteFile(CString strFileName,CString strMsg)
{
	CString strAddress1,strAddress2;
	int ret;
	unsigned char waddress[4]={0};
	
	ret=_open(strFileName,_O_RDWR | _O_CREAT | _O_BINARY,_S_IREAD | _S_IWRITE);
	if(!ret)
	{
		AfxMessageBox("Error open.");
		return;
	}

    // 把新的入口地址寫入文件,程序的入口地址在偏移PE文件頭開始第40位.
	if(!WriteNewEntry(ret,(long)(dwPeAddress+40),dwNewEntryAddress)) return;

    // 把對話框代碼寫入到應用程序中.
	if(!WriteMessageBox(ret,(long)dwEntryWrite,"Test",strMsg)) return;

	_close(ret);
}

下面我們實現編程修改OEP

#include <windows.h>
#include <stdio.h>

BOOL ReadOEPbyMemory(LPCSTR szFileName);
BOOL ReadOEPbyFile(LPCSTR szFileName);

void main()
{
	ReadOEPbyFile("..\\calc.exe");
	ReadOEPbyMemory("..\\calc.exe");
	getchar();
}

// 通過文件讀取OEP值.
BOOL ReadOEPbyFile(LPCSTR szFileName)
{
	HANDLE hFile;
	
	// 打開文件.
	if ((hFile = CreateFile(szFileName, GENERIC_READ,
		FILE_SHARE_READ, 0, OPEN_EXISTING, 
		FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
	{
		printf("can't not open file.\n");
		return FALSE;
	}
	
	DWORD dwOEP,cbRead;
	IMAGE_DOS_HEADER dos_head[sizeof(IMAGE_DOS_HEADER)];
    if (!ReadFile(hFile, dos_head, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)){ 
		printf("read image_dos_header failed.\n");
		CloseHandle(hFile);
		return FALSE;
	}
	
	int nEntryPos=dos_head->e_lfanew+40;
    SetFilePointer(hFile, nEntryPos, NULL, FILE_BEGIN);
	
    if (!ReadFile(hFile, &dwOEP, sizeof(dwOEP), &cbRead, NULL)){ 
		printf("read OEP failed.\n");
		CloseHandle(hFile);
		return FALSE;
	}
	
	// 關閉文件.
	CloseHandle(hFile);
	
	// 顯示OEP地址.
	printf("OEP by file:%d\n",dwOEP);
	return TRUE;
}

// 通過文件內存映射讀取OEP值.
BOOL ReadOEPbyMemory(LPCSTR szFileName)
{
	struct PE_HEADER_MAP
	{
		DWORD signature;
		IMAGE_FILE_HEADER _head;
		IMAGE_OPTIONAL_HEADER opt_head;
		IMAGE_SECTION_HEADER section_header[6];
	} *header;

	HANDLE hFile;
	HANDLE hMapping;
	void *basepointer;
	
	// 打開文件.
	if ((hFile = CreateFile(szFileName, GENERIC_READ,
		FILE_SHARE_READ,0,OPEN_EXISTING, 
		FILE_FLAG_SEQUENTIAL_SCAN,0)) == INVALID_HANDLE_VALUE)
	{
		printf("can't open file.\n");
		return FALSE;
	}
	
	// 創建內存映射文件.
	if (!(hMapping = CreateFileMapping(hFile,0,PAGE_READONLY|SEC_COMMIT,0,0,0)))
	{
		printf("mapping failed\n");
		CloseHandle(hFile);
		return FALSE;
	}
	
	// 把文件頭映象存入baseointer.
	if (!(basepointer = MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0)))
	{
		printf("view failed.\n");
		CloseHandle(hMapping);
		CloseHandle(hFile);
		return FALSE;
	}
	IMAGE_DOS_HEADER * dos_head =(IMAGE_DOS_HEADER *)basepointer;
	
	// 得到PE文件頭.
	header = (PE_HEADER_MAP *)((char *)dos_head + dos_head->e_lfanew);
	
	// 得到OEP地址.
	DWORD dwOEP=header->opt_head.AddressOfEntryPoint;
	
    // 清除內存映射和關閉文件.
	UnmapViewOfFile(basepointer);
	CloseHandle(hMapping);
	CloseHandle(hFile);	
	
	// 顯示OEP地址.
	printf("OEP by memory:%d\n",dwOEP);
	return TRUE;
}

彈出對話框匯編代碼如下

;msgbx.asm file.
.386p
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib 

.code

start:
    push MB_ICONINFORMATION or MB_OK
    call Func1
    db "Test",0
Func1:
    call Func2
    db "Hello",0
Func2:
    push NULL    
    call MessageBoxA
;    ret
end start

總結

以上是生活随笔為你收集整理的基于VC++实现PE的修改编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。