PEtools PE操作工具类C++
源码来自各大网友并非原创修改了部分函数 仅供参考(PE没源码参考应该是很吃力的)
暂未更新完持续更新中.......
PETools.h
//函数头 int GetFileLength(FILE *pf, DWORD *Length); int ReadFileByPathToBuffer(IN LPSTR FilePath, OUT LPVOID* pFileAddress); int PrintPEFileHeader(void* pFileAddress); int PrintPESectionHeader(PVOID pFileAddress); int WriteFileFromFileAddress(PVOID pFileAddress, DWORD FileSize, LPSTR FilePath); int FileBufferToImageBuffer(PVOID pFileBuffer, OUT PVOID *pImageBuffer); int ExtendLastSection(PVOID FileAddress, OUT PVOID *NewFileAddress, IN LPSTR FilePath, IN DWORD size0xold, IN DWORD size0xadd, bool ChangeCharacteristics, LPSTR newLastSecName); int ImageBufferToNewFileBuffer(PVOID pImageBuffer, OUT PVOID* pNewFileBuffer, PDWORD pNewFileBufferSize); int ExtendLastSection(PVOID FileAddress, OUT PVOID *NewFileAddress, IN LPSTR FilePath, IN DWORD size0xold, IN DWORD size0xadd);
PETools.cpp
#pragma once
# define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
# include "stdio.h"
# include "stdlib.h"
# include "windows.h"
#include "PETools.h"
/*d通过路径读文件返回文件指针
@参数一位 要输入的文件路径
@参数二是 返回读入内存的指针地址;
返回的是文件大小
*/
int ReadFileByPathToBuffer(IN LPSTR FilePath, OUT void** pFileAddress)
{
;
DWORD Length = ;
//打开文件
FILE* pf = fopen(FilePath, "rb");
if (pf == NULL)
{
ret = -;
printf("func ReadFile() Error!\n");
return ret;
}
//获取文件长度
ret = GetFileLength(pf, &Length);
&& Length == -)
{
ret = -;
printf("func GetFileLength() Error!\n");
return ret;
}
//分配空间
*pFileAddress = (PVOID)malloc(Length);
if (*pFileAddress == NULL)
{
ret = -;
printf("func malloc() Error!\n");
return ret;
}
memset(*pFileAddress, , Length);//申请的空间初始化为0
//读取文件进入内存(自己申请的缓冲区)
fread(*pFileAddress, Length, , pf);
//printf("%s", ret);
fclose(pf);//关闭文件打开流
return Length;
}
/*得到文件size但返回0一定要fseek(pf, 0, SEEK_SET);//把指针指导最前*/
int GetFileLength(FILE *pf, DWORD *Length)
{
;
fseek(pf, , SEEK_END);//把指针指导最后
*Length = ftell(pf);
fseek(pf, , SEEK_SET);//把指针指导最前
return ret;
}
/*
从内存中输出文件
@参数一 要输出的文件地址
@参数二 输出的文件大小size
@参数三 输出的文件路径
成功返回1 失败<0
*/
int WriteFileFromFileAddress(PVOID pFileAddress, DWORD FileSize, LPSTR FilePath)
{
;
FILE *pf = fopen(FilePath, "wb");
if (pf == NULL)
{
ret = -;
printf("func fopen() error :%d!\n", ret);
return ret;
}
fwrite(pFileAddress, FileSize, , pf);
fclose(pf);
return ret;
}
int PrintPEOptionalHeader(PVOID pFileAddress)
{
;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + );
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
printf("****************OPTIONAL_HEADER32 STAR*************************\n");
printf("OptionalHeader->Magic : %02X\n", pOptionalHeader->Magic);
printf("OptionalHeader->MajorLinkerVersion : %01X\n", pOptionalHeader->MajorLinkerVersion);
printf("OptionalHeader->MinorLinkerVersion : %01X\n", pOptionalHeader->MinorLinkerVersion);
printf("OptionalHeader->SizeOfCode : %04X\n", pOptionalHeader->SizeOfCode);
printf("OptionalHeader->SizeOfInitializedData : %04X\n", pOptionalHeader->SizeOfInitializedData);
printf("OptionalHeader->SizeOfUninitializedData : %04X\n", pOptionalHeader->SizeOfUninitializedData);
printf("OptionalHeader->AddressOfEntryPoint : %04X\n", pOptionalHeader->AddressOfEntryPoint);
printf("OptionalHeader->BaseOfCode : %04X\n", pOptionalHeader->BaseOfCode);
printf("OptionalHeader->BaseOfData : %04X\n", pOptionalHeader->BaseOfData);
printf("OptionalHeader->ImageBase : %04X\n", pOptionalHeader->ImageBase);
printf("OptionalHeader->SectionAlignment : %04X\n", pOptionalHeader->SectionAlignment);
printf("OptionalHeader->FileAlignment : %04X\n", pOptionalHeader->FileAlignment);
printf("OptionalHeader->MajorOperatingSystemVersion : %02X\n", pOptionalHeader->MajorOperatingSystemVersion);
printf("OptionalHeader->MinorOperatingSystemVersion : %02X\n", pOptionalHeader->MinorOperatingSystemVersion);
printf("OptionalHeader->MajorImageVersion : %02X\n", pOptionalHeader->MajorImageVersion);
printf("OptionalHeader->MinorImageVersion : %02X\n", pOptionalHeader->MinorImageVersion);
printf("OptionalHeader->MajorSubsystemVersion : %02X\n", pOptionalHeader->MajorSubsystemVersion);
printf("OptionalHeader->MinorSubsystemVersion : %02X\n", pOptionalHeader->MinorSubsystemVersion);
printf("OptionalHeader->Win32VersionValue : %04X\n", pOptionalHeader->Win32VersionValue);
printf("OptionalHeader->SizeOfImage : %04X\n", pOptionalHeader->SizeOfImage);
printf("OptionalHeader->SizeOfHeaders : %04X\n", pOptionalHeader->SizeOfHeaders);
printf("OptionalHeader->CheckSum : %04X\n", pOptionalHeader->CheckSum);
printf("OptionalHeader->Subsystem : %02X\n", pOptionalHeader->Subsystem);
printf("OptionalHeader->DllCharacteristics : %02X\n", pOptionalHeader->DllCharacteristics);
printf("OptionalHeader->SizeOfStackReserv : %04X\n", pOptionalHeader->SizeOfStackReserve);
printf("OptionalHeader->SizeOfStackCommit : %04X\n", pOptionalHeader->SizeOfStackCommit);
printf("OptionalHeader->SizeOfHeapReserve : %04X\n", pOptionalHeader->SizeOfHeapReserve);
printf("OptionalHeader->SizeOfHeapCommit : %04X\n", pOptionalHeader->SizeOfHeapCommit);
printf("OptionalHeader->LoaderFlags : %04X\n", pOptionalHeader->LoaderFlags);
printf("OptionalHeader->NumberOfRvaAndSizes : %04X\n", pOptionalHeader->NumberOfRvaAndSizes);
printf("*****************OPTIONAL_HEADER32 END************************\n");
return ret;
}
int PrintPEFileHeader(PVOID pFileAddress)
{
;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + );//相当于文件tou加上那个lfanew 现在指向PE位置
printf("FileHeader->Machine : %02X\n", pDosHeader->e_lfanew);
printf("****************FILE_HEADER STAR*************************\n");
printf("FileHeader->Machine : %02X\n", pFileHeader->Machine);
printf("FileHeader->NumberOfSections : %02X\n", pFileHeader->NumberOfSections);
printf("FileHeader->TimeDateStamp : %04X\n", pFileHeader->TimeDateStamp);
printf("FileHeader->PointerToSymbolTable : %04X\n", pFileHeader->PointerToSymbolTable);
printf("FileHeader->NumberOfSymbols : %04X\n", pFileHeader->NumberOfSymbols);
printf("FileHeader->SizeOfOptionalHeader : %02X\n", pFileHeader->SizeOfOptionalHeader);//可选PE头大小
printf("FileHeader->Characteristics : %02X\n", pFileHeader->Characteristics);
printf("*****************FILE_HEADER END************************\n");
return ret;
}
int PrintPESectionHeader(PVOID pFileAddress)
{
;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + );
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
printf("****************SECTION_HEADER STAR*************************\n");
printf(]);
; i < pFileHeader->NumberOfSections; i++)
{
printf("pSectionGroup[%d].Name : %s\n", i, pSectionGroup[i].Name);
printf("pSectionGroup[%d].Misc.VirtualSize : %04X\n", i, pSectionGroup[i].Misc.VirtualSize);
printf("pSectionGroup[%d].VirtualAddress : %04X\n", i, pSectionGroup[i].VirtualAddress);
printf("pSectionGroup[%d].SizeOfRawData : %04X\n", i, pSectionGroup[i].SizeOfRawData);
printf("pSectionGroup[%d].PointerToRawData : %04X\n", i, pSectionGroup[i].PointerToRawData);
printf("pSectionGroup[%d].PointerToRelocations : %04X\n", i, pSectionGroup[i].PointerToRelocations);
printf("pSectionGroup[%d].PointerToLinenumbers : %04X\n", i, pSectionGroup[i].PointerToLinenumbers);
printf("pSectionGroup[%d].NumberOfRelocations : %02X\n", i, pSectionGroup[i].NumberOfRelocations);
printf("pSectionGroup[%d].NumberOfLinenumbers : %02X\n", i, pSectionGroup[i].NumberOfLinenumbers);
printf("pSectionGroup[%d].Characteristics : %04X\n\n\n", i, pSectionGroup[i].Characteristics);
}
printf("*****************SECTION_HEADER END************************\n");
return ret;
}
/*
@参数一 要输出的文件pFileBuffer
@参数二 *pImageBuffer 拉伸后镜像缓冲区首地址
成功返回1 失败<0
*/
int FileBufferToImageBuffer(PVOID pFileBuffer, OUT PVOID *pImageBuffer)
{
;
DWORD ImageBufferSize = ;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + );
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
//1、获取ImageBufffer的内存大小
ImageBufferSize = pOptionalHeader->SizeOfImage;
//2、为pImageBuffer分配内存空间
*pImageBuffer = (PVOID)malloc(ImageBufferSize);
if (pImageBuffer == NULL)
{
ret = -;
printf("func malloc() Error : %d!\n", ret);
return ret;
}
memset(*pImageBuffer, , ImageBufferSize);
//3、将FileBuffer的数据拷贝到ImageBuffer中
// 文件头直接拷贝
memcpy(*pImageBuffer, pFileBuffer, pOptionalHeader->SizeOfHeaders);
// 节区循环拷贝
; i < pFileHeader->NumberOfSections; i++)
{
//按照PointerToRawData 复制是不会出错的
memcpy((PVOID)((DWORD)*pImageBuffer + pSectionGroup[i].VirtualAddress),
(PVOID)((DWORD)pFileBuffer + pSectionGroup[i].PointerToRawData),
pSectionGroup[i].SizeOfRawData);
}
return ret;
}
/*
从内存中输出文件
@参数一 要输出的文件pFileBuffer
@参数二 pNewFileBuffer 新的文件缓冲
@参数三 新缓冲地址
成功返回1 失败<0
*/
int ImageBufferToNewFileBuffer(PVOID pImageBuffer, OUT PVOID* pNewFileBuffer, OUT PDWORD pNewFileBufferSize)
{
;
DWORD NewFileBufferSize = ;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + );
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
//1、获取NewFileBuffer的内存大小
NewFileBufferSize += pOptionalHeader->SizeOfHeaders; //文件头大小
; i < pFileHeader->NumberOfSections; i++)
{
NewFileBufferSize += pSectionGroup[i].SizeOfRawData;
}
//2、为pNewFileBuffer分配内存空间
*pNewFileBuffer = (PVOID)malloc(NewFileBufferSize);
if (pNewFileBuffer == NULL)
{
ret = -;
printf("func malloc() Error : %d!\n", ret);
return ret;
}
memset(*pNewFileBuffer, , NewFileBufferSize);
//3、将ImageBuffer的数据拷贝到NewFileBuffer中
// 文件头直接拷贝
memcpy(*pNewFileBuffer, pImageBuffer, pOptionalHeader->SizeOfHeaders);
// 节区循环拷贝
; i < pFileHeader->NumberOfSections; i++)
{
memcpy((PVOID)((DWORD)*pNewFileBuffer + pSectionGroup[i].PointerToRawData),
(PVOID)((DWORD)pImageBuffer + pSectionGroup[i].VirtualAddress),
pSectionGroup[i].SizeOfRawData);
}
*pNewFileBufferSize = NewFileBufferSize;
return ret;
}
/*
成功返回新文件size'
失败返回<0
参数一 文件读入内存地址
参数二 输出新文件内存地址
参数三 输入的文件路径+名称
参数四 源文件大小size
参数五 要增加的字节长度十六进制
参数六 是否更改节属性为可读可写可执行 true为更改
参数七 更改最后一个节的名称可空 null 长度最大为8字节
*/
int ExtendLastSection(PVOID FileAddress, OUT PVOID *NewFileAddress, IN LPSTR FilePath, IN DWORD size0xold, IN DWORD size0xadd, bool ChangeCharacteristics, LPSTR newLastSecName)
{
;
DWORD OldLength = ;
DWORD NewLength = ;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionGroup = NULL;
PIMAGE_SECTION_HEADER pLastSection = NULL;
OldLength = size0xold;
)
{
ret = -;
printf("func GetFileLength() Error!\n");
return ret;
}
//将旧的空间增加0x1000
NewLength = OldLength + size0xadd;
*NewFileAddress = (LPVOID)malloc(NewLength);
if (*NewFileAddress == NULL)
{
ret = -;
printf("func malloc() Error!\n");
return ret;
}
memset(*NewFileAddress, , NewLength);
//2、将旧空间的内容copy到新的空间
memcpy(*NewFileAddress, FileAddress, OldLength);
//3、将指针指向对应位置
pDosHeader = (PIMAGE_DOS_HEADER)(*NewFileAddress);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + );
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
pLastSection = &pSectionGroup[pFileHeader->NumberOfSections - ];
//4、修改相关内容 需要用更高级的指针进行操作
LPDWORD pSizeOfImage = &pOptionalHeader->SizeOfImage;
LPDWORD pSecMisc = &pLastSection->Misc.VirtualSize;
LPDWORD pSecSizeOfRawData = &pLastSection->SizeOfRawData;
LPDWORD Characteristics = &pLastSection->Characteristics;//获取是否哦可执行
PVOID pSecName = &pLastSection->Name;//获取节的名称
*pSizeOfImage = *pSizeOfImage + size0xadd;
*pSecMisc = *pSecMisc + size0xadd;
*pSecSizeOfRawData = *pSecSizeOfRawData + size0xadd;
//更改最后一个节属性
if (ChangeCharacteristics)
{
*Characteristics = );
//增加可读可写可执行
}
if (newLastSecName!=NULL)
{
//修改最后一个节的名称
memcpy(pSecName, newLastSecName, );
}
//*pNewLength = NewLength;
return NewLength;
}
int ExtendLastSection(PVOID FileAddress, OUT PVOID *NewFileAddress, IN LPSTR FilePath, IN DWORD size0xold, IN DWORD size0xadd)
{
;
DWORD OldLength = ;
DWORD NewLength = ;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionGroup = NULL;
PIMAGE_SECTION_HEADER pLastSection = NULL;
OldLength = size0xold;
)
{
ret = -;
printf("func GetFileLength() Error!\n");
return ret;
}
//将旧的空间增加0x1000
NewLength = OldLength + size0xadd;
*NewFileAddress = (LPVOID)malloc(NewLength);
if (*NewFileAddress == NULL)
{
ret = -;
printf("func malloc() Error!\n");
return ret;
}
memset(*NewFileAddress, , NewLength);
//2、将旧空间的内容copy到新的空间
memcpy(*NewFileAddress, FileAddress, OldLength);
//3、将指针指向对应位置
pDosHeader = (PIMAGE_DOS_HEADER)(*NewFileAddress);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + );
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
pLastSection = &pSectionGroup[pFileHeader->NumberOfSections - ];
//4、修改相关内容 需要用更高级的指针进行操作
LPDWORD pSizeOfImage = &pOptionalHeader->SizeOfImage;
LPDWORD pSecMisc = &pLastSection->Misc.VirtualSize;
LPDWORD pSecSizeOfRawData = &pLastSection->SizeOfRawData;
LPDWORD Characteristics = &pLastSection->Characteristics;//获取是否哦可执行
PVOID pSecName = &pLastSection->Name;//获取节的名称
*pSizeOfImage = *pSizeOfImage + size0xadd;
*pSecMisc = *pSecMisc + size0xadd;
*pSecSizeOfRawData = *pSecSizeOfRawData + size0xadd;
//*pNewLength = NewLength;
return NewLength;
}
PEtools PE操作工具类C++的更多相关文章
- Code片段 : .properties属性文件操作工具类 & JSON工具类
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “贵专” — 泥瓦匠 一.java.util.Properties API & 案例 j ...
- [转载]C# FTP操作工具类
本文转载自<C# Ftp操作工具类>,仅对原文格式进行了整理. 介绍了几种FTP操作的函数,供后期编程时查阅. 参考一: using System; using System.Collec ...
- 拼音操作工具类 - PinyinUtil.java
拼音操作工具类,提供字符串转换成拼音数组.汉字转换成拼音.取汉字的首字母等方法. 源码如下:(点击下载 -PinyinUtil.java.pinyin4j-2.5.0.jar ) import net ...
- DataTable操作工具类DataTableHelper
DataTable操作工具类DataTableHelper. 功能介绍: 将泛型集合填充为数据表 将泛型填充为数据表 将对象集合填充为数据表 将对象填充为数据表 将定IDictionary数据转换为D ...
- 【转载】ASP.NET工具类:文件夹目录Directory操作工具类
在ASP.NET开发网站的过程中,有时候会涉及到文件夹相关操作,如判断文件夹目录是否存在.删除文件夹目录.创建文件.删除文件.复制文件夹等等.这一批有关文件目录的操作可以通过Directory类.Fi ...
- Java SE 之 数据库操作工具类(DBUtil)设计
JDBC创建数据库基本连接 //1.加载驱动程序 Class.forName(driveName); //2.获得数据库连接 Connection connection = DriverManager ...
- JavaScript时间操作工具类
/** * 时间操作工具类 * * @author zwq * */ var TimeFrameUtil = { /** * 格式化日期 * @param date {Date} 日期 * @para ...
- java基础37 集合框架工具类Collections和数组操作工具类Arrays
一.集合框架工具类:Collections 1.1.Collections类的特点 该工具类中所有的方法都是静态的 1.2.Collections类的常用方法 binarySearch(List< ...
- 文件操作工具类: 文件/目录的创建、删除、移动、复制、zip压缩与解压.
FileOperationUtils.java package com.xnl.utils; import java.io.BufferedInputStream; import java.io.Bu ...
随机推荐
- Mac安装php扩展redis遇到的问题,执行phpize问题
1.安装redis在mac OS中可以使用brew命令进行安装redis:mac OS使用brew命令安装软件安装命令:brew install redis因为我已经安装过了,这里就不在赘述.安装完之 ...
- win10中,vscode安装go插件排雷指南
最近学习go,想着使用强大的vscode编写go,在安装go插件过程中,遇到了很多问题.下面记录解决方案. 1)win10环境,安装go,vscode,git 配置GOPATH环境变量,在我的电脑-& ...
- python opencv:保存图像
- 【代码审计】appcms 文件包含漏洞
index.php的开头系统都做了过滤 一个是 htmlspecialchars($v), 另一个是/^[\x{4e00}-\x{9fa5}\w {0}]+$/u. 前一个过滤是把预定义的字符 &qu ...
- 解决前端项目启动时报错:Use // eslint-disable-next-line to ignore the next line.
首先说一下这个问题产生的原因: 项目创建时设置了使用 eslint 进行代码规范检查. 解决办法: 找到webpack.base.conf.js文件,并且将下满这行代码注释掉. ...(config. ...
- spark-env.sh增加HADOOP_CONF_DIR使得spark运行文件是hdfs文件
spark-env.sh增加HADOOP_CONF_DIR使得spark读写的是hdfs文件 刚装了spark,运行wordcount程序,local方式,执行的spark-submit,读和写的文件 ...
- 【PAT甲级】1046 Shortest Distance (20 分)
题意: 输入一个正整数N(<=1e5),代表出口的数量,接下来输入N个正整数表示当前出口到下一个出口的距离.接着输入一个正整数M(<=10000),代表询问的次数,每次询问输入两个出口的序 ...
- 虚拟机与ubuntu系统的安装与基础操作
1.虚拟机的下载: 常见的虚拟机软件有:VMware VirtuaIBOX Virtual PC 等. 这里主要介绍VMware ,VMware目前已经有很多个版本,可以根据个人情况进行选择.安 ...
- laravel 排除csrf验证
中(*排除所有路由)
- 【快学SpringBoot】SpringBoot+Docker构建、运行、部署应用
前言 Docker技术发展为当前流行的微服务提供了更加便利的环境,使用SpringBoot+Docker部署和发布应用,其实也是一件比较简单的事情.当前,前提是得有Docker的基础. 源码在文末 文 ...