滴水逆向-文件读写&内存分配-PE准备阶段
相关练习验证测试代码及课后练习
1.宏定义说明: 一、无参数的宏定义的一般形式为:# define 标识符 字符序列 如: #define TRUE 1 #define FALSE 0 int fun() { return TRUE; } #define PI 3.1415926 double Function(int r) { return 2*PI*r; } #define DEBUG 1 void Function() { //.... if(DEBUG) printf("测试信息"); } 注意事项: 1.只作字符序列的替换工作,不作任何语法的检查 2.如果宏定义不当,错误要到预处理之后的编译阶段才能发现 测试验证代码 #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <malloc.h> #define TRUE 998 #define FALSE 668 #define PI 3.1415926 #define DEBUG 1 int fun1() { return TRUE; } double fun2(int r) { return 2*PI*r; } void fun3() { //.... if(DEBUG) printf("Testing information\n"); } int main(int argc, char* argv[]) { int x = fun1(); printf("%d \n",x); float y = fun2(2); printf("%f \n",y); fun3(); return 0; } 二、带参数宏定义:#define 标识符(参数表)字符序列 #define MAX(A,B) ((A) > (B)?(A):(B)) 代码 x= MAX(p,q)将被替换成 y=((p) >(q)?(p):(q) 注意事项: 1.宏名标识符与左圆括号之间不允许有空白符,应紧接在一起. 2.宏与函数的区别:函数分配额外的堆栈空间,而宏只是替换. 3.为了避免出错,宏定义中给形参加上括号. 4.末尾不需要分号. 5.define可以替代多行的代码,记得后面加 \ #define MALLOC(n,type)\ ((type*)malloc((n)*sizeof(type))) 验证测试代码 #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <malloc.h> #define MAX(A,B) ((A) > (B)?(A):(B)) // 这个的功能就是比较大小,谁大输出谁 int fun() { return MAX(9,18); } int main(int argc, char* argv[]) { int x = fun(); printf("%d \n",x); return 0; } 2.头文件的使用 测试验证 步骤一: #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <malloc.h> void fun( ) { printf("Hello World!\n"); } int main(int argc, char* argv[]) { fun(); return 0; } 可以正常执行 如果换成: #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <malloc.h> int main(int argc, char* argv[]) { fun(); return 0; } void fun( ) { printf("Hello World!\n"); } 不能正常执行! error C2065: 'fun' : undeclared identifier error C2373: 'fun' : redefinition; different type modifiers 显示无法识别定义的函数,所以为了解决这类问题,就有了下面的办法 解决办法:新增头文件(.h),在.h文件中对函数进行说明 具体在VC6++上操作:在classes上右键新建,写一个名字x和y的两个文件,此时会在File栏目生成.cpp和.h的文件 (如果是C语言就是生成.c 如果C++那么就是生成.cpp) .cpp文件在Source Files .h文件在Header Files 例如: x.cpp文件的文件内容如下: // x.cpp: implementation of the x class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "x.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// void funx() { printf("Hello x\n"); } y.cpp文件的文件内容如下: // y.cpp: implementation of the y class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "y.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// void funy() { printf("Hello y\n"); } x.h文件的文件内容如下 // x.h: interface for the x class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_X_H__E1420025_F5C1_4883_8C12_957B2D2EEFD3__INCLUDED_) #define AFX_X_H__E1420025_F5C1_4883_8C12_957B2D2EEFD3__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 void funx(); #endif // !defined(AFX_X_H__E1420025_F5C1_4883_8C12_957B2D2EEFD3__INCLUDED_) y.h文件的文件内容如下 // y.h: interface for the y class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_Y_H__0CCFD82D_D027_419E_810C_22E1F91EA018__INCLUDED_) #define AFX_Y_H__0CCFD82D_D027_419E_810C_22E1F91EA018__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 void funy(); #endif // !defined(AFX_Y_H__0CCFD82D_D027_419E_810C_22E1F91EA018__INCLUDED_) 下面是在上面的基础上,在主main函数下加载函数名称并执行,下面是主文件的源代码 // sjlx.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "x.h" #include "y.h" int main(int argc, char* argv[]) { funx(); funy(); return 0; } 上述可以编译成功并执行,打印结果如下: Hello x Hello y Press any key to continue 3.重复包含的问题 什么是重复包含问题: x.h #include z.h y.h #include z.h z.h struct Student { int level; }; 上面是课堂给的例子,我这里测试就更改下,例子依然是2.中头文件使用的那个例子 如果此时有个文件同时包含了x.h和y.h会出问题。 例如: #include "stdafx.h" #include "x.h" #include "y.h" int main(int argc, char* argv[]) { return 0; } 解决方案 #if !defined(ZZZ) //这句话的意思可以这样去理解,如果ZZZ已经存在了,就不在声明 #define ZZZ //ZZZ相当于一个编号,越复杂越好,唯一的. struct Student { int level; }; #endif 4.内存分配与释放 malloc函数使用的例子 #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <malloc.h> void fun( ) { char* string; /* Allocate space for a path name */ string = (char*) malloc( _MAX_PATH ); if( string == NULL ) printf( "Insufficient memory available\n" ); else { printf( "Memory space allocated for path name\n" ); free( string ); printf( "Memory freed\n" ); } } int main(int argc, char* argv[]) { fun(); return 0; } malloc函数的使用和解释可参考下面链接 https://blog.csdn.net/wang13342322203/article/details/80862382 malloc函数使用流程 //声明指针 int* ptr; //在堆中申请内存,分配128个int ptr = (int *)malloc(sizeof(int)*128); //无论申请的空间大小,一定要进行校验判断是否申请成功 if(ptr == NULL) { return 0; } //初始化分配的内存空间 memset(ptr,0,sizeof(int)*128); //开始使用 *(ptr) = 1; //使用完毕 释放申请的堆空间 free(ptr); //将指针设置为NULL ptr = NULL; 下面是测试验证代码,可以正常运行 #include "stdafx.h" #include <malloc.h> #include <memory.h> int fun() { //什么指针 int* ptr; //在堆中申请内存,分配128个int ptr = (int*)malloc(sizeof(int)*128); //无论申请空间的大小,一定要先进行判断内存申请是否成功 if (ptr == NULL) { return 0; } //初始化分配的内存空间 memset(ptr,0,sizeof(int*)128); //开始使用内存 *(ptr) = 1; //使用完内存,要释放申请的堆空间 free(ptr); //将指针设置为NULL ptr = NULL; printf("%x \n",ptr); } int main(int argc, char* argv[]) { fun(); printf("Hello World!\n"); return 0; } 5.文件读写相关函数 可以参考下面网站: http://c.biancheng.net/cpp/u/stdio_h/ (1)fopen函数 打开文件函数 (打开一个文件并返回文件指针) (2)fseek函数 查找文件头或者尾函数(移动文件的读写指针到指定的位置) (3)ftell函数 定位指针函数 (获取文件读写指针的当前位置) (4)fclose函数 关闭文件函数 (关闭文件流) (5)fread函数 读取文件内容函数 (从文件流中读取数据) fopen函数相关参考: https://www.runoob.com/cprogramming/c-function-fopen.html http://c.biancheng.net/cpp/html/250.html https://www.cnblogs.com/sky-of-chuanqingchen/p/4123163.html 测试的例子 #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> #define F_PATH "C:\\cntflx\\notepad.exe" void pe_openfiles() { FILE* fstream; fstream = fopen(F_PATH,"ab+"); if (fstream == NULL) { printf("Open the notepad failed\n"); exit(1); } else { printf("Open the notepad success!\n"); printf("%x \n",fstream); } fclose(fstream); //return 0; } fseek函数相关参考: https://blog.csdn.net/yutianzuijin/article/details/27205121 ftell函数相关参考: http://c.biancheng.net/cpp/html/2519.html int pe_getfile_size() { FILE* fp=fopen(F_PATH,"r"); if (!fp) { return -1; } fseek(fp,0L,SEEK_END); int size = ftell(fp); fseek(fp,0,SEEK_SET); fclose(fp); return size; } fclose函数相关参考: http://c.biancheng.net/cpp/html/2505.html fread函数相关参考: http://c.biancheng.net/cpp/html/2516.html C语言获取文件大小 https://blog.csdn.net/yutianzuijin/article/details/27205121 课后练习 1.用十六进制文本编辑器,打开一个记事本的.exe文件,再打开在内存中的记事本进程,记录下这两个文件的不同. 有两处不同 操作方式: (1)使用winhex打开在硬盘上没有执行的notepad.exe文件,此时是没被执行的普通文件内容 (2)正常执行notepad.exe文件,然后最小化,再使用winhex的工具选项-打开RAM内存--选择名称为notepad.exe的内容 编号不同,没有运行前的编号为00000000开头,运行后变成了0100000开头 数据不同:运行前的部分内容和运行后的部分内容有一点相同的,到后面就不同了 2.将记事本的.exe文件读取到内存,并返回读取后在内存中的地址. 大概思路: (1)打开文件 (2)得到文件的大小 --> 读取文件到内存,然后跳转到文件末尾,查看跳转的长度 (3)根据大小申请内存 (4)把文件中内容读取到内存里 (5)返回内存编号 记事本路径:C:\cntflx #define F_PATH "C:\\cntflx\\notepad.exe" 第一种写法: #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> #define F_PATH "C:\\cntflx\\notepad.exe" int Pe_Getfile_Size() { FILE* fp=fopen(F_PATH,"r"); if (!fp) { return -1; } fseek(fp,0L,SEEK_END); int size = ftell(fp); fseek(fp,0,SEEK_SET); fclose(fp); return size; } int FileSizes = Pe_Getfile_Size(); int Pe_ReadMemtory_addrs1() { //定义一个文件的指针,并初始化其为NULL FILE* fstream = NULL; //初始化exe文件长度 int FstreamSizes = 0; //准备打开文件notepad.exe ,读写,且是读二进制文件 fstream = fopen(F_PATH,"ab+"); //获取打开文件的exe大小 FstreamSizes = FileSizes; // printf("%d \n",FstreamSizes); //申请动态内存指向FileBuffer int* FileBuffer = (int*)malloc(FstreamSizes); //判断申请的内存是否成功,不成功就返回0,成功就开始读exe内容写入申请的内存中 if (FileBuffer == NULL) { return 0; } else { fread(FileBuffer,FstreamSizes,1,fstream); } memset(FileBuffer,0,Pe_Getfile_Size()); //返回内存编号 int addr = (int)FileBuffer; printf("%x \n",addr); //释放申请的内存空间 free(FileBuffer); FileBuffer = NULL; fclose(fstream); return 0; } int main(int argc, char* argv[]) { Pe_ReadMemtory_addrs1(); return 0; } 第二种写法: #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> #define F_PATH "C:\\cntflx\\notepad.exe" int Pe_Getfile_Size() { FILE* fp=fopen(F_PATH,"r"); if (!fp) { return -1; } fseek(fp,0L,SEEK_END); int size = ftell(fp); fseek(fp,0,SEEK_SET); fclose(fp); return size; } int FileSizes = Pe_Getfile_Size(); int Pe_ReadMemtory_addrs2() { FILE* fd = fopen(F_PATH,"ab+"); int* ptr; ptr = (int*)malloc(FileSizes); if (ptr == NULL) { return 0; } memset(ptr,0,Pe_Getfile_Size()); fread(ptr, FileSizes, 1,fd); int addrs = (int)ptr; printf("%x \n",addrs); free(ptr); ptr = NULL; fclose(fd); return 0; } int main(int argc, char* argv[]) { Pe_ReadMemtory_addrs2(); return 0; } 第三种写法: 下面这个是摘自互联网上的,对应地址:https://www.it610.com/article/1304362410466906112.htm #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> int file_length(FILE *fp); void fun_02() { // 定义一个文件指针 FILE *fp1 = NULL; int FpSize = 0; // 初始化exe文件长度 // 打开文件(读和写) fp1 = fopen("C:\\Windows\\System32\\notepad.exe","rb"); //获取exe大小 FpSize = file_length(fp1); // 开辟一段动态内存,用FileBuffer指向 char * FileBuffer = (char *)malloc(FpSize); // 将.exe写入内存中 if(FileBuffer != NULL) { fread(FileBuffer,FpSize,1,fp1); } // 返回内存编号 int addr = (int)FileBuffer; printf("%x",FileBuffer); // 释放开辟的内存 free(FileBuffer); fclose(fp1); } int file_length(FILE *fp) { // 初始化一个计数器 int num; fseek(fp,0,SEEK_END); num = ftell(fp); // 使用完毕后,要将文件指针指向文件开始 fseek(fp,0,SEEK_SET); return num; } int main(int argc, char* argv[]) { fun_02(); getchar(); return 0; } 执行结果是0x00530068 3.将内存中的数据存储到一个文件中,(.exe格式),然后双击打开,看是否能够使用. 源代码如下: #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> #define F_PATH "C:\\cntflx\\notepad.exe" #define W_PATH "C:\\cntflx\\newnotepad.exe" int Pe_Getfile_Size() { FILE* fp=fopen(F_PATH,"r"); if (!fp) { return -1; } fseek(fp,0L,SEEK_END); int size = ftell(fp); fseek(fp,0,SEEK_SET); fclose(fp); return size; } int FileSizes = Pe_Getfile_Size(); int Pe_ReadMemtory_addrs1() { //定义两个文件的指针,并初始化为NULL FILE* fstream1 = NULL; FILE* fstream2 = NULL; //初始化exe文件长度 int FstreamSizes = 0; //准备打开文件notepad.exe ,读写,且是读二进制文件 fstream1 = fopen(F_PATH,"ab+"); //写入一个新的不存在的exe文件 fstream2 = fopen(W_PATH,"ab+"); //获取打开文件的exe大小 FstreamSizes = FileSizes; // printf("%d \n",FstreamSizes); //申请动态内存指向FileBuffer int* FileBuffer = (int*)malloc(FstreamSizes); //判断申请的内存是否成功,不成功就返回0 //成功的话就开始读exe文件内容,写入到另一个exe文件中 if (FileBuffer == NULL) { return 0; } else { fread(FileBuffer,FstreamSizes+1,1,fstream1); fwrite(FileBuffer,FstreamSizes,1,fstream2); } memset(FileBuffer,0,Pe_Getfile_Size()); //释放堆中申请的内存,并关闭打开的文件流 free(FileBuffer); FileBuffer = NULL; fclose(fstream1); fclose(fstream2); return 0; } int main(int argc, char* argv[]) { Pe_ReadMemtory_addrs1(); return 0; } 下面这个是摘自互联网上的,对应地址:https://www.it610.com/article/1304362410466906112.htm #include "stdafx.h" #include <malloc.h> #include <string.h> #include <stdlib.h> #include <memory.h> int file_length(FILE *fp); void fun_02() { // 定义两个文件指针,一个读一个写 FILE *fp1 = NULL; FILE *fp2 = NULL; int FpSize = 0; // 初始化exe文件长度 // 打开文件(读和写) fp1 = fopen("C:\\Windows\\System32\\notepad.exe","rb"); fp2 = fopen("C:\\cntflx\\hehetest.exe","wb"); //获取exe大小 FpSize = file_length(fp1); // 开辟一段动态内存,用FileBuffer指向 char * FileBuffer = (char *)malloc(FpSize); // 将.exe写入内存中 if(FileBuffer != NULL) { fread(FileBuffer,FpSize+1,1,fp1); fwrite(FileBuffer,FpSize,1,fp2); } // 释放堆中开辟的内存、关闭流 free(FileBuffer); fclose(fp1); fclose(fp2); } int file_length(FILE *fp) { // 初始化一个计数器 int num; fseek(fp,0,SEEK_END); num = ftell(fp); // 使用完毕后,要将文件指针指向文件开始 fseek(fp,0,SEEK_SET); return num; } int main(int argc, char* argv[]) { fun_02(); getchar(); return 0; }
迷茫的人生,需要不断努力,才能看清远方模糊的志向!