这里的话是新加的,过了好几天,终于想明白了问题,因为系统只帮我们加载了外壳软件的dll,并没有帮我们加载节里的dll,所以我们需要加一步把IAT表修复了,这是课程没有的内容,网络上大部分回答(我看到的都没有讲)也没有解决这个东西
该踩的坑有,ASLR基址,大意就是不能修改imagebase不然后系统一检测到就报c0000005,所以大多数软件都不需要修复重定位表,大部分imagebase都是0x400000。
还有就是滴水给出的细节没给全,因为我的试验是,两个软件如果文件对齐,内存对齐是一样的就能运行,否则不行。我试了两组例子,一组1000 10000,一组1000 200,一样就能够运行,不一样就不行。
我猜测是因为上下文(context)里还有需要修改的东西。总之是一项过时的技术,体验思路就行,没必要抓着这些细枝末节刨根问底
//附加
VOID AttachShell(PVOID pShellFileName, PVOID pInFileName, PVOID pOutFileName) {
//读取shell文件和src文件,再给壳增加节
PVOID pShellFileBuffer = 0;
PBYTE pInFileBuffer = 0;
PVOID pOutFileBuffer = 0;
DWORD dwOutFileSize = 0;
DWORD dwShellSize = FileToFileBuffer(pShellFileName, &pShellFileBuffer);
DWORD dwInFileSize = FileToFileBuffer(pInFileName, &pInFileBuffer);
dwOutFileSize = IncreaseSection(pShellFileBuffer, &pOutFileBuffer, dwInFileSize);
//套入headers结构
PIMAGE_DOS_HEADER pDosHeader = pOutFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (DWORD)pDosHeader + pDosHeader->e_lfanew;
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
printf("File is not PE\n");
free(pOutFileBuffer);
return FALSE;
}
PIMAGE_FILE_HEADER pFileHeader = &pNTHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER pOptHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
PIMAGE_SECTION_HEADER pSecHeader = (DWORD)pOptHeader + pFileHeader->SizeOfOptionalHeader;
//计算最后一个节,然后转换成文件偏移
PIMAGE_SECTION_HEADER pLastSection = pSecHeader + pFileHeader->NumberOfSections - 1;
PBYTE pEncryPosition = VAToFOA(pLastSection->VirtualAddress, pOutFileBuffer);
//给每一个字节的代码做加密并且写入新加的节里
for (size_t i = 0; i < dwInFileSize; i++) {
pEncryPosition[i] = ~pInFileBuffer[i];
}
//存盘
FileBufferToFile(pOutFileName, pOutFileBuffer, dwOutFileSize);
free(pShellFileBuffer);
free(pInFileBuffer);
free(pOutFileBuffer);
}
VOID DumpShell(PVOID pFileName) {
//函数指针显示调用
typedef NTSTATUS(WINAPI* FnNtUnmapViewOfSection)(HANDLE, PVOID);
STARTUPINFO si = { 0 };
si.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION pi;
//定位最后一节
PBYTE pFileBuffer = 0;
DWORD dwFileSize = FileToFileBuffer(pFileName, &pFileBuffer);
PIMAGE_DOS_HEADER pDosHeader = pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader= (DWORD)pFileBuffer + pDosHeader->e_lfanew;
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
printf("File is not PE\n");
free(pFileBuffer);
return FALSE;
}
PIMAGE_FILE_HEADER pFileHeader = (DWORD)pNTHeader + sizeof(pNTHeader->Signature);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
PIMAGE_SECTION_HEADER pSectionHeader = (DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader;
PIMAGE_SECTION_HEADER pLastSecHeader = pSectionHeader + pFileHeader->NumberOfSections - 1;
//解密
PBYTE pDecryPosition = pLastSecHeader->PointerToRawData + (DWORD)pFileBuffer;
for (size_t i = 0; i < pLastSecHeader->SizeOfRawData; i++) {
pDecryPosition[i] = ~pDecryPosition[i];
}
/*拉伸因为之前那些函数太高耦合了,而且参数输入输出有问题,所以打算重写。。。
一重写就重写一堆因为全是嵌套的学到教训了*/
PBYTE pImageBuffer;
DWORD dwImageSize = FileBufferToImageBuffer(pDecryPosition, &pImageBuffer, pLastSecHeader->SizeOfRawData);
PIMAGE_DOS_HEADER pNewDosHeader = pImageBuffer;
PIMAGE_NT_HEADERS pNewNTHeader = (DWORD)pImageBuffer + pNewDosHeader->e_lfanew;
if (pNewNTHeader->Signature != IMAGE_NT_SIGNATURE) {
printf("File is not PE\n");
free(pFileBuffer);
return FALSE;
}
PIMAGE_FILE_HEADER pNewFileHeader = (DWORD)pNewNTHeader + sizeof(pNewNTHeader->Signature);
PIMAGE_OPTIONAL_HEADER pNewOptionalHeader = (DWORD)pNewFileHeader + sizeof(IMAGE_FILE_HEADER);
PIMAGE_SECTION_HEADER pNewSectionHeader = (DWORD)pNewOptionalHeader + pNewFileHeader->SizeOfOptionalHeader;
//自启动然后挂起然后卸载然后不用申请特定位置的imagebase,直接去修复重定位表,我觉得没有软件没有重定位表吧
//我收回那句话。。。之后调试的时候遇到了
if (!CreateProcessA(pFileName, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi)) {
printf("create process failed\n");
return;
}
CONTEXT threadContext;
threadContext.ContextFlags = CONTEXT_ALL;
GetThreadContext(pi.hThread, &threadContext);
PVOID pImageBase;
ReadProcessMemory(pi.hProcess, (LPCVOID)(threadContext.Ebx + 8), &pImageBase, sizeof(PVOID), 0);
FnNtUnmapViewOfSection NtUnmapViewOfSection =
(FnNtUnmapViewOfSection)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtUnmapViewOfSection");
if (!NtUnmapViewOfSection) {
printf("NtUnmapViewOfSection \n");
return;
}
NtUnmapViewOfSection(pi.hProcess, pImageBase);
/*上网反复查询资料才发现有ASLR这种东西的存在所以,如果imagebase发生了变化就会报c00005
也就是说我们只能只能在原基址上加载,不然就会报错。。。不过修复重定位表还是要做的要是imagebase不是0x400000
也能用上*/
PVOID pNewImageBase =
VirtualAllocEx(pi.hProcess, pNewOptionalHeader->ImageBase, pNewOptionalHeader->SizeOfImage
, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pNewImageBase) {
printf("VirtualAllocEx failed\n");
return;
}
//计算镜像差然后改掉imagebuffer里的imagebase,顺带把oep也改一下
DWORD dwImageDiff = (DWORD)pNewImageBase - pNewOptionalHeader->ImageBase;
pNewOptionalHeader->ImageBase = (DWORD)pNewImageBase;
threadContext.Eax = pNewOptionalHeader->ImageBase + pNewOptionalHeader->AddressOfEntryPoint;
pImageBase = pNewImageBase;
/*因为aslr的存在我们都不需要修改这里的imagebase
if (!WriteProcessMemory(pi.hProcess, (LPCVOID)(threadContext.Ebx + 8), &pImageBase, sizeof(PVOID), 0)) {
printf("WriteProcessMemory failed\n");
return;
}*/
//设置上下文
SetThreadContext(pi.hThread, &threadContext);
//定位重定位表,然后修复
if (pNewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress || dwImageDiff) {
PIMAGE_BASE_RELOCATION pBaseRelocation =
pNewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (DWORD)pImageBuffer;
while (pBaseRelocation->VirtualAddress) {
DWORD items = (pBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
PWORD pBlock = pBaseRelocation + 1;
for (size_t i = 0; i < items; i++) {
if (*pBlock & 0x3000) {
DWORD VA = pBaseRelocation->VirtualAddress + (*pBlock & 0xfff);
//printf("%x + %x\n", *((PDWORD)(pImageBuffer + VA)), dwImageDiff);
*((PDWORD)(pImageBuffer + VA)) += dwImageDiff;
}
pBlock++;
}
pBaseRelocation = (DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock;
}
}
//应该还要修复IAT因为两个软件之间的加载的dll不一样
FixIATTable(&pImageBuffer);
//终于做到了...
//修复完后将imageBuffer写入内存中
if (!WriteProcessMemory(pi.hProcess, pNewImageBase, pImageBuffer, dwImageSize, 0)) {
printf("WriteProcessMemory failed\n");
return;
}
ResumeThread(pi.hThread);
free(pFileBuffer);
free(pImageBuffer);
return;
}
这是我自己写的IAT修复函数
//修复IAT表,递归导入互相依赖的dll(还没有实现)
VOID FixIATTable(PVOID* pImageBufferBuffer) {
//MessageBoxA(0, 0, 0, 0);
PBYTE pImageBuffer = *pImageBufferBuffer;
PIMAGE_DOS_HEADER pDosHeader = pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (DWORD)pImageBuffer + pDosHeader->e_lfanew;
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
printf("File is not PE\n");
free(pImageBuffer);
return;
}
PIMAGE_FILE_HEADER pFileHeader = (DWORD)pNTHeader + sizeof(pNTHeader->Signature);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
if (!pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) {
//printf("No import table\n");
return;
}
PIMAGE_IMPORT_DESCRIPTOR pImportTable =
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + pImageBuffer;
while (pImportTable->OriginalFirstThunk) {
//大坑,不要乱用函数,因为还没有加载过...
PBYTE lpDllName = pImportTable->Name + pImageBuffer;
HANDLE hModule = GetModuleHandleA(lpDllName);
if (!hModule) {
hModule = LoadLibraryA(lpDllName);
if (!hModule) {
//printf("hModule获取失败\n");
return;
}
}
PDWORD pINT = pImportTable->OriginalFirstThunk + pImageBuffer;
PDWORD pIAT = pImportTable->FirstThunk + pImageBuffer;
while (*pINT) {
PVOID pFunAddr;
if (*pINT & 0x80000000) {
pFunAddr = GetProcAddress(hModule, *pINT & 0x7fffffff);
}
else {
PIMAGE_IMPORT_BY_NAME pImportByName = *pINT + pImageBuffer;
pFunAddr = GetProcAddress(hModule, pImportByName->Name);
}
DWORD dwProtect;
if (!VirtualProtectEx(-1, pIAT, sizeof(PVOID), PAGE_READWRITE, &dwProtect)) {
printf("VirtualProtect");
return;
}
*pIAT = pFunAddr;
pIAT++;
pINT++;
}
//FixIATTable(&hModule);
pImportTable++;
}
}