进程4GB空间简析,PE重定位表【滴水逆向三期50笔记+作业】

这一章我们继续来了解数据目录,我们来了解数据目录的第6个结构:重定位表。

一.进程4GB空间

在了解重定位表之前,我们先来了解进程4GB空间,这有助于我们理解为什么PE中需要重定位表。
当我们双击一个exe程序时,操作系统会为它分配虚拟的4GB空间(注意这里是虚拟的4GB空间,不同于物理4GB空间,如果是真实的4GB空间的话,我们的机器运行不了几个程序就会挂掉)。
那么操作系统是如何分配这4GB的空间的呢?我们来简单解析一下:
首先我们给出一张图,帮助我们理解:
进程4GB空间
1.低2G为用户空间,前后64K不会被分配,0-FFFF用于做各种无效检查(空指针就指向这里)
2.后64K :7FFF0000 - 8000000 用来做内核的交互。在这一部分,会加载exe和dll,通常情况下,exe会调用dll文件,而dll文件会调用其他的dll文件。
因为打开的是该exe进程,则这块空间最先由 exe 占第一块空间。所以一般情况下,exe都是可以按照 ImageBase 的地址进行加载的,因为exe有自己独立的4GB虚拟空间。而 ImageBase 通常是 400000H。但dll不是 dll是有exe或其他 dll使用它,它才加载到相关EXE的进程空间的。
3.高2G空间是内核使用的。
4.为了提高搜索速度,模块之间也是要对齐的,模块地址对齐为1000h,也就是64k。

二.为什么要有重定位表

我们打开一个exe程序,观察它的反汇编:
反汇编
这里我们查看的时printf函数的地址,通过前面章节的学习,我们知道E8是call的硬编码,E8后面跟着的四字节数据就是函数地址,我们会发现:这个函数地址是直接被编译器写入文件的,那么我们来做一个假设:dll每次被加载的基址不同,如果程序继续通过这个地址去找函数的话,那不是就出错了吗?
这时候重定位表的作用就体现出来了:重定位表记录下文件中需要改变的地址的位置,程序在运行的时候,通过重定位表修改函数地址和其他需要修改的值,以保证程序正常运行。
也就是说,如果程序每次都按照ImageBase来加载的话,那么就不需要改变函数地址,也就不需要重定位表了,这个是为什么exe中很少有重定位表(因为exe有自己独立的4GB空间,exe在每次加载的时候通常可以按照预定的IamgeBase来加载)的原因。
如果某个模块没有按照预定的ImageBase加载,那么像函数地址这些都需要修改,而重定位表记录这些需要修改的数据。

三.重定位表

1.如何定位重定位表:

前一章我们已经提及数据目录,数据目录的第6个结构,就记录了重定位表的地址。

2.重定位表的结构:

我们来看看重定位表的结构:

typedef struct _IMAGE_BASE_RELOCATION{
DWORD VirtualAddress;//重定位内存页的起始RVA
DWORD SizeOfBlock;//重定位块的大小
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED *PIMAGE_BASE_RELOCATION;

需要注意的是,如果要在FileBufer中找到重定位表,需要将数据目录第6个结构中的VirtualAddress转换为FOA。
在重定位内存页中,有若干个上述IMAGE_BASE_RELCOATION结构,而在每个结构后,有一大块数据,就记录了需要修改的地址,被称为具体项。
我们给出图示来帮助我们了解:
重定位表内存页
解释说明:
1.通过IMAGE_DATA_DIRECTORY结构的第6个结构的VirtualAddress属性,找到第一个IMAGE_BASE_RELOCATION;
2.最后一个块:最后一块的VirtualAddress和SizeOfBlock的值都为0
3.具体项:
内存页中的大小为1000h,也就是说,用2的12次方就可以表示一个内存页中所有的地址偏移
具体项的宽度为16位,用低12位表示需要修改的数据的地址,高3位代表类型,高三位总是为011或000,011表示该项需要修改,000表示该项不需要修改,我们只需要关注高三位为011的就可以了
4.VirtualAddress:宽度为4字节
具体项中的地址为偏移,加上VirtualAddress才是真正要修改的地址
5.SizeOfBlock:当前块的总大小具体项的数量=(SizeOfBlock的值-8)/2

四.作业

根据以上对重定位表的介绍,我们可以打印出重定位表的信息(不需要打印具体项,但是要打印出具体项的数量)
这里给出程序源码:
main.c

#include <stdio.h>
#include <windows.h>
#include "PEFunction.h"
int main(int argc, char** argv) {
	char filename[50];
	char* FileBuffer=NULL;
	printf("*******************************打印PE重定位表程序*******************************\n");
	printf("---------------------------版权所有:https://blog.csdn.net/qq_73985089?type=blog\n");
	printf("----------------------------------------------------------如需引用,请告知原作者!"); 
	printf("请输入您需要按操作的文件路径:");
	scanf("%s",filename);
	FileBuffer=ReadToFileBuffer(filename);
	ShowBaseRelocation(FileBuffer);
	free(FileBuffer);
	return 0;
}

PEFunction.h

/********1.打开文件函数(将文件二进制读取到FileBuffer) *******/
/*函数说明:
该函数需要一个char类型的指针,指向想要打开的文件路径
该函数返回一个char类型的指针,指向FileBuffer
该函数完成文件的打开,并且将文件的二进制读取到FIleBuffer
该函数读取完成后会将文件关闭,避免出现误操作
该函数会通过移动文件指针获取文件大小
获取的文件大小用于动态申请内存 
该函数会动态申请内存,用于存放FileBuffer
*/ 
char* ReadToFileBuffer(char* filename);
/**********************2.获取PE导出表数据函数********************
该函数需要一个指针,指向FileBuffer
该函数在找到导出表之前,需要定义DOS结构指针,标准PE头结构指针和可选PE头指针,以方便找到导出表
该函数会在控制台输出函数地址表,函数名称表和函数导出序号表 */
void ShowExportDirectory(char* FileBuffer);
/***********************3.RVA转换为FOA函数*************************
该函数需要一个指针,指向FileBuffer
该函数需要一个整数,为要转换的RVA
该函数返回一个整数,为转换后的FOA地址*/ 
int RVAToFOA(IN LPVOID pFileBuffer, IN DWORD dwRva);
/***********************4.根据函数名称查找函数地址函数***********************
该函数需要一个指针,指向要打开的文件路径 
该函数返回一个整数,指向得到的函数地址
*/
void GetAddressOfFunctionByName(char* filename);
/***********************5.根据函数序号查找函数地址函数***********************
该函数需要一个指针,指向要打开的文件路径 
该函数返回一个整数,指向得到的函数地址
*/
void GetAddressOfFunctionByOrdinal(char* filename);
/***********************6.打印PE重定位表函数***********************
该函数需要一个指针,指向FIleBuffer
该函数会在控制台打印出重定位表信息(重定位表偏移,重定位表大小,但不打印重定位表具体项*/
void ShowBaseRelocation(char* FileBuffer);

Function.c

/**************************************************************/ 
//*********************自编PE函数库****************************/
#include <stdio.h>
#include <windows.h>


/********1.打开文件函数(将文件二进制读取到FileBuffer) *******/
/*函数说明:
该函数需要一个char类型的指针,指向想要打开的文件路径
该函数返回一个char类型的指针,指向FileBuffer
该函数完成文件的打开,并且将文件的二进制读取到FIleBuffer
该函数读取完成后会将文件关闭,避免出现误操作
该函数会通过移动文件指针获取文件大小
获取的文件大小用于动态申请内存 
该函数会动态申请内存,用于存放FileBuffer
*/ 
char* ReadToFileBuffer(char* filename){
	FILE* fp;
	char* FileBuffer=NULL;
	int length;
	if((fp=fopen(filename,"rb"))==NULL){
		printf("%s打开失败!",filename);
		exit(0);
	}else{
		printf("文件打开成功,正在计算文件大小...\n");
	}
	fseek(fp,0,SEEK_END);
	length=ftell(fp);
	fseek(fp,0,SEEK_SET);
	printf("文件大小:%d字节\n正在申请FileBuffer内存...\n",length);
	FileBuffer=(char*)malloc(length);
	if(FileBuffer!=NULL){
		printf("FileBuffer内存申请成功。准备向FileBuffer写入数据...\n");
	}
	if((fread(FileBuffer,length,1,fp))==0){
		printf("写入数据失败!\n");
		exit(0);
	}else{
		printf("写入数据成功。\n");
	}
	if(fclose(fp)){
		printf("文件关闭失败!\n");
		exit(0);
	}else{
		printf("文件关闭成功。\n");
	}
	return FileBuffer;
}
/**********************2.获取PE导出表数据函数********************
该函数需要一个指针,指向FileBuffer
该函数在找到导出表之前,需要定义DOS结构指针,标准PE头结构指针和可选PE头指针,以方便找到导出表
该函数会在控制台输出函数地址表,函数名称表和函数导出序号表 */
void ShowExportDirectory(char* FileBuffer){
	IMAGE_DOS_HEADER* pDosHeader=NULL;
	IMAGE_FILE_HEADER* pFileHeader=NULL;
	IMAGE_OPTIONAL_HEADER32* pOptionalHeader=NULL;
	IMAGE_EXPORT_DIRECTORY* pExportDirectory=NULL;
	pDosHeader=(IMAGE_DOS_HEADER*)FileBuffer;
	if(*((PDWORD)((DWORD)FileBuffer + pDosHeader->e_lfanew))!= IMAGE_NT_SIGNATURE){
		printf("不是有效的PE标志!\n");
	}else{
		printf("检测到有效的PE标志。\n");
	}
	pFileHeader=(IMAGE_FILE_HEADER*)(FileBuffer+pDosHeader->e_lfanew+4);
	pOptionalHeader=(IMAGE_OPTIONAL_HEADER32*)((DWORD)pFileHeader+20);
	pExportDirectory = (IMAGE_EXPORT_DIRECTORY*)(FileBuffer+RVAToFOA(FileBuffer,pOptionalHeader->DataDirectory[0].VirtualAddress));
	printf("导出表RVA:%x\n",pOptionalHeader->DataDirectory[0].VirtualAddress);
	printf("导出表大小:%x字节\n",pOptionalHeader->DataDirectory[0].Size);
	printf("导出表FOA:%d\n",RVAToFOA(FileBuffer,pOptionalHeader->DataDirectory[0].VirtualAddress));
	printf("*******************************导出表*********************************\n");
	printf("TimeDataStamp(经加密):%d\n",pExportDirectory->TimeDateStamp);
	printf("Name(导出表文件名字符串):%s\n",FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->Name));
	printf("Base(函数起始序号):%d\n",pExportDirectory->Base);
	printf("NumberOfFunction(导出函数总数):%d\n",pExportDirectory->NumberOfFunctions);
	printf("NumberOfNames(以名称导出函数的总数):%d\n",pExportDirectory->NumberOfNames);
	printf("*****************************函数地址表********************************\n");
	int i=0;
	char* AddressOfFunction;
	AddressOfFunction=(char*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfFunctions));
	for(i=0;i<pExportDirectory->NumberOfFunctions;i++){
		printf("下标:%d     函数地址:0x%x\n",i,*(PWORD)((DWORD)AddressOfFunction+i*4));
	}
	printf("*****************************函数名称表********************************\n");
	int* AddressOfNames=NULL;
	char* name=NULL;
	AddressOfNames=(int*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfNames));
	for(i=0;i<pExportDirectory->NumberOfNames;i++){
		name=(char*)(FileBuffer+RVAToFOA(FileBuffer,*AddressOfNames));
		printf("下标:%d     函数名称:%s\n",i,name);
		AddressOfNames++;
	}
	printf("*******************************函数序号表*******************************\n");
	char *AddressOfNameOrdinals=NULL;
	char *Ordinal=NULL;
	AddressOfNameOrdinals=(int*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfNameOrdinals));
	for(i=0;i<pExportDirectory->NumberOfNames;i++){
		Ordinal=(int*)(FileBuffer+RVAToFOA(FileBuffer,*AddressOfNameOrdinals));
		printf("下标:%d      函数序号(加Base):%d\n",i,*(AddressOfNameOrdinals+i*2)+pExportDirectory->Base);
	} 
}
/***********************3.RVA转换为FOA函数*************************
该函数需要一个指针,指向FileBuffer
该函数需要一个整数,为要转换的RVA
该函数返回一个整数,为转换后的FOA地址*/ 
int RVAToFOA(IN LPVOID pFileBuffer, IN DWORD dwRva)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	PIMAGE_SECTION_HEADER pNextSectionHeader = NULL;
	//DOS头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	if (dwRva < pOptionalHeader->SizeOfHeaders)	//偏移小于头的大小,内存偏移则为文件偏移
	{
		return dwRva;
	}
    //首个节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	//下一个节表
	pNextSectionHeader = pSectionHeader + 1;
	//循环遍历节表
	int i;
	for (i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++, pNextSectionHeader++)//注意这里i从1开始 i < NumberOfSections
	{	//注意这里的pSectionHeader已经是加了基址的,不是偏移, 是绝对地址。而dwRva是偏移地址
		if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pNextSectionHeader->VirtualAddress)//大于当前节的内存偏移而小于下一节的内存偏移
		{	//则dwRva属于当前节,则dwRva - VirtualAddress为dwRva基于当前节的偏移。此偏移加上当前节的文件起始偏移地址 则为dwRva在文件中的偏移
			DWORD PointerToRawData = pSectionHeader->PointerToRawData;
			DWORD VirtualAddress = pSectionHeader->VirtualAddress;
			DWORD aa = pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
			return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress ;
		}
	}	//出循环后pSectionHeader指向最后一个节表
	//大于当前节(最后一节)的内存偏移且小于内存映射大小
	if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pOptionalHeader->SizeOfImage)
	{	//同上return
		return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
	}
	else //大于内存映射大小
	{
		printf("dwRva大于内存映射大小\n");
		return -1;
	}
}
/***********************4.根据函数名称查找函数地址函数***********************
该函数需要一个指针,指向要打开的文件路径 
该函数返回一个整数,指向得到的函数地址
*/
void GetAddressOfFunctionByName(char* filename){
	char* FileBuffer=NULL;
	char inname[5];
	int i;
	FileBuffer=ReadToFileBuffer(filename);
	IMAGE_DOS_HEADER* pDosHeader=NULL;
	IMAGE_FILE_HEADER* pFileHeader=NULL;
	IMAGE_OPTIONAL_HEADER32* pOptionalHeader=NULL;
	IMAGE_EXPORT_DIRECTORY* pExportDirectory=NULL;
	pDosHeader=(IMAGE_DOS_HEADER*)FileBuffer;
	if(*(PWORD)((DWORD)FileBuffer+pDosHeader->e_lfanew)==IMAGE_NT_SIGNATURE){
		printf("检测到有效的PE标志。\n");
	}else{
		printf("未检测到有效的PE标志!\n");
		exit(0);
	}
	pFileHeader=(IMAGE_FILE_HEADER*)((DWORD)FileBuffer+pDosHeader->e_lfanew+4);
	pOptionalHeader=(IMAGE_OPTIONAL_HEADER32*)((DWORD)pFileHeader+20);
	pExportDirectory = (IMAGE_EXPORT_DIRECTORY*)(FileBuffer+RVAToFOA(FileBuffer,pOptionalHeader->DataDirectory[0].VirtualAddress));
	int* AddressOfNames=NULL;
	char* name=NULL;
	AddressOfNames=(int*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfNames));
	
	printf("请输入您需要查找的函数名:");
	scanf("%s",inname);
	printf("正在查找中...\n");
	for(i=0;i<pExportDirectory->NumberOfNames;i++){
	name=(int*)(FileBuffer+RVAToFOA(FileBuffer,RVAToFOA(FileBuffer,*AddressOfNames)));
		if(!(strcmp("Plus",name))){
			printf("下标为%d的函数名符合。\n",i);
			break;
		}
		AddressOfNames++;
	}
	char* Ordinal=NULL;
	Ordinal=(char*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfNameOrdinals)+i*2);
	char* AddressOfFunction=NULL;
	AddressOfFunction=(char*)(FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfFunctions)+*Ordinal*4);
	printf("Ordinal(函数导出序号(未加Base)):%d    AddressOfFunction:0x%x",*Ordinal,*(PWORD)((DWORD)AddressOfFunction));
}
/***********************5.根据函数序号查找函数地址函数***********************
该函数需要一个指针,指向要打开的文件路径 
该函数返回一个整数,指向得到的函数地址
*/
void GetAddressOfFunctionByOrdinal(char* filename){
	char* FileBuffer=NULL;
	int InOrdinal=0;
	IMAGE_DOS_HEADER* pDosHeader=NULL;
	IMAGE_FILE_HEADER* pFileHeader=NULL;
	IMAGE_OPTIONAL_HEADER32* pOptionalHeader=NULL;
	IMAGE_EXPORT_DIRECTORY* pExportDirectory=NULL;
	FileBuffer=ReadToFileBuffer(filename);
	char* AddressOfFunction;
	pDosHeader=(IMAGE_DOS_HEADER*)FileBuffer;
	if(*(PWORD)((DWORD)FileBuffer+pDosHeader->e_lfanew)==IMAGE_NT_SIGNATURE){
		printf("检测到PE标志。");
	}else{
		printf("未检测到PE标志!\n");
		exit(0);
	}
	pFileHeader=(IMAGE_FILE_HEADER*)(FileBuffer+pDosHeader->e_lfanew+4);
	pOptionalHeader=(IMAGE_OPTIONAL_HEADER32*)((DWORD)pFileHeader+20);
	
	pExportDirectory = (IMAGE_EXPORT_DIRECTORY*)(FileBuffer+RVAToFOA(FileBuffer,pOptionalHeader->DataDirectory[0].VirtualAddress));
	printf("请输入您需要查找的函数导出序号:");
	scanf("%d",&InOrdinal);
	InOrdinal=InOrdinal-pExportDirectory->Base;
	AddressOfFunction=(char*)FileBuffer+RVAToFOA(FileBuffer,pExportDirectory->AddressOfFunctions+InOrdinal*4);
	printf("函数导出序号(未加Base):%d       函数地址:0x%x",InOrdinal,*(PWORD)((DWORD)AddressOfFunction));
}
/***********************6.打印PE重定位表函数***********************
该函数需要一个指针,指向FIleBuffer
该函数会在控制台打印出重定位表信息(重定位表偏移,重定位表大小,但不打印重定位表具体项*/
void ShowBaseRelocation(char* FileBuffer){
	int i=1;
	IMAGE_DOS_HEADER* pDosHeader=NULL;
	IMAGE_OPTIONAL_HEADER32* pOptionalHeader=NULL;
	IMAGE_BASE_RELOCATION* pBaseRelocation=NULL;
	pDosHeader=(IMAGE_DOS_HEADER*)FileBuffer;
	if(*(PWORD)((DWORD)FileBuffer+pDosHeader->e_lfanew)==IMAGE_NT_SIGNATURE){
		printf("检测到有效的PE标志。\n");
	}else{
		printf("未检测到有效的PE标志!\n");
		exit(0);
	}
	pOptionalHeader=(IMAGE_OPTIONAL_HEADER32*)((DWORD)FileBuffer+pDosHeader->e_lfanew+24);
	pBaseRelocation=(IMAGE_BASE_RELOCATION*)((DWORD)FileBuffer+RVAToFOA(FileBuffer,pOptionalHeader->DataDirectory[5].VirtualAddress));
	printf("%x",pOptionalHeader->DataDirectory[5].VirtualAddress);
	while(pBaseRelocation->VirtualAddress&&pBaseRelocation->SizeOfBlock){
		printf("************                 第%d块                 ************\n",i);
		printf("VirtulaAddress(重定位表在文件中的偏移):0x%x\n",RVAToFOA(FileBuffer,pBaseRelocation->VirtualAddress));
		printf("Size(该块重定位表的大小):%d字节\n",pBaseRelocation->SizeOfBlock);
		printf("重定位表具体项数目:%d\n\n",(pBaseRelocation->SizeOfBlock-8)/2);
		pBaseRelocation = (IMAGE_BASE_RELOCATION*)(pBaseRelocation->SizeOfBlock + (DWORD)pBaseRelocation);
		i++;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第1讲:2015-01-12(进制01) 第2讲:2015-01-13(进制02) 第3讲:2015-01-14(数据宽度-逻辑运算03) 第4讲:2015-01-15(通用寄存器-内存读写04) 第5讲:2015-01-16(内存寻址-堆栈05) 第6讲:2015-01-19(EFLAGS寄存器06) 第7讲:2015-01-20(JCC) 第8讲:2015-01-21(堆栈图) 第8讲:2015-01-21(宝马问题) 第9讲:2015-01-22(堆栈图2) 第10讲:2015-01-23(C语言01_后半段) 第10讲:2015-01-23(C语言完整版) 第11讲:2015-01-26(C语言02_数据类型) 第12讲:2015-01-27(C语言03_数据类型_IF语句) 第13讲:2015-01-28(C语言04_IF语句逆向分析上) 第14讲:2015-01-28(C语言04_IF语句逆向分析下) 第15讲:2015-01-29(C语言04_正向基础) 第16讲:2015-01-30(C语言05_循环语句) 第17讲:2015-02-02(C语言06_参数_返回值_局部变量_数组反汇编) 第18讲:2015-02-02(2015-01-30课后练习) 第19讲:2015-02-03(C语言07_多维数组) 第20讲:2015-02-03(2015-02-02课后练习) 第21讲:2015-02-04(C语言08_结构体) 第22讲:2015-02-05(C语言09_字节对齐_结构体数组) 第23讲:2015-02-06(C语言10_Switch语句反汇编) 第24讲:2015-02-26(C语言11_指针1) 第25讲:2015-02-27(C语言11_指针2) 第26讲:2015-02-28(C语言11_指针3) 第27讲:2015-02-28(C语言11_指针4) 第28讲:2015-03-02(C语言11_指针5) 第29讲:2015-03-03(C语言11_指针6) 第30讲:2015-03-04(C语言11_指针7) 第31讲:2015-03-06(C语言11_指针8) 第32讲:2015-03-09(位运算) 第33讲:2015-03-10(内存分配_文件读写) 第34讲:2015-03-11(PE头解析_手动) 第35讲:2015-03-12(PE头字段说明) 第36讲:2015-03-13(PE) 第37讲:2015-03-16(FileBuffer转ImageBuffer) 第38讲:2015-03-17(代码节空白区添加代码) 第39讲:2015-03-18(任意节空白区添加代码) 第40讲:2015-03-19(新增节添加代码) 第41讲:2015-03-20(扩大节-合并节-数据目录) 第42讲:2015-03-23(静态连接库-动态链接库) 第43讲:2015-03-24(导出) 第44讲:2015-03-25(定位) 第45讲:2015-03-26(移动导出-定位) 第46讲:2015-03-27(IAT) 第47讲:2015-03-27(导入) 第48讲:2015-03-30(绑定导入) 第49讲:2015-03-31(导入注入) 第50讲:2015-04-01(C++ this指针 类 上) 第51讲:2015-04-01(C++ this指针 类 下) 第52讲:2015-04-02(C++ 构造-析构函数 继承) 第53讲:2015-04-03(C++ 权限控制) 第54讲:2015-04-07(C++ 虚函数) 第55讲:2015-04-08(C++ 动态绑定-多态-上) 第56讲:2015-04-08(C++ 动态绑定-多态-下) 第57讲:2015-04-09(C++ 模版) 第58讲:2015-04-10(C++ 引用-友元-运算符载) 第59讲:2015-04-13(C++ new-delete-Vector) 第60讲:2015-04-14(C++Vector实现) 第61讲:2015-04-15(C++链) 第62讲:2015-04-16(C++链实现) 第63讲:2015-04-16(C++二叉树) 第64讲:2015-04-17(C++二叉树实现) 第65讲:2015-04-20(Win32 宽字符) 第66讲:2015-04-21(Win32 事件-消息-消息处理函数) 第67讲:2015-04-22(Win32 ESP寻址-定位回调函数-条件断点) 第68讲:2015-04-23(Win32 子窗口-消息处理函数定位) 第69讲:2015-04-24(Win32 资源文件-消息断点) 第70讲:2015-04-27(Win32 提取图标-修改标题) 第71讲:2015-04-28(Win32 通用控件-VM_NOTIFY) 第72讲:2015-04-29(Win32 PE查看器-项目要求) 项目一:PE查看器 开发周期(5天) 需求文档 第73讲:2015-05-07(Win32 创建线程) 第74讲:2015-05-08(Win32 线程控制_CONTEXT) 第75讲:2015-05-11(Win32 临界区) 第76讲:2015-05-12(Win32 互斥体) 第77讲:2015-05-13(Win32 事件) 第78讲:2015-05-14(Win32 信号量) 第79讲:2015-05-15(Win32 线程同步与线程互斥) 第80讲:2015-05-18(Win32 进程创建_句柄) 第81讲:2015-05-20(Win32 以挂起形式创建进程) 第82讲:2015-05-21(Win32 加密壳_项目说明) 项目二:加密壳 开发周期(5天) 需求文档 第83讲:2015-05-28(Win32 枚举窗口_鼠标键盘事件) 第84讲:2015-05-29(Win32 CE练习) 第85讲:2015-06-01(Win32 OD练习) 第86讲:2015-06-03(Win32 ShellCode_远程线程注入) 第87讲:2015-06-04(Win32 加载EXE_模块隐藏) 第88讲:2015-06-09(Win32 IAT_HOOK) 第89讲:2015-06-10(Win32 InlineHook) 第90讲:2015-06-11(Win32 进程通信) 第91讲:2015-06-11(Win32 进程监控_项目说明) 项目三:进程监控 开发周期(5天) 需求文档 第92讲:2015-06-15(硬编码_01) 第93讲:2015-06-16(硬编码_02) 第94讲:2015-06-17(硬编码_03) 第95讲:2015-06-18(硬编码_04) 第96讲:2015-06-19(硬编码_05)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shad0w-2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值