Dephi MemoryLoadLibrary
2020-03-08 17:14:18 Author: bbs.pediy.com(查看原文) 阅读量:239 收藏

[原创]Dephi MemoryLoadLibrary

2020-2-29 22:10 640

[原创]Dephi MemoryLoadLibrary

//去年写的一份内存加载,某人用的还不错,扔出来给有需要的人,无坑。

unit MemoryLoadLibraryUnit;

interface
uses
  Winapi.Windows,System.SysUtils;

function LoadLibraryX(const FileMemory:Pointer;const FileSize:ULONG):THandle;overload;
function LoadLibraryX(LibraryName:PAnsiChar):THandle;overload;
function GetMemoryLibraryProcAddress(Const MemoryLibrary:THandle;const API:PAnsiChar):Pointer;
implementation

function LoadLibraryX(LibraryName:PAnsiChar):THandle;
var
  fileHandle:THandle;
  Mem:Pointer;
  Size,bytesize:ULONG32;
begin
 Result := 0;
 fileHandle := CreateFileA(LibraryName,
                           GENERIC_READ,
                           FILE_SHARE_READ,
                           0,
                           OPEN_EXISTING,
                           0,
                           0);
 if fileHandle <> INVALID_HANDLE_VALUE then
 begin
   Size := GetFileSize(fileHandle,nil);
   Mem := AllocMem(Size);
   if ReadFile(fileHandle,MEM^,Size,bytesize,NIL) then
   begin
     Result := LoadLibraryX(Mem,Size);
   end;
   CloseHandle(fileHandle);
   FreeMem(Mem,Size);
 end;
end;

//兼容32&64的内存加载模块  作者:妖蛋
//参考:各种各种
//特别崇拜一下武大神
function LoadLibraryX(const FileMemory:Pointer;const FileSize:ULONG):THandle;
type
    _IMAGE_BASE_RELOCATION = packed record
      VirtualAddress:dword;  //重定位数据开始RVA
      SizeOfBlock:dword;     //重定位长度     (SizeOfBlock - $8)/2
    end;
    PIMAGE_BASE_RELOCATION = ^_IMAGE_BASE_RELOCATION;
    PImageBaseRelocation = PIMAGE_BASE_RELOCATION;

    PRUNTIME_FUNCTION = ^TRUNTIME_FUNCTION;
    TRUNTIME_FUNCTION = record
      FunctionStart: LongWord;
      FunctionEnd: LongWord;
      UnwindInfo: LongWord;
    end;

var
  NtHeaders:PImageNtHeaders;
  ImageBase:NativeUInt;
  LoadMem:Pointer;
  IsImageBaseLoad:Boolean;
  SectionArray:array of TImageSectionHeader;
  protectvalue, oldProtect: DWORD;
  pgsize: NativeUInt;
  protect:DWORD;
  i:DWORD;
  ImportHeader:PImageImportDescriptor;
  ImportDllHandle:THandle;
  ThunkP,WriteThunkP:PImageThunkData;
  BaseRelocation:PImageBaseRelocation;
  BaseItem:Pointer;
  BaseNumber:NativeUInt;
  P:Pointer;
  PruntIME: PRUNTIME_FUNCTION;
  PruntIMENumber:DWORD;
  DllMain: function (dwHandle:THandle;dwReason:DWORD;dwReserved: Pointer): Boolean; stdcall;
  RtlAddFunctionTable:function (FunctionTable:PRUNTIME_FUNCTION;EntryCount:DWORD;BaseAddress:DWORD64):Boolean;cdecl;
label
  OVER;
begin
  Result := 0;
  //获取PE头信息
  NtHeaders := PImageNtHeaders(Pointer(NativeUInt(FileMemory) + PImageDosHeader(FileMemory)^._lfanew));
  //获取加载地址
  ImageBase := NtHeaders.OptionalHeader.ImageBase;
  //尝试加载
  LoadMem := AllocMem(NtHeaders.OptionalHeader.SizeOfImage);
  //将页面调整为可读可写可执行,所有都完成后再修正内存页属性
  VirtualProtect(LoadMem,NtHeaders.OptionalHeader.SizeOfImage,PAGE_EXECUTE_READWRITE,&protect);
  //判断是否为默认加载位置
  if NativeUInt(LoadMem) = ImageBase then  IsImageBaseLoad := True
  else IsImageBaseLoad := False;
  //复制PE头到加载地址
  Move(FileMemory^,LoadMem^,NtHeaders.OptionalHeader.SizeOfHeaders);
  //申请区块数组
  SetLength(SectionArray,NtHeaders.FileHeader.NumberOfSections);
  //得到节表
  {$IFDEF  Win64}
  Move(Pointer(NativeUInt(FileMemory) + SizeOf(TImageNtHeaders64) + PImageDosHeader(FileMemory)^._lfanew)^,
      SectionArray[0],
      SizeOf(TImageSectionHeader) * NtHeaders.FileHeader.NumberOfSections);
  {$ELSE}
  Move(Pointer(NativeUInt(FileMemory) + SizeOf(TImageNtHeaders32) + PImageDosHeader(FileMemory)^._lfanew)^,
      SectionArray[0],
      SizeOf(TImageSectionHeader) * NtHeaders.FileHeader.NumberOfSections);
  {$ENDIF}
   if NtHeaders.OptionalHeader.AddressOfEntryPoint <> 0 then
      @DllMain := Pointer(NtHeaders.OptionalHeader.AddressOfEntryPoint + NativeUInt(LoadMem));
  //循环加载区块
  for I := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do
  begin
    //未初始化数据区段的计算方式,参考自武稀松的MemoryModule
    pgsize := SectionArray[I].SizeOfRawData;
    if pgsize = 0 then
    begin
      if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA) = IMAGE_SCN_CNT_INITIALIZED_DATA
      then
      begin
        pgsize := NtHeaders.OptionalHeader.SizeOfInitializedData;
      end
      else if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA) = IMAGE_SCN_CNT_UNINITIALIZED_DATA
      then
      begin
        pgsize := NtHeaders.OptionalHeader.SizeOfUninitializedData;
      end
    end;
    //注意未初始化的数据的SizeOfRawData为0,需要计算。
    Move(Pointer(NativeUInt(FileMemory) + SectionArray[I].PointerToRawData)^,Pointer(NativeUInt(LoadMem) + SectionArray[I].VirtualAddress)^,pgsize);
  end;
  //判断是否有导入表
  //提示:这里其实可以不必判断,因为NT系统要求至少有一个导入表项。
  if (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress <> 0) and
     (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size <> 0) then
  begin
    //获取导入表指针
    ImportHeader := Pointer(NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + NativeUInt(LoadMem));
    //循环遍历指针
    while (ImportHeader.FirstThunk <> 0) and (ImportHeader.Name <> 0) do
    begin
      //加载DLL
      ImportDllHandle := LoadLibraryA(PAnsiChar(Pointer(ImportHeader.Name + NativeUInt(LoadMem))));
      //加载失败则失败
      if ImportDllHandle = 0 then goto OVER;
      //这里会有加载失败的情况,暂时不做处理,自己必须进行处理。
        //获取FirstThunk数组的指针
        ThunkP := Pointer(ImportHeader.FirstThunk + NativeUInt(LoadMem));
        WriteThunkP := Pointer(ImportHeader.FirstThunk + NativeUInt(LoadMem));
        //TimeDateStamp为-1时,使用Characteristics作为绑定地址   参考自:MemLibrary
        if ImportHeader.TimeDateStamp = -1 then ThunkP := Pointer(ImportHeader.Characteristics + NativeUInt(LoadMem))
        else ThunkP := WriteThunkP;

        while ThunkP._Function <> 0 do
        begin
            {$IFDEF  Win64}
            if (ThunkP._Function and $8000000000000000) <> 0 then
            {$ELSE}
            if (ThunkP._Function and $80000000) <> 0 then
            {$ENDIF}
            begin
              WriteThunkP._Function :=  NativeUInt(GetMemoryLibraryProcAddress(ImportDllHandle, PAnsiChar(Cardinal(ThunkP._Function) and $FFFF)));
            end
            else
            begin
              //这个name前面有2个字节的头,要加2才能得到正确的字符串
              WriteThunkP._Function := NativeUInt(GetMemoryLibraryProcAddress(ImportDllHandle, PAnsiChar(Pointer(NativeUInt(LoadMem) + ThunkP.ForwarderString + 2))));
            end;
           ThunkP:=  Pointer(NativeUInt(Pointer(ThunkP)) + SizeOf(TImageThunkData));
           WriteThunkP := Pointer(NativeUInt(Pointer(WriteThunkP)) + SizeOf(TImageThunkData));
        end;


      ImportHeader := Pointer(NativeUInt(ImportHeader) + SizeOf(_IMAGE_IMPORT_DESCRIPTOR));
    end;
  end;
  //重定位表
  //如果存在重定位表且需要重定位则进行重定位
  //注意:由于NT中PE的重定位表并不是必须存在,为兼容加壳后的没有重定位表存在的PE结构,这里不强制要求进行重定位。
  if (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) and
     (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> 0) and not IsImageBaseLoad then
  begin

     //获取重定位结构指针
     BaseRelocation := Pointer(NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + NativeUInt(LoadMem));
     while BaseRelocation.VirtualAddress <> 0 do
     begin

       //计算重定位数组数量
       BaseNumber := (BaseRelocation.SizeOfBlock - SizeOf(_IMAGE_BASE_RELOCATION)) div $2;
       BaseItem := Pointer(NativeUInt(BaseRelocation) + SizeOf(_IMAGE_BASE_RELOCATION));

       for i := 0 to BaseNumber - 1 do
       begin
         {$IFDEF Win64}
         if ((WORD(BaseItem^) and $A000) = $A000) then
         begin
           P := Pointer((WORD(BaseItem^) mod $A000 + BaseRelocation.VirtualAddress) +  NativeUInt(LoadMem));
           //如果重定位目标指针无效则忽略
           if Assigned(P) then
            NativeUInt(P^) := NativeUInt(p^) - ImageBase + NativeUInt(LoadMem);
         end;
        {$ELSE}
         if ((WORD(BaseItem^) and $3000) = $3000) then
         begin
           P := Pointer((WORD(BaseItem^) mod $3000 + BaseRelocation.VirtualAddress) +  NativeUInt(LoadMem));
           //如果重定位目标指针无效则忽略
           if Assigned(P) then
            NativeUInt(P^) := NativeUInt(p^) - ImageBase + NativeUInt(LoadMem);
         end;
        {$ENDIF}
        NativeUInt(BaseItem) :=  NativeUInt(BaseItem) + SizeOf(WORD);
       end;

       NativeUInt(BaseRelocation) :=  NativeUInt(BaseRelocation) + BaseRelocation.SizeOfBlock;
     end;
  end;

  //异常表
  //修改加载地址
  PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.ImageBase := NativeUInt(LoadMem);
  {$IFDEF  Win64}
  //64位需要注册异常结构  参考:Engine、安全客、msdn、MahdiSafsafi/DebugEngine
  @RtlAddFunctionTable := GetMemoryLibraryProcAddress(GetModuleHandle('Kernel32.dll'),'RtlAddFunctionTable');
  //如果api获取成功则开始注册
  if Assigned(RtlAddFunctionTable) then
  begin
    //判断是否有异常表
    if PImageNtHeaders(Pointer(NativeUInt(LoadMem) +
      PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].
      VirtualAddress <> 0 then
    begin
       PruntIME := Pointer(PImageNtHeaders(Pointer(NativeUInt(LoadMem) +
          PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].
          VirtualAddress + NativeUInt(LoadMem));

       PruntIMENumber :=PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size div sizeof(TRUNTIME_FUNCTION);
       RtlAddFunctionTable(PruntIME,PruntIMENumber,dword64(LoadMem));
    end;
  end;
  {$ENDIF}
  //去掉所有页执行权限
  VirtualProtect(LoadMem,NtHeaders.OptionalHeader.SizeOfHeaders,PAGE_READWRITE,&protect);
  //按照权限进行设置,参考自武稀松的MemoryModule
  for I := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do
  begin
    protectvalue := 0;

    //设置权限,武大的算法写错了!
    if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE)
    and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE)
    and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then
       protectvalue := PAGE_EXECUTE_READWRITE
    else
    if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE)
    and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then
       protectvalue := PAGE_EXECUTE_READ
    else
    if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE)
    and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE)
    and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then
       protectvalue := PAGE_EXECUTE_WRITECOPY
    else
    begin
      if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ)
         and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) then
         protectvalue := PAGE_READWRITE
      else
      if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then
         protectvalue := PAGE_READONLY
      else
      if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) then
         protectvalue := PAGE_WRITECOPY;
    end;

  {
    //错因:PAGE_XXXX并非位标志符,很多标识符是功能冲突而位不冲突,如果冲突就会设置失败!

    if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE
    then
    begin

      protectvalue := protectvalue or PAGE_EXECUTE;
    end;
    if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ
    then
    begin
      protectvalue := protectvalue or PAGE_EXECUTE_READ;
    end;
    if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE
    then
    begin
      protectvalue := protectvalue or PAGE_WRITECOPY;
    end;
    if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_NOT_CACHED) = IMAGE_SCN_MEM_NOT_CACHED
    then
    begin
      protectvalue := protectvalue or PAGE_NOCACHE;
    end;   }

    pgsize := SectionArray[I].SizeOfRawData;
    if pgsize = 0 then
    begin
      if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA) = IMAGE_SCN_CNT_INITIALIZED_DATA
      then
      begin
        pgsize := NtHeaders.OptionalHeader.SizeOfInitializedData;
      end
      else if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA) = IMAGE_SCN_CNT_UNINITIALIZED_DATA
      then
      begin
        pgsize := NtHeaders.OptionalHeader.SizeOfUninitializedData;
      end
    end;

    //对齐
    if pgsize mod NtHeaders.OptionalHeader.SectionAlignment > 0 then
       pgsize := ((pgsize div NtHeaders.OptionalHeader.SectionAlignment) + 1) * NtHeaders.OptionalHeader.SectionAlignment;

    if pgsize > 0 then
    begin
      OutputDebugString(PWideChar(Format('设置地址:%Xd   设置大小:%Xd    结束地址:%Xd',
      [SectionArray[I].VirtualAddress + NativeUInt(LoadMem),
      pgsize,
      SectionArray[I].VirtualAddress + NativeUInt(LoadMem) + pgsize])));
      if VirtualProtect(Pointer(SectionArray[I].VirtualAddress + NativeUInt(LoadMem)), pgsize, protectvalue, oldProtect)
      then
        OutputDebugString('设置成功!');
    end;
  end;

  //全部完成后,返回加载指针为句柄
  //存在的问题:带压缩的VMP加载失败,不压缩可以加载
  if NtHeaders.OptionalHeader.AddressOfEntryPoint <> 0 then
    DllMain(Result, DLL_PROCESS_ATTACH,nil);

  //返回句柄
  Result := THandle(LoadMem);
  OVER:
  SetLength(SectionArray,0);
end;


function GetMemoryLibraryProcAddress(Const MemoryLibrary:THandle;const API:PAnsiChar):Pointer;
var
  Memory:Pointer;
  NTHeader:PImageNtHeaders;
  ExportHeader:PImageExportDirectory;
  ExporFunPointer:PDWORD;
  ExporNamePointer:PDWORD;
  NameOrdinalsPointer:PWORD;
  FindendOrdinal:Integer;
  ExporFunStr:PAnsiChar;
  //为了避免申请内存,也是拼了
  ExporFunDllName:array [0..255] of AnsiChar;
  ExporFunAPIName:array [0..255] of AnsiChar;
  TmpBoolean :Boolean;
  i,dlllength: Integer;
begin

   Result := nil;
   Memory := Pointer(MemoryLibrary);
   NTHeader := PImageNtHeaders(Pointer(PImageDosHeader(Memory)._lfanew + NativeUInt(Memory)));
   if (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress <> 0) and
      (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size <> 0) then
   begin
       ExportHeader := PImageExportDirectory(Pointer(NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + NativeUInt(Memory)));
       ExporFunPointer := Pointer(ExportHeader.AddressOfFunctions + NativeUInt(Memory));
       ExporNamePointer := Pointer(ExportHeader.AddressOfNames + NativeUInt(Memory));
       NameOrdinalsPointer := Pointer(ExportHeader.AddressOfNameOrdinals + NativeUInt(Memory));
       FindendOrdinal := -1;
                                    
       if (NativeUInt(API) > $FFFF) then
       begin
       
         for i := 0 to exportheader.NumberOfNames - 1 do
         begin
         // OutputDebugStringA(Pointer(ExporNamePointer^ + NativeUInt(Memory)));
           if StrIComp(PAnsiChar(ExporNamePointer^ + NativeUInt(Memory)),API) = 0 then
           begin
              FindendOrdinal := NameOrdinalsPointer^;
              Break;
           end;

           NativeUInt(ExporNamePointer) :=  NativeUInt(ExporNamePointer) + SizeOf(dword);
           NativeUInt(NameOrdinalsPointer) :=  NativeUInt(NameOrdinalsPointer) + SizeOf(Word);
         end;

       end
       else
        FindendOrdinal := DWord(API) - ExportHeader.Base;    {减法!!不是加法}

       if FindendOrdinal = -1 then Exit;

       NativeUInt(ExporFunPointer) :=  NativeUInt(ExporFunPointer) + (SizeOf(DWORD) * FindendOrdinal);
      //两种类型,需要处理导出表link
       if  (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress > NativeUInt(ExporFunPointer^))
        or (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < NativeUInt(ExporFunPointer^))then
                   Result := Pointer(NativeUInt(ExporFunPointer^) + NativeUInt(Memory))
       else
       begin
          Result := GetProcAddress(MemoryLibrary,API);
          if Result = nil then
          begin
            //处理导出表指针问题
            //强撸字符串
            ExporFunStr := PAnsiChar(NativeUInt(ExporFunPointer^) + NativeUInt(Memory));
            //格式:DLLNAME.API
            i:= 0;
            dlllength:= 0;
            TmpBoolean := False;
            while Byte(ExporFunStr[i]) <> 0 do
            begin
              if (Byte(ExporFunStr[i]) <> 46) and (not TmpBoolean) then
              begin
                 ExporFunDllName[I] := ExporFunStr[i];
              end
              else
              if (Byte(ExporFunStr[i]) = 46) and (not TmpBoolean) then
              begin
                TmpBoolean := True;
                dlllength := i;
              end
              else
              begin
               ExporFunAPIName[I - 1 - dlllength] := ExporFunStr[i];
              end;
              I:=I+1;
              if i > 255 then
                Break;
            end;
            Byte(ExporFunAPIName[i]) := 0;
            //拼接字符串
            if dlllength > 1 then
            begin
              Byte(ExporFunDllName[dlllength]) := 46;
              Byte(ExporFunDllName[dlllength + 1]) := 68;
              Byte(ExporFunDllName[dlllength + 2]) := 76;
              Byte(ExporFunDllName[dlllength + 3]) := 76;
              Byte(ExporFunDllName[dlllength + 4]) := 0;
              Result := GetProcAddress(LoadLibraryA(ExporFunDllName),ExporFunAPIName);
              if Result = nil then
              begin
                 OutputDebugString('发现异常的搜索api');
                 OutputDebugStringA(api);
                 Exit;
              end;
            end;
            // 不管了,瞎构造的谁也管不了
          end;
       end;
   end;
end;
end.

[招生]科锐逆向工程师培训(3月6日远程教学报名特惠, 第37期)


文章来源: https://bbs.pediy.com/thread-257885.htm
如有侵权请联系:admin#unsafe.sh