能够支持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可否自己分配?
先写到这里
休息去