现在的位置: 首页 > 综合 > 正文

Pascal 手写PE结构的实现

2014年01月21日 ⁄ 综合 ⁄ 共 7877字 ⁄ 字号 评论关闭
unit Unit2;

interface
uses
  windows,JwaWinNT;

const
Shellcodesize :dword = 391;           //shellcode的大小
PEHandleSize :DWORD = 512;            //PE头总大小
SectionSize :DWORD = 2000;
data: array[0..390] of byte = (       //shellcode数组
//自己加把。
);

procedure PackPE(lpszFileName2:PChar);
implementation
//DLL名,API名,缓存,RVA修正
function NewImputHandle(DllName,APIName:PAnsiChar;var Pdata:Pointer;dwFVA:DWORD):DWORD;
{
输入表构建的思路
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
     IMAGE_IMPORT_DESCRIPTOR作为父结构,一共需要一个子结构
 IMAGE_THUNK_DATA,IMAGE_THUNK_DATA中还需要一个IMAGE_IMPORT_BY_NAME
 用于存放API信息
 IMAGE_IMPORT_DESCRIPTOR的OriginalFirstThunk指向一个IMAGE_THUNK_DATA
 IMAGE_IMPORT_DESCRIPTOR的Name指向一个Pansichar
 IMAGE_IMPORT_DESCRIPTOR的FirstThunk同样指向一个IMAGE_THUNK_DATA
 这里的所有指向全部使用rva
 编写代码时一层一层构建,构建完后修正指针并复制到用于存放的Pdata指针中
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
}
var
  PDescriptor:PIMAGE_IMPORT_DESCRIPTOR;                //输入表父结构
  PThunk:PIMAGE_THUNK_DATA;                            //api子结构
  PNameHandle:PIMAGE_IMPORT_BY_NAME;                   //api名称结构

  PNameHandleSize:DWORD;                               //API名称大小
  PThunkSize:DWORD;                                    //api子结构大小
  PDescriptorSize:DWORD;                               //输入表父结构大小
const
  ZeroData:DWORD = 50;      //每个结构之间的间隙大小
begin
  //计算大小
  PNameHandleSize :=  SizeOf(IMAGE_IMPORT_BY_NAME) + Length(APIName) + ZeroData;
  PThunkSize := SizeOf(IMAGE_THUNK_DATA) + ZeroData;
  PDescriptorSize := SizeOf(IMAGE_IMPORT_DESCRIPTOR) + ZeroData;
  Result :=  PNameHandleSize + PThunkSize + PDescriptorSize + Length(APIName) + Length(DLLName) + ZeroData;
  GetMem(Pdata,Result);
  ZeroMemory(Pdata,Result);
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //构建IMAGE_IMPORT_BY_NAME
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //申请内存
  GetMem(PNameHandle,PNameHandleSize);
  ZeroMemory(PNameHandle,PNameHandleSize);
  //设置PIMAGE_IMPORT_BY_NAME
  PNameHandle.Hint := 0;
  //将API名称直接copy到结构中,Name是个不定长数组,需要在申请内存的时候多申请点空间
  Move(APIName^,PNameHandle.Name[0],Length(APIName));
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //构建IMAGE_THUNK_DATA
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //申请内存
  GetMem(PThunk,PThunkSize);
  ZeroMemory(PThunk,PThunkSize);
  //传说中的共用结构体,啊呸..
  PThunk.ForwarderString := dwFVA + PDescriptorSize + PThunkSize;
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //构建IMAGE_IMPORT_DESCRIPTOR
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  GetMem(PDescriptor,PDescriptorSize);
  ZeroMemory(PDescriptor,PDescriptorSize);
  PDescriptor.Union.OriginalFirstThunk := dwFVA + PDescriptorSize;
  PDescriptor.FirstThunk := PDescriptor.Union.OriginalFirstThunk;
  PDescriptor.TimeDateStamp := 0;
  PDescriptor.ForwarderChain := 0;
  PDescriptor.Name := dwFVA + PNameHandleSize + PThunkSize + PDescriptorSize;
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //组装内存    PDescriptor PThunk PNameHandle
  Move(PDescriptor^,Pdata^,PDescriptorSize);
  Inc(DWORD(Pdata),PDescriptorSize);
  Move(PThunk^,Pdata^,PThunkSize);
  Inc(DWORD(Pdata),PThunkSize);
  Move(PNameHandle^,Pdata^,PNameHandleSize);
  Inc(DWORD(Pdata),PNameHandleSize);
  Move(dllname^,Pdata^,Length(dllname));
  //恢复指针
  dec(DWORD(Pdata),PDescriptorSize);
  dec(DWORD(Pdata),PThunkSize);
  dec(DWORD(Pdata),PNameHandleSize);
  //×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  //释放内存
  FreeMem(PNameHandle,PNameHandleSize);
  FreeMem(PThunk,PThunkSize);
  FreeMem(PDescriptor,PDescriptorSize);
end;


//缓存,缓存大小,区段名,文件位置,内存位置,区段大小
procedure NewSection(Data:PIMAGE_SECTION_HEADER;Size:DWORD;Name:PAnsiChar;PointerToRawData,VirtualAddress,SizeOfRawData,vero:DWORD);
var
  Section:IMAGE_SECTION_HEADER;
begin
  //复制区节名
  Move(Name^,Section.name,7);
  //设置偏移、大小
  Section.VirtualAddress := VirtualAddress;
  Section.PointerToRawData := PointerToRawData;
  Section.SizeOfRawData := SizeOfRawData;
  Section.Misc.VirtualSize := SizeOfRawData;

  //置零数据
  Section.PointerToRelocations := 0;
  Section.PointerToLinenumbers := 0;
 // Section.Misc.PhysicalAddress := 0;

  //设置区节属性
  if vero = 0 then           {代码节}
  begin
    Section.Characteristics := IMAGE_SCN_MEM_EXECUTE + IMAGE_SCN_CNT_CODE + IMAGE_SCN_MEM_READ;  //可执行代码节
  end
  else if vero = 1 then      {数据节}
  begin
    Section.Characteristics := IMAGE_SCN_CNT_INITIALIZED_DATA + IMAGE_SCN_MEM_READ + IMAGE_SCN_MEM_WRITE;//数据节
  end
  else if vero = 2 then      {资源节}
  begin
    Section.Characteristics := IMAGE_SCN_CNT_INITIALIZED_DATA + IMAGE_SCN_MEM_READ + IMAGE_SCN_MEM_SHARED; //资源节
  end
  else                       {未知ERROR}
  begin
    Section.Characteristics := 0;
  end;
  Move(Section,Data^,Size);
end;


procedure PackPE(lpszFileName2:PChar);
var
  PDosHeader:PIMAGE_DOS_HEADER;                   //dos头部结构
  PNtHeader:PIMAGE_NT_HEADERS32;                  //nt头结构
  hFile: THandle;                                 //文件指针
  dwBytes: DWORD;                                 //无用
  dwFVA:DWORD;                                    //文件偏移
  SectionHeader:array of PIMAGE_SECTION_HEADER;   //节表数组
  ZeroData:Pointer;                               //对齐数据指针
  ZeroSize:DWORD;                                 //对齐数据大小
  DosSize:DWORD;
  i:Integer;                                      //for
  imputData:Pointer;                              //输入表数据指针
  imputsize:DWORD;                                //输入表数据大小
const
  ZeroMaxSize: DWORD = 65535;                     //预留的最大对齐数据
  Vero :PAnsiChar = 'This program cannot be run in DOS mode.';  //win32信息
begin
   //初始化
   dwFVA := 0;
   DeleteFile(lpszFileName2);

   //创建对齐用数据
   GetMem(ZeroData,ZeroMaxSize);
   ZeroMemory(ZeroData,ZeroMaxSize);

   //构建dos头部
   DosSize := SizeOf(IMAGE_DOS_HEADER) + Length(Vero) + 4;
   GetMem(PDosHeader,DosSize);
   ZeroMemory(PDosHeader,DosSize);
   Inc(dword(PDosHeader),SizeOf(IMAGE_DOS_HEADER));
   Move(Vero^,PDosHeader^,Length(Vero));
   Dec(dword(PDosHeader),SizeOf(IMAGE_DOS_HEADER));
   Inc(dwFVA,DosSize);


   //复制PE头部
   GetMem(PNtHeader,SizeOf(IMAGE_NT_HEADERS32));
   ZeroMemory(PNtHeader,SizeOf(IMAGE_NT_HEADERS32));
   PDosHeader.e_lfanew := dwFVA;
   Inc(dwFVA,SizeOf(IMAGE_NT_HEADERS32));
   //清零数据表
   for i:= 0 to 15 do
   begin
     PNtHeader.OptionalHeader.DataDirectory[i].VirtualAddress := 0;
     PNtHeader.OptionalHeader.DataDirectory[i].Size := 0;
   end;


   //设置代码段大小
   PNtHeader.OptionalHeader.SizeOfCode := Shellcodesize;
   //设置数据段位置
   PNtHeader.OptionalHeader.BaseOfData := 0;
   //设置映像位置
   PNtHeader.OptionalHeader.ImageBase := $4000;
   //节对齐大小
   PNtHeader.OptionalHeader.SectionAlignment := 1000;
   //设置PE结构大小
   PNtHeader.FileHeader.SizeOfOptionalHeader := SizeOf(IMAGE_OPTIONAL_HEADER32);
   //设置节区数量
   pNTHeader.FileHeader.NumberOfSections := 1;
   //申请节表数组
   SetLength(SectionHeader,pNTHeader.FileHeader.NumberOfSections);
   //复制节表
   GetMem(SectionHeader[0],SizeOf(IMAGE_SECTION_HEADER));
   ZeroMemory(SectionHeader[0],SizeOf(IMAGE_SECTION_HEADER));
   //构建输入表
   imputsize := NewImputHandle('Kernel32.dll','ExitProcess',imputData,PEHandleSize);
   //shellcode的大小是391
   //申请.data段,大小2000,类型为1(代码段类型)
   NewSection(SectionHeader[0],SizeOf(IMAGE_SECTION_HEADER),'.test',PEHandleSize,PEHandleSize,SectionSize,0);
   Inc(dwFVA,SizeOf(IMAGE_SECTION_HEADER));
   //设置入口点信息
   PNtHeader.OptionalHeader.AddressOfEntryPoint := PEHandleSize + imputsize;
   //设置设置代码段起始位置
   PNtHeader.OptionalHeader.BaseOfCode := PEHandleSize;
   //设置导入表位置
   PNtHeader.OptionalHeader.DataDirectory[1].VirtualAddress := PEHandleSize;
   //设置输入表大小
   PNtHeader.OptionalHeader.DataDirectory[1].Size := imputsize;
   //计算对齐数据
   ZeroSize := PEHandleSize - dwFVA;
   //递增对齐数据
   Inc(dwFVA,ZeroSize);

   //初始化其他PE数据
   PDosHeader.e_magic := $5A4D;                                   //MZ
   PNtHeader.Signature := $4550;                                  //PE00
   PNtHeader.FileHeader.Machine := $14C;                          //cpu标志
   PNtHeader.OptionalHeader.Magic := $10B;                        //标志字
   PNtHeader.OptionalHeader.Subsystem := $2;                      //子系统
   PNtHeader.OptionalHeader.NumberOfRvaAndSizes := $10;           //项目个数
   PNtHeader.OptionalHeader.FileAlignment := 200;                 //文件对齐
   PNtHeader.OptionalHeader.SectionAlignment := 200;              //节对齐
   PNtHeader.OptionalHeader.SizeOfImage := PEHandleSize;          //映像大小
   PNtHeader.OptionalHeader.MajorOperatingSystemVersion := 5; //子系统版本
   PNtHeader.FileHeader.Characteristics := $103;                  //DLL标志

   //PE结构构建完毕...下面开始进行文件操作

   //创建文件
   hFile := CreateFile(lpszFileName2, GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
   //写入dos头部
   WriteFile(hFile, PDosHeader^, DosSize, dwBytes, nil);
   //写入NT头部
   WriteFile(hFile, PNtHeader^, SizeOf(IMAGE_NT_HEADERS32), dwBytes, nil);
   //写入节区信息
   WriteFile(hFile, SectionHeader[0]^, SizeOf(IMAGE_SECTION_HEADER), dwBytes, nil);
   //写入补齐数据
   WriteFile(hFile, ZeroData^, ZeroSize, dwBytes, nil);
   //写入输入表节
   WriteFile(hFile, imputData^, imputsize, dwBytes, nil);
   //写入shellcode
   WriteFile(hFile, data, Shellcodesize, dwBytes, nil);
   //计算节对齐,这里可以忽略
   ZeroSize := SectionSize - Shellcodesize - imputsize;
   //补齐节对齐数据
   WriteFile(hFile, ZeroData^, ZeroSize + 10, dwBytes, nil);
   //关闭文件
   CloseHandle(hFile);

   //释放Dos头
   FreeMem(PDosHeader,DosSize);
   //释放NT头
   FreeMem(PNtHeader,SizeOf(IMAGE_NT_HEADERS32));
   //释放节表信息
   FreeMem(SectionHeader[0],SizeOf(IMAGE_SECTION_HEADER));
   //释放对齐数据
   FreeMem(ZeroData,ZeroMaxSize);
   //释放输入表结构
   FreeMem(imputData,imputsize);
end;

end.

 

转自:http://www.oschina.net/code/snippet_214684_7538

 

抱歉!评论已关闭.