PE——滴水的壳项目

滴水的壳项目

滴水的教程对于我的编程学习影响是巨大的,感觉滴水的教程对于编程学习的方方面面都有涉及到,例如:汇编,C/C++,编译原理中的栈管理,操作系统,数据结构等等,而且还涉及到各种工具的使用,感觉是一门适合打好基础的好课程,在B站上看到的免费课程,如果没有了可以去淘宝赞助一下滴水官方。

滴水的壳项目是滴水课程的最后几个实验之一,也是比较难的几个实验之一,在本篇博客中主要解析一下滴水壳的实现过程

滴水壳的代码主要包括两部分:一部分用来加密,一部分用来解密
加密部分主要实现的功能就是:

  • 我们首先需要知道的是解密部分生产了exe A
  • 我们将某个我们需要加密的程序B读出来
  • 对A进行分析,对A进行节的扩充
  • 将B程序进行取反操作
  • 将B写入到A的扩充的节中
  • 生产C程序,也就是加密后的程序

解密部分主要实现的功能就是:

  • 读取当前EXE程序C,也就是argv[0]
  • 将在加密部分扩充的节读出,解密,分析,拉伸,得到的为D部分
  • 为当前EXE程序C创建进程,以阻塞的方式,并获得上下文
  • 将进程C卸载
  • 将D部分填充到C中
  • 恢复线程,将程序执行流恢复到D的开始位置

以下为代码部分:
加密部分:此文件叫“壳项目.cpp”

#include<stdio.h>
#include<windows.h>
PIMAGE_DOS_HEADER pdos=(PIMAGE_DOS_HEADER)new char[sizeof(IMAGE_DOS_HEADER)];
PIMAGE_NT_HEADERS pnt=(PIMAGE_NT_HEADERS)new char[sizeof(IMAGE_NT_HEADERS)];
PIMAGE_SECTION_HEADER psec,pt; 
char* file_buffer;
int f_s;
char* new_file_buffer;
typedef struct p{
	PIMAGE_DOS_HEADER pdos;
	PIMAGE_NT_HEADERS pnt;
	PIMAGE_SECTION_HEADER psec;
	char* file_buffer;
	int size;//需要添加的节的大小 
	char* new_file_buffer;
	int f_s;
	//out
	int new_section_add;
	int new_file_size;
} struct_big; 	
struct_big *aim;

void analyse(char* shell_name){
	//计算大小 读出文件 
	FILE* fp;
	fp=fopen(shell_name,"rb");
	fseek(fp,0,2);
	f_s=ftell(fp);
	fseek(fp,0,0);
	file_buffer=new char[f_s];
	fread(file_buffer,f_s,1,fp);//读出原来shell文件 
	fseek(fp,0,0);
	
	fread(pdos,sizeof(IMAGE_DOS_HEADER),1,fp);						// 读出文件dos头 
	fseek(fp,pdos->e_lfanew,0);
	fread(pnt,sizeof(IMAGE_NT_HEADERS),1,fp);						// 读出文件nt头
	psec=(PIMAGE_SECTION_HEADER)new char[sizeof(IMAGE_SECTION_HEADER)*(pnt->FileHeader.NumberOfSections+1)]; 
	int a=pnt->FileHeader.SizeOfOptionalHeader+24+pdos->e_lfanew;	// 得出节表所在字节 
	fseek(fp,a,0);
	PIMAGE_SECTION_HEADER ptmp=(PIMAGE_SECTION_HEADER)new char[sizeof(IMAGE_SECTION_HEADER)];
	for(int i=1;i<=pnt->FileHeader.NumberOfSections;i++){//读出节表的信息 
		fread(&psec[i],sizeof(IMAGE_SECTION_HEADER),1,fp);
		printf("%x\n",psec[i].SizeOfRawData);
	}
	fclose(fp);
}
int get_vs(int vs){
	if(vs<=pnt->OptionalHeader.SectionAlignment){
		return pnt->OptionalHeader.SectionAlignment;
	}
	else{
		return pnt->OptionalHeader.SectionAlignment*(vs/pnt->OptionalHeader.SectionAlignment+1);
	}
}
void turn_big(struct_big* aim){
	int last_section=aim->pnt->FileHeader.NumberOfSections;
	//节表开始的位置
	int a=pnt->FileHeader.SizeOfOptionalHeader+24+pdos->e_lfanew;
	//判断在最后一张节表结束的位置到 第一张节表开始的位置是否有80字节的大小; 
	//使用第一个节的开头位置-(节表开始的位置+原来的节表个数*40) 40为一张节表的大小 
	BOOL judge=aim->psec[1].PointerToRawData-(a+last_section*40)>=80;
	if(judge){
		char* new_section=new char[aim->size];
		memset(new_section,0,aim->size);
		//获得加节后的文件大小 == 原来的文件大小+目标节大小 
		int size_new_file_buffer=aim->f_s+aim->size;
		aim->new_file_buffer=new char[size_new_file_buffer];
		memset(aim->new_file_buffer,0,size_new_file_buffer);		
		
		//获得新节的基本数据用于之后填入节表 
		PIMAGE_SECTION_HEADER pt=(PIMAGE_SECTION_HEADER)new char[sizeof(IMAGE_SECTION_HEADER)];
		//最后一个节的文件地址+文件大小就是新节的文件地址
		pt->PointerToRawData=aim->psec[last_section].PointerToRawData+aim->psec[last_section].SizeOfRawData; 
		//最后一个节的内存地址+内存大小就是新节的内存地址 
		pt->VirtualAddress=aim->psec[last_section].VirtualAddress+get_vs(aim->psec[last_section].Misc.VirtualSize);
		//新节的文件大小 	
		pt->SizeOfRawData=aim->size;
		//新节的内存大小 
		pt->Misc.VirtualSize=aim->size;
		pt->Characteristics=0x60000020;
	
		//拷贝数据
		memcpy(aim->new_file_buffer,aim->file_buffer,aim->f_s);
		
		//修改nt头数据	 
		aim->pnt->FileHeader.NumberOfSections+=1;
		aim->pnt->OptionalHeader.SizeOfImage+=aim->size;	
					
		//修改数据 	
		memcpy(aim->new_file_buffer+aim->pdos->e_lfanew,aim->pnt,sizeof(IMAGE_NT_HEADERS));		
		//节表填充的位置
		//节表开始的位置+40*节的数量
		int move=a+40*last_section;
		memcpy(aim->new_file_buffer+move,pt,sizeof(IMAGE_SECTION_HEADER));
		//out参数	
		aim->new_file_size=size_new_file_buffer;
		aim->new_section_add=pt->PointerToRawData;			
	}
	else{
		printf("无可扩展大小\n"); 
	}
}

void expand(char* file_name){
	//加密数据
	FILE* fp;
	fp=fopen(file_name,"rb");
	fseek(fp,0,2);
	int size=ftell(fp);
	char* p=new char[size];
	fseek(fp,0,0);
	fread(p,size,1,fp);
	char* new_p=new char[size];
	int i=0;
	while(i<=size){
		*(new_p+i)=*(p+i)^0xff;
		i++;
	}
	
	printf("扩充开始\n");
	int vs_ali=get_vs(size);
	aim=(struct_big*)new char[sizeof(struct_big)];
	aim->file_buffer=file_buffer;	
	aim->new_file_buffer=new_file_buffer;
	aim->pdos=pdos;	
	aim->pnt=pnt;
	aim->psec=psec;
	aim->size=vs_ali;
	aim->f_s=f_s;		
	//判断是否有空间开辟一个节表
	turn_big(aim);
	memcpy(aim->new_file_buffer+aim->new_section_add,new_p,size);
	printf("----------");
}
int main(){
	char* shell_name="C:\\Users\\Ych\\Desktop\\test_壳.exe";
	char* file_name="C:\\Users\\Ych\\Desktop\\PETool 1.0.0.5.exe";
	//分析shell 
	analyse(shell_name);	
	//扩充shell的节
	expand(file_name);
	
	FILE* fp;
	fp=fopen("C:\\Users\\Ych\\Desktop\\shell_file.exe","wb");
	fwrite(aim->new_file_buffer,aim->new_file_size,1,fp);
	printf("写入成功\n");
	fclose(fp);
	return 0;
} 

解密部分:此文件叫做"test_壳.cpp"

#include<stdio.h>
#include<windows.h>
PIMAGE_DOS_HEADER pdos=(PIMAGE_DOS_HEADER)new char[sizeof(IMAGE_DOS_HEADER)];
PIMAGE_NT_HEADERS pnt=(PIMAGE_NT_HEADERS)new char[sizeof(IMAGE_NT_HEADERS)];
PIMAGE_SECTION_HEADER psec,pt,pnewsec;
PIMAGE_DOS_HEADER pnewdos=(PIMAGE_DOS_HEADER)new char[sizeof(IMAGE_DOS_HEADER)];
PIMAGE_NT_HEADERS pnewnt=(PIMAGE_NT_HEADERS)new char[sizeof(IMAGE_NT_HEADERS)];
char* file_buffer;
char* new_file_buffer;
int f_s;
int size;
int size_a;
char* decode_section_add;
LPPROCESS_INFORMATION pi=(LPPROCESS_INFORMATION)new char[sizeof(PROCESS_INFORMATION)];
char* load(char* des,char* res);
char* load_add;
typedef struct p{
	PIMAGE_DOS_HEADER pdos;
	PIMAGE_NT_HEADERS pnt;
	PIMAGE_SECTION_HEADER psec;
	char* file_buffer;
	int size;//需要添加的节的大小 
	char* new_file_buffer;
	int f_s;
	//out
	int new_section_add;
	int new_file_size;
} struct_big; 
struct_big* info=(struct_big*)new char[sizeof(struct_big)];

void analyse(char* shell_name){
	//计算大小 读出文件 
	FILE* fp;
	fp=fopen(shell_name,"rb");
	fseek(fp,0,2);
	f_s=ftell(fp);
	fseek(fp,0,0);
	file_buffer=new char[f_s];
	fread(file_buffer,f_s,1,fp);//读出原来shell文件 
	fseek(fp,0,0);
	
	fread(pdos,sizeof(IMAGE_DOS_HEADER),1,fp);						// 读出文件dos头 
	fseek(fp,pdos->e_lfanew,0);
	fread(pnt,sizeof(IMAGE_NT_HEADERS),1,fp);						// 读出文件nt头
	psec=(PIMAGE_SECTION_HEADER)new char[sizeof(IMAGE_SECTION_HEADER)*(pnt->FileHeader.NumberOfSections+1)]; 
	int a=pnt->FileHeader.SizeOfOptionalHeader+24+pdos->e_lfanew;	// 得出节表所在字节 
	fseek(fp,a,0);
	PIMAGE_SECTION_HEADER ptmp=(PIMAGE_SECTION_HEADER)new char[sizeof(IMAGE_SECTION_HEADER)];
	for(int i=1;i<=pnt->FileHeader.NumberOfSections;i++){//读出节表的信息 
		fread(&psec[i],sizeof(IMAGE_SECTION_HEADER),1,fp);
	}
	info->file_buffer=file_buffer;
	info->f_s=f_s;
	info->pdos=pdos;
	info->pnt=pnt;
	info->psec=psec;
	fclose(fp);	
};

char* read_section(int sec_num,struct_big* info){
	char* get_section_add=(char*)new char[info->psec[sec_num].SizeOfRawData];
	memcpy(get_section_add,info->file_buffer+psec[sec_num].PointerToRawData,info->psec[sec_num].SizeOfRawData);
	printf("检查点");
	return get_section_add;
} 

char* decode(char* get_section_add,int size){
	char* new_p=(char*)new char[size];
	int i=0;
	while(i<=size){//解密此文件 
		*(new_p+i)=*(get_section_add+i)^0xff;
		i++;
	}
	return new_p;
}
void createp(char* file_name){
	LPSTARTUPINFO si=(LPSTARTUPINFO)new char[sizeof(STARTUPINFO)];//创建进程
	memset(si,0,sizeof(STARTUPINFO));
	si->cb=sizeof(STARTUPINFO);
	int ret=CreateProcess(file_name,NULL,NULL,NULL,NULL,CREATE_SUSPENDED,NULL,NULL,si,pi);
	printf("error is %d\n", GetLastError());
	printf("%d\n",ret);
};
BOOL del(){
	typedef long NTSTATUS;
	typedef NTSTATUS(__stdcall * pfnZwUnmapViewOfSection)(unsigned long ProcessHandle, unsigned long BaseAddress);
	pfnZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
	HMODULE hModule = LoadLibrary("ntdll.dll");
	if (hModule)
	{
		ZwUnmapViewOfSection = (pfnZwUnmapViewOfSection)GetProcAddress(hModule, "ZwUnmapViewOfSection");
		if (ZwUnmapViewOfSection)
		{
			printf("%x\n",pi->hProcess);
			printf("%x\n",pnt->OptionalHeader.ImageBase);
			if (ZwUnmapViewOfSection((unsigned long)pi->hProcess,pnt->OptionalHeader.ImageBase))
			{ // 卸载掉 壳子程序自身的ImageBase 地址
				printf("ZwUnmapViewOfSection success\n");
				return 1;
			}
			printf("2222222222222222222222222\n");
			printf("%d\n",GetLastError());
		}
		FreeLibrary(hModule);
		printf("111111111111111111111111\n");
	}
	return 0;
	
}

void alloc(){
	    /*typedef void *(__stdcall *pfVirtualAllocEx)(unsigned long, void *, unsigned long, unsigned long, unsigned long); 
	    pfVirtualAllocEx MyVirtualAllocEx = NULL; 
	    MyVirtualAllocEx = (pfVirtualAllocEx)GetProcAddress(GetModuleHandle("Kernel32.dll"), "VirtualAllocEx");*/
		printf("%d\n",GetLastError());
	int ret=1;
		printf("handle %d\n",pi->hProcess);
		printf("imagebase:%x    sizeofimage:%x\n",pnt->OptionalHeader.ImageBase, pnt->OptionalHeader.SizeOfImage);
	ret=(int)VirtualAllocEx(pi->hProcess, (LPVOID)pnt->OptionalHeader.ImageBase, pnt->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		printf("%d\n",GetLastError());
		printf("%d\n",ret);
	ret=WriteProcessMemory(pi->hProcess, (LPVOID)pnewnt->OptionalHeader.ImageBase,load_add,pnewnt->OptionalHeader.SizeOfImage, NULL);
		printf("%d\n",ret);
		printf("%d\n",GetLastError());
}
char* an_new_file(){
	//分析读出的数据文件 
/*	FILE* fp;
	fp=fopen("C:/Users/Ych/Desktop/123.exe","wb");
	fwrite(decode_section_add,size_a,1,fp);
	fclose(fp);*/
	memcpy(pnewdos,decode_section_add,sizeof(IMAGE_DOS_HEADER));
	memcpy(pnewnt,decode_section_add+pnewdos->e_lfanew,sizeof(IMAGE_NT_HEADERS));
	pnewsec=(PIMAGE_SECTION_HEADER)new char[40*pnewnt->FileHeader.NumberOfSections+1];
	int move=pnewnt->FileHeader.SizeOfOptionalHeader+pnewdos->e_lfanew+24;
	for(int i=1;i<=pnewnt->FileHeader.NumberOfSections;i++){
		memcpy(&pnewsec[i],decode_section_add+move,40);
		move+=40;
	}
	char* load_add=(char*)new char[pnewnt->OptionalHeader.ImageBase];//load文件 
	memset(load_add,0,pnewnt->OptionalHeader.ImageBase);
	return load(load_add,decode_section_add);//文件拉伸 
}
char* load(char* des,char* res){
	memcpy(des,res,pnewnt->OptionalHeader.SizeOfHeaders);
	for(int i=1;i<=pnewnt->FileHeader.NumberOfSections;i++){
		memcpy(des+pnewsec[i].VirtualAddress,res+pnewsec[i].PointerToRawData,pnewsec[i].SizeOfRawData);
	}
	return des;
}
void re_context(){
	CONTEXT cont;
	cont.ContextFlags = CONTEXT_FULL;
	::GetThreadContext(pi->hThread, &cont);
	
	cont.Eax = pnewnt->OptionalHeader.AddressOfEntryPoint + pnewnt->OptionalHeader.ImageBase; // set origin oep
	
	DWORD theOep = cont.Ebx + 8;
	DWORD dwBytes = 0;
	DWORD dwBufferImageBaseSrc=pnewnt->OptionalHeader.ImageBase;
	if(WriteProcessMemory(pi->hProcess, &theOep, &dwBufferImageBaseSrc, 4, &dwBytes)){
		printf("写入完成\n");
	}
	
	if(SetThreadContext(pi->hThread, &cont)){
		printf("设置完成\n");	
	}
	//记得恢复线程
	if(ResumeThread(pi->hThread)){
		printf("恢复完成\n");	
	}	
} 
int main(int arg,char* argv[]){
	analyse(argv[0]);//分析
	printf("分析完成\n");
	char* get_section_add=read_section(pnt->FileHeader.NumberOfSections,info);		//读取节 需要参数 第几个节
	printf("读取完成\n");
	int size=psec[pnt->FileHeader.NumberOfSections].SizeOfRawData;
	size_a=size;
	decode_section_add=decode(get_section_add,size);								//解密
	printf("解密完成\n");

	createp(argv[0]);																//创建进程 并获得context;
	if(del()==0){
		printf("卸载失败\n");
	}
	else{
		printf("卸载成功\n");
	}//卸载壳
	load_add=an_new_file();															//分析new_file_buffer  拉伸
	printf("拉升完成\n");
	alloc();																		//安装新的程序 
	printf("安装完成\n");
	re_context();		//恢复线程
	printf("恢复完成\n");
	getchar();
	return 0;
}

在这里插入图片描述
使用了PETools这个小工具作为实验对象,生产的加壳程序是shell_file.exe

执行shell_file.exe程序
在这里插入图片描述得到的结果就是就是上图PETools程序正常启动

因为写博客的时候,离完成这个小东西已经过了将近一年时间了,这一年对于相关内容的学习没有啥进步,所以具体的实现细节难免忘记,如果博客中出现错误欢迎指出;如果你有任何疑问,可以私聊我,如果具体细节我还有找出,一定帮你解答。
另外,这些代码应该只是对32位程序有效,64位程序可能会失效,应该是吧(比较玄学),反正对于一些程序是不能生效的,请原谅我能力有限。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值