VC2015,C++內存中運行EXE文件

VC2015,C++內存中運行EXE文件,點擊可以下載

VC2015項目中所用的源碼主要源自于網絡,修正一些錯誤后,源碼如下,此源碼在VC6中可以正常編譯,
但在VC2015中,就會出現一些錯誤,本項目源碼已經把錯誤修正,可以正常編譯并運行,感興趣的可以下載。
本例中默認調用C:\111.EXE,測試時,請在這個位置放一個可以正常打開的程序,運行本例程序即可正常調用。
主要源碼如下:

#include "stdafx.h"
#include "stdio.h"
#include "windows.h"typedef IMAGE_SECTION_HEADER(*PIMAGE_SECTION_HEADERS)[1];// 計算對齊后的大小   
unsigned long GetAlignedSize(unsigned long Origin, unsigned long Alignment)
{return (Origin + Alignment - 1) / Alignment * Alignment;
}// 計算加載pe并對齊需要占用多少內存   
// 未直接使用OptionalHeader.SizeOfImage作為結果是因為據說有的編譯器生成的exe這個值會填0   
unsigned long CalcTotalImageSize(PIMAGE_DOS_HEADER MzH, unsigned long FileLen, PIMAGE_NT_HEADERS peH, PIMAGE_SECTION_HEADERS peSecH)
{unsigned long res;// 計算pe頭的大小   res = GetAlignedSize(peH->OptionalHeader.SizeOfHeaders, peH->OptionalHeader.SectionAlignment);// 計算所有節的大小   for (int i = 0; i < peH->FileHeader.NumberOfSections; ++i){// 超出文件范圍   if (peSecH[i]->PointerToRawData + peSecH[i]->SizeOfRawData > FileLen)return 0;else if (peSecH[i]->VirtualAddress)//計算對齊后某節的大小   {if (peSecH[i]->Misc.VirtualSize){res = GetAlignedSize(peSecH[i]->VirtualAddress + peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment);}else{res = GetAlignedSize(peSecH[i]->VirtualAddress + peSecH[i]->SizeOfRawData, peH->OptionalHeader.SectionAlignment);}}else if (peSecH[i]->Misc.VirtualSize < peSecH[i]->SizeOfRawData){res += GetAlignedSize(peSecH[i]->SizeOfRawData, peH->OptionalHeader.SectionAlignment);}else{res += GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment);}// if_else   }// for   return res;
}// 加載pe到內存并對齊所有節   
BOOL AlignPEToMem(void *Buf, long Len, PIMAGE_NT_HEADERS &peH, PIMAGE_SECTION_HEADERS &peSecH, void *&Mem, unsigned long &ImageSize)
{PIMAGE_DOS_HEADER SrcMz;// DOS頭   PIMAGE_NT_HEADERS SrcPeH;// PE頭   PIMAGE_SECTION_HEADERS SrcPeSecH;// 節表   SrcMz = (PIMAGE_DOS_HEADER)Buf;if (Len < sizeof(IMAGE_DOS_HEADER))return FALSE;if (SrcMz->e_magic != IMAGE_DOS_SIGNATURE)return FALSE;if (Len < SrcMz->e_lfanew + (long)sizeof(IMAGE_NT_HEADERS))return FALSE;SrcPeH = (PIMAGE_NT_HEADERS)((int)SrcMz + SrcMz->e_lfanew);if (SrcPeH->Signature != IMAGE_NT_SIGNATURE)return FALSE;if ((SrcPeH->FileHeader.Characteristics & IMAGE_FILE_DLL) ||(SrcPeH->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE == 0) ||(SrcPeH->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER))){return FALSE;}SrcPeSecH = (PIMAGE_SECTION_HEADERS)((int)SrcPeH + sizeof(IMAGE_NT_HEADERS));ImageSize = CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH);if (ImageSize == 0)return FALSE;Mem = VirtualAlloc(NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配內存   if (Mem != NULL){// 計算需要復制的PE頭字節數   unsigned long l = SrcPeH->OptionalHeader.SizeOfHeaders;for (int i = 0; i < SrcPeH->FileHeader.NumberOfSections; ++i){if ((SrcPeSecH[i]->PointerToRawData) && (SrcPeSecH[i]->PointerToRawData < l)){l = SrcPeSecH[i]->PointerToRawData;}}memmove(Mem, SrcMz, l);peH = (PIMAGE_NT_HEADERS)((int)Mem + ((PIMAGE_DOS_HEADER)Mem)->e_lfanew);peSecH = (PIMAGE_SECTION_HEADERS)((int)peH + sizeof(IMAGE_NT_HEADERS));void *Pt = (void *)((unsigned long)Mem+ GetAlignedSize(peH->OptionalHeader.SizeOfHeaders, peH->OptionalHeader.SectionAlignment));for (int i = 0; i < peH->FileHeader.NumberOfSections; ++i){// 定位該節在內存中的位置   if (peSecH[i]->VirtualAddress)Pt = (void *)((unsigned long)Mem + peSecH[i]->VirtualAddress);if (peSecH[i]->SizeOfRawData){// 復制數據到內存   memmove(Pt, (const void *)((unsigned long)(SrcMz)+peSecH[i]->PointerToRawData), peSecH[i]->SizeOfRawData);if (peSecH[i]->Misc.VirtualSize < peSecH[i]->SizeOfRawData)Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->SizeOfRawData, peH->OptionalHeader.SectionAlignment));else // pt 定位到下一節開始位置   Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment));}else{Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment));}}}return TRUE;
}typedef void *(__stdcall *pfVirtualAllocEx)(unsigned long, void *, unsigned long, unsigned long, unsigned long);
pfVirtualAllocEx MyVirtualAllocEx = NULL;BOOL IsNT()
{return MyVirtualAllocEx != NULL;
}// 生成外殼程序命令行   
char *PrepareShellExe(char *CmdParam, unsigned long BaseAddr, unsigned long ImageSize)
{if (IsNT()){char *Buf = new char[256];memset(Buf, 0, 256);GetModuleFileName(0, Buf, 256);strcat(Buf, CmdParam);return Buf; // 請記得釋放內存;-)   }else{// Win98下的處理請參考原文;-)   // http://community.csdn.net/Expert/topic/4416/4416252.xml?temp=8.709133E-03   return NULL;}
}// 是否包含可重定向列表   
BOOL HasRelocationTable(PIMAGE_NT_HEADERS peH)
{return (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)&& (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
}#pragma pack(push, 1)   
typedef struct ImageBaseRelocation {unsigned long VirtualAddress;unsigned long SizeOfBlock;
}*PImageBaseRelocation;
#pragma pack(pop)   // 重定向PE用到的地址   
void DoRelocation(PIMAGE_NT_HEADERS peH, void *OldBase, void *NewBase)
{unsigned long Delta = (unsigned long)NewBase - peH->OptionalHeader.ImageBase;PImageBaseRelocation p = (PImageBaseRelocation)((unsigned long)OldBase+ peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);while (p->VirtualAddress + p->SizeOfBlock){unsigned short *pw = (unsigned short *)((int)p + sizeof(*p));for (unsigned int i = 1; i <= (p->SizeOfBlock - sizeof(*p)) / 2; ++i){if ((*pw) & 0xF000 == 0x3000) {unsigned long *t = (unsigned long *)((unsigned long)(OldBase)+p->VirtualAddress + ((*pw) & 0x0FFF));*t += Delta;}++pw;}p = (PImageBaseRelocation)pw;}
}// 卸載原外殼占用內存   
BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr)
{typedef unsigned long(__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long);pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;BOOL res = FALSE;HMODULE m = LoadLibrary("ntdll.dll");if (m) {ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(m, "ZwUnmapViewOfSection");if (ZwUnmapViewOfSection)res = (ZwUnmapViewOfSection((unsigned long)ProcHnd, BaseAddr) == 0);FreeLibrary(m);}return res;
}// 創建外殼進程并獲取其基址、大小和當前運行狀態   
BOOL CreateChild(char *Cmd, CONTEXT &Ctx, HANDLE &ProcHnd, HANDLE &ThrdHnd,unsigned long &ProcId, unsigned long &BaseAddr, unsigned long &ImageSize)
{STARTUPINFOA si;PROCESS_INFORMATION pi;unsigned long old;MEMORY_BASIC_INFORMATION MemInfo;memset(&si, 0, sizeof(si));memset(&pi, 0, sizeof(pi));si.cb = sizeof(si);BOOL res = CreateProcess(NULL, Cmd, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); // 以掛起方式運行進程;   if (res) {ProcHnd = pi.hProcess;ThrdHnd = pi.hThread;ProcId = pi.dwProcessId;// 獲取外殼進程運行狀態,[ctx.Ebx+8]內存處存的是外殼進程的加載基址,ctx.Eax存放有外殼進程的入口地址   Ctx.ContextFlags = CONTEXT_FULL;GetThreadContext(ThrdHnd, &Ctx);ReadProcessMemory(ProcHnd, (void *)(Ctx.Ebx + 8), &BaseAddr, sizeof(unsigned long), &old); // 讀取加載基址   void *p = (void *)BaseAddr;// 計算外殼進程占有的內存   while (VirtualQueryEx(ProcHnd, p, &MemInfo, sizeof(MemInfo))){if (MemInfo.State = MEM_FREE) break;p = (void *)((unsigned long)p + MemInfo.RegionSize);}ImageSize = (unsigned long)p - (unsigned long)BaseAddr;}return res;
}// 創建外殼進程并用目標進程替換它然后執行   
HANDLE AttachPE(char *CmdParam, PIMAGE_NT_HEADERS peH, PIMAGE_SECTION_HEADERS peSecH,void *Ptr, unsigned long ImageSize, unsigned long &ProcId)
{HANDLE res = INVALID_HANDLE_VALUE;CONTEXT Ctx;HANDLE Thrd;unsigned long Addr, Size;char *s = PrepareShellExe(CmdParam, peH->OptionalHeader.ImageBase, ImageSize);if (s == NULL) return res;if (CreateChild(s, Ctx, res, Thrd, ProcId, Addr, Size)) {void *p = NULL;unsigned long old;if ((peH->OptionalHeader.ImageBase == Addr) && (Size >= ImageSize)) {// 外殼進程可以容納目標進程并且加載地址一致   p = (void *)Addr;VirtualProtectEx(res, p, Size, PAGE_EXECUTE_READWRITE, &old);}else if (IsNT()) {if (UnloadShell(res, Addr)) {// 卸載外殼進程占有內存   p = MyVirtualAllocEx((unsigned long)res, (void *)peH->OptionalHeader.ImageBase, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);}if ((p == NULL) && HasRelocationTable(peH)) {// 分配內存失敗并且目標進程支持重定向   p = MyVirtualAllocEx((unsigned long)res, NULL, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (p) DoRelocation(peH, Ptr, p); // 重定向   }}if (p) {WriteProcessMemory(res, (void *)(Ctx.Ebx + 8), &p, sizeof(DWORD), &old); // 重置目標進程運行環境中的基址   peH->OptionalHeader.ImageBase = (unsigned long)p;if (WriteProcessMemory(res, p, Ptr, ImageSize, &old)) {// 復制PE數據到目標進程   Ctx.ContextFlags = CONTEXT_FULL;if ((unsigned long)p == Addr)Ctx.Eax = peH->OptionalHeader.ImageBase + peH->OptionalHeader.AddressOfEntryPoint; // 重置運行環境中的入口地址   elseCtx.Eax = (unsigned long)p + peH->OptionalHeader.AddressOfEntryPoint;SetThreadContext(Thrd, &Ctx);// 更新運行環境   ResumeThread(Thrd);// 執行   CloseHandle(Thrd);}else {// 加載失敗,殺掉外殼進程   TerminateProcess(res, 0);CloseHandle(Thrd);CloseHandle(res);res = INVALID_HANDLE_VALUE;}}else {// 加載失敗,殺掉外殼進程   TerminateProcess(res, 0);CloseHandle(Thrd);CloseHandle(res);res = INVALID_HANDLE_VALUE;}}delete[] s;return res;
}/*******************************************************\
{ ******************************************************* }
{ *                 從內存中加載并運行exe               * }
{ ******************************************************* }
{ * 參數:                                                }
{ * Buffer: 內存中的exe地址                               }
{ * Len: 內存中exe占用長度                                }
{ * CmdParam: 命令行參數(不包含exe文件名的剩余命令行參數)}
{ * ProcessId: 返回的進程Id                               }
{ * 返回值: 如果成功則返回進程的Handle(ProcessHandle),   }
{            如果失敗則返回INVALID_HANDLE_VALUE           }
{ ******************************************************* }
\*******************************************************/
HANDLE MemExecute(void *ABuffer, long Len, char *CmdParam, unsigned long *ProcessId)
{HANDLE res = INVALID_HANDLE_VALUE;PIMAGE_NT_HEADERS peH;PIMAGE_SECTION_HEADERS peSecH;void *Ptr;unsigned long peSz;if (AlignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz)){res = AttachPE(CmdParam, peH, peSecH, Ptr, peSz, *ProcessId);VirtualFree(Ptr, peSz, MEM_DECOMMIT);}return res;
}// 初始化   
class CInit
{
public:CInit(){MyVirtualAllocEx = (pfVirtualAllocEx)GetProcAddress(GetModuleHandle("Kernel32.dll"), "VirtualAllocEx");}
}Init;int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR     lpCmdLine,int       nCmdShow)
{HANDLE hFile = NULL;hFile = CreateFile("c:\\111.exe", GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);if (hFile == INVALID_HANDLE_VALUE)return -1;::SetFilePointer(hFile, 0, NULL, FILE_BEGIN);DWORD dwFileSize = ::GetFileSize(hFile, NULL);LPBYTE pBuf = new BYTE[dwFileSize];memset(pBuf, 0, dwFileSize);DWORD dwNumberOfBytesRead = 0;::ReadFile(hFile, pBuf, dwFileSize, &dwNumberOfBytesRead, NULL);::CloseHandle(hFile);unsigned long ulProcessId = 0;MemExecute(pBuf, dwFileSize, "", &ulProcessId);delete[] pBuf;return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38613.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38613.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38613.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

文件批量重命名001到100

文件批量重命名001到100如何搞定&#xff1f;這是一個許多朋友都在熱議的話題&#xff0c;今天我將向大家介紹這方面的內容&#xff0c;希望你會喜歡。總的來說&#xff0c;文件重命名快捷鍵CtrlF2在日常工作中非常實用。它可以輕松快速地完成文件和文件夾的重命名操作&#xf…

儲能pcb的布局注意事項與制造難點

隨著新能源需求的不斷增長和能源結構的轉型&#xff0c;儲能技術的市場規模不斷擴大。儲能PCB作為儲能系統中電池模塊的重要組成部分&#xff0c;對整個系統的安全性和性能起到關鍵作用。今天我們就來聊聊&#xff0c;儲能pcb有什么特征。 什么是儲能&#xff1a;儲能是指能量…

用友Java后端筆試2023-8-5

計算被直線劃分區域 在笛卡爾坐標系&#xff0c;存在區域[A,B],被不同線劃分成多塊小的區域&#xff0c;簡單起見&#xff0c;假設這些不同線都直線并且不存在三條直線相交于一點的情況。 img 那么&#xff0c;如何快速計算某個時刻&#xff0c;在 X 坐標軸上[ A&#xff0c;…

kubernetes 中的事件(event)簡介以及如何收集event和基于event告警

引用另外一篇文章對k8s event的介紹 1.什么是kubernetes事件 Kubernetes Events 是一種 Kubernetes 資源對象&#xff0c;記錄了某個組件在某個時間做了某個動作&#xff0c;用于展示集群內發生的情況&#xff0c;當 Kubernetes 集群中資源狀態發生變化時&#xff0c;可以產生…

PostMan 教程

安裝https://www.cnblogs.com/mafly/p/postman.html Postman 使用方法詳解https://blog.csdn.net/fxbin123/article/details/80428216 postman進行http接口測試https://blog.csdn.net/five3/article/details/53021084 postman的使用方法詳解&#xff01;最全面的教程https:/…

Golang項目中如何輕松實現私有倉庫pkg包的引入

在企業內部創建一個公共的Golang模塊工程可以幫助提高代碼復用性和開發效率。本文將從如何創建一個公共的Golang工程開始&#xff0c;指導你一步步創建它、并引入到你的工程中。 1、公共模塊規范 下面是一個簡單的步驟指南來創建這樣一個公共模塊項目。 創建版本控制倉庫&am…

Verdi_traceX and autotrace

Verdi_traceX and autotrace Trace X From nWave/nTrace of from the Teporal Flow View. Show Paths on Flow ViewShow Paths on nWave 若Waveform中有X態&#xff0c;鼠標右鍵會有Trace X的選項&#xff1b; 會自動打開Temporal Flow View窗口&#xff0c;展示對應路徑&am…

RocketMQ、Dashboard部署以及安全設置

RocketMQ、dashboard部署以及安全設置 一、啟動RocketMQ1.1 下載RocketMQ1.2 修改配置文件1.2.1 修改nameServer Jvm內存配置1.2.2 修改broker參數 1.3 啟動1.3.1 啟動NameServer1.3.2 啟動Broker1.3.3 測試是否啟動成功1.3.3.1 測試消息發送1.3.3.2 測試消息接收1.3.3.3 Java程…

數據結構——配對堆

引入 配對堆是一個支持插入&#xff0c;查詢/刪除最小值&#xff0c;合并&#xff0c;修改元素等操作的數據結構&#xff0c;是一種可并堆。有速度快和結構簡單的優勢&#xff0c;但由于其為基于勢能分析的均攤復雜度&#xff0c;無法可持久化。 定義 配對堆是一棵滿足堆性質…

C語言暑假刷題沖刺篇——day1

目錄 一、選擇題 二、編程題 &#x1f388;個人主頁&#xff1a;庫庫的里昂 &#x1f390;CSDN新晉作者 &#x1f389;歡迎 &#x1f44d;點贊?評論?收藏?收錄專欄&#xff1a;C語言每日一練 ?其他專欄&#xff1a;代碼小游戲C語言初階&#x1f91d;希望作者的文章能對你…

問道管理:網上如何打新股?

隨著資本市場的不斷敞開&#xff0c;越來越多的人開始重視股票市場&#xff0c;并想經過網上打新股來取得更大的出資收益。但是&#xff0c;網上打新股的辦法并不簡略&#xff0c;怎樣才能成功地打新股呢&#xff1f;本文將從多個角度剖析&#xff0c;協助廣闊出資者處理這一問…

海信聚好看將攜新品DBdoctor,亮相中國數據庫技術大會(DTCC2023)

海信聚好看將攜新品DBdoctor&#xff0c;亮相中國數據庫技術大會 8月16日—18日&#xff0c;第14屆中國數據庫技術大會&#xff08;DTCC-2023&#xff09;將在北京國際會議中心隆重召開。作為國內數據庫領域規模最大的技術交流盛會&#xff0c;吸引了眾多業內知名企業和數百名…

[謙實思紀 01]整理自2023雷軍年度演講——《成長》(上篇)武大回憶(夢想與成長)

文章目錄 [謙實思紀]整理自2023雷軍年度演講 ——《成長》&#xff08;上篇&#xff09;武大回憶&#xff08;夢想與成長&#xff09;0. 寫在前面1. 夢開始的地方1.1 要有夢想&#xff0c;要用目標量化夢想 2. 在兩年內修完所有的學分。2.1 別老自己琢磨&#xff0c;找個懂的人…

【LeetCode 算法】Matrix Diagonal Sum 矩陣對角線元素的和

文章目錄 Matrix Diagonal Sum 矩陣對角線元素的和問題描述&#xff1a;分析代碼Math Tag Matrix Diagonal Sum 矩陣對角線元素的和 問題描述&#xff1a; 給你一個正方形矩陣 mat&#xff0c;請你返回矩陣對角線元素的和。 請你返回在矩陣主對角線上的元素和副對角線上且不…

Python爬蟲IP代理池的建立和使用

寫在前面 建立Python爬蟲IP代理池可以提高爬蟲的穩定性和效率&#xff0c;可以有效避免IP被封鎖或限制訪問等問題。 下面是建立Python爬蟲IP代理池的詳細步驟和代碼實現&#xff1a; 1. 獲取代理IP 我們可以從一些代理IP網站上獲取免費或付費的代理IP&#xff0c;或者自己租…

【深度學習所有損失函數】在 NumPy、TensorFlow 和 PyTorch 中實現(1/2)

一、說明 在本文中&#xff0c;討論了深度學習中使用的所有常見損失函數&#xff0c;并在NumPy&#xff0c;PyTorch和TensorFlow中實現了它們。 二、內容提要 我們本文所談的代價函數如下所列&#xff1a; 均方誤差 &#xff08;MSE&#xff09; 損失二進制交叉熵損失加權二進…

“深入解析JVM內部機制:探索Java虛擬機的奧秘“

標題&#xff1a;深入解析JVM內部機制&#xff1a;探索Java虛擬機的奧秘 JVM&#xff08;Java虛擬機&#xff09;是Java程序的核心執行環境&#xff0c;它負責將Java字節碼轉換為機器碼并執行。了解JVM的內部機制對于理解Java程序的執行過程和性能優化至關重要。本文將深入解析…

開啟想象翅膀:輕松實現文本生成模型的創作應用,支持LLaMA、ChatGLM、UDA、GPT2、Seq2Seq、BART、T5、SongNet等模型,開箱即用

開啟想象翅膀&#xff1a;輕松實現文本生成模型的創作應用&#xff0c;支持LLaMA、ChatGLM、UDA、GPT2、Seq2Seq、BART、T5、SongNet等模型&#xff0c;開箱即用 TextGen: Implementation of Text Generation models 1.介紹 TextGen實現了多種文本生成模型&#xff0c;包括&a…

c++——::作用域、命名空間、using(聲明和編譯指令)

c 作用域和名字控制 一、::(雙冒號) 作用域 <::>運算符是一個作用域如果<::>前面什么都沒有加 代表是全局作用域 二、命名空間&#xff08;namespace) 1、namespace 本質是作用域,可以更好的控制標識符的作用域命名空間 就可以存放 變量 函數 類 結構體 … 2…

【kubernetes】在k8s集群環境上,部署kubesphere

部署kubesphere 學習于尚硅谷kubesphere課程 前置環境配置-部署默認存儲類型 這里使用nfs #所有節點安裝 yum install -y nfs-utils# 在master節點執行以下命令 echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports # 執行以下命令&#xff…