首页
论坛
专栏
课程

[分享]感染程序+复习PE

2019-2-13 14:43 4955

[分享]感染程序+复习PE

2019-2-13 14:43
4955
感谢滴水的培训视频,让我学到了很多知识。

程序的流程: 文件状态(FileBuffer)->运行状态(ImageBuffer)->文件状态(NewFileBuffer)->存盘(新文件)   在运行状态中插入代码,然后修改程序入口点。
运行状态就是PE拉伸后的样子,有很多的地址需要注意,程序弄这么复杂就是为了复习PE知识。

#define SHELLCODESIZE 18
#define INFECTPATH "C:\\Users\\Guxy\\Desktop\\3.exe"

//文件状态
LPVOID GetFileContent(char* FilePath);
//文件状态转运行状态  --  PE拉伸
LPVOID FileBufferTOImageBuffer(LPVOID FileBuffer);
//获取运行状态的PE文件在磁盘中的大小
DWORD szFileSize(LPVOID ImageBuffer);
//运行状态转文件状态
LPVOID ImageBufferTOFileBuffer(LPVOID ImageBuffer, DWORD FileSize);
//修改运行状态的数据
void ModifyingData(LPVOID ImageBuffer, DWORD ProcAddr);

char shellcode[] = 
{ 
	0x6A, 00, 0x6A, 00, 0x6A, 00, 0x6A, 00, 
	0xE8, 00, 00, 00, 00, 
	0xE9, 00, 00, 00, 00 
};



//文件中状态
LPVOID GetFileContent(char* FilePath)
{
	FILE* pFile = fopen(FilePath, "rb");
	if (pFile == NULL)
	{
		printf("Fopen File Error:%d\n", GetLastError());
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	DWORD FileSize = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);

	LPVOID Filebuffer = malloc(FileSize);
	if (!Filebuffer)
	{
		printf("Malloc Error:%d\n", GetLastError());
		fclose(pFile);
		return 0;
	}
	memset(Filebuffer, 0, FileSize);

	if (!fread(Filebuffer, FileSize, 1, pFile))
	{
		printf("Fread Error:%d\n", GetLastError());
		free(Filebuffer);
		fclose(pFile);
		return 0;
	}

	fclose(pFile);
	return Filebuffer;
}
//文件状态转运行状态
LPVOID FileBufferTOImageBuffer(LPVOID FileBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)FileBuffer + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pec = (PIMAGE_SECTION_HEADER)((char*)FileBuffer + pDosHeader->e_lfanew + 24 + pNtHeader->FileHeader.SizeOfOptionalHeader);
        //PE文件在内存中的大小
	DWORD SizeOfImage = pNtHeader->OptionalHeader.SizeOfImage;
	LPVOID Imagebuff = malloc(SizeOfImage);
	if (!Imagebuff)
	{
		printf("FileBufferTOImageBuffer Malloc Error:%d\n", GetLastError());
		return 0;
	}

	memset(Imagebuff, 0, SizeOfImage);

	//复制所有头过去
	for (int i = 0; i < pNtHeader->OptionalHeader.SizeOfHeaders; i++)
	{
		*((char*)Imagebuff + i) = *((char*)FileBuffer + i);
	}

	//复制节,复制的位置是VirtualAddress 内存中的偏移,起始位置,从文件PointerToRawData 偏移处开始复制
	for (int m = 0; m < pNtHeader->FileHeader.NumberOfSections; m++, pec++)
	{
		for (int n = 0; n < pec->SizeOfRawData; n++)
		{
			*((char*)Imagebuff + pec->VirtualAddress + n) = *((char*)FileBuffer + pec->PointerToRawData + n);
		}
	}

	return Imagebuff;
}
//插入shellcode,修改程序入口地址  (注意有很多转换)
void ModifyingData(LPVOID ImageBuffer, DWORD ProcAddr)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)ImageBuffer + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pec = (PIMAGE_SECTION_HEADER)((char*)ImageBuffer + pDosHeader->e_lfanew + 24 + pNtHeader->FileHeader.SizeOfOptionalHeader);

        //判断是否有空闲空间
	for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, pec++)
	{
		if ((DWORD)(pec->SizeOfRawData) - (DWORD)(pec->VirtualAddress) > SHELLCODESIZE)
		{
			printf("%s\n", pec->Name);

                        //定位shellcode插入到哪里。 内存偏移VirtualAddress  + VirtualSize节在内存中的大小
			char* codeBegin = (char*)ImageBuffer + pec->VirtualAddress + pec->Misc.VirtualSize;
                        //插入数据
			for (int s = 0; s < SHELLCODESIZE; s++)
			{
				*((char*)codeBegin + s) = shellcode[s];
			}

                        //修正E8后面的地址  要跳往的地址 - E8所在的地址(内存中) - 5
                        //E8在内存中的偏移 = ImageBuffer - (codeBegin + 8) + ImageBase (ImageBuffer是拉伸后的状态,所以偏移跟运行状态的偏移是一样的)
			DWORD callAddr = (DWORD)ProcAddr - ((codeBegin + 8) - ImageBuffer + (pNtHeader->OptionalHeader.ImageBase)) - 5;

			*(DWORD*)((char*)codeBegin + 9) = callAddr;
                        //修正E9后面的地址,同上。
			DWORD jmpAddr = (pNtHeader->OptionalHeader.AddressOfEntryPoint + pNtHeader->OptionalHeader.ImageBase) - ((DWORD)codeBegin + 13 - (DWORD)ImageBuffer + pNtHeader->OptionalHeader.ImageBase) - 5;

			*(DWORD*)((char*)codeBegin + 14) = jmpAddr;

                        //修改节属性
			pec->Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;


                        //修改入口点,也是偏移的计算。
			DWORD Oep = (DWORD)((char*)ImageBuffer + pec->VirtualAddress + pec->Misc.VirtualSize) - DWORD(ImageBuffer);

			pNtHeader->OptionalHeader.AddressOfEntryPoint = Oep;

			printf("Successful infection...\n");
			break;
		}
		else
		{
			printf("Insufficient space...\n");
			return;
		}
	}

	return;
}
//计算运行状态的PE文件在磁盘中的大小
//所有头的大小 + 所以节SizeOfRawData的大小
DWORD szFileSize(LPVOID ImageBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)ImageBuffer + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pec = (PIMAGE_SECTION_HEADER)((char*)ImageBuffer + pDosHeader->e_lfanew + 24 + pNtHeader->FileHeader.SizeOfOptionalHeader);

	DWORD SectionsSize = 0;
	for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
	{
		SectionsSize = SectionsSize + pec[i].SizeOfRawData;
	}
	DWORD FileSize = pNtHeader->OptionalHeader.SizeOfHeaders + SectionsSize;

	return FileSize;
}
//运行状态转文件状态->存盘
//又是转换,地址定位
LPVOID ImageBufferTOFileBuffer(LPVOID ImageBuffer, DWORD FileSize)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)ImageBuffer + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pec = (PIMAGE_SECTION_HEADER)((char*)ImageBuffer + pDosHeader->e_lfanew + 24 + pNtHeader->FileHeader.SizeOfOptionalHeader);

	LPVOID FileBuffer = malloc(FileSize);
	if (FileBuffer == NULL)
	{
		printf("ImageBufferTOFileBuffer Malloc Error:%d\n", GetLastError());
		return 0;
	}
	memset(FileBuffer, 0, FileSize);

	//Headers Copy
	for (int i = 0; i < pNtHeader->OptionalHeader.SizeOfHeaders; i++)
	{
		*((char*)FileBuffer + i) = *((char*)ImageBuffer + i);
	}

	//Sections Copy
	for (int n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++, pec++)
	{
		for (int m = 0; m < pec->SizeOfRawData; m++)
		{
			*((char*)FileBuffer + pec->PointerToRawData + m) = *((char*)ImageBuffer + pec->VirtualAddress + m);
		}
	}


	FILE* pFile = fopen(INFECTPATH, "wb");

	fwrite(FileBuffer, FileSize, 1, pFile);

	fclose(pFile);

	return FileBuffer;
}

如果都没有空间的话,还可以自己新增一个节。里面少了shellcode要跳往的地址。
HMODULE Hmodule = LoadLibrary(L"User32.dll");
DWORD ProcAddr = (DWORD)GetProcAddress(Hmodule, "MessageBoxA");
因为我技术有限,现在还做不出通用的shellcode 谅解。

程序不方便理解,希望对正在学习PE的大哥有帮助。
非常感谢滴水培训视频!


[招聘]欢迎市场人员加入看雪学院团队!

上传的附件:
打赏 + 1.00
打赏次数 1 金额 + 1.00
收起 
赞赏  小冏子   +1.00 2019/02/17
最新回复 (15)
bbsshop 2019-2-13 15:37
2
0
ielts 2019-2-15 14:09
3
0
hbkccccc 2019-2-17 11:40
4
0
小冏子 2019-2-17 13:41
5
0
       
小冏子 2019-2-17 13:43
6
0
楼主加油
广州勇哥 2019-2-24 17:27
7
0
ImageBufferToFileBuffer为啥还要这样   
FILE* pFile = fopen(INFECTPATH, "wb");
 
    fwrite(FileBuffer, FileSize, 1, pFile);
 
    fclose(pFile);
不是已经把头部和节表的信息写到FileBuffer中了吗。为什么还要重新打开文件再写出文件呢
Guxy 2019-2-25 20:51
8
0
广州勇哥 ImageBufferToFileBuffer为啥还要这样 FILE* pFile = fopen(INFECTPATH, "wb"); fwrite(Fi ...
修改的只是缓存中的内容,保存修改了的内容,到一个新的文件。
wx_顾尬聊 2019-4-16 15:11
9
0
楼主,判断节区有没有多余的空间,应该是:
if ((DWORD)(pec->SizeOfRawData) - (DWORD)(pec->Misc.VirtualSize) > SHELLCODESIZE)

QZ2019 2019-6-13 20:59
10
0
感谢海东老师
blck四 2019-6-18 22:35
11
0
感谢分享,海东老师。
Kiopler 2019-6-23 18:57
12
0
您好,这段代码我有点没看明白
if ((DWORD)(pec->SizeOfRawData) - (DWORD)(pec->VirtualAddress) > SHELLCODESIZE)
根据我的知识,pec->SizeOfRawData是该节在磁盘中的大小(以FileAlignment对齐)而VirtualAddress是该节(以SectionAlignment对齐)位于内存中的RVA。这两个相减为什么得出该节是否有剩余的位置呢? 楼主能不能和我解释下
最后于 2019-6-23 18:58 被Kiopler编辑 ,原因:
Guxy 2019-6-23 21:53
13
0
Kiopler 您好,这段代码我有点没看明白 if&nbsp;((DWORD)(pec-&gt;SizeOfRawData)&nbsp;-&nbsp;(DWORD)(pec- ...
4个月前写的代码了,忘了当时的思路了,也有4个月没有碰PE了。
Guxy 2019-6-23 22:08
14
0
Kiopler 您好,这段代码我有点没看明白 if&nbsp;((DWORD)(pec-&gt;SizeOfRawData)&nbsp;-&nbsp;(DWORD)(pec- ...
代码应该写错了,是减去在内存中的大小,Misc.VirtualSize
Guxy 2019-6-23 22:10
15
0
wx_顾尬聊 楼主,判断节区有没有多余的空间,应该是:if&nbsp;((DWORD)(pec-&gt;SizeOfRawData)&nbsp;-&nbsp;(DWORD)(pec- ...
谢谢你。
Kiopler 2019-6-24 08:23
16
0
好的,学习你的代码。学到很多,谢谢楼主分享
游客
登录 | 注册 方可回帖
返回