滴水逆向-文件读写&内存分配-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
");
}

int main(int argc, char* argv[])
{
    int x = fun1();
    printf("%d 
",x);
    float y = fun2(2);
    printf("%f 
",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 
",x);
    return 0;
}


2.头文件的使用        

测试验证
                
步骤一:                
                    
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

void fun( )
{
    printf("Hello World!
");
}


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!
");
}                
                
不能正常执行!                

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
");
}

y.cpp文件的文件内容如下:

// y.cpp: implementation of the y class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "y.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

void funy()
{
    printf("Hello y
");
}

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
" );
   else
   {
       printf( "Memory space allocated for path name
" );
       free( string );
       printf( "Memory freed
" );
   }
}


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 
",ptr);
}

int main(int argc, char* argv[])
{
    fun();
    printf("Hello World!
");
    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
");
        exit(1);
    }
    else
    {
        printf("Open the notepad success!
");
        printf("%x 
",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 
",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 
",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 
",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 
",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;
}
迷茫的人生,需要不断努力,才能看清远方模糊的志向!
原文地址:https://www.cnblogs.com/autopwn/p/15189169.html