吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2291|回复: 17
收起左侧

[系统底层] win32笔记 十六 文件系统相关

  [复制链接]
huchen 发表于 2024-5-12 10:10
本帖最后由 huchen 于 2024-5-12 22:25 编辑

之前的索引

文件系统

文件系统是操作系统用于管理磁盘上文件的方法和数据结构,简单点说就是在磁盘上如何组织文件的方法

任何东西存在硬盘上就是一堆0和1,但是文件怎么存,以什么格式存就是文件系统说了算。不同的操作系统都有自己用来管理文件的方法和数据结构,我们称之为文件系统

我们如何来看我们的盘是什么系统呢

在盘右键点击属性就能看到

image-20240507205035704.png

这是我主机的C盘的文件系统,D盘(我主机就两个盘)

image-20240507205257935.png

这是我的U盘的文件系统

NTFS FAT32
磁盘分区容量 2T 32G
单个文件容量 4G以上 最大4G
EFS加密 支持 不支持
磁盘配额 支持 不支持

这里就列出这两个文件的容易理解的区别

EFS加密就是,比如我在C盘创建了两个文本文件,分别写上一些文字,保存然后右键属性,点击高级,有个加密的选项

image-20240507215926394.png

这个加密就是EFS加密,确定以后,会发现文件的颜色都变了

这个加密对当前用户是没有影响的,当我切换一个用户,就会提示拒绝访问

image-20240507220129426.png

磁盘配额就是限制别的用户在使用本盘的大小

image-20240507220329993.png

这里并不是学习NTFS和FAT32是怎么组织的,只是学习相关的API,虽然这两者有许多的区别,但是在API层面是没有太大的差别

分层

在文件系统中,不论是哪种文件系统,最上层的组织形式为卷

逻辑驱动器,就是假设你的硬盘有80G,在装系统的时候把这80G分成4个每个20G,那这个每个20G就是一个卷,这就是文件系统最顶层的组织形式

卷之后就是目录,目录后面可能还有目录

目录之后是文件了

所以文件系统的三层结构:卷、目录、文件

头文件相关解释

在写代码开始之前,需要说明一下需要自定义头文件的事情,下面的代码就是自定义头文件来创建新函数,这里可以自己搜一搜,我这直接提供我写的,有关函数里具体的操作是在FileSystem.cpp文件,而文件系统.cpp是来调用创建的函数的

image-20240510164152225.png

//FileSystem.h
#ifndef FILESYSTEM_H
#define FILESYSTEM_H

void TestVolume();
void TestDirectory();
void TestFile();

#endif

我的编译器有StdAfx.h文件,如果是VS高版本的话,可能默认没有,不过也不影响,只是要重复加几个头文件罢了

//StdAfx.h  注释给删了,有点占位置

#if !defined(AFX_STDAFX_H__EB17CBDE_4691_4C5B_8747_8F00823AFAFA__INCLUDED_)
#define AFX_STDAFX_H__EB17CBDE_4691_4C5B_8747_8F00823AFAFA__INCLUDED_

#include<stdio.h>
#include<windows.h>

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// 文件系统.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "FileSystem.h"

int main(int argc, char* argv[])
{
        //卷的相关API
        //TestVolume();

        //目录相关API
        //TestDirectory();

        //文件相关API
        TestFile();
        return 0;
}

卷相关API

1.获取卷

GetLogicalDrives()

这个函数是没有参数的,返回值是有用的

Return Values

If the function succeeds, the return value is a bitmask representing the  currently available disk drives. Bit position 0 (the least-significant bit) is  drive A, bit position 1 is drive B, bit position 2 is drive C, and so on.

If the function fails, the return value is zero. To get extended error  information, call [GetLastError](JavaScript:hhobj_1.Click()).

返回值

如果函数成功执行,其返回值是一个位掩码,表示当前可用的磁盘驱动器。位位置0(最低有效位)代表驱动器A,位位置1代表驱动器B,位位置2代表驱动器C,以此类推。

如果函数执行失败,返回值为0。要获取扩展的错误信息,请调用GetLastError函数。

就比如说,我运行一下

#include "stdafx.h"
#include "FileSystem.h"

void TestVolume()
{
        //1.获取卷名
        DWORD volumeName = GetLogicalDrives();
}//下断点,看VolumeName的值

image-20240508164544499.png

得到一个十进制数,转换成16进制为:80 000C

拆成二进制

1000 0000 0000 0000 0000 1100

从低位开始:A、B、C、D、……,依次

所以是C、D、X是有效的

image-20240508165307473.png

也确实如此

2.获取一个卷的盘符的字符串

GetLogicalDriveStrings

DWORD GetLogicalDriveStrings(
  DWORD nBufferLength,  // size of buffer
  LPTSTR lpBuffer       // drive strings buffer
);
nBufferLength

[in] Specifies the maximum size, in characters, of the buffer pointed to by lpBuffer. This size does not include the terminating null character.

[输入] 指定由 lpBuffer 指向的缓冲区可以容纳的最大字符数。这个大小不包括终止的空字符

lpBuffer

[out] Pointer to a buffer that receives a series of null-terminated strings, one for each valid drive in the system, that end with a second null character.

[输出] 指向一个缓冲区的指针,该缓冲区接收一系列以空字符(null)结尾的字符串,每个字符串对应于系统中的一个有效驱动器,并且以一个额外的空字符结尾。

#include "stdafx.h"
#include "FileSystem.h"

void TestVolume()
{
        //2.获取卷的标识符
        DWORD BufLen = 100;
        char lpBuff[100];
        GetLogicalDriveStrings(BufLen,lpBuff);
}

image-20240508181424371.png

3.获取卷的类型

GetDriveType

UINT GetDriveType(
  LPCTSTR lpRootPathName   // root directory
);
lpRootPathName

[in] Pointer to a null-terminated string that specifies the root directory of the disk to return information about. A trailing backslash is required. If lpRootPathName is NULL, the function uses the root of the current directory.

[输入] 指向一个以空字符(null)结尾的字符串的指针,该字符串指定了要返回其信息的磁盘的根目录。末尾需要有一个反斜杠(\)。如果 lpRootPathName 为 NULL,则该函数将使用当前目录的根目录。

#include "stdafx.h"
#include "FileSystem.h"

void TestVolume()
{
        //3.获取卷的类型
        UINT uType = GetDriveType("X:\\");
}

image-20240508204835445.png

得到的值是5,然后再去文档查

image-20240508204927313.png

从0开始往下数,也就是DRIVE_CDROM

虚拟机的C盘和D盘都是3,DRIVE_FIXED

该磁盘无法从驱动器中移除。

下面的是用主机来测试U盘的效果(我的虚拟机不读U盘T_T),得到2,DRIVE_REMOVABLE

该磁盘可以从驱动器中移除。

image-20240508210335167.png

4.获取卷的详细信息

GetVolumeInformation

BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,           // IN 磁盘驱动器字符串
  LPTSTR lpVolumeNameBuffer,        // OUT 磁盘驱动器卷标名称
  DWORD nVolumeNameSize,            // IN 磁盘驱动器卷标名称长度
  LPDWORD lpVolumeSerialNumber,     // OUT 磁盘驱动器卷标序列号
  LPDWORD lpMaximumComponentLength, // OUT 系统允许的最大文件名长度
  LPDWORD lpFileSystemFlags,        // OUT 文件系统标识
  LPTSTR lpFileSystemNameBuffer,    // OUT 文件操作系统名称
  DWORD nFileSystemNameSize         // IN 文件操作系统名称长度
);
lpRootPathName

查看那个盘,就写那个盘的标识,比如"C:\\"

lpVolumeNameBuffer

这是个OUT参数,意思为卷标,也就是盘符的别名

image-20240508220825038.png

这里我就改成了123,右键属性里可以修改

nVolumeNameSize

卷标的长度,假设卷标有20,但你设置了10,那么就只会取这10

lpVolumeSerialNumber

卷标序列号,并不是硬盘序列号,硬盘序列号是生产厂家设置的,用来区别产品,设置好了就不会改变。而卷标就是硬盘划分而来的,如果重装系统,重新格式化硬盘的话,卷标就会发生变化

lpMaximumComponentLength

允许的文件或文件夹名字的长度

lpFileSystemFlags

image-20240508221959784.png

会得到里面的宏,这个可以自行查阅

lpFileSystemNameBuffer

文件系统名称,就是NTFS和FAT32

nFileSystemNameSize

文件操作系统名称长度

#include "stdafx.h"
#include "FileSystem.h"

void TestVolume()
{
        //4.获取卷的信息
        TCHAR volumeName[260] = {0};
        DWORD volumeSerial = 0;
        DWORD MaxLength = 0;
        DWORD FileSysFlags = 0;
        TCHAR FileSystName[260] = {0};

        GetVolumeInformation(
                "C:\\",
                volumeName,
                260,
                &volumeSerial,
                &MaxLength,
                &FileSysFlags,
                FileSystName,
                260
                );
        printf("盘符:%s\n卷标序列号:%08X\n最大文件长度:%d\n文件系统标识:%08X\n文件系统名称:%s\n",volumeName,volumeSerial,MaxLength,FileSysFlags,FileSystName);
}

image-20240508223733839.png

这样就能得到相关信息

目录相关API

1.创建目录

CreateDirectory

BOOL CreateDirectory(
  LPCTSTR lpPathName,                         // directory name
  LPSECURITY_ATTRIBUTES lpSecurityAttributes  // SD
);

第一个参数是创建目录的路径加上名字,第二个是安全描述符

#include "stdafx.h"
#include "FileSystem.h"
void TestDirectory()
{
        //1.创建目录
        CreateDirectory("C:\\A",NULL);
}

执行后会发现多了个A文件夹

2.删除目录

RemoveDirectory

BOOL RemoveDirectory(  LPCTSTR lpPathName   // directory name);

参数是创建目录的路径加上名字

#include "stdafx.h"
#include "FileSystem.h"
void TestDirectory()
{
        //2.删除目录
        RemoveDirectory("C:\\A");
}

执行后发现A目录就删除了,如果目录里有东西就删不掉了

3.修改目录名称

MoveFile

BOOL MoveFile(
  LPCTSTR lpExistingFileName, // file name
  LPCTSTR lpNewFileName       // new file name
);

第一个原目录名称,第二个新目录名称

void TestDirectory()
{
        //3.修改目录名称
        MoveFile("C:\\B","C:\\C");
}

先在C盘下创建B文件夹,执行后修改成了C,并且,里面有文件也可修改

4.获取并设置程序(进程)当前目录

GetCurrentDirectory(获取)

这个就参考进程章节里的模块路径和工作路径,在这章节是讲过的,就不重复了

void TestDirectory()
{
        //4.获取程序当前目录
        CHAR CurrDirectory[MAX_PATH];
        GetCurrentDirectoryA(MAX_PATH, CurrDirectory);
        printf("当前目录:%s\n", CurrDirectory);
}

image-20240508231818426.png

当我不设置路径创建目录时,就会在这个目录下创建

SetCurrentDirectory(设置)

当我不想在上面的目录下创建,但又不想写全路径,就可以用这个函数

void TestDirectory()
{
        //5.设置程序当前目录
        SetCurrentDirectory("C:\\");
        CreateDirectory("A",NULL);
}

可以看到在C盘下创建了,而不是文件系统这个目录下创建的

文件相关API

1.创建文件

CreateFile

HANDLE CreateFile(
  LPCTSTR lpFileName,                         // file name
  DWORD dwDesiredAccess,                      // access mode
  DWORD dwShareMode,                          // share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
  DWORD dwCreationDisposition,                // how to create
  DWORD dwFlagsAndAttributes,                 // file attributes
  HANDLE hTemplateFile                        // handle to template file
);
lpFileName

文件路径

dwDesiredAccess

文件的权限

image-20240508232554786.png

可读还是可写

dwShareMode

指定该对象的共享类型

当设置为0时,就是排他的,就是除了我别人都不能使用,只有等我用完了关掉后,别人才能对这个有操作,有点互斥的意思了

当我想共享时,就要设置成如下

image-20240508233054988.png

看名字就能猜到是干啥的,比如说第一个,可共享删除,当我执行后,还没关闭之前,别的程序是可以删掉这个文件的,但是也仅限于删除,其它的也同理

lpSecurityAttributes

安全描述符

dwCreationDisposition

以什么形式创建

image-20240508233531414.png

CREATE_NEW:创建一个新文件,如果已经存在则失败

CREATE_ALWAYS:创建一个新文件,如果已经存在,则覆盖

OPEN_EXISTING:打开文件,如果不存在则失败

dwFlagsAndAttributes

文件的属性

image-20240508234054422.png

有点小多就不截完,看名字也能猜出来,这里就自行查阅

hTemplateFile

很少用到,不用管,直接填空

#include "stdafx.h"
#include "FileSystem.h"
void TestFile()
{
        //1.创建文件
        HANDLE hFile = CreateFile(
                "C:\\A.txt",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                CREATE_ALWAYS,
                FILE_ATTRIBUTE_HIDDEN,                //隐藏属性
                NULL
                );

        //2.关闭文件
        CloseHandle(hFile);

}

image-20240508235307896.png

3.获取文件长度

GetFileSize

DWORD GetFileSize(
  HANDLE hFile,           // handle to file
  LPDWORD lpFileSizeHigh  // high-order word of file size
);

第一个是文件句柄,第二个参数返回文件大小的高32位字,这个参数对32位没用

返回值是文件大小的低序双字,详情看MSDN

void TestFile()
{
        //1.创建文件
        HANDLE hFile = CreateFile(
                "C:\\notepad.exe",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_HIDDEN,                //隐藏属性
                NULL
                );

        //3.获取文件长度
        DWORD High_32 = 0;
        DWORD Sizelow = GetFileSize(hFile,&High_32);

        CloseHandle(hFile);

}

image-20240509001249152.png

返回的值与大小一样,执行成功

4.获取文件的属性和信息

GetFileAttributesEx

BOOL GetFileAttributesEx(
  LPCTSTR lpFileName,                   // file or directory name
  GET_FILEEX_INFO_LEVELS fInfoLevelId,  // attribute 
  LPVOID lpFileInformation              // attribute information 
);
lpFileName

文件路径

fInfoLevelId
  • fInfoLevelId

    [in] Specifies a GET_FILEEX_INFO_LEVELS enumeration type that  gives the set of attribute information to obtain.

可以看到只有这一个可选参数

lpFileInformation

lpFileInformation

[out] Pointer to a buffer that receives the attribute information. The type  of attribute information stored into this buffer is determined by the value of  fInfoLevelId.

[输出] 指向一个缓冲区的指针,该缓冲区用于接收属性信息。存储在此缓冲区中的属性信息的类型由 fInfoLevelId 的值确定。

所以去看看上面的唯一的可选参数

image-20240509205158367.png

后面意思是,返回值会返回在WIN32_FILE_ATTRIBUTE_DATA结构体中,所以第三个参数就是这个类型了

WIN32_FILE_ATTRIBUTE_DATA
typedef struct _WIN32_FILE_ATTRIBUTE_DATA{ 
  DWORD      dwFileAttributes; 
  FILETIME   ftCreationTime; 
  FILETIME   ftLastAccessTime; 
  FILETIME   ftLastWriteTime; 
  DWORD      nFileSizeHigh; 
  DWORD      nFileSizeLow; 
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA; 
dwFileAttributes

定义文件或目录的属性信息,属性有点多,就不截图了,自行查阅文档

ftCreationTime

指定文件或目录被创建的时间

ftLastAccessTime

对于文件,指定了最后一次被读取或写入的时间,对于目录是创建的时间

ftLastWriteTime

对于文件,指定了最后一次被写入的时间,对于目录是创建的时间

由于以上的三个成员都是FILETIME结构体的,并不直接是时间戳,所以要转换成时间,还要看看FILETIME结构体是怎么说的

若要将 FILETIME 结构转换为易于向用户显示的时间,请使用 FileTimeToSystemTime 函数。

nFileSizeHigh

文件大小的高32位

nFileSizeLow

文件大小的低32位

void TestFile()
{
        //4.获取文件的属性和信息
        WIN32_FILE_ATTRIBUTE_DATA data = {0};
        SYSTEMTIME SystTime;
        GetFileAttributesEx("C:\\A.txt",GetFileExInfoStandard,&data);
        printf("属性:%d\n",data.dwFileAttributes);
        printf("创建时间:%x\n",data.ftCreationTime);
        printf("最后访问时间:%x\n",data.ftLastAccessTime);
        printf("最后写入时间:%x\n",data.ftLastWriteTime);
        printf("文件大小高32位:%d\n",data.nFileSizeHigh);
        printf("文件大小低32位:%d\n",data.nFileSizeLow);

        FileTimeToSystemTime(&data.ftCreationTime, &SystTime);
        printf("%d年%d月%d日-%d时%d分%d秒\n", SystTime.wYear, SystTime.wMonth, SystTime.wDay, SystTime.wHour, SystTime.wMinute, SystTime.wSecond);
}

说一下属性,就是属性的宏组合相加而得出的值

这里我运算出的时间,却与我在文件的属性中相比不一样,只有小时不一样

image-20240509222304542.png image-20240509222322938.png

还请大佬解释一下,我百思不得其解

5.读取文件

ReadFile

BOOL ReadFile(
  HANDLE hFile,                // handle to file
  LPVOID lpBuffer,             // data buffer
  DWORD nNumberOfBytesToRead,  // number of bytes to read
  LPDWORD lpNumberOfBytesRead, // number of bytes read
  LPOVERLAPPED lpOverlapped    // overlapped buffer
);

这里就快速过一遍

文件句柄、存的数据空间、读多少个、读了多少个、网络编程相关,填空

void TestFile()
{
        //1.创建文件
        HANDLE hFile = CreateFile(
                "C:\\A.txt",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_HIDDEN,                //隐藏属性
                NULL
                );
        //5.读取文件
        //5.1分配空间
        LPSTR buff = (LPSTR)malloc(10);
        ZeroMemory(buff,10);
        //5.2设置当前读取位置
        SetFilePointer(hFile,0,NULL,FILE_BEGIN);
        //5.3读取数据
        DWORD ReadLen = 0;
        ReadFile(hFile,buff,10,&ReadLen,NULL);
        printf("%s\n",buff);
        //5.4释放内存
        free(buff);
        //5.5关闭句柄
        CloseHandle(hFile);
}

这里注意别越界了加上NULL不要超过10

6.写入文件

WriteFile

BOOL WriteFile(
  HANDLE hFile,                    // handle to file
  LPCVOID lpBuffer,                // data buffer
  DWORD nNumberOfBytesToWrite,     // number of bytes to write
  LPDWORD lpNumberOfBytesWritten,  // number of bytes written
  LPOVERLAPPED lpOverlapped        // overlapped buffer
);

这些参数我相信大家都能理解了,就不多说了

void TestFile()
{
        //1.创建文件
        HANDLE hFile = CreateFile(
                "C:\\A.txt",
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_HIDDEN,                //隐藏属性
                NULL
                );

        //6.写入文件
        TCHAR buff[] = TEXT("吾爱破解");
        DWORD WriteLen = 0;
        WriteFile(hFile,buff,strlen(buff),&WriteLen,NULL);
        //关闭文件
        CloseHandle(hFile);
}

7.拷贝文件

CopyFile

BOOL CopyFile(
  LPCTSTR lpExistingFileName, // name of an existing file
  LPCTSTR lpNewFileName,      // name of new file
  BOOL bFailIfExists          // operation if file exists
);

这里说说第三个参数

比如CopyFile(A,B,FALSE),这里为FALSE的话,不管原来是否存在B,直接覆盖,如果存在且此处为TRUE,就返回失败

void TestFile()
{        
        //7.拷贝文件
        CopyFile("C:\\A.txt","C:\\B.txt",FALSE);
}

8.删除文件

void TestFile()
{        
        //8.删除文件
        DeleteFile("C:\\B.txt");
}

9.查找文件

FindFirstFile

HANDLE FindFirstFile(
  LPCTSTR lpFileName,               // file name
  LPWIN32_FIND_DATA lpFindFileData  // data buffer
);

FindNextFile

BOOL FindNextFile(
  HANDLE hFindFile,                // search handle 
  LPWIN32_FIND_DATA lpFindFileData // data buffer
);

可以自行查阅其用法,来查找相关文件

void TestFile()
{        
        //9.查找文件
WIN32_FIND_DATA FindData;
        HANDLE hFile = FindFirstFile("C:\\*.*",&FindData);
        if(hFile == INVALID_HANDLE_VALUE)
        {
                printf("没有找到文件,错误代码:%d\n",GetLastError());
        }
        else
        {
                printf("找到文件\n");
                // printf("%s\n",&FindData.cFileName);
        }
        BOOL Result = FALSE;
        int i = 0;
        while(!Result)
        {
                if(!FindNextFile(hFile,&FindData))
                {
                        Result = TRUE;
                        return;
                }

                printf("%s\n",FindData.cFileName);
                i++;
        }

}

这里就是循环查找C盘下的所有文件和目录,搞了好久才弄懂FindNextFile,弄得太晚了,脑袋都不灵光了,意思就是看你查找下一个的还有这种类型的文件吗?比如说我C盘下只有一个A.txt,如果我还去查有无其它的txt,那么就打印不出A.txt,即便你有两个比如A和B两个txt,那也只会打印出B(这里是按照ASCII编码顺序打印的),因为B在A的后面,所以这个函数是只会找满足条件的下一个文件

内存映射文件

什么是内存映射文件

image-20240510150610031.png

把硬盘上的文件直接映射在物理内存上,这个物理内存也映射在虚拟内存上,这样操作这个进程就相当于在操作这个文件

这样操作就避免了一些麻烦的打开文件、关闭文件等繁琐操作

最重要的优点是当我们读取的文件较大时,内存映射文件的性能较好

代码实现

这里建议自行写,因为都是前面说过的,可以用来巩固一边,翻看以前的笔记来编写,加深印象

#include "stdafx.h"
#include "MappingFile.h"

#define MAPNAME "共享内存"

DWORD MapFile(LPSTR lpcFile)
{
        HANDLE hFile;
        HANDLE hMap;
        LPVOID lpAdr;

        //1.得到文件的句柄
        hFile = CreateFile(
                lpcFile,
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_ALWAYS,
                FILE_ATTRIBUTE_NORMAL,
                NULL
                );
        if(hFile == INVALID_HANDLE_VALUE)
        {
                printf("文件句柄获取失败:%d\n",GetLastError);
                return 0;
        }

        //2.创建物理页并关联文件
        hMap = CreateFileMapping(
                hFile,
                NULL,
                PAGE_READWRITE,
                0,
                0,
                MAPNAME
                );
        if(hMap == NULL)
        {
                printf("物理页与文件关联失败:%d\n",GetLastError);
                return 0;
        }
        //3.映射到虚拟内存
        lpAdr = MapViewOfFile(
                hMap,
                FILE_MAP_COPY,//FILE_MAP_COPY                FILE_MAP_ALL_ACCESS
                0,0,0
                );
        if(lpAdr == NULL)
        {
                printf("映射虚拟内存失败:%d\n",GetLastError);
                CloseHandle(hFile);
                CloseHandle(hMap);
                return 0;
        }
        //4.读取文件
        DWORD Test1 = *(PDWORD)lpAdr;
        DWORD Test2 = *((PDWORD)lpAdr+0x40FC);
        printf("%x %x\n",Test1,Test2);
        return 0;
}

1.得到文件的句柄:这部分没什么说的

2.创建物理页并关联文件:CreateFileMapping函数在以前的学习中是用-1的宏来写的,意为不要文件只要物理页,这里就要传文件句柄了

3.映射到虚拟内存:这里没什么讲的,至于FILE_MAP_COPY,后面会讲到

4.读取文件:上面说过了只要映射了,操作内存就是操作文件,所以这里的*(PDWORD)lpAdr就是文件开始地方

*((PDWORD)lpAdr+0x40FC)是文件末

image-20240510171915627.png

可以看到并不是0x40FC,这是因为lpAdr本就是一个指针类型

LPVOID  A 32-bit pointer to an unspecified type.

一个指向未指定类型的32位指针

相当于是4字节的指针,所以指针运算的时候需要先砍掉一颗星,在与数字乘以砍掉一颗星后的宽度运算

比如

int* x = (int*)1;
x++;
//得到的x的值是5

再回到这里就能理解了,原来是0x103F0除以4就得到0x40FC

运行结果

image-20240510174311373.png

可以得知完全一样

内存映射文件之共享

image-20240510203259440.png

内存映射文件也是可以共享的,并且系统中绝大多数都是这样的

        //5.写入文件
        *(PDWORD)lpAdr = 0x41414141;
        printf("写入A %x\n",*(PDWORD)lpAdr);
        getchar();
        //强制更新缓存
        //FlushViewOfFile(((PDWORD)lpAdr),4)        //当修改文件的时候,数据并不是立即修改,而是在关闭资源中做收尾工作,要想立即修改就用本函数

        //6.关闭资源
        UnmapViewOfFile(lpAdr);
        CloseHandle(hFile);
        CloseHandle(hMap);

在3.映射到虚拟内存中FILE_MAP_COPY换成FILE_MAP_ALL_ACCESS

原因后面再说,然后在另写一个测试代码

#include<stdio.h>
#include<windows.h>
#define MAPNAME "共享内存"

int main()
{
        HANDLE hMap;
        LPVOID lpAdr;

        //1.打开对象
        hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,MAPNAME);
        if(hMap == NULL)
        {
                printf("打开失败:%d\n",GetLastError);
                return 0;
        }
        //2.映射到内存
        lpAdr = MapViewOfFile(
                hMap,
                FILE_MAP_ALL_ACCESS,//FILE_MAP_COPY                FILE_MAP_ALL_ACCESS
                0,0,0
                );
        if(lpAdr == NULL)
        {
                printf("映射虚拟内存失败:%d\n",GetLastError);
                CloseHandle(hMap);
                return 0;
        }
        //3.读取数据
        DWORD Test = *(PDWORD)lpAdr;
        printf("读取A %x\n",Test);

        //4.释放资源
        UnmapViewOfFile(lpAdr);
        CloseHandle(hMap);
        return 0;
}

这里面很简单的,只要学过了就知道了,即便不知道查查文档就可以了

运行

image-20240510211044209.png

两个进程共享了同一个物理页

两个进程都运行结束去看看文件

image-20240510211247713.png

发现也是成功的

写拷贝:FILE_MAP_COPY

image-20240510212226182.png

如图,每个进程都会用到图中的三个dll,这些也是映射了物理内存供大家共享,这样的话,(左为A,右为B)AB进程都能读,但如果能写呢?

那A进程对dll修改了,那B进程使用的dll不也就被修改了吗?

这里可以做个实验,用OD去载入一个进程,停在一个断点,都知道断点为CC(int 3),如果我重新去运行它却又能运行起来,不应该被断下来吗?

要实现这个现象就要使用到写拷贝即FILE_MAP_COPY

image-20240510212841886.png

在MapViewOfFile函数中的参数,只用看第一句话,写的时候拷贝、复制

什么意思,看一眼下面的图

image-20240510213221850.png

每当进程执行写的操作时,就会拷贝一份新的物理页,原来的内容就不会改变,别的进程访问也不会变,就相当与有个存档,玩闯关游戏需要的就是存档了

代码就是上面的,就不贴了,记得把参数改一下

结果

image-20240510213743247.png

可以看到没有影响,还是读取原来的0,结束后看看文件

image-20240510213852280.png

也是没有改变


免费评分

参与人数 11吾爱币 +17 热心值 +9 收起 理由
asciibase64 + 1 用心讨论,共获提升!
jhoneyr + 1 + 1 鼓励转贴优秀软件安全工具和文档!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
52rap + 1 + 1 用心讨论,共获提升!
allspark + 1 + 1 用心讨论,共获提升!
zd53011 + 1 用心讨论,共获提升!
何西烛 + 1 用心讨论,共获提升!
Issacclark1 + 1 谢谢@Thanks!
你好,再见 + 2 + 1 用心讨论,共获提升!
lccccccc + 2 + 1 用心讨论,共获提升!
wumianzw + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

稻海香 发表于 2024-5-12 10:20
板凳我来坐
 楼主| huchen 发表于 2024-5-12 10:23
pdanny 发表于 2024-5-12 12:25
jz4128 发表于 2024-5-12 16:04
多谢科普!
zngray 发表于 2024-5-12 17:17
多谢科普!
dwcj 发表于 2024-5-12 17:35
学习了,吾爱这学习笔记是真好,win32 笔记系统
林辰 发表于 2024-5-12 19:36
多谢科普!
你好,再见 发表于 2024-5-12 21:03
楼主的学习笔记系列写得真好
想发却不会 发表于 2024-5-13 03:23
我很喜欢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 22:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表