Bootstrap

写时复制,读时加载

实现写时复制,读时加载,原理为,申请内存时,只给一段线性地址空间,并不分配物理内存,当cpu读、写该内存时,发生缺页中,或者写错误,中断处理程序根据前面设置的内容,决写分配物理内存,或者是共享内存,如果需要读取文件时,则根据需要读取相应的文件数据。

主要代码如下:

LPPE_FILE_ELEM load_pe_dll(char * lpFileName) {
	LPPE_FILE_ELEM lpSysFE, lpUserFE; //文件列表中的元素,保存文件信息
	//文件加载内存的默认地址
	//PIMAGE_DOS_HEADER pdh; //dos头
	PIMAGE_NT_HEADERS pnh; 	//pe文件头
	LPVOID pfile, pKrFile;
	PMEMORY_BASIC_INFORMATION pmbi;
	BYTE buf[512]; 		//临时块
	//补全文件目录路径
	add_patch((char *) buf, lpFileName);

	//1、从系统进程中查找是否已经加载
	lpSysFE = pe_find_file((char*) buf, get_sys_files_list());
	if (lpSysFE) {
		//系统中已经加载,进入下一步
		pKrFile = (LPVOID) lpSysFE->hModule;
		DbgPrint("dll已读入内存 : %s ,%x\n", lpSysFE->strFilePath,
				lpSysFE->dwNumberOfShares);
		//return lpFE->hModule;
	} else {
		//将文件加入系统文件列表中

		lpSysFE = kr_malloc(sizeof(PE_FILE_ELEM) + strlen((char*) buf) + 1);
		strcpy(lpSysFE->strFilePath, (char*) buf);
		if (load_file(lpSysFE->strFilePath, 0, 512, buf)) {

			//新exe头部的文件地址 指向PE
			pnh = (PIMAGE_NT_HEADERS) ((DWORD) buf
					+ ((PIMAGE_DOS_HEADER) buf)->e_lfanew);
			//系统进程中仅申请空间,读时加载
			pmbi = mem_virtual_alloc(&stMbiSys, 0,
					pnh->OptionalHeader.SizeOfImage, MEM_COMMIT,
					PAGE_READWRITE,
					MEM_IMAGE);
			pKrFile = (PBYTE) pmbi->BaseAddress;
			lpSysFE->hModule = (DWORD) pKrFile;
			lpSysFE->dwImgsize = pnh->OptionalHeader.SizeOfImage;
			lpSysFE->dwNumberOfShares = 0;
			list_push(get_sys_files_list(), (PLIST_ELEM) lpSysFE);
			load_pe_text((PIMAGE_DOS_HEADER) buf, (DWORD) lpSysFE->hModule,
					lpSysFE->strFilePath);

			if (!pKrFile) {
				//失败
				//DbgPrint("dll已读入内存 : %s ,%x\n");
			} else {
				//初始化时设置文件共享的次数为0,加入系统加载文件队列。

			}
		}
	}		//	if (lpFE)
	//2、检查用户空间 当前文件是否已经加载
	lpUserFE = pe_find_file(lpSysFE->strFilePath, get_current_file_list());
	if (lpUserFE) {
		//2、已经加载 直接返回
		return lpUserFE;
	} else {
		//第一次加入进程,将pKrfile的物理地址映射到 pfile

		pnh = (PIMAGE_NT_HEADERS) ((((PIMAGE_DOS_HEADER) pKrFile)->e_lfanew)
				+ (DWORD) pKrFile);

		pmbi = mem_virtual_alloc(
		MBI_USER_BASE, (LPVOID) pnh->OptionalHeader.ImageBase,
				pnh->OptionalHeader.SizeOfImage,
				MEM_COMMIT, PAGE_READWRITE, MEM_IMAGE);
		pfile = (PBYTE) pmbi->BaseAddress;
		DbgPrint("共享 dll: %s,%x,%x,%x,%x\n", lpSysFE->strFilePath, pfile,
				pKrFile, pnh->OptionalHeader.ImageBase);
		lpUserFE = kr_malloc(
				strlen((char*) lpSysFE->strFilePath) + sizeof(PE_FILE_ELEM)
						+ 1);
		strcpy(lpUserFE->strFilePath, lpSysFE->strFilePath);
		lpUserFE->hModule = (DWORD) pfile;
		lpUserFE->dwImgsize = lpSysFE->dwImgsize;
		lpUserFE->hSysModule = pKrFile;
		list_push(get_current_file_list(), (PLIST_ELEM) lpUserFE);
		lpSysFE->dwNumberOfShares++;
		DoRelocationTable(pfile);
		link_import(pfile);
		//文件头映射

		//	mem_map_demand(pKrFile, pfile,
		//		_ALIGN(	pdh->e_lfanew + sizeof(IMAGE_NT_HEADERS32)
		//					+ pnh->FileHeader.NumberOfSections
		//								* sizeof(IMAGE_SECTION_HEADER), 4096),
		//		PG_USER_R_P);

	}

	return lpUserFE;
}
BOOL mem_fail_sys(DWORD code, DWORD addr) {
	DWORD temp;
	LPPE_FILE_ELEM fe;
	PMEMORY_BASIC_INFORMATION pmbi =mem_find_mbi(&stMbiSys,
			addr);
	//DbgPrint("内核%s错误  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",(code&2?"写":"读"), addr, code,
	//		pmbi->BaseAddress, pmbi->RegionSize, pmbi->State, pmbi->Type);
	if (!pmbi) {
		return FALSE;
	}

//如果是映射
	if (pmbi->State == MEM_COMMIT) {

		//分配物理内存,以4K为单位,

		if (pmbi->Type == MEM_IMAGE) {
			mem_physics(addr, 4096, PGE_SYS_RW_P);
			fe = (LPPE_FILE_ELEM) pe_find_from_addr(get_sys_files_list(), addr);
			if (fe->dwNumberOfShares != 0) {
				//直接分配内存,再读取文件
				//200对齐
				load_file_4k(fe, addr);
			} else {
				//正在读文件头
			}
		} else if (pmbi->Type == MEM_4MB_PAGES) {
			// MEM_4MB_PAGES:
			//二级目录
			//	DbgPrint("二级页表%x,%x\n", addr, (DWORD *) MiGetPteAddress(addr));
			mem_physics(addr, 4096, PG_USER_RW_P);
			//*(DWORD *) MiGetPteAddress(addr) =
			//		get_freed_physics() + PG_USER_RW_P;
		} else {
			mem_physics(addr, 4096, PGE_SYS_RW_P);
		}
		//直接分配内存

	}	//if (pmbi->State == MEM_COMMIT)
	else {
		return FALSE;
	}
	return TRUE;
}
BOOL mem_fail_user(DWORD code, DWORD addr) {
	DWORD temp;
	LPPE_FILE_ELEM fe;

	PMEMORY_BASIC_INFORMATION pmbi =mem_find_mbi(MBI_USER_BASE, addr);
	//DbgPrint("用户%s错误  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", (code&2?"写":"读"),addr, code,
	//		pmbi->BaseAddress, pmbi->RegionSize, pmbi->State, pmbi->Type);
	if (!pmbi) {
//找不分配的段。
		return FALSE;
	}
//状态是不是已经提交
//只读,读写,还共享的读写
//如果是map类,分配内存,如果Image类,则读时加载,写时复制
	if (pmbi->State == MEM_COMMIT) {
		if (pmbi->Type == MEM_IMAGE) {
			fe = (LPPE_FILE_ELEM) pe_find_from_addr(get_current_file_list(),
					addr);
			if (fe->hSysModule == 0) {
				//说明是不是共享文件
				//DbgPrint("用户内存错误  0x%x , 0x%x , 0x%x , 0x%x \n", addr, code,
				//			pmbi->BaseAddress, pmbi->State);
				mem_physics(addr, 4096, PG_USER_RW_P);
				load_file_4k(fe, addr);
				//break;
			} else {
				//DbgPrint("用户内存错误 %x,%x\n",addr,fe);
				//先读取系统内存,如果出错则会中断到mem_fail_sys,加载文件
				//如果是读取,则映射,如果是写则分配内存再复制
				if (code & 2) {
					//重新申请物理内,再复制
					//DbgPrint("用户写错误%x\n", addr);
					mem_physics(addr, 4096, PG_USER_RW_P);
					memcpy((PVOID) (addr & 0xFFFFF000),
							(PVOID) ((fe->hSysModule + (addr - fe->hModule))
									& 0xFFFFF000), 4096);
				} else {
					//DbgPrint("用户读错误%x\n", addr);
					//说明可能内核没有读取文件,也可能没有映射
					//先试读下,没有读取文件会中断后读取,再映射
					//mem_fail_sys(0,fe->hSysModule+addr-fe->hModule);
					temp = *(PDWORD) (fe->hSysModule + addr - fe->hModule);
					//函数自己内部齐,size=1~4096效果应是一样
					mem_map_demand(fe->hSysModule + addr - fe->hModule, addr,
							4096, PG_USER_R_P);
				}

				//直接分配内存,再读取文件
				//addr-pmbi->BaseAddressg
			}
		} else if ((pmbi->Type == MEM_PRIVATE)) {//if (pmbi->Type == MEM_IMAGE)
			mem_physics(addr, 4096, PG_USER_RW_P);
		}
	}		//	if (pmbi->State == MEM_COMMIT)
	else {
		return FALSE;
	}
	return TRUE;
}

;