Chinaunix首页 | 论坛 | 博客
  • 博客访问: 652740
  • 博文数量: 151
  • 博客积分: 3498
  • 博客等级: 中校
  • 技术积分: 1570
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-28 18:10
文章分类

全部博文(151)

文章存档

2014年(12)

2013年(17)

2012年(17)

2011年(5)

2010年(12)

2009年(2)

2007年(26)

2006年(22)

2005年(38)

分类: WINDOWS

2012-04-11 13:49:39

写这篇文章的目的,是为了加强对系统的了解,强化一下编程的技术。
这篇文章是“ ”的升级版本。所以这篇文章的代码是在之前文章的基础上增加的。
我把这篇文章分成如下几部分。
一.PE基础知识
二.基本思路
三.修改引入表
四.修改代码段

一.PE基础知识
 手工打造可执行程序.rar  
这是一个高人写的关于PE基础知识的资料,看了能详细的了解PE结构的各个部分。
二.基本思路
 有了“ ”这篇文章做为基础,下面要做的就是,
    1)设计一个名为my.dll的DLL,同时导出一个MyFun的函数;
    2)修改引入表,将my.dll插入到引入表中;
    3)在代码段的最后部分增加一条jmp MyFun指令和一条jmp 原EntryPoint指令;
    4)修改选项头(Optional Header)的EntryPoint,使其指向 Jmp MyFun的地址;
三.修改引入表
   参考“ ”
四.修改代码段
    需要了解call,jmp指令的编码格式。
    call指令编码格式如下:E8 偏移量的小字节序排列 
    偏移量 = 目标地址 -  ( call指令所在地址  + call指令长度 )   
    call指令长度 一般为5个字节。
    例如,当前call指令的地址是 0x2d0028,call指令长度为5个字节,那么下一条指令的地址就是0x2d0028 + 5 =   0x2d0033,假如 Jmp MyFun函数的起始地址是0x2d0043,那么我们call后边的数就是 0x2d0043- 0x2d0033 = 0x10,那么 call MyFun的实际的机器码就是 E8 10 00 00 00
   
     jmp指令的使用比较复杂:
     1)机器码为E9的JMP指令,后面的4字节是跳转的相对偏移。相对偏移 = 目标地址 -  ( jmp指令所在地址  + jmp指令长度 )。intel处理器使用的是小尾序,所以这里的字节1B80FFFF 就是偏移FFFF801B,由此可以推算出这条JMP指令的地址是00401020 - FFFF801B  -5 = 409000
     所以说应该是在409000这里写了这条JMP指令,这样就好理解了。在409000这里第一个字节是JMP机器码E9,后面的就是JMP指令的下一条指令的地址409005 到 你的跳转目的地址00401020的偏移。
    00401020 - (409000+5) = FFFF801B =小尾序> 1B80FFFF
   所以这条jmp指令的机器码为: E9 1B 80 FF FF
     2)跳转指令有三种方式:短(short),近(near)和远(far)。短是指要跳至的目标地址与当前地址前后相差不超过128字节。近是指跳转的目标地址与当前地址在用一个段内,即CS的值不变,只改变EIP的值。远指跳到另一个代码段去执行,CS/EIP都要改变。短和近在编码上有所不同,在汇编指令中一般很少显式指定,只要写 "jmp 目标地址",几乎任何汇编器都会根据目标地址的距离采用适当的编码。远转移在32位系统中很少见到,原因前面已经讲过,由于有足够的线性空间,一个程序很少需要两个代码段,就连用到的系统模块也被映射到同一个地址空间。

  jmp的操作数自然是目标地址,这个指令支持直接寻址和间接寻址。间接寻址又可分为寄存器间接寻址和内存间接寻址。举例如下(32位系统):

  jmp 8E347D60                        ;直接寻址段内跳转
  jmp EBX                                  ;寄存器间接寻址:只能段内跳转
  jmp dword ptr [EBX]              ;内存间接寻址,段内跳转
  jmp dword ptr [00903DEC]   ;同上
  jmp fward ptr [00903DF0]     ;内存间接寻址,段间跳转

  解释:
   在32位系统中,完整目标地址由16位段选择子和32位偏移量组成。因为寄存器的宽度是32位,因此寄存器间接寻址只能给出32位偏移量,所以只能是段内近转移。在内存间接寻址时,指令后面是方括号内的有效地址,在这个地址上存放跳转的目标地址。比如,在[00903DEC]处有如下数据:7C 82 59 00 A7 01 85 65 9F 01

      把指定存储单元的字内容送到IP寄存器(0x827C),并把下一个字的内容送到CS寄存器(0x0059);
      例子 jmp dword ptr [00903DEC]  的指令编码为: FF 25 EC 3D 90 00

 
    在代码段的VirtualSize的末尾添加代码,首先调用MyFun函数,然后再跳转到 旧的Entry Point处执行。

//变量声明   
const  BYTE jmpOpcode[2]= {0xFF,0x25};
BYTE   jmpOperand[4]={0x0};
DWORD  dwRvaMyFun = 0;
unsigned long ulOldEntryPoint = 0;

    //修改代码段
    //old EntryPoint
    ulOldEntryPoint = ntHeader->OptionalHeader.AddressOfEntryPoint;
    dwRvaMyFun =  myImport.FirstThunk;
    int nIdx=sectionNum(lpBase,ntHeader->OptionalHeader.AddressOfEntryPoint);
    //通过entry point 找到代码段的section Header
    sectionHeader=(IMAGE_SECTION_HEADER*)((BYTE*)lpBase+dosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS))+nIdx;
    BYTE *pAddr =((BYTE*)lpBase+sectionHeader->PointerToRawData+sectionHeader->Misc.VirtualSize); 
  //找到代码段的末尾
    DWORD dwNewEntryPoint = sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize;  
    //新的 Entrypoint,是一个file offset 需要变成 Rva;
    //call 的地址为jmp MyFun函数所在的地址,
    //call 的操作码为call的下一条指令到 jmp MyFun 的地址的Offset
    *pAddr = 0xE8;
    *(DWORD*)(pAddr +1 ) =  5 ; //到Jmp Myfun的Offset 是5个字节
    //jmp oldEntryPoint
    pAddr = pAddr + 5;
    //jmp的相对偏移 = 目标地址- ( jmp当前地址 + 5)
    *pAddr     = 0xE9;
    *(DWORD*)(pAddr+1) = ((BYTE*)lpBase+sectionHeader->PointerToRawData) - (pAddr + 5 );    
    //jmp MyFun
    pAddr = pAddr + 5;
    *pAddr     = jmpOpcode[0];
    *(pAddr+1) = jmpOpcode[1];
    *(DWORD*)(pAddr+2) = dwRvaMyFun + ntHeader->OptionalHeader.ImageBase; 
   
    //填充新的entry point
    ntHeader->OptionalHeader.AddressOfEntryPoint = dwNewEntryPoint;
    sectionHeader->Misc.VirtualSize = 0X150;  //修改代码段的size;

    //修改.rdata头结构的size
 nIdx=sectionNum(lpBase,ntHeader->OptionalHeader.DataDirectory
                                               [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
    sectionHeader=(IMAGE_SECTION_HEADER*)((BYTE*)lpBase+dosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS))+nIdx;
    sectionHeader->Misc.VirtualSize = 0X192;
    //---------------------------------

 IIDMod.rar    
   
此种方法加载my.dll时,如果MyFun的加载地址比较高,则容易失败。还需要继续查找原因。
阅读(1102) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~