能够支持Driver加壳的软件还是很少很少的
貌似除了vmp cv之外就没有了
vmp cv都是基于代码加密理念的 因此移植到驱动下面不是什么困难的事情
驱动加密壳实现了代码加密已经足够,毕竟 公开的驱动dump高端内存dump到文件的东东偶还没看见过
偶想实现的东东是DriverUpx,众所周知
upx是把pe文件完整压缩了保存起来,从而可以实现完美自身脱壳回来
偶也想实现这个功能 申请1块内存 将驱动文件完整释放 自己处理重定位 输入表
最后建立线程执行driverentry
每个加壳软件都有他关键的几个api
GetModuleHandle
GetProcAddress
LoadLibrary
我们将一一实现他们
先别急着实现stub,我们先的准备好packer
packer的目的是压缩sys文件 并且组装到stub指定地方
驱动有个东西必须处理
驱动程序被加壳后必须重新进行校验和的计算,否则加壳后的驱动不能加载
这个功能我们在r3下面完成
代码:
/*++ Routine Description: Calculates a new checksum for the PE image by calling imagehlp.dll Arguments: szPeFile - PE file name Return Value: void --*/ void CalcChecksum( char *szPeFile ) { DWORD dwHeaderSum = 0; DWORD dwCheckSum = 0; HANDLE hFile; DWORD cb; IMAGE_DOS_HEADER dosHdr; IMAGE_NT_HEADERS ntHdr; // // Open the file and calculate the CheckSum // if( MapFileAndCheckSum(szPeFile, &dwHeaderSum, &dwCheckSum) != CHECKSUM_SUCCESS ) { printf("Failed to open specified PE file!\n"); return; } hFile = CreateFile( szPeFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if( hFile == INVALID_HANDLE_VALUE ) { printf("Failed to open specified PE file!\n"); return; } // // Seek to the beginning of the file // SetFilePointer( hFile, 0, 0, FILE_BEGIN ); // // Read in the DOS header // if( (ReadFile(hFile, &dosHdr, sizeof(dosHdr), &cb, 0) == FALSE) || (cb != sizeof(dosHdr)) ) { printf("Failed to read DOS header!\n"); CloseHandle(hFile); return; } // // Seek the PE header // if( (dosHdr.e_magic != IMAGE_DOS_SIGNATURE) || (SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L) ) { printf("Failed to read NT header!\n"); CloseHandle(hFile); return; } // // Read in the NT header // if( (!ReadFile(hFile, &ntHdr, sizeof(ntHdr), &cb, 0)) || (cb != sizeof(ntHdr)) ) { printf("Failed to read NT header!\n"); CloseHandle(hFile); return; } // // Search the PE sisnature // if(ntHdr.Signature != IMAGE_NT_SIGNATURE) { printf("The file is not a valid PE file!\n"); CloseHandle(hFile); return; } // // Check if the PE file's checksum need adjusted // if(ntHdr.OptionalHeader.CheckSum == dwCheckSum) { printf("The PE file CheckSum needn't to be adjusted\n"); CloseHandle(hFile); return; } // // Seek the PE header // if( SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L ) { printf("Failed to locate PE header!\n"); CloseHandle(hFile); return; } printf("Old Checksum = 0x%08X\n", ntHdr.OptionalHeader.CheckSum); printf("New Checksum = 0x%08X\n", dwCheckSum); // // Modify the CheckSum // ntHdr.OptionalHeader.CheckSum = dwCheckSum; if( !WriteFile(hFile, &ntHdr, sizeof(ntHdr), &cb, NULL) ) { printf("Failed to Adjust Checksum!\n"); } else { printf("Adjust Checksum successfully!\n"); } CloseHandle(hFile); return; }
参考驱动壳编写总结
偶还是把驱动完整的压缩起来 释放到1片新申请的内存
这样偶不用处理复杂的区段 VA转换 Reloc修改了
关键API编写
其实老V的reloadandcall已经给我们很多必须代码了
另外 老V代码的GetModuleHandle(NULL)
其实应该返回驱动本身基址,其实加载基址可以在驱动driverenter的参数中获得
驱动对象可以从堆栈当中找到,将驱动对象通过DRIVEROBJECT的DriverSection成员遍历这个链表,是能够获得的
代码:
PVOID GetModuleHandle(char *pModuleName) { PVOID pModuleBase = NULL; ULONG i; PSYSTEM_MODULE_INFORMATION ModulesInfo = (PSYSTEM_MODULE_INFORMATION)GetSysInf(SystemModuleInformation); if (ModulesInfo == NULL) return NULL; if (!strcmp(pModuleName, "ntoskrnl.exe")) { pModuleBase = (PVOID)ModulesInfo->aSM[0].Base; } else { for (i = 0; i < ModulesInfo->uCount; i++) { if (strstr(ModulesInfo->aSM.ImageName, pModuleName)) pModuleBase = (PVOID)ModulesInfo->aSM.Base; } } ExFreePool(ModulesInfo); return pModuleBase; } PVOID GetProcAddress(PVOID ModuleBase, char *pFunctionName) { PVOID pFunctionAddress = NULL; PIMAGE_EXPORT_DIRECTORY exports; ULONG i, addr, ord, size = 0; PULONG names, functions; PSHORT ordinals; if (ModuleBase == NULL) return NULL; __try { exports = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(ModuleBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size); addr = (ULONG)exports - (ULONG)ModuleBase; functions = (PULONG)((ULONG)ModuleBase + exports->AddressOfFunctions); ordinals = (PSHORT)((ULONG)ModuleBase + exports->AddressOfNameOrdinals); names = (PULONG)((ULONG)ModuleBase + exports->AddressOfNames); for (i = 0; i < exports->NumberOfNames; i++) { ord = ordinals; if (i >= exports->NumberOfNames || ord >= exports->NumberOfFunctions) return NULL; if (functions[ord] < addr || functions[ord] >= addr + size) { if (strcmp((char *)((ULONG)ModuleBase + names), pFunctionName) == 0) { pFunctionAddress =(PVOID)((ULONG)ModuleBase + functions[ord]); break; } } } } __except(EXCEPTION_EXECUTE_HANDLER) { DbgMsg("KernelGetProcAddress() EXEPTION\n"); pFunctionAddress = NULL; } return pFunctionAddress; } PVOID GetSysInf(SYSTEMINFOCLASS pdData) { NTSTATUS ns; ULONG dSize = 4096; ULONG dData = 0; PVOID shi; do { shi = ExAllocatePool(NonPagedPool, dSize); if (shi == NULL) return 0; ns = ZwQuerySystemInformation(pdData, shi, dSize, &dData); if (ns == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(shi); dSize *= 2; } } while (ns != 0); return shi; }
3.申请一片大小为原始文件SizeOfImage的内存,用来存放我们的释放体
这个不知道是不是需要必须是nonpagedpool?
实现重定位表处理 输入表处理
代码:
BOOLEAN ProcessImports(ULONG ImageBase) { IMAGE_THUNK_DATA32 *pThunk; PVOID LibAddr; char *LibName, *FuncName; PIMAGE_IMPORT_BY_NAME pImageImportByName; ULONG FuncAddr; __try { PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS) (ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew); PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(RVATOVA(ImageBase, pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while (pImageImportDescriptor->Name != 0) { LibName = (char *)RVATOVA(ImageBase, pImageImportDescriptor->Name); LibAddr = KernelGetModuleBase(LibName); if (LibAddr == NULL) return FALSE; DbgMsg("0x%.8x:%s\n", LibAddr, LibName); pThunk = (IMAGE_THUNK_DATA32 *)RVATOVA(ImageBase, pImageImportDescriptor->FirstThunk); while (pThunk->u1.Ordinal != 0) { pImageImportByName = (PIMAGE_IMPORT_BY_NAME)RVATOVA(ImageBase, pThunk->u1.AddressOfData); FuncName = (char *)(&pImageImportByName->Name); FuncAddr = (ULONG)KernelGetProcAddress(LibAddr, FuncName); DbgMsg(" 0x%.8x:%s\n", FuncAddr, FuncName); if (FuncAddr == 0) return FALSE; *(PULONG)pThunk = FuncAddr; pThunk++; } pImageImportDescriptor++; } } __except(EXCEPTION_EXECUTE_HANDLER) { DbgMsg("ProcessImports() EXEPTION\n"); return FALSE; } return TRUE; } BOOLEAN ProcessRelocs(ULONG ImageBase) { __try { PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS) (ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew); ULONG RellocsSize = pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; PIMAGE_BASE_RELOCATION pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(RVATOVA(ImageBase, pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)); PIMAGE_BASE_RELOCATION pRelocation = pImageBaseRelocation; ULONG ImageBaseDelta = ImageBase - pImageNtHeaders->OptionalHeader.ImageBase, Number, i, Size = 0; PUSHORT Rel; DbgMsg("pImageBaseRelocation: 0x%.8x; Size: %d\n", pImageBaseRelocation, RellocsSize); while (RellocsSize > Size) { Size += pRelocation->SizeOfBlock; Number = (pRelocation->SizeOfBlock - 8) / 2; Rel = (PUSHORT)((ULONG)pRelocation + 8); DbgMsg("VirtualAddress: 0x%.8x; Number of Relocs: %d; Size: %d\n", pRelocation->VirtualAddress, Number, pRelocation->SizeOfBlock); for (i = 0; i < Number - 1; i++) { DbgMsg(" %d:0x%.8x\n", i, Rel & 0x0FFF); *(PULONG)(RVATOVA(ImageBase, pRelocation->VirtualAddress + (Rel & 0x0FFF))) += ImageBaseDelta; } pRelocation = (PIMAGE_BASE_RELOCATION)((ULONG)pImageBaseRelocation + Size); } } __except(EXCEPTION_EXECUTE_HANDLER) { DbgMsg("ProcessRelocs() EXEPTION\n"); return FALSE; } return TRUE; }
其实一个简单的壳也就是自己实现PE文件加载运行的过程
下面这一步有点麻烦
我们已经把原始驱动文件放在了不分页内存里面
并且处理好了重定位 输入表
下面是启动这个驱动了
这个驱动对象DriverOblect偶不是很明白
不知道可否自己分配1个 还是只能继承系统分配下来的这一个
偶目前做的是直接PsCreateSystemThread(DriverEntry)
使用系统传过来的
IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath
发送下去
不知道这个RegistryPath可否自己“乱改”呢?DriverObject可否自己分配?
先写到这里
休息去