當前位置:首頁 > IT技術

通過WriteProcessMemory改寫進程的內存

時間:2019-06-03 04:45:06來源:IT技術作者:seo實驗室小編閱讀:62次「手機版」
 

writeprocessmemory

http://www.cnblogs.com/feiyucq/archive/2009/10/21/1587628.html

以PROCESS_ALL_ACCESS權限打開進程以后既能夠使用ReadProcessMemory讀取程序內存,也能夠使用writeprocessmemory改敲代碼的內存,這也是一些內存補丁使用的招數,下面是程序的實現代碼

#include <windows.h>
#include <tlhelp32.h>
BOOL CALLBACK EnumChildWindowProc(HWND hWnd,LPARAM lParam);//枚舉記事本中的子窗體
char mess[999999];
int WINAPI WinMain(Hinstance hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
	HWND nphWnd=::FindWindow("notepad",NULL);
	if(nphWnd)
	{
		char temp[1024];
		PROCESSENTRY32 pe32;
		pe32.dwSize=sizeof(pe32);
		handle hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//獲得進程列表的快照,第一個參數能夠有其它選項,具體請參考MSDN
		if(hProcessSnap==INvalid_HANDLE_VALUE)
		{
			::messageBox(NULL,"CreateToolhelp32Snapshot ERROR","error",MB_OK);
			return 0;
		}
		HANDLE hProcess;
		BOOL bMore=::Process32First(hProcessSnap,&pe32);//獲得第一個進程的信息
		while(bMore)
		{
			::wsprintf(temp,"%s",pe32.szExeFile);
			if(!::strcmp(temp,"button.exe"))
			{
				hProcess=::OpenProcess(PROCESS_ALL_ACCESS,false,(Dword)pe32.th32ProcessID);
				if(hProcess==NULL)
				{
					::wsprintf(temp,"%s","打開進程失敗!");
					::strcat(mess,temp);
				}
				else
				{
					::wsprintf(temp,"%s","打開進程成功!");
					::strcat(mess,temp);
					//改寫內存中內容
					int tmp=97;//ascii:a
					dword dwNumberOfBytesRead;
					if(!::WriteProcessMemory(hProcess,(LPVOID)0x0040505d,&tmp,1,&dwNumberOfBytesRead))
					{
						::wsprintf(temp,"%s","寫入失敗");
						::strcat(mess,temp);
					}
					else
					{
						::wsprintf(temp,"%s","寫入成功");
						::strcat(mess,temp);
					}
				}
				break;
			}
			bMore=::Process32Next(hProcessSnap,&pe32);//獲得其它進程信息
		}
		::EnumChildWindows(nphWnd,EnumChildWindowProc,0);//獲得記事本的edit窗體,打印進程信息
		return 0;
	}
	else
	{
		::MessageBox(NULL,"please open notepad","error",MB_OK);
		return 0;
	}
}
BOOL CALLBACK EnumChildWindowProc(HWND hWnd,LPARAM lParam)
{
	char temp1[256];
	if(hWnd)
	{
		::GetClassName(hWnd,temp1,255);
		if(!::strcmp(temp1,"Edit"))//得到edit子窗體句柄
		{
			::SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)mess);
			return 0;
		}
	}
	return true;
}

程序的功能是改寫名為button.exe程序中內存地址為0x0040505d的值為97,即ASCII值的a,此處內存的原內容為ASCII值的m

被改動的程序實現代碼例如以下:

#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK _procWinMain(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
	HWND hWinMain,hButton1,hButton2;
	MSG stMsg;
	WNDCLASSEX stWndClass;
	Rtlzeromemory(&stWndClass,sizeof(stWndClass));//WNDCLASSEX結構置零
	//注冊窗體類
	stWndClass.hCursor=::LoadCursor(0,IDC_ARROW);
	stWndClass.hInstance=hInstance;
	stWndClass.cbSize=sizeof(WNDCLASSEX);
	stWndClass.style=CS_HREDRAW||CS_VREDRAW;
	stWndClass.lpfnWndProc=_procWinMain;
	stWndClass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
	stWndClass.lpszClassName="myclass";
	::RegisterClassEx(&stWndClass);
	//建立并顯示窗體
	hWinMain=::createwindowEx(WS_EX_CLIENTEDGE,"myclass","firstwindow",WS_overlapPEDWINDOW,100,100,600,400,NULL,NULL,hInstance,NULL);
	//建立button
	hButton1=::CreateWindowEx(NULL,"BUTTON","button1",WS_VISIBLE|WS_CHILD,300,200,60,20,hWinMain,(HMENU)1,hInstance,NULL);
	hButton2=::CreateWindowEx(NULL,"BUTTON","button2",WS_VISIBLE|WS_CHILD,100,200,60,20,hWinMain,(HMENU)2,hInstance,NULL);
	
	::ShowWindow(hWinMain,SW_SHOWNORMAL);
	::UpdateWindow(hWinMain);
	while(1)
	{
		if(::GetMessage(&stMsg,NULL,0,0)==0)//消息為WM_QUIT
			break;
		else
		{
			::TranslateMessage(&stMsg);
			::DispatchMessage(&stMsg);
		}
	}
	return 0;
}
LRESULT CALLBACK _procWinMain(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	if(uMsg==WM_CLOSE)
	{
		::DestroyWindow(hWnd);
	}
	else if(uMsg==WM_DESTROY)
	{
		::PostQuitMessage(NULL);
	}
	else if(uMsg==WM_commaND)
	{
		char temp1[256],temp2[256];
		::itoa((int)wParam,temp1,10);
		::strcpy(temp2,"wParam: ");
		::strcat(temp2,temp1);
		::strcat(temp2," lParam: ");
		::itoa((int)lParam,temp1,10);
		::strcat(temp2,temp1);
		::strcat(temp2," mess");
		::MessageBox(NULL,temp2,"command",MB_OK);
	}
	else
	{
		return ::DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
	return 0;
}

這個程序的功能是在窗體上建立兩個button,點擊不論什么一個button都會彈出一個對話框,輸出button回調函數的wParam、lParam參數的值,外加一段字符串“mess”,我們要改動的就是字符串“mess”的第一個字符“m”為“a”。

將兩端代碼編譯以后先打開button程序,點擊窗體上的隨意一個按鈕,應該彈出例如以下的對話框

--------------------------------------------------------------------------------------華麗的切割線------------------------------------------------------------------------------

http://www.cppblog.com/windcsn/archive/2006/04/20/5981.html

這一篇主要講了怎樣使用消息進行進程間通信

近期在寫個程序的時候須要在進程間通訊,詳細需求是這樣。

1.?????? 主要有兩個進程:一個進程作為被請求進程,我們稱為 SERVER 進程;還有一個進程是請求進程,稱為 CLIENG 進程。

2.?????? SERVER 進程提供一些服務,其完畢計算功能;而 CLIENT 進程須要在它運行完計算之后將結果取會。

因為計算結果可能是一個結構,也可能是一個復雜的數據,所以通過消息來在進程傳遞信息是有限的。還有一方面通常是單方向的通訊,實際上這里的需求有一個雙向性,看下圖:

ProcessComm.JPG

這里兩個進程都能夠有自己的窗體,因此實際上我們能夠通過消息來通知對方。但細致一想,請求服務通過windows消息是沒有問題的,通知結果通過消息是不妥當的,實際上我們須要在請求服務完以后馬上得到運行結果,而使用windows消息可能有時間上的問題,并且同步很麻煩。

想要的結果是在 CLIENT 請求服務以后馬上得到返回結果,但我們能夠改變一下思路就easy多了:結果不是有 SERVER 返回,而由 CLIENG 自己去獲取。這樣,我們能夠在請求消息的時候使用 SendMessage 來發送這個請求。這是必須的, SendMessage 發送消息是同步的方式,必須等到消息處理完成之后才返回,而這正是我們想要的結果。那么 CLIENT 怎么樣才干獲得 SERVER 進程的結果來。

我們知道, WINDOWS 下每一個進程都有自己的地址空間,普通情況下一個進程訪問你一個進程的地址是不對的,最簡單的是提示該地址無效,程序崩潰。當然還是有非常多中方式來讀取對方進程的地址空間上的數據。

1.? 能夠做一個 DLL ,利用注入 DLL 的方式,來將該 DLL 注入到遠程進程的地址空間上,然后通過 DLL 的 API 來讀取。這個方式有點麻煩,還必須寫一個 DLL ,還要注入 DLL 。

2.? 使用 WriteProcessMemory 和 ReadProcessMemory 來讀寫遠程進程的內存。這樣的方式相對照較簡單,其有一個參數遠程進程的 HANDLE 指示你須要讀寫哪個進程的內存。當然,使用這兩個函數是須要遠程進程的地址空間的,這個地址是通過 virtualAllocEx 來分配的,其通過 VirtualFreeEx 來釋放。這兩個 API 也有一個進程句柄參數。

有人問,為什么不使用 WM_COPYDATA 來傳遞大塊數據?好,該消息是能夠傳遞大塊數據,但我們的應用中須要消息的雙向,所以這里使用 WM_COPYDATA 是不合適的。

以下我們看一下第 2 種方法的詳細步驟:

1.? 找到對方進程的窗體。

2.? 找到他的進程 ID

3.? 使用 PROCESS_VM_OPERATION| PROCESS_VM_WRITE|PROCESS_VM_READ 標志來打開該進程,得到該繼承的 HANDLE 。

4.? 使用 VirtualAllocEx 來在該進程上分配適當大小的內存,得到一個地址,這個地址是遠程進程的,通過不同的方式來改動該地址上的值是無效的。

5.? 假設須要傳遞一些參數到遠程進程,我們能夠在該內存上寫一些內容,通過 WriteProcessMemory 來完畢

6.? 使用 SendMessage 來發送請求服務消息,同一時候將上面分配的內存地址作為參數傳遞給遠程進程。

7.? 遠程進程得到指定消息后處理該消息,取得參數,計算結果,將結果寫到指定的地址。因為這個地址是遠程進程自己的地址空間,其操作這塊內存的方法沒有什么特別之處。

8.? SendMessage 消息返回, CLIENT 知道后,從上面的內存地址中讀取返回結果;這里必須使用 ReadProcessMemory 來讀取。好了,整個過程結束。

9.? 調用 VritualFreeEx 將上面的內存釋放。

這里須要強調兩點:

1.? 打開進程的時候必須設置對虛擬內存可操作、可寫、可讀,假設僅僅是可寫,那么 ReadProcessMemory 將讀取不對。

2.? 必須釋放該內存。

以下是部分程序:

CLIENT 程序:

#define???? WM_COMPAREIMAGE WM_USER +100

void?CTestCompareDlg::OnBnClickedButton1()

{

HANDLE hProcess = NULL;

DWORD dwProcessId = 0;

HWND hServerWnd = ::FindWindow(NULL,"CompareServer");

if?(hServerWnd == NULL)

{

//Need create the process

return?;

}

::GetWindowThreadProcessId(hServerWnd,&dwProcessId);

hProcess = OpenProcess(PROCESS_VM_OPERATION|

PROCESS_VM_WRITE|PROCESS_VM_READ,FALSE,dwProcessId);

if?(hProcess == NULL)?return?;

MyInfo * pMyInfo = NULL;

pMyInfo = (MyInfo *)VirtualAllocEx(hProcess,NULL,

sizeof?(MyInfo),MEM_COMMIT,PAGE_READWRITE);

if?(pMyInfo == NULL)?return?;

MyInfo myInfo;

myInfo.blue = 20.01;

myInfo.red = 3333;

WriteProcessMemory(hProcess,pMyInfo,&myInfo,?sizeof?(MyInfo),NULL);

::SendMessage(hServerWnd,WM_COMPAREIMAGE,?sizeof?(MyInfo),(LPARAM)pMyInfo);

DWORD dwRead = 0;

MyInfo myInfo2;

BOOL bRet = ::ReadProcessMemory(hProcess,pMyInfo,&myInfo2,?sizeof?(MyInfo),&dwRead);

dwRead = GetLastError();

m_log.Format("red =%.2f,blue=%.2f",myInfo2.blue,myInfo2.red);

TRACE(m_log);

VirtualFreeEx(hProcess,pMyInfo,0,MEM_RELEASE);

updatedata(FALSE);

}

SERVER 程序:

LRESULT??? CMyWindow::OnCompareImage(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

if?(wParam <?sizeof?(MyInfo))?return?-1;

MyInfo * pMyInfo = (MyInfo *)lParam;

sprintf(m_strLog,"client:red=%.2f,blue=%.2f",pMyInfo->red,pMyInfo->blue);

::textout(GetDC(hWnd),0,50,m_strLog,strlen(m_strLog));

pMyInfo->blue = 1.0;

pMyInfo->red = 2.0;

return?0;

}

--------------------------------------------------------------------------------------華麗的切割線------------------------------------------------------------------------------

http://www.hackbase.com/tech/2012-05-30/66582.html

這一篇主要講述了怎樣創建遠程線程, 實際上沒有講dll注入

所謂DLL注入就是將一個DLL放進某個進程的地址空間里,讓它成為那個進程的一部分。要實現DLL注入,首先須要打開目標進程。

hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //同意遠程創建線程

PROCESS_VM_OPERATION | //同意遠程VM操作

PROCESS_VM_WRITE, //同意遠程VM寫

FALSE, dwRemoteProcessId )

因為我們后面須要寫入遠程進程的內存地址空間并建立遠程線程,所以須要申請足夠的權限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。

假設進程打不開,以后的操作就別想了。進程打開后,就能夠建立遠線程了,只是別急,先想想這個遠線程的線程函數是什么?我們的目的是注入一個DLL。并且我們知道用loadlibrary能夠載入一個DLL到本進程的地址空間。于是,自然會想到假設能夠在目標進程中調用LoadLibrary,不就能夠把DLL載入到目標進程的地址空間了嗎?對!就是這樣。遠線程就在這兒用了一次,建立的遠線程的線程函數就是LoadLibrary,而參數就是要注入的DLL的文件名稱。(這里須要自己想一想,注意到了嗎,線程函數ThreadProc和LoadLibrary函數很相似,返回值,參數個數都一樣) 另一個問題,LoadLibrary這個函數的地址在哪兒?或許你會說,這個簡單,GetProcAddress就能夠得出。于是代碼就出來了。

char *pszLibFileRemote="my.dll";

PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");

CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);

可是不正確!不要忘了,這是遠線程,不是在你的進程里,而pszLibFileRemote指向的是你的進程里的數據,到了目標進程,這個指針都不知道指向哪兒去了,相同pfnStartAddr這個地址上的代碼到了目標進程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。可是,問題總是能夠解決的,Windows有些非常強大的API函數,他們能夠在目標進程里分配內存,能夠將你的進程中的數據復制到目標進程中。因此pszLibFileRemote的問題能夠攻克了。

char *pszLibFileName="my.dll";//注意,這個一定要是全路徑文件名稱,除非它在系統文件夾里;原因大家自己想想。

//計算DLL路徑名須要的內存空間

int cb = (1 + lstrlenA(pszLibFileName)) * sizeof(char);

//使用VirtualAllocEx函數在遠程進程的內存地址空間分配DLL文件名稱緩沖區

pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);

//使用WriteProcessMemory函數將DLL的路徑名拷貝到遠程進程的內存空間

iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);

OK,如今目標進程也認識pszLibFileRemote了,可是pfnStartAddr好像不好辦,我怎么可能知道LoadLibraryA在目標進程中的地址呢?事實上Windows為我們攻克了這個問題,LoadLibraryA這個函數是在Kernel32.dll這個核心DLL里的,而這個DLL非常特殊,無論對于哪個進程,Windows總是把它載入到同樣的地址上去。因此你的進程中LoadLibraryA的地址和目標進程中LoadLibraryA的地址是同樣的(事實上,這個DLL里的全部函數都是如此)。至此,DLL注入結束了。

[cpp]

/*??

遠程注入explorer.exe,不停Beep,注入完就退出。??

*/????

#include <windows.h>??????

#include <stdio.h>??????

#include <tlhelp32.h>?????

#include <Shlwapi.h>?????

#include <tchar.h>?????

#pragma comment(lib,"Shlwapi.lib")?????

#pragma comment(linker, "/BASE:0x14000000")?????

//#define NoWindow?????

#ifdef NoWindow?????

#pragma comment(linker,"/subsystem:windows /FILEALIGN:0x200 /ENTRY:main")?????

#pragma comment(linker,"/INCREMENTAL:NO /ignore:4078")?????

#pragma comment(linker,"/MERGE:.idata=.text /MERGE:.data=.text /MERGE:.rdata=.text /MERGE:.text=Anskya /SECTION:Anskya,EWR")??????

#endif?????

typedef int (__stdcall *fnMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);????

typedef int (__stdcall *fnBeep)(int,int);????

#define ProcessName "services.exe"http://"rundll32.exe"http://"svchost.exe"http://"explorer.exe"http://"avp.exe"http://"lsass.exe"http://?????

//????

//依據進程名,獲得進程ID?????

DWORD GetProcessID(char *FileName)????

{????

?? HANDLE hProcess;????

?? PROCESSENTRY32 pe;????

?? BOOL bRet;????

?? //進行進程快照?????

?? hProcess=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);????

?? //開始進程查找?????

?? bRet=::Process32First(hProcess,&pe);????

?? //循環比較,得出ProcessID?????

?? while(bRet)????

?? {????

?? if(strcmp(FileName,pe.szExeFile)==0)????

?? return pe.th32ProcessID;????

?? else????

?? bRet=::Process32Next(hProcess,&pe);????

?? }????

?? //返回得到的ProcessID?????

//? printf("Process not found!\n");?????

?? return 9999;????

}????

//???

//遠程注入函數???www.2cto.com???????

void __stdcall RmoteThread()????

{????

HMODULE hMod,hMod2;????

fnMessageBoxA myMessageBoxA;????

fnBeep myBeep;????

char* path[MAX_PATH];????

hMod = GetModuleHandle("user32.dll");????

hMod2 = GetModuleHandle("kernel32.dll");????

myMessageBoxA = (fnMessageBoxA)GetProcAddress(hMod, (LPCSTR)"MessageBoxA");????

myBeep = (fnBeep)GetProcAddress(hMod2, (LPCSTR)"Beep");????

/*for(int i=0;i<30;i++)??

{??

?? myBeep(800,400);??

}??

*/????

//?? while(1)?????

for(int i=0;i<6;i++)????

{????

Beep(600,100);????

Sleep(200);????

}????

GetModuleFileName(NULL,(char*)path,MAX_PATH);????

//?? myMessageBoxA(NULL, (char*)path, NULL, 64);?????

}????

//?

// 提升應用級調試權限?????

BOOL EnablePrivilege(HANDLE hToken,LPCTSTR szPrivName,BOOL fEnable)????

{????

?? TOKEN_privileges tp;????

?? tp.PrivilegeCount = 1;????

?? LookupPrivilegeValue(NULL,szPrivName,&tp.Privileges[0].Luid);????

?? tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED:0;????

?? AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);????

?? return((GetLastError() == ERROR_SUCCESS));????

}????

//?

///說明: 插入代碼,遠程線程為RmoteThread()?????

///參數: Pid = 進程PID?????

///返回: 成功True,否則False?????

bool InjectExe(DWORD Pid)????

{????

bool?????? status = false;????

LPVOID???? pBaseAddr = NULL;????

HMODULE??? hMod = GetModuleHandle(NULL);????

LONG?????? hNHoffset = PIMAGE_DOS_HEADER(hMod)->e_lfanew;????

HANDLE???? hThread,????

?? hProcess,????

?? hToken;????

DWORD????? cbImage;????

//cbImage=內存中整個PE映像體的尺寸?????

cbImage= PIMAGE_NT_headers((DWORD)hMod + (DWORD)hNHOffset)->optionalHeader.SizeOfImage;????

//重要,否則不能注入lsass?????

OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);????

EnablePrivilege(hToken,SE_DEBUG_NAME,TRUE);????

hProcess? = OpenProcess(PROCESS_ALL_ACCESS, TRUE, Pid);????

if (hProcess == NULL)????

{????

#ifdef debug?????

MessageBoxA(NULL, "錯誤OpenProcess", NULL, 64);????

#endif?????

goto Err;????

}????

//釋放遠程內存?????

VirtualFreeEx(hProcess, LPVOID(hMod), 0, MEM_RELEASE);????

//分配遠程內存?????

pBaseAddr = VirtualAllocEx(hProcess, LPVOID(hMod), cbImage, MEM_COMMIT | MEM_RESERVE,????

?????????????????? PAGE_EXECUTE_READWRITE);????

if (pBaseAddr == NULL)????

{????

#ifdef debug?????

MessageBoxA(NULL, "VirtualAllocEx failed", NULL, 64);????

#endif?????

goto Err;????

}????

//寫進去,將本進程的整個PE體 全寫進目標進程,夠狠~?????

if (!WriteProcessMemory(hProcess, pBaseAddr, LPVOID(hMod), cbImage, NULL))????

{????

#ifdef debug?????

MessageBoxA(NULL, "WriteProcessMemory failed", NULL, 64);????

#endif?????

goto Err;????

}????

hThread = CreateRemoteThread(hProcess, NULL, NULL, \????

(LPTHREAD_START_ROUTINE)&RmoteThread, NULL, NULL, NULL);????

if (hThread == NULL)????

{????

#ifdef debug?????

MessageBoxA(NULL, "CreateRemoteThread failed", NULL, 64);????

#endif?????

goto Err;????

}????

//?? waitforSingleObject(hThread, INFINITE);?????

CloseHandle(hThread);????

CloseHandle(hProcess);????

status = TRUE;????

return status; //自己返回即可,不要VirtualFreeEx;,否則宿主就掛了!?????

Err:????

if (pBaseAddr != NULL)????

VirtualFreeEx(hProcess, pBaseAddr, 0, MEM_RELEASE);????

if (hProcess != NULL)????

CloseHandle(hProcess);????

return status;????

}????

//?????

int main()????

{????

?? char aa[]="aBcDdddFFFF asfd";????

?? strupr((char*)aa);????

?? printf(aa);????

if (!InjectExe(GetProcessID(ProcessName)))????

Beep(1800,500);????

return 0;????

} ??

相關閱讀

利用DuplicateHandle()無驅動的另類讀寫內存方法,理論

突發奇想出的原創思路,在理論上經過完善應該可以做到過游戲保護無驅動讀寫內存 首先看一下MSDN對這個函數的定義 BOOL DuplicateH

電腦忽然死機自動關機,重啟后不一會會花屏死機, 建議

電腦忽然死機自動關機,重啟后不一會會花屏死機, 建議可以先嘗試拔下內存條去去灰塵在試一下.? 博主就是這樣恢復好.反正已經關機

Android文件存儲與讀取(手機內存、SD卡)

1.activity_main.xml文件中 <!-- 保存數據到手機內存中 --> <Button android:id="@+id/btnSave" android:la

內存映像文件

象使用虛擬內存一樣,使用內存映像文件同樣需要經過保留和提交兩個過程,首先在進程內存空間保留一塊區域,然后提交物理存儲給這段區域

直接內存溢出

JVM虛擬機是運行在操作系統上的進程,操作系統分配給JVM的內存在啟動是有限的,不可能把全部內存都分配給JVM,Java NIO又用到了直接內

分享到:

欄目導航

推薦閱讀

熱門閱讀

云南彩票投注站申请