如何将一个EXE文件读入内存,并在内存中启动它?

时间:2021-06-15 03:40:21
大家好!

我希望做一个应用程序,这个程序在启动的时候可以要求用户输入一个注册的用户名和密码,如果检查通过,则启动一个应用程序。这个应用程序不是一个磁盘文件,而是被加密存储到我的EXE文件尾部。
请问大家:
1、如何从自身的EXE中提取出这个EXE文件的内容?
2、如何将提取出来的这个EXE文件内容,作为应用程序启动起来?(不能释放到磁盘上再启动,因为这样可能会被用户复制出来,加以利用)。
谢谢!

23 个解决方案

#1


占个沙发

#2


回去研究一下

#3


顶一下

  友情占楼

#4


占个位置,记得以前看到过,查一下

#5


实质上就是加壳,现在网上这方面的资料不少
你得去研究一下PE格式和汇编
主要思路是在被加密程序作为Section写入另一个exe,
在需要的地方跳转到被加密程序的入口地址

#6


文件捆绑机。遇到高手还是能找出来

#7


学习

#8


不用这样吧。直接把功能写道你的exe中间不好么?

你参考一下upx的做法吧

#9


文件加壳...

{加壳}
procedure TForm1.LockFile;
var
  FsName, FtName, FbName, FCode: string;
  iTargetFile, iSourceFile: Integer;
  MyBuf: array[0..MaxBufferSize - 1] of Char;
  LockedFile: TLockedFile;
  NumRead, NumWritten: Integer;
  bSuccessed: Boolean;
begin
    BusyStatus;
    FsName := sExeFilename;
    
    FbName := FsName + '.TMP'; 
    
    FCode := ExtractFilePath(paramstr(0))+'DialogPass.exe';
    if not fileexists(FCode) then
       raise exception.create(FCode+'文件没找到.');

    
    if CheckBox1.Checked then
    begin
      CopyFile(FsName, FbName);
    end;
    iSourceFile := FileOpen(FsName, fmOpenRead or fmShareDenyNone);
    try
      with LockedFile do
      begin
        Flag := CFlag;
        Name := ExtractFileName(FsName);
        Caption := '';
        Password := StringEncrypt(sPassword);
        AdditionalCodeLen := GetFileSize2(FCode);
      end;
      
      FtName := ExtractFilePath(FsName) + '__' + LockedFile.Name;
 //     showmessage(ftname);
      CopyFile(FCode, FtName);

      
      bSuccessed := False;
      iTargetFile := FileOpen(FtName, fmOpenReadWrite);
      try
       
        FileSeek(iTargetFile, 0, soFromEnd);
        repeat
          NumRead := FileRead(iSourceFile, MyBuf, SizeOf(MyBuf));
          NumWritten := FileWrite(iTargetFile, MyBuf, NumRead);
        until (NumRead = 0) or (NumWritten <> NumRead);
        
        FileWrite(iTargetFile, LockedFile, SizeOf(LockedFile));
        bSuccessed := True;
        showmessage('Encryption successful');
      finally
        FileClose(iTargetFile);
      end;
    finally
      FileClose(iSourceFile);
    end;
    if bSuccessed then
    begin
            DeleteFile(FsName);
         RenameFile(FtName, FsName+'.exe');
    end;
  //  FileAddShellOrNot(EditFileName.Text);
end;

#10


谢谢 sdzeng(大头鸟) 的回复,我想将那个被加密EXE也做一个加密存储,我的检验通过之后才由我的程序解密这一部分,是否可行?这样的话,可能这个入口地址就不好办了吧?
谢谢soweb()的深夜关注,要是遇到高手,我只能自认倒霉了!:-)

#11


没有想到,我这个帖子半个小时没有看,就有这么多人回复,谢谢大家!

akirya(坏[其实偶不是什么所谓的坏人]) ,你说的写到EXE里面的功能,我没有办法实现,因为这个需要加密的程序是第三方的,我这里没有源代码。:-(

brightyang(其实我是一个程序员),你用的这个方法是将一个文件加密,如果是一个EXE文件,那么加密之后这个程序还能启动吗?我要的效果是加入我的检验代码之后,启动这个EXE,最好这个第三方EXE可以被加密保存。可能是你没有看清楚我问题的意思。谢谢!

#12


to楼主

可以使用,不过你要另外写一个程序来作为外壳,用这个外壳程序跳到你的主程序

#13


楼上的,问题来了!关键的问题就是如何来这个“跳”?怎么由我的程序跳到主程序呢?

#14


外壳程序有一个文件大小,将,外壳的文件大小作为加壳后的偏移量,在外壳中读偏移量后的内容
这样执行出来的就是你的主程序了

#15


首先要说的就是此代码并非我或者CJT原创

其原形来自于国外一个在资源文件里加载 DLL 文件的单元

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20974729.html

unit MemoryRun;

interface

uses Windows;

type
TImportItem = record
Name: String;
PProcVar: ^Pointer;
end;

TwordArr = array [0..0] of Word;
PwordArr = ^TwordArr;
TdwordArr = array [0..0] of DWORD;
PdwordArr = ^TdwordArr;

TImageImportDescriptor = packed record
OriginalFirstThunk: DWORD;
TimeDateStamp: DWORD;
ForwarderChain: DWORD;
Name: DWORD;
FirstThunk: DWORD;
end;
PImageImportDescriptor = ^TImageImportDescriptor;

TImageBaseRelocation = packed record
VirtualAddress: Cardinal;
SizeOfBlock: Cardinal;
end;
PImageBaseRelocation = ^TImageBaseRelocation;

TEXEEntryProc = function(hinstDLL: HMODULE; hPrevInstance: HMODULE; lpCmdLine: PChar; nCmdShow: Integer): Boolean; stdcall;

TStringArray = array of String;

TLibInfo = record
ImageBase: Pointer;
EXEProc: TEXEEntryProc;
LibsUsed: TStringArray;
end;

PLibInfo = ^TLibInfo;
PPointer = ^Pointer;
TSections = packed array [0..100000] of TImageSectionHeader;

const
IMPORTED_NAME_OFFSET = $00000002;
IMAGE_ORDINAL_FLAG32 = $80000000;
IMAGE_ORDINAL_MASK32 = $0000FFFF;
PROCESS_ATTACH = 1;

procedure MemoryRunExe(pExe: Pointer);
procedure MemoryRunExeName(HIns: LongWord; Field: Pchar; ResName: Pchar);
function PeLoader(Src: Pointer): Pointer;
function PeFree(Src: Pointer): Boolean;

procedure Add(Strings: TStringArray; Text: string);
function StrToInt(S: string): integer;
function Find(Strings: array of string; Text: string; var Index: integer): boolean;
function GetSectionProtection(ImageScn: cardinal): cardinal;

implementation

procedure MemoryRunExe(pExe: Pointer);
var pSrc: Pointer;
begin
pSrc:=PeLoader(pExe);
PeFree(pSrc);
end;

procedure MemoryRunExeName(HIns: LongWord; Field: Pchar; ResName: Pchar);
var pExe,pSrc: Pointer;
begin
pExe:=LockResource(LoadResource(HIns,FindResource(HIns,Field,ResName)));
pSrc:=PeLoader(pExe);
PeFree(pSrc);
end;

function PeLoader(Src: Pointer): Pointer;
var
ImageBase: Pointer;
ImageBaseDelta: Integer;
ImageNtHeaders: PImageNtHeaders;
PSections: ^TSections;
SectionLoop: Integer;
SectionBase: Pointer;
VirtualSectionSize: Cardinal;
RawSectionSize: Cardinal;
OldProtect: Cardinal;
NewLibInfo: TLibInfo;

function IsImportByOrdinal(ImportDescriptor: dword; HLib: THandle): boolean;
begin
Result := (ImportDescriptor and IMAGE_ORDINAL_FLAG32) <> 0;
end;

procedure ProcessRelocs(PRelocs:PImageBaseRelocation);
var
PReloc: PImageBaseRelocation;
RelocsSize: cardinal;
Reloc: PWord;
ModCount: cardinal;
RelocLoop: cardinal;
begin
PReloc := PRelocs;
RelocsSize := ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
while cardinal(PReloc) - cardinal(PRelocs) < RelocsSize do
begin
ModCount := (PReloc.SizeOfBlock-Sizeof(PReloc^)) div 2;
Reloc := pointer(cardinal(PReloc)+sizeof(PReloc^));
for RelocLoop := 0 to ModCount - 1 do begin
if Reloc^ and $f000 <> 0 then Inc(pdword(cardinal(ImageBase) + PReloc.VirtualAddress + (Reloc^ and $0fff))^, ImageBaseDelta);
Inc(Reloc);
end;
PReloc := pointer(Reloc);
end;
end;

procedure ProcessImports(PImports: PImageImportDescriptor);
var
PImport: PImageImportDescriptor;
Import: LPDword;
PImportedName: pchar;
LibHandle: HModule;
ProcAddress: pointer;
PLibName: pchar;
ImportLoop: integer;
begin
PImport := PImports;
while PImport.Name<>0 do
begin
PLibName := pchar(cardinal(PImport.Name) + cardinal(ImageBase));
if not Find(NewLibInfo.LibsUsed, PLibName, ImportLoop) then
begin
LibHandle := LoadLibrary(PLibName);
Add(NewLibInfo.LibsUsed, PLibName);
end else begin
LibHandle := cardinal(NewLibInfo.LibsUsed[ImportLoop]);
end;

if PImport.TimeDateStamp = 0 then
Import := LPDword(pImport.FirstThunk+cardinal(ImageBase))
else
Import := LPDword(pImport.OriginalFirstThunk + cardinal(ImageBase));

while Import^ <> 0 do
begin
if IsImportByOrdinal(Import^, LibHandle) then
ProcAddress := GetProcAddress(LibHandle, pchar(Import^ and $ffff))
else begin
PImportedName := pchar(Import^ + cardinal(ImageBase) + IMPORTED_NAME_OFFSET);
ProcAddress := GetProcAddress(LibHandle, PImportedName);
end;
PPointer(Import)^ := ProcAddress;
Inc(Import);
end;
Inc(PImport);
end;
end;

begin
ImageNtHeaders := pointer(int64(cardinal(Src)) + PImageDosHeader(Src)._lfanew);
ImageBase := VirtualAlloc(nil, ImageNtHeaders.OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_NOACCESS);
ImageBaseDelta := cardinal(ImageBase) - ImageNtHeaders.OptionalHeader.ImageBase;
SectionBase := VirtualAlloc(ImageBase, ImageNtHeaders.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE);
Move(Src^, SectionBase^, ImageNtHeaders.OptionalHeader.SizeOfHeaders);
VirtualProtect(SectionBase, ImageNtHeaders.OptionalHeader.SizeOfHeaders, PAGE_READONLY, OldProtect);
PSections := pointer(pchar(@(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);

for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
VirtualSectionSize := PSections[SectionLoop].Misc.VirtualSize;
RawSectionSize := PSections[SectionLoop].SizeOfRawData;

if VirtualSectionSize < RawSectionSize then
begin
VirtualSectionSize := VirtualSectionSize xor RawSectionSize;
RawSectionSize := VirtualSectionSize xor RawSectionSize;
VirtualSectionSize := VirtualSectionSize xor RawSectionSize;
end;

SectionBase := VirtualAlloc(PSections[SectionLoop].VirtualAddress + pchar(ImageBase), VirtualSectionSize, MEM_COMMIT, PAGE_READWRITE);
FillChar(SectionBase^, VirtualSectionSize, 0);
Move((pchar(src) + PSections[SectionLoop].PointerToRawData)^, SectionBase^, RawSectionSize);
end;

NewLibInfo.ExeProc := TEXEEntryProc(ImageNtHeaders.OptionalHeader.AddressOfEntryPoint + cardinal(ImageBase));
NewLibInfo.ImageBase := ImageBase;
SetLength(NewLibInfo.LibsUsed, 0);

if ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0 then ProcessRelocs(pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + cardinal(ImageBase)));

if ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress <> 0 then ProcessImports(pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + cardinal(ImageBase)));

for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
VirtualProtect(PSections[SectionLoop].VirtualAddress + pchar(ImageBase), PSections[SectionLoop].Misc.VirtualSize, GetSectionProtection(PSections[SectionLoop].Characteristics), OldProtect);

if @NewLibInfo.ExeProc <> nil then
begin
if not NewLibInfo.ExeProc(cardinal(ImageBase), DLL_PROCESS_ATTACH, nil,0) then
begin
NewLibInfo.ExeProc := nil;
PeFree(NewLibInfo.ImageBase);
end;
end;

Result:=NewLibInfo.ImageBase;
end;

function PeFree(Src: Pointer): Boolean;
begin
try
VirtualFree(Src, 0, MEM_RELEASE);
finally
Result:= True;
end;
end;

function StrToInt(S: string): integer;
begin
Val(S, Result, Result);
end;

procedure Add(Strings: TStringArray; Text: string);
begin
SetLength(Strings, Length(Strings) + 1);
Strings[Length(Strings)-1] := Text;
end;

function Find(Strings: array of string; Text: string; var Index: integer): boolean;
var StringLoop: integer;
begin
Result := False;
for StringLoop := 0 to Length(Strings) - 1 do
begin
if lstrcmpi(pchar(Strings[StringLoop]), pchar(Text)) = 0 then
begin
Index := StringLoop;
Result := True;
end;
end;
end;

#16


function GetSectionProtection(ImageScn: cardinal): cardinal;
begin
Result := 0;
if (ImageScn and IMAGE_SCN_MEM_NOT_CACHED) <> 0 then Result := Result or PAGE_NOCACHE;

if (ImageScn and IMAGE_SCN_MEM_EXECUTE) <> 0 then begin

if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then  begin

if (ImageScn and IMAGE_SCN_MEM_WRITE)<> 0 then Result := Result or PAGE_EXECUTE_READWRITE  else Result := Result or PAGE_EXECUTE_READ;

end else if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_EXECUTE_WRITECOPY else Result := Result or PAGE_EXECUTE;

end else if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then  begin

if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_READWRITE else Result := Result or PAGE_READONLY;

end else if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_WRITECOPY else Result := Result or PAGE_NOACCESS;

end;

end.
/////////////////////////////////
运行
program Demo;

uses
  Forms,Windows,MemoryRun;

{$R *.res}
{$R BuildExE.res}
//RC内容 setup exefile buildexe.exe
//buildexe.exe为任意exe文件

begin
MemoryRunExe(LockResource(LoadResource(HInstance, FindResource(HInstance, pchar('setup'), 'exefile'))));
end.

#17


从看雪学院的《加密解密技术内幕》转一段壳的加载步骤:

1)获取壳自己所需要使用的API地址 

如果你用PE查看工具看看加壳后的程序文件,会发现未加壳的程序文件和加壳后的程序文件的Import Table不太一样, 
加壳后的Import Table一般所引入的DLL和API很少,甚至只有Kernel32.dll以及GetProcAddress这个API。 

我想你不会认为壳只用这个API就可以做所有的事吧。 
壳还需要很多其他的API来完成它的工作。 
当然他并不想让你知道他想用哪个API,所以一般他只是在壳的代码中动态加载这些API,而只把一些你嗅不过什么味道的几个API放在Import Table中。 

当然这其中壳可能会用到一些Anti技术,不过这和本文主旨无关,所以就不说了。 

2)解密原程序的各个节区(Section)的数据 

壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个节区。既然是加密保存,那么在程序执行时你总不能也保持加密状态吧,所以解密是壳必做的工作之一。 
一般壳按节区加密的,那么在解密时也按节区解密,并且把解密的节区数据按照节区的定义放在合适的内存位置。 

如果加壳时用到了压缩技术,那么在解密之前还有一道工序,当然是解压缩。 
这也是一些壳的特色之一,比如说原来的程序文件未加壳时1-2M大小,加壳后反而只有几百K,这种瘦身技术当然会吸引了不少眼球。 

3)重定位 
前面我们提到了ImageBase,即程序的基地址,当然这只是程序文件中声明的,程序运行时能够保证系统一定满足你的要求吗? 
对于EXE的程序文件来说,Windows系统会尽量满足你的要求。 
比如一般EXE文件的基地址为0x400000,而运行时Windows系统提供给程序的基地址也同样是0x400000。在这种情况下就不需要进行地址"重定位"了。 

由于不需要对EXE文件进行"重定位",所以很多壳在加壳时把原程序文件中用于保存重定位信息的节区干脆也去掉了,这样使得加壳后的文件更加小巧。有些工具提供Wipe Reloc的功能,其实就是这个作用。 

不过对于DLL的动态链接库文件来说,Windows系统没有办法保证每一次DLL运行时提供相同的基地址。这样"重定位"就很重要了。 

此时壳中也需要提供进行"重定位"的代码,否则原程序中的代码是无法正常运行起来的。从这点来说,加壳的DLL比加壳的EXE更难修正。 

4)HOOK-API 
我们知道程序文件中的Import Table的作用是让Windows系统在程序运行时提供API的实际地址给程序使用。 
在程序的第一行代码执行之前,Windows系统就完成了这个工作。 

而壳一般都修改了原程序文件的Import Table,那么原程序文件的Import Table由谁来处理呢? 
这当然由壳来自己处理了,因此壳不得不模仿Windows系统的工作来填充Import Table中相关的数据。 
Import Table结构中与实际运行相关的主要是IAT结构, 
这个结构中用于保存API的实际地址,因此壳所需要的就是填充这个结构中的数据。 
不过壳不是填充这些实际的API地址,而是填充壳中用来HOOK-API的代码的地址。 

这样壳中的代码一旦完成了加载工作,在进入原程序的代码之后,仍然能够间接地获得程序的控制权。 
因为程序总是需要与系统打交道,与系统交道的途径是API,而API的地址已经替换成了壳的HOOK-API的地址,那么每一次程序与系统打交道,都会让壳的代码获得一次控制权,一来壳可以进行反跟踪继续保护软件,二来可以完成某些特殊的任务。 
其实这就是所谓HOOK技术。 

5)最后当然是跳转到程序原入口点。 
这个大家比较熟悉,找的就是它。脱壳时大多数也是在这个时候。从这个时候起壳要把控制权交还给原程序了。 

以上是一个简单的总结。这代表了大多数壳的加载过程,不过特殊的不包括在内,介绍一下让大家了解一些。 
当然还有一些壳充分利用了PE结构的特点,比如利用TLS加载的特点也挺有趣。。。。。 

#18


“把解密的节区数据按照节区的定义放在合适的内存位置”,
实际上就是在内存中把目标程序重新拼接,拼接以后,接着要实现的是运行目标程序

一个是用类似进程注入的方式,用WriteProcessMemory写入壳所在进程,
这种办法可以参考楼上,或是下面这几篇
http://topic.csdn.net/t/20051124/17/4416252.html
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2612942
http://blog.csdn.net/xylary/archive/2007/01/08/1477454.aspx

另一个办法是自己写一个PE Loader,用来加载内存中的程序,具体可以看下面几篇
http://blog.csdn.net/rageliu/archive/2007/02/26/1514524.aspx
http://blog.vckbase.com/windowssky/archive/2007/07/30/27827.html
http://www.codeproject.com/useritems/KernelExec.asp

资料都在这了,剩下就看你能吃透多少,
不过现在有很多很好的加壳工具,用起来都很方便,
如果不是搞加密解密这行,可以考虑用现成的工具

#19


就算不释放到硬盘上别人照样可以dump出来放到硬盘上,这样的工具多的很

#20


谢谢楼上几位提供的代码,我来好好研究一下。再次感谢!

to coffeemay :会用内存Dump工具的我也没有办法了,就像最上面有人说,文件加壳照样可以被破解一样,遇到了这样的人,我只有自认倒霉了。

#21



http://pmis.cn/bbs/showthread.asp?threadid=8
可能对你会有帮助

#22


多谢楼上提供的链接,我来看看!

#23


to flashtong:这个帖子上面的内容,我测试过,在我的windows2003server上面,执行的时候,把我的机器搞的蓝屏了。

#1


占个沙发

#2


回去研究一下

#3


顶一下

  友情占楼

#4


占个位置,记得以前看到过,查一下

#5


实质上就是加壳,现在网上这方面的资料不少
你得去研究一下PE格式和汇编
主要思路是在被加密程序作为Section写入另一个exe,
在需要的地方跳转到被加密程序的入口地址

#6


文件捆绑机。遇到高手还是能找出来

#7


学习

#8


不用这样吧。直接把功能写道你的exe中间不好么?

你参考一下upx的做法吧

#9


文件加壳...

{加壳}
procedure TForm1.LockFile;
var
  FsName, FtName, FbName, FCode: string;
  iTargetFile, iSourceFile: Integer;
  MyBuf: array[0..MaxBufferSize - 1] of Char;
  LockedFile: TLockedFile;
  NumRead, NumWritten: Integer;
  bSuccessed: Boolean;
begin
    BusyStatus;
    FsName := sExeFilename;
    
    FbName := FsName + '.TMP'; 
    
    FCode := ExtractFilePath(paramstr(0))+'DialogPass.exe';
    if not fileexists(FCode) then
       raise exception.create(FCode+'文件没找到.');

    
    if CheckBox1.Checked then
    begin
      CopyFile(FsName, FbName);
    end;
    iSourceFile := FileOpen(FsName, fmOpenRead or fmShareDenyNone);
    try
      with LockedFile do
      begin
        Flag := CFlag;
        Name := ExtractFileName(FsName);
        Caption := '';
        Password := StringEncrypt(sPassword);
        AdditionalCodeLen := GetFileSize2(FCode);
      end;
      
      FtName := ExtractFilePath(FsName) + '__' + LockedFile.Name;
 //     showmessage(ftname);
      CopyFile(FCode, FtName);

      
      bSuccessed := False;
      iTargetFile := FileOpen(FtName, fmOpenReadWrite);
      try
       
        FileSeek(iTargetFile, 0, soFromEnd);
        repeat
          NumRead := FileRead(iSourceFile, MyBuf, SizeOf(MyBuf));
          NumWritten := FileWrite(iTargetFile, MyBuf, NumRead);
        until (NumRead = 0) or (NumWritten <> NumRead);
        
        FileWrite(iTargetFile, LockedFile, SizeOf(LockedFile));
        bSuccessed := True;
        showmessage('Encryption successful');
      finally
        FileClose(iTargetFile);
      end;
    finally
      FileClose(iSourceFile);
    end;
    if bSuccessed then
    begin
            DeleteFile(FsName);
         RenameFile(FtName, FsName+'.exe');
    end;
  //  FileAddShellOrNot(EditFileName.Text);
end;

#10


谢谢 sdzeng(大头鸟) 的回复,我想将那个被加密EXE也做一个加密存储,我的检验通过之后才由我的程序解密这一部分,是否可行?这样的话,可能这个入口地址就不好办了吧?
谢谢soweb()的深夜关注,要是遇到高手,我只能自认倒霉了!:-)

#11


没有想到,我这个帖子半个小时没有看,就有这么多人回复,谢谢大家!

akirya(坏[其实偶不是什么所谓的坏人]) ,你说的写到EXE里面的功能,我没有办法实现,因为这个需要加密的程序是第三方的,我这里没有源代码。:-(

brightyang(其实我是一个程序员),你用的这个方法是将一个文件加密,如果是一个EXE文件,那么加密之后这个程序还能启动吗?我要的效果是加入我的检验代码之后,启动这个EXE,最好这个第三方EXE可以被加密保存。可能是你没有看清楚我问题的意思。谢谢!

#12


to楼主

可以使用,不过你要另外写一个程序来作为外壳,用这个外壳程序跳到你的主程序

#13


楼上的,问题来了!关键的问题就是如何来这个“跳”?怎么由我的程序跳到主程序呢?

#14


外壳程序有一个文件大小,将,外壳的文件大小作为加壳后的偏移量,在外壳中读偏移量后的内容
这样执行出来的就是你的主程序了

#15


首先要说的就是此代码并非我或者CJT原创

其原形来自于国外一个在资源文件里加载 DLL 文件的单元

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20974729.html

unit MemoryRun;

interface

uses Windows;

type
TImportItem = record
Name: String;
PProcVar: ^Pointer;
end;

TwordArr = array [0..0] of Word;
PwordArr = ^TwordArr;
TdwordArr = array [0..0] of DWORD;
PdwordArr = ^TdwordArr;

TImageImportDescriptor = packed record
OriginalFirstThunk: DWORD;
TimeDateStamp: DWORD;
ForwarderChain: DWORD;
Name: DWORD;
FirstThunk: DWORD;
end;
PImageImportDescriptor = ^TImageImportDescriptor;

TImageBaseRelocation = packed record
VirtualAddress: Cardinal;
SizeOfBlock: Cardinal;
end;
PImageBaseRelocation = ^TImageBaseRelocation;

TEXEEntryProc = function(hinstDLL: HMODULE; hPrevInstance: HMODULE; lpCmdLine: PChar; nCmdShow: Integer): Boolean; stdcall;

TStringArray = array of String;

TLibInfo = record
ImageBase: Pointer;
EXEProc: TEXEEntryProc;
LibsUsed: TStringArray;
end;

PLibInfo = ^TLibInfo;
PPointer = ^Pointer;
TSections = packed array [0..100000] of TImageSectionHeader;

const
IMPORTED_NAME_OFFSET = $00000002;
IMAGE_ORDINAL_FLAG32 = $80000000;
IMAGE_ORDINAL_MASK32 = $0000FFFF;
PROCESS_ATTACH = 1;

procedure MemoryRunExe(pExe: Pointer);
procedure MemoryRunExeName(HIns: LongWord; Field: Pchar; ResName: Pchar);
function PeLoader(Src: Pointer): Pointer;
function PeFree(Src: Pointer): Boolean;

procedure Add(Strings: TStringArray; Text: string);
function StrToInt(S: string): integer;
function Find(Strings: array of string; Text: string; var Index: integer): boolean;
function GetSectionProtection(ImageScn: cardinal): cardinal;

implementation

procedure MemoryRunExe(pExe: Pointer);
var pSrc: Pointer;
begin
pSrc:=PeLoader(pExe);
PeFree(pSrc);
end;

procedure MemoryRunExeName(HIns: LongWord; Field: Pchar; ResName: Pchar);
var pExe,pSrc: Pointer;
begin
pExe:=LockResource(LoadResource(HIns,FindResource(HIns,Field,ResName)));
pSrc:=PeLoader(pExe);
PeFree(pSrc);
end;

function PeLoader(Src: Pointer): Pointer;
var
ImageBase: Pointer;
ImageBaseDelta: Integer;
ImageNtHeaders: PImageNtHeaders;
PSections: ^TSections;
SectionLoop: Integer;
SectionBase: Pointer;
VirtualSectionSize: Cardinal;
RawSectionSize: Cardinal;
OldProtect: Cardinal;
NewLibInfo: TLibInfo;

function IsImportByOrdinal(ImportDescriptor: dword; HLib: THandle): boolean;
begin
Result := (ImportDescriptor and IMAGE_ORDINAL_FLAG32) <> 0;
end;

procedure ProcessRelocs(PRelocs:PImageBaseRelocation);
var
PReloc: PImageBaseRelocation;
RelocsSize: cardinal;
Reloc: PWord;
ModCount: cardinal;
RelocLoop: cardinal;
begin
PReloc := PRelocs;
RelocsSize := ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
while cardinal(PReloc) - cardinal(PRelocs) < RelocsSize do
begin
ModCount := (PReloc.SizeOfBlock-Sizeof(PReloc^)) div 2;
Reloc := pointer(cardinal(PReloc)+sizeof(PReloc^));
for RelocLoop := 0 to ModCount - 1 do begin
if Reloc^ and $f000 <> 0 then Inc(pdword(cardinal(ImageBase) + PReloc.VirtualAddress + (Reloc^ and $0fff))^, ImageBaseDelta);
Inc(Reloc);
end;
PReloc := pointer(Reloc);
end;
end;

procedure ProcessImports(PImports: PImageImportDescriptor);
var
PImport: PImageImportDescriptor;
Import: LPDword;
PImportedName: pchar;
LibHandle: HModule;
ProcAddress: pointer;
PLibName: pchar;
ImportLoop: integer;
begin
PImport := PImports;
while PImport.Name<>0 do
begin
PLibName := pchar(cardinal(PImport.Name) + cardinal(ImageBase));
if not Find(NewLibInfo.LibsUsed, PLibName, ImportLoop) then
begin
LibHandle := LoadLibrary(PLibName);
Add(NewLibInfo.LibsUsed, PLibName);
end else begin
LibHandle := cardinal(NewLibInfo.LibsUsed[ImportLoop]);
end;

if PImport.TimeDateStamp = 0 then
Import := LPDword(pImport.FirstThunk+cardinal(ImageBase))
else
Import := LPDword(pImport.OriginalFirstThunk + cardinal(ImageBase));

while Import^ <> 0 do
begin
if IsImportByOrdinal(Import^, LibHandle) then
ProcAddress := GetProcAddress(LibHandle, pchar(Import^ and $ffff))
else begin
PImportedName := pchar(Import^ + cardinal(ImageBase) + IMPORTED_NAME_OFFSET);
ProcAddress := GetProcAddress(LibHandle, PImportedName);
end;
PPointer(Import)^ := ProcAddress;
Inc(Import);
end;
Inc(PImport);
end;
end;

begin
ImageNtHeaders := pointer(int64(cardinal(Src)) + PImageDosHeader(Src)._lfanew);
ImageBase := VirtualAlloc(nil, ImageNtHeaders.OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_NOACCESS);
ImageBaseDelta := cardinal(ImageBase) - ImageNtHeaders.OptionalHeader.ImageBase;
SectionBase := VirtualAlloc(ImageBase, ImageNtHeaders.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE);
Move(Src^, SectionBase^, ImageNtHeaders.OptionalHeader.SizeOfHeaders);
VirtualProtect(SectionBase, ImageNtHeaders.OptionalHeader.SizeOfHeaders, PAGE_READONLY, OldProtect);
PSections := pointer(pchar(@(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);

for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
VirtualSectionSize := PSections[SectionLoop].Misc.VirtualSize;
RawSectionSize := PSections[SectionLoop].SizeOfRawData;

if VirtualSectionSize < RawSectionSize then
begin
VirtualSectionSize := VirtualSectionSize xor RawSectionSize;
RawSectionSize := VirtualSectionSize xor RawSectionSize;
VirtualSectionSize := VirtualSectionSize xor RawSectionSize;
end;

SectionBase := VirtualAlloc(PSections[SectionLoop].VirtualAddress + pchar(ImageBase), VirtualSectionSize, MEM_COMMIT, PAGE_READWRITE);
FillChar(SectionBase^, VirtualSectionSize, 0);
Move((pchar(src) + PSections[SectionLoop].PointerToRawData)^, SectionBase^, RawSectionSize);
end;

NewLibInfo.ExeProc := TEXEEntryProc(ImageNtHeaders.OptionalHeader.AddressOfEntryPoint + cardinal(ImageBase));
NewLibInfo.ImageBase := ImageBase;
SetLength(NewLibInfo.LibsUsed, 0);

if ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0 then ProcessRelocs(pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + cardinal(ImageBase)));

if ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress <> 0 then ProcessImports(pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + cardinal(ImageBase)));

for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
VirtualProtect(PSections[SectionLoop].VirtualAddress + pchar(ImageBase), PSections[SectionLoop].Misc.VirtualSize, GetSectionProtection(PSections[SectionLoop].Characteristics), OldProtect);

if @NewLibInfo.ExeProc <> nil then
begin
if not NewLibInfo.ExeProc(cardinal(ImageBase), DLL_PROCESS_ATTACH, nil,0) then
begin
NewLibInfo.ExeProc := nil;
PeFree(NewLibInfo.ImageBase);
end;
end;

Result:=NewLibInfo.ImageBase;
end;

function PeFree(Src: Pointer): Boolean;
begin
try
VirtualFree(Src, 0, MEM_RELEASE);
finally
Result:= True;
end;
end;

function StrToInt(S: string): integer;
begin
Val(S, Result, Result);
end;

procedure Add(Strings: TStringArray; Text: string);
begin
SetLength(Strings, Length(Strings) + 1);
Strings[Length(Strings)-1] := Text;
end;

function Find(Strings: array of string; Text: string; var Index: integer): boolean;
var StringLoop: integer;
begin
Result := False;
for StringLoop := 0 to Length(Strings) - 1 do
begin
if lstrcmpi(pchar(Strings[StringLoop]), pchar(Text)) = 0 then
begin
Index := StringLoop;
Result := True;
end;
end;
end;

#16


function GetSectionProtection(ImageScn: cardinal): cardinal;
begin
Result := 0;
if (ImageScn and IMAGE_SCN_MEM_NOT_CACHED) <> 0 then Result := Result or PAGE_NOCACHE;

if (ImageScn and IMAGE_SCN_MEM_EXECUTE) <> 0 then begin

if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then  begin

if (ImageScn and IMAGE_SCN_MEM_WRITE)<> 0 then Result := Result or PAGE_EXECUTE_READWRITE  else Result := Result or PAGE_EXECUTE_READ;

end else if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_EXECUTE_WRITECOPY else Result := Result or PAGE_EXECUTE;

end else if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then  begin

if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_READWRITE else Result := Result or PAGE_READONLY;

end else if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
Result := Result or PAGE_WRITECOPY else Result := Result or PAGE_NOACCESS;

end;

end.
/////////////////////////////////
运行
program Demo;

uses
  Forms,Windows,MemoryRun;

{$R *.res}
{$R BuildExE.res}
//RC内容 setup exefile buildexe.exe
//buildexe.exe为任意exe文件

begin
MemoryRunExe(LockResource(LoadResource(HInstance, FindResource(HInstance, pchar('setup'), 'exefile'))));
end.

#17


从看雪学院的《加密解密技术内幕》转一段壳的加载步骤:

1)获取壳自己所需要使用的API地址 

如果你用PE查看工具看看加壳后的程序文件,会发现未加壳的程序文件和加壳后的程序文件的Import Table不太一样, 
加壳后的Import Table一般所引入的DLL和API很少,甚至只有Kernel32.dll以及GetProcAddress这个API。 

我想你不会认为壳只用这个API就可以做所有的事吧。 
壳还需要很多其他的API来完成它的工作。 
当然他并不想让你知道他想用哪个API,所以一般他只是在壳的代码中动态加载这些API,而只把一些你嗅不过什么味道的几个API放在Import Table中。 

当然这其中壳可能会用到一些Anti技术,不过这和本文主旨无关,所以就不说了。 

2)解密原程序的各个节区(Section)的数据 

壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个节区。既然是加密保存,那么在程序执行时你总不能也保持加密状态吧,所以解密是壳必做的工作之一。 
一般壳按节区加密的,那么在解密时也按节区解密,并且把解密的节区数据按照节区的定义放在合适的内存位置。 

如果加壳时用到了压缩技术,那么在解密之前还有一道工序,当然是解压缩。 
这也是一些壳的特色之一,比如说原来的程序文件未加壳时1-2M大小,加壳后反而只有几百K,这种瘦身技术当然会吸引了不少眼球。 

3)重定位 
前面我们提到了ImageBase,即程序的基地址,当然这只是程序文件中声明的,程序运行时能够保证系统一定满足你的要求吗? 
对于EXE的程序文件来说,Windows系统会尽量满足你的要求。 
比如一般EXE文件的基地址为0x400000,而运行时Windows系统提供给程序的基地址也同样是0x400000。在这种情况下就不需要进行地址"重定位"了。 

由于不需要对EXE文件进行"重定位",所以很多壳在加壳时把原程序文件中用于保存重定位信息的节区干脆也去掉了,这样使得加壳后的文件更加小巧。有些工具提供Wipe Reloc的功能,其实就是这个作用。 

不过对于DLL的动态链接库文件来说,Windows系统没有办法保证每一次DLL运行时提供相同的基地址。这样"重定位"就很重要了。 

此时壳中也需要提供进行"重定位"的代码,否则原程序中的代码是无法正常运行起来的。从这点来说,加壳的DLL比加壳的EXE更难修正。 

4)HOOK-API 
我们知道程序文件中的Import Table的作用是让Windows系统在程序运行时提供API的实际地址给程序使用。 
在程序的第一行代码执行之前,Windows系统就完成了这个工作。 

而壳一般都修改了原程序文件的Import Table,那么原程序文件的Import Table由谁来处理呢? 
这当然由壳来自己处理了,因此壳不得不模仿Windows系统的工作来填充Import Table中相关的数据。 
Import Table结构中与实际运行相关的主要是IAT结构, 
这个结构中用于保存API的实际地址,因此壳所需要的就是填充这个结构中的数据。 
不过壳不是填充这些实际的API地址,而是填充壳中用来HOOK-API的代码的地址。 

这样壳中的代码一旦完成了加载工作,在进入原程序的代码之后,仍然能够间接地获得程序的控制权。 
因为程序总是需要与系统打交道,与系统交道的途径是API,而API的地址已经替换成了壳的HOOK-API的地址,那么每一次程序与系统打交道,都会让壳的代码获得一次控制权,一来壳可以进行反跟踪继续保护软件,二来可以完成某些特殊的任务。 
其实这就是所谓HOOK技术。 

5)最后当然是跳转到程序原入口点。 
这个大家比较熟悉,找的就是它。脱壳时大多数也是在这个时候。从这个时候起壳要把控制权交还给原程序了。 

以上是一个简单的总结。这代表了大多数壳的加载过程,不过特殊的不包括在内,介绍一下让大家了解一些。 
当然还有一些壳充分利用了PE结构的特点,比如利用TLS加载的特点也挺有趣。。。。。 

#18


“把解密的节区数据按照节区的定义放在合适的内存位置”,
实际上就是在内存中把目标程序重新拼接,拼接以后,接着要实现的是运行目标程序

一个是用类似进程注入的方式,用WriteProcessMemory写入壳所在进程,
这种办法可以参考楼上,或是下面这几篇
http://topic.csdn.net/t/20051124/17/4416252.html
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2612942
http://blog.csdn.net/xylary/archive/2007/01/08/1477454.aspx

另一个办法是自己写一个PE Loader,用来加载内存中的程序,具体可以看下面几篇
http://blog.csdn.net/rageliu/archive/2007/02/26/1514524.aspx
http://blog.vckbase.com/windowssky/archive/2007/07/30/27827.html
http://www.codeproject.com/useritems/KernelExec.asp

资料都在这了,剩下就看你能吃透多少,
不过现在有很多很好的加壳工具,用起来都很方便,
如果不是搞加密解密这行,可以考虑用现成的工具

#19


就算不释放到硬盘上别人照样可以dump出来放到硬盘上,这样的工具多的很

#20


谢谢楼上几位提供的代码,我来好好研究一下。再次感谢!

to coffeemay :会用内存Dump工具的我也没有办法了,就像最上面有人说,文件加壳照样可以被破解一样,遇到了这样的人,我只有自认倒霉了。

#21



http://pmis.cn/bbs/showthread.asp?threadid=8
可能对你会有帮助

#22


多谢楼上提供的链接,我来看看!

#23


to flashtong:这个帖子上面的内容,我测试过,在我的windows2003server上面,执行的时候,把我的机器搞的蓝屏了。