前言
很多木马在初次释放恶意代码时,会加载资源文件到内存中。 在内存中展开区段数据,之后再修复重定位、导入表,然后执行恶意代码。 此次用代码实现一下。详细代码
1.获取资源文件
//获取资源数据
LPVOID LoadDll::GetResourceData(DWORD index)
{
//声明变量初始化
HMODULE hModule = NULL;
HRSRC hRes = NULL;
HGLOBAL hLoadRes= NULL;
DWORD error = 0;
DWORD size = 0;
LPVOID pResData = NULL;
LPVOID pVirtualAddress = NULL;
//获取句柄 null默认为进程模块句柄
hModule = GetModuleHandle(NULL);
//获取资源数据
hRes = FindResourceA(hModule, MAKEINTRESOURCEA(index), "dll");
if (hRes == NULL) error = GetLastError();
size = SizeofResource(hModule, hRes);
hLoadRes = LoadResource(hModule, hRes);
pResData = LockResource(hLoadRes);
//申请内存,保存资源数据
pVirtualAddress = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(pVirtualAddress, pResData, size);
//释放句柄
FreeResource(pResData);
CloseHandle(hModule);
CloseHandle(hRes);
CloseHandle(hLoadRes);
return pVirtualAddress;
}
2.内存对齐
//内存对齐默认为0x1000
DWORD LoadDll::GetOffset(DWORD size)
{
DWORD argc1 = 0;
DWORD argc2 = 0;
argc1 = size % 0x1000;
if (argc1 > 0)
{
argc2 = (size / 0x1000) + 1;
return argc2;
}
else
{
argc2 = size / 0x1000;
return argc2;
}
}
3.PE文件的检测
//检测PE文件
BOOL LoadDll::CheckPE(LPVOID data)
{
WORD e_magic = 0;
DWORD Signature = 0;
DWORD offset = 0;
PIMAGE_DOS_HEADER pdos = NULL;
PIMAGE_NT_HEADERS pnt = NULL;
//获取e_magic
pdos = (PIMAGE_DOS_HEADER)data;
e_magic = pdos->e_magic;
if (e_magic != 0x5a4d) return FALSE;
//获取e_lfanew NT头的偏移
offset = pdos->e_lfanew;
//获取 Signature
pnt = (PIMAGE_NT_HEADERS)((DWORD)data + offset);
Signature = pnt->Signature;
if (Signature != 0x4550) return FALSE;
return TRUE;
}
4.内存展开区段数据
//内存展开区段数据
//结构体保存区段信息,用于修复重定位
BOOL LoadDll::ExpandVirtualData(LPVOID data, RetData* retdata)
{
//声明变量
PIMAGE_DOS_HEADER pdos = NULL;
PIMAGE_NT_HEADERS pnt = NULL;
PIMAGE_FILE_HEADER pfile = NULL;
PIMAGE_OPTIONAL_HEADER poptional = NULL;
DWORD count = 0; //区段数量
DWORD imagebase = 0; //镜像基址
DWORD imagesize = 0; //镜像大小
DWORD oep = 0; //入口点
DWORD AlignmentSize = 0; //内存对齐后的大小
LPVOID p_address = NULL; //申请的内存地址
DWORD index = 0; //区段数量
//初始化
pdos = (PIMAGE_DOS_HEADER)data;
pnt = (PIMAGE_NT_HEADERS)((DWORD)data + pdos->e_lfanew);
pfile = &(pnt->FileHeader);
poptional = &(pnt->OptionalHeader);
imagebase = poptional->ImageBase;
imagesize = poptional->SizeOfImage;
oep = poptional->AddressOfEntryPoint;
//在内存中申请展开后的大小
p_address = VirtualAlloc(NULL, imagesize, MEM_RESERVE, PAGE_READWRITE);
if (p_address == NULL) return FALSE;
//展开PE头
p_address = VirtualAlloc(p_address, 0x1000, MEM_COMMIT, PAGE_READWRITE);
if (p_address == NULL) return FALSE;
oep = (DWORD)p_address + oep;
retdata->old_imagebase = imagebase;
retdata->new_imagebase = (DWORD)p_address;
retdata->oep = oep;
//复制PE头数据
memcpy(p_address, data, 0x400);
p_address = (LPVOID)((DWORD)p_address + 0x1000);
//获取区段的数量和大小
count = pfile->NumberOfSections;
//获取区段信息 区段头表在NT头后面
PIMAGE_SECTION_HEADER psection = IMAGE_FIRST_SECTION(pnt);
//展开区段数据
while (count)
{
DWORD rawsize = psection->SizeOfRawData; //文件大小
DWORD v_size = psection->Misc.VirtualSize;
DWORD rawdata = psection->PointerToRawData; //文件数据指针
LPVOID data_addr = (LPVOID)((DWORD)data + rawdata);
if (rawsize == 0)
{
if(v_size !=0)
{
v_size = GetOffset(v_size) * 0x1000;
p_address = VirtualAlloc(p_address, v_size, MEM_COMMIT, PAGE_READWRITE);
if (p_address == NULL) return FALSE;
}
}
else
{
v_size = GetOffset(rawsize) * 0x1000;
p_address = VirtualAlloc(p_address, v_size, MEM_COMMIT, PAGE_READWRITE);
if (p_address == NULL) return FALSE;
memcpy(p_address, data_addr, rawsize);
}
retdata->s_info[index].address = (DWORD)p_address;
retdata->s_info[index].size = (DWORD)v_size;
memcpy(retdata->s_info[index].name, psection->Name, sizeof(psection->Name));
p_address = (LPVOID)((DWORD)p_address + v_size);
psection++;
count--;
index++;
}
retdata->index = index;
return TRUE;
}
5.修复重定位
//修复重定位
void LoadDll::FixReloc(RetData* rdata)
{
//声明变量
PIMAGE_DOS_HEADER pdos = NULL;
PIMAGE_NT_HEADERS pnt = NULL;
PIMAGE_FILE_HEADER pfile = NULL;
PIMAGE_OPTIONAL_HEADER poptional = NULL;
DWORD old_imagebase;
DWORD new_imagebase;
struct typeoffset
{
WORD offset : 12;
WORD type : 4;
}*pTpOffset;
//初始化
pdos = (PIMAGE_DOS_HEADER)new_imagebase;
pnt = (PIMAGE_NT_HEADERS)(new_imagebase + pdos->e_lfanew);
poptional = &(pnt->OptionalHeader);
old_imagebase = rdata->old_imagebase;
new_imagebase = rdata->new_imagebase;
//获取重定位表
PIMAGE_BASE_RELOCATION pRelTable = (PIMAGE_BASE_RELOCATION)(poptional->DataDirectory[5].VirtualAddress + new_imagebase);
while (pRelTable->SizeOfBlock != 0)
{
pTpOffset = (typeoffset*)(pRelTable + 1);
DWORD RELcount = (pRelTable->SizeOfBlock - 8) / 2;
for (int i = 0; i < RELcount; i++)
{
if (pTpOffset[i].type == 3)
{
DWORD fix_addr = new_imagebase + pRelTable->VirtualAddress + pTpOffset[i].offset;
*(DWORD*)fix_addr -= old_imagebase;
*(DWORD*)fix_addr += new_imagebase;
}
}
pRelTable = (IMAGE_BASE_RELOCATION*)((LPBYTE)pRelTable + pRelTable->SizeOfBlock);
}
}
6.修复导入表
//修复导入表
BOOL LoadDll::FixImport(RetData* rdata)
{
//声明变量
PIMAGE_DOS_HEADER pDos = NULL;
PIMAGE_NT_HEADERS pNt = NULL;
PIMAGE_OPTIONAL_HEADER pOptional = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImport = NULL;
//基址
DWORD ImageBase = 0;
//导入表结构体的数量 加载dll的数量
DWORD count = 0;
//dll句柄
HMODULE hModule = NULL;
//加载dll的名称的va
char* dllname = NULL;
//导入名称表
PIMAGE_THUNK_DATA32 pINT = NULL;
//导入地址表
PIMAGE_THUNK_DATA32 pIAT = NULL;
//函数名称地址
FARPROC pFunAddress = NULL;
PIMAGE_IMPORT_BY_NAME pBname = NULL;
//变量初始化
ImageBase = (DWORD)rdata->new_imagebase;
pDos = (PIMAGE_DOS_HEADER)ImageBase;
pNt = (PIMAGE_NT_HEADERS)(ImageBase + pDos->e_lfanew);
pOptional = &(pNt->OptionalHeader);
//检测导入表
if (pOptional->DataDirectory[1].Size != 0)
{
count = (pOptional->DataDirectory[1].Size / 20) - 1;
//导入表结构体指针
pImport = (PIMAGE_IMPORT_DESCRIPTOR)(ImageBase + pOptional->DataDirectory[1].VirtualAddress);
while (count)
{
dllname = (char*)(ImageBase + pImport->Name);
if (dllname == NULL) return FALSE;
hModule = LoadLibraryA(dllname);
if (hModule == NULL) return FALSE;
//获取导入地址表
pIAT = (PIMAGE_THUNK_DATA32)(ImageBase + pImport->FirstThunk);
while (pIAT->u1.Ordinal)
{
if (pIAT->u1.Ordinal & 0x80000000)
{
pFunAddress = GetProcAddress(hModule, (LPCSTR)(pIAT->u1.Ordinal & 0x7FFFFFFF));
}
else
{
pBname = (PIMAGE_IMPORT_BY_NAME)(ImageBase + pIAT->u1.AddressOfData);
pFunAddress = GetProcAddress(hModule, pBname->Name);
}
pIAT->u1.Function = (DWORD)pFunAddress;
pIAT++;
}
pImport++;
count--;
}
}
return TRUE;
}
7.修改区段属性
//修改区段内存属性
BOOL LoadDll::ChangeProtect(RetData* rdata)
{
//修改内存属性
char str1[MAX_PATH] = ".text";
char str2[MAX_PATH] = ".textbss";
BOOL ret = 0;
DWORD oldprotect = 0;
for (int i = 0; i < rdata->index; i++)
{
if (strcmp(rdata->s_info[i].name, str1) == 0 || strcmp(rdata->s_info[i].name, str2) == 0)
{
ret = VirtualProtect((LPVOID)rdata->s_info[i].address, rdata->s_info[i].size, PAGE_EXECUTE_READWRITE, &oldprotect);
if (ret == FALSE) return FALSE;
}
else
{
ret = VirtualProtect((LPVOID)rdata->s_info[i].address, rdata->s_info[i].size, PAGE_READWRITE, &oldprotect);
if (ret == FALSE) return FALSE;
}
}
return TRUE;
}
8.获取指定导出函数
DWORD LoadDll::GetExportFun(RetData* rdata)
{
//声明变量
PIMAGE_DOS_HEADER pDos = NULL;
PIMAGE_NT_HEADERS pNt = NULL;
PIMAGE_OPTIONAL_HEADER pOptional = NULL;
PIMAGE_EXPORT_DIRECTORY pExport = NULL;
DWORD* EAT = NULL;
DWORD* ENT = NULL;
DWORD* EOT = NULL;
char str[MAX_PATH] = "fun";
DWORD addr = 0;
DWORD imagebase = rdata->new_imagebase;
pDos = (PIMAGE_DOS_HEADER)imagebase;
pNt = (PIMAGE_NT_HEADERS)(imagebase + pDos->e_lfanew);
pOptional = &pNt->OptionalHeader;
pExport = (PIMAGE_EXPORT_DIRECTORY)(imagebase + pOptional->DataDirectory[0].VirtualAddress);
if (pOptional->DataDirectory[0].Size != 0)
{
EAT = (DWORD*)(imagebase + pExport->AddressOfFunctions);
ENT = (DWORD*)(imagebase + pExport->AddressOfNames);
EOT = (DWORD*)(imagebase + pExport->AddressOfNameOrdinals);
DWORD count = pExport->NumberOfFunctions;
DWORD index = 0;
char* name = NULL;
for (int i = 0; i < count; i++)
{
if (EAT[i] != 0)
{
name = (char*)(imagebase + ENT[i]);
if (!strcmp(str, name))
{
index = (WORD)EOT[i];
addr = imagebase + EAT[index];
}
}
}
}
return addr;
}
二、调用
1.头文件
代码如下:
class LoadDll
{
public:
LoadDll();
~LoadDll();
//保存区段信息
struct Section_Info
{
DWORD address;
DWORD size;
char name[MAX_PATH];
};
//保存数据
struct RetData
{
DWORD old_imagebase;
DWORD new_imagebase;
DWORD oep;
DWORD index;
Section_Info* s_info;
};
Section_Info g_info[16] = { 0 };
RetData obj = {0};
RetData* retdata = &obj;
DWORD GetOffset(DWORD argc);
LPVOID GetResourceData(DWORD index);
BOOL CheckPE(LPVOID data);
BOOL ExpandVirtualData(LPVOID data, RetData* retdata);
void FixReloc(RetData* rdata);
BOOL FixImport(RetData* rdata);
BOOL ChangeProtect(RetData* rdata);
DWORD GetExportFun(RetData* rdata);
};
2.main
代码如下:
int main()
{
//变量声明
LPVOID pData = NULL;
BOOL ret = FALSE;
DWORD oep = 0;
DWORD index = 110; //资源ID
LoadDll dllobj;
dllobj.retdata->s_info = dllobj.g_info;
//获取资源文件数据
pData = dllobj.GetResourceData(index);
if (pData!=NULL)
{
ret= dllobj.CheckPE(pData);
if (ret == FALSE) return 0;
}
//在内存中展开资源数据
ret= dllobj.ExpandVirtualData(pData,dllobj.retdata);
if (ret==FALSE) return FALSE;
dllobj.FixReloc(dllobj.retdata);
dllobj.FixImport(dllobj.retdata);
ret = dllobj.ChangeProtect(dllobj.retdata);
if (ret == FALSE) return FALSE;
//获取加载dll的导出函数,并运行
DWORD exportfun= dllobj.GetExportFun(dllobj.retdata);
DWORD argc = dllobj.retdata->new_imagebase;
oep = dllobj.retdata->oep;
//运行dll一种是重命名函数指针,指向dll的oep
//typedef void (_stdcall *My_DllMain)(HMODULE, DWORD, LPVOID);
//My_DllMain DllMain;
//DllMain = (My_DllMain)(oep);
//DllMain((HMODULE)argc,1,0);
DWORD retaddr = 0;
__asm
{
push eax;
call geteip
geteip:
pop eax;
mov retaddr, eax;
add retaddr, 0x22;
pop eax;
push 0;
push 1;
push argc;
push retaddr;
jmp oep;
add retaddr, 0x13;
push retaddr;
jmp exportfun;
}
cout <<"成功运行" << endl;
system("pause");
return 0;
}
总结
因为有的木马会在dllmian中什么也不做,而是加载导出函数执行恶意代码,
所以实现了获取导出函数执行,并且安全返回。
大致流程就是:
1.加载资源中的恶意dll
2.内存展开dll数据
3.修复重定位
4.修复导入表
5.修改区段内存属性
6.获取导出函数
7.执行恶意代码。