cuckoo源码分析
MapNtdllIntoMemory()主要作用在内存中加载一份ntdll.dll,然后获取导出目录表地址。
PVOID MapNtdllIntoMemory()
{
NTSTATUS status;
HANDLE hSection;
OBJECT_ATTRIBUTES objAttr;
UNICODE_STRING pathFile;
USHORT NumberOfSections;
SECTION_IMAGE_INFORMATION sii = {0};
PVOID pSection = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeader = NULL;
PIMAGE_NT_HEADERS64 pNtHeader64 = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
DWORD dwExportRVA, dwExportSize;
RtlInitUnicodeString(&pathFile, L"\\KnownDlls\\ntdll.dll");
InitializeObjectAttributes(&objAttr, &pathFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
if(NT_SUCCESS(status = ZwOpenSection(&hSection, SECTION_MAP_READ, &objAttr)))
{
ZwQuerySection(hSection, 1, &sii, sizeof(sii), 0);
Dbg("ntdll entry point : %llx\n", sii.EntryPoint);
Ntdll_ImageBase = sii.EntryPoint;
pDosHeader = (PIMAGE_DOS_HEADER)Ntdll_ImageBase;
#ifdef _M_X64
pNtHeader64 = (PIMAGE_NT_HEADERS64)((unsigned char*)Ntdll_ImageBase+pDosHeader->e_lfanew);
pSectionHeader = (PIMAGE_SECTION_HEADER)((unsigned char*)pNtHeader64+sizeof(IMAGE_NT_HEADERS64));
dwExportRVA = pNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
dwExportSize = pNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
#endif
pNtHeader = (PIMAGE_NT_HEADERS)((unsigned char*)Ntdll_ImageBase+pDosHeader->e_lfanew);
pSectionHeader = (PIMAGE_SECTION_HEADER)((unsigned char*)pNtHeader+sizeof(IMAGE_NT_HEADERS));
dwExportRVA = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
dwExportSize = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
Dbg("Export table address : 0x%08x\n", dwExportRVA);
Dbg("Export table size : 0x%08x\n", dwExportSize);
Dbg("EAT : 0x%08X\n", (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)Ntdll_ImageBase+dwExportRVA));
pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)Ntdll_ImageBase+dwExportRVA);
Dbg("number of exported functions : 0x%08x\n", pImageExportDirectory->NumberOfFunctions);
}
ZwClose(hSection);
return pImageExportDirectory;
}
GetSyscallNumber()通过导出目录表的地址,获取要hook的函数地址,得到函数体,在通过硬编码来获取操作系统索引id的数字编码,然后取得到系统服务号(syscall)。
ULONG GetSyscallNumber(__in PIMAGE_EXPORT_DIRECTORY pImageExportDirectory,
__in PUCHAR funcName,
__in ULONG offsetSyscall)
{
PULONG addrName = NULL, addrFunc = NULL;
PWORD addrOrdinal = NULL;
ULONG i = 0;
PCHAR name = NULL;
SIZE_T n;
if(pImageExportDirectory && funcName)
{
addrName = (PULONG)((unsigned char*)Ntdll_ImageBase + pImageExportDirectory->AddressOfNames);
addrFunc = (PULONG)((unsigned char*)Ntdll_ImageBase + pImageExportDirectory->AddressOfFunctions);
addrOrdinal = (PWORD)((unsigned char*)Ntdll_ImageBase + pImageExportDirectory->AddressOfNameOrdinals);
for(i=0; i < pImageExportDirectory->NumberOfNames; ++i)
{
name = ((unsigned char*)Ntdll_ImageBase + addrName[i]);
__try
{
ProbeForRead(name, 0, 1);
RtlStringCchLengthA(funcName, NTSTRSAFE_MAX_CCH, &n);
if(RtlEqualMemory(funcName, name, n))
{
Dbg("[+] FOUND : %s\n", name);
Dbg("addr : 0x%08x\n", ((unsigned char*)Ntdll_ImageBase + addrFunc[addrOrdinal[i]]));
Dbg("syscall : %x\n", *(PULONG)((PUCHAR)((unsigned char*)Ntdll_ImageBase + addrFunc[addrOrdinal[i]]+offsetSyscall)));
return *(PULONG)((PUCHAR)((unsigned char*)Ntdll_ImageBase + addrFunc[addrOrdinal[i]]+offsetSyscall));
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
Dbg("Exception : %x\n", GetExceptionCode());
}
}
}
return 0;
}
以下是每个NT函数的汇编代码,函数地址+1的位置就是系统服务号,通过系统服务号(syscall)就可以从ssdt表中找到对应的函数
Install_Hook()
针对win32的ssdt_hook
因为在windows 7 32位的操作系统中ssdt表是导出的,所以可以直接import获取到这个结构体的地址
__declspec(dllimport) ServiceDescriptorTableEntry KeServiceDescriptorTable;
源码中定义一个宏
#define SYSTEMSERVICE(_syscall)
KeServiceDescriptorTable.ServiceTableBase[_syscall]
核心代码
OrigFunc是原函数地址,hookedFunc是新函数地址
*origFunc = (PVOID)SYSTEMSERVICE(syscall);
(PVOID)SYSTEMSERVICE(syscall) = hookedFunc;