首页
社区
课程
招聘
[原创]Delphi下构建无导入表程序-使用:hash获取API,k32Base等
2006-2-6 16:37 9911

[原创]Delphi下构建无导入表程序-使用:hash获取API,k32Base等

2006-2-6 16:37
9911
小弟菜菜~这篇是以前写的东西一直没有发出来献丑。。
新年到了余下的时间也不多了就发出来大家一起斜视一下好了
//Start~~~
《Delphi实现无导入表程序》
初学编程,请勿奸笑:P
首先感谢,一些认识不认识的前辈高人的资料.谢谢~

前提假设您已经有了一定的PE Virus的知识.
好了~先简单的说说无导入表程序的几点基本的构成
1.GetkernelBase(获取kernel32.dll的基址)
由于我们是无导入表的程序所以,所有API函数都是依靠内存搜索完成的,
想必大家已经知道EXE载入到内存中ESP保存ExitThread函数地址
ExitThread函数是在kernel32.dll 模块中的,所以证明EXE载入的时候就已经加载
了kernel32.dll模块,于是我们的工作就是确定kernel32.dll的基址。

这里我使用"PEB获取地址kernel32.dll基址"的方法(简单嘛。。Delphi调试这种程序很麻烦的~所以使用这个。。)
asm
  mov eax,fs:$30
  mov eax,[eax + $0c]
  mov esi,[eax + $1c]
  lodsd
  mov eax,[eax+$08]                //这个时候eax中保存的就是k32的基址了
end;

基址获取到了剩下的就是需要确定我们需要的两个重要的函数
"GetProcAddress"和"LoadLibraryA"两个函数~有了这两个函数我们就可以
获取到我们需要的任何函数了..

2.自构建"GetProcAddress"函数
上面我们已经获取到k32的基址了~但是问题是我们还需要一些其他函数来完成我们程序
的功能,首先我们回顾一下~前辈们写的API搜索函数,为了减少程序体积和保护程序自身
他们基本上都是使用hash值来搜索模块的导入表的,这样我们就可以自己构建一个API搜索
函数了。直接贴代码好了~其实Delphi版本的API搜索函数许多前辈都写过
Aming,老王,liumazi等等~
FUNCTION GetProcAddress(Module:Cardinal;ProcessCRC:DWORD) : Pointer;
VAR
  ExportName           : pChar;
  Address              : Cardinal;
  J                    : Cardinal;
  ImageDosHeader       : PImageDosHeader;
  ImageNTHeaders       : PImageNTHeaders;
  ImageExportDirectory : PImageExportDirectory;
BEGIN
  ImageDosHeader:=Pointer(Module);
  ImageNTHeaders:=Pointer(Module+ImageDosHeader._lfanew);
  ImageExportDirectory:=Pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress+Module);
  J:=0;
  Address:=0;
  REPEAT
    ExportName:=Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames)+Module+J*4)^)+Module);
    IF CalculateCRC32(ExportName^,StrLen(ExportName))=ProcessCRC THEN
      Address:=Cardinal(Pointer(Word(Pointer(J SHL 1+Cardinal(
               ImageExportDirectory.AddressOfNameOrdinals)+Module)^) AND
               $0000FFFF SHL 2+Cardinal(ImageExportDirectory.AddressOfFunctions)
               +Module)^)+Module;
    Inc(J);
  UNTIL (Address<>0)OR(J=ImageExportDirectory.NumberOfNames);
  Result:=Pointer(Address);
END;

好了~此函数就可以帮我们,循环对比ProcessCRC参数中给出的API的hash值
然后发挥这个函数的地址,我这里使用的是Crc32算法~
上面的PE的数据结构可以从Windows中Copy出来

PROCEDURE BuildCRC32Table; ASSEMBLER;
ASM
    mov       ebx, 0EDB88320h
    lea       edi, crc32tab
    xor       ecx, ecx
  @loc1:
    mov       eax, ecx
    mov       edx, 8
  @loc2:
    test eax, 1
    jz  @loc3
    shr       eax, 1
    xor       eax, ebx
    jmp       @loc4
  @loc3:
    shr       eax, 1
  @loc4:
    dec       edx
    jnz       @loc2
    stosd
    inc       ecx
    cmp       ecx, 256
    jb  @loc1
END;

FUNCTION CalculateCRC32(VAR Buffer;CONST Size:DWORD) : DWORD; ASSEMBLER;
ASM
    push esi
    push edi
    push ebx
    mov edi,edx
    mov esi,eax
    xor ebx,ebx
    mov eax,$ffffffff
    mov ecx,edi
    shr ecx,2
    jecxz @Rest
  @Loop:
    mov edx,[esi]
    mov bl,al
    xor bl,dl
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    mov bl,al
    xor bl,dh
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    shr edx,16
    mov bl,al
    xor bl,dl
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    mov bl,al
    xor bl,dh
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    add esi,4
    loop @Loop
  @Rest:
    mov ecx,edi
    and ecx,3
    jecxz @End
  @Loop_Rest:
    mov bl,al
    xor bl,[esi]
    shr eax,8
    inc esi
    xor eax,dword ptr [CRC32tab+ebx*4]
    loop @Loop_Rest
@End:
    xor eax,$ffffffff
    pop ebx
    pop edi
    pop esi
END;

这个是计算一个数据的Crc32数值--返回是10进制的~可以自己改变成16进制

好了现在我们已经有了GetProcAddress函数了~好像还少一个??
前面我们提到过需要两个函数~GetProcAddress有了・k32的基址也有了
这样我们就可以搜索出LoadLibraryA的地址....
好了完成前面的代码
FUNCTION MyLoadLibraryA:Pointer;
const
  MyLoadLibraryA=$3FC1BD8D;//LoadLibraryA的Crc32数值
asm
  mov eax,fs:$30
  mov eax,[eax + $0c]
  mov esi,[eax + $1c]
  lodsd
  mov eax,[eax+$08]                //此时eax中就保存了k32的基址
  mov edx,MyLoadLibraryA        //压入LoadLibraryA的Hash
  call GetProcAddress                //开始获取LoadLibraryA的地址
end;


好了现在我们两个函数都有了~现在我们的任务已经完成一半了!
(为什么是一半??因为Delphi非等同于VC和XXXASM,他不能够自己构建PE结构)
准确的说~即使你写一个只有一条begin end.的程序~编译器编译的时候还是会连接N多
的API函数~进来....
为了完成任务,我们这里就使用了Nico Bendlin前辈的miniEXE的模板...
自己打造了一个无导入表的程序...
这里需要我们自己编译system.pas和SysInit.pas单元
//SysInit.pas单元代码
unit SysInit;

interface

var
  TlsIndex: LongWord;

implementation

end.

System.pas单元代码
unit System;

interface

type
  DWORD=LongWord;
  PLongWord= ^LongWord;
  PWord= ^Word;
  TGUID = record
    D1: LongWord;
    D2: Word;
    D3: Word;
    D4: array[0..7] of Byte;
  end;

const
  Kernel32 = 'kernel32.dll'

var
  ExitCode: LongWord;
  HKernel32: LongWord;

procedure _InitExe;
procedure _HandleFinally;
procedure _halt0;
PROCEDURE BuildCRC32Table;
//procedure ExitProcess(uExitCode: LongWord); stdcall;
FUNCTION  CalculateCRC32(VAR Buffer;CONST Size:DWORD) : DWORD;
FUNCTION GetProcAddress(Module:Cardinal;ProcessCRC:DWORD) : Pointer;
FUNCTION MyLoadLibraryA:Pointer;

implementation

CONST
  IMAGE_DIRECTORY_ENTRY_EXPORT     = 0;
  SIZE_OF_80387_REGISTERS          = 80;
  IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
  MAX_API_STRING_LENGTH            = 150;
  IMAGE_DOS_SIGNATURE              = $5A4D;
  IMAGE_NT_SIGNATURE               = $00004550;
  MIN_KERNEL_SEARCH_BASE           = $70000000;

TYPE
  PImageDosHeader               =^TImageDosHeader;
  TImageDosHeader               = PACKED RECORD
    e_magic                     : WORD;
    e_cblp                      : WORD;
    e_cp                        : WORD;
    e_crlc                      : WORD;
    e_cparhdr                   : WORD;
    e_minalloc                  : WORD;
    e_maxalloc                  : WORD;
    e_ss                        : WORD;
    e_sp                        : WORD;
    e_csum                      : WORD;
    e_ip                        : WORD;
    e_cs                        : WORD;
    e_lfarlc                    : WORD;
    e_ovno                      : WORD;
    e_res                       : ARRAY [0..3] OF WORD;
    e_oemid                     : WORD;
    e_oeminfo                   : WORD;
    e_res2                      : ARRAY [0..9] OF WORD;
    _lfanew                     : LongInt;
  END;

  PImageFileHeader              =^TImageFileHeader;
  TImageFileHeader              = PACKED RECORD
    Machine                     : WORD;
    NumberOfSections            : WORD;
    TimeDateStamp               : LongWord;
    PointerToSymbolTable        : LongWord;
    NumberOfSymbols             : LongWord;
    SizeOfOptionalHeader        : WORD;
    Characteristics             : WORD;
  END;

  PImageDataDirectory           =^TImageDataDirectory;
  TImageDataDirectory           = RECORD
    VirtualAddress              : LongWord;
    Size                        : LongWord;
  END;

  PImageOptionalHeader          =^TImageOptionalHeader;
  TImageOptionalHeader          = PACKED RECORD
    Magic                       : WORD;
    MajorLinkerVersion          : Byte;
    MinorLinkerVersion          : Byte;
    SizeOfCode                  : LongWord;
    SizeOfInitializedData       : LongWord;
    SizeOfUninitializedData     : LongWord;
    AddressOfEntryPoint         : LongWord;
    BaseOfCode                  : LongWord;
    BaseOfData                  : LongWord;
    ImageBase                   : LongWord;
    SectionAlignment            : LongWord;
    FileAlignment               : LongWord;
    MajorOperatingSystemVersion : WORD;
    MinorOperatingSystemVersion : WORD;
    MajorImageVersion           : WORD;
    MinorImageVersion           : WORD;
    MajorSubsystemVersion       : WORD;
    MinorSubsystemVersion       : WORD;
    Win32VersionValue           : LongWord;
    SizeOfImage                 : LongWord;
    SizeOfHeaders               : LongWord;
    CheckSum                    : LongWord;
    Subsystem                   : WORD;
    DllCharacteristics          : WORD;
    SizeOfStackReserve          : LongWord;
    SizeOfStackCommit           : LongWord;
    SizeOfHeapReserve           : LongWord;
    SizeOfHeapCommit            : LongWord;
    LoaderFlags                 : LongWord;
    NumberOfRvaAndSizes         : LongWord;
    DataDirectory               : PACKED ARRAY[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] OF TImageDataDirectory;
  END;

  PImageNtHeaders               =^TImageNtHeaders;
  TImageNtHeaders               = PACKED RECORD
    Signature                   : LongWord;
    FileHeader                  : TImageFileHeader;
    OptionalHeader              : TImageOptionalHeader;
  END;

  PImageExportDirectory         =^TImageExportDirectory;
  TImageExportDirectory         = PACKED RECORD
    Characteristics             : LongWord;
    TimeDateStamp               : LongWord;
    MajorVersion                : WORD;
    MinorVersion                : WORD;
    Name                        : LongWord;
    Base                        : LongWord;
    NumberOfFunctions           : LongWord;
    NumberOfNames               : LongWord;
    AddressOfFunctions          :^PLongWord;
    AddressOfNames              :^PLongWord;
    AddressOfNameOrdinals       :^PWord;
  END;

VAR
  CRC32TAB : ARRAY[0..255] OF DWORD;

PROCEDURE BuildCRC32Table; ASSEMBLER;
ASM
    mov       ebx, 0EDB88320h
    lea       edi, crc32tab
    xor       ecx, ecx
  @loc1:
    mov       eax, ecx
    mov       edx, 8
  @loc2:
    test eax, 1
    jz  @loc3
    shr       eax, 1
    xor       eax, ebx
    jmp       @loc4
  @loc3:
    shr       eax, 1
  @loc4:
    dec       edx
    jnz       @loc2
    stosd
    inc       ecx
    cmp       ecx, 256
    jb  @loc1
END;

FUNCTION CalculateCRC32(VAR Buffer;CONST Size:DWORD) : DWORD; ASSEMBLER;
ASM
    push esi
    push edi
    push ebx
    mov edi,edx
    mov esi,eax
    xor ebx,ebx
    mov eax,$ffffffff
    mov ecx,edi
    shr ecx,2
    jecxz @Rest
  @Loop:
    mov edx,[esi]
    mov bl,al
    xor bl,dl
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    mov bl,al
    xor bl,dh
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    shr edx,16
    mov bl,al
    xor bl,dl
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    mov bl,al
    xor bl,dh
    shr eax,8
    xor eax,dword ptr [CRC32tab+ebx*4]
    add esi,4
    loop @Loop
  @Rest:
    mov ecx,edi
    and ecx,3
    jecxz @End
  @Loop_Rest:
    mov bl,al
    xor bl,[esi]
    shr eax,8
    inc esi
    xor eax,dword ptr [CRC32tab+ebx*4]
    loop @Loop_Rest
@End:
    xor eax,$ffffffff
    pop ebx
    pop edi
    pop esi
END;

function StrLen(const Str: PChar): Cardinal; assembler;
asm
  {$IFDEF F_P}
        MOV     EAX, [Str]
  {$ENDIF F_P}
        XCHG    EAX, EDI
        XCHG    EDX, EAX
        OR      ECX, -1
        XOR     EAX, EAX
        CMP     EAX, EDI
        JE      @@exit0
        REPNE   SCASB
        DEC     EAX
        DEC     EAX
        SUB     EAX,ECX
@@exit0:
        MOV     EDI,EDX
end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};

//------------------------------------------------------------------------------
FUNCTION GetProcAddress(Module:Cardinal;ProcessCRC:DWORD) : Pointer;
VAR
  ExportName           : pChar;
  Address              : Cardinal;
  J                    : Cardinal;
  ImageDosHeader       : PImageDosHeader;
  ImageNTHeaders       : PImageNTHeaders;
  ImageExportDirectory : PImageExportDirectory;
BEGIN
  ImageDosHeader:=Pointer(Module);
  ImageNTHeaders:=Pointer(Module+ImageDosHeader._lfanew);
  ImageExportDirectory:=Pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress+Module);
  J:=0;
  Address:=0;
  REPEAT
    ExportName:=Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames)+Module+J*4)^)+Module);
    IF CalculateCRC32(ExportName^,StrLen(ExportName))=ProcessCRC THEN
      Address:=Cardinal(Pointer(Word(Pointer(J SHL 1+Cardinal(
               ImageExportDirectory.AddressOfNameOrdinals)+Module)^) AND
               $0000FFFF SHL 2+Cardinal(ImageExportDirectory.AddressOfFunctions)
               +Module)^)+Module;
    Inc(J);
  UNTIL (Address<>0)OR(J=ImageExportDirectory.NumberOfNames);
  Result:=Pointer(Address);
END;

FUNCTION MyLoadLibraryA:Pointer;
const
  MyLoadLibraryA=$3FC1BD8D;//LoadLibraryA的Crc32数值
asm
  mov eax,fs:$30
       mov eax,[eax + $0c]
       mov esi,[eax + $1c]
       lodsd
       mov eax,[eax+$08]
  mov edx,MyLoadLibraryA
  call GetProcAddress
end;

procedure ExitProcess; external kernel32 name 'ExitProcess'

procedure _InitExe;
asm
end;

procedure _HandleFinally;
asm
end;

procedure _halt0;                 //由于Win2k下无导入表的程序无法运行
asm
  PUSH ExitCode
  CALL ExitProcess               //这里保留了一个ExitProcess
end;                        //您可以删除这个地方的代码~编译后使用Upack加壳也可以Run

end.


主程序代码
{
  Anskya NoImport APISearchEngine Demo By Anskya
  Email:Anskya@Gmail.com
  Web:[url]Www.Anskya.Net[/url]
  QQ:115447
}
PROGRAM Project;

CONST
  MessageBoxA = $572D5D8E; //的Crc32数值

VAR
  MessageBox : FUNCTION(hWnd:longWord;lpText,lpCaption:PChar;uType:longWord) : Integer; STDCALL;
  LoadLibrary:function(lpLibFileName: PChar): longWord; stdcall;

BEGIN
  BuildCRC32Table;
  @LoadLibrary:=MyLoadLibraryA;
  MessageBox:=GetProcAddress(LoadLibrary('user32.dll'),MessageBoxA);
  Messagebox(0,'无导入表EXE[Win2k下PE没有导入表无法运行所以保留ExitProcess函数]','By Anskya!',0);
END.


到此程序基本上完成了~测试了Win2k,XP,2k3都可以正常运行
(实在找不到Win9x测试了~~不好意思)

附件有完整的程序代码和~编译说明以及演示图片
//The_end~~

程序编译后4k压缩一下1.13k(for Delphi 6)

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

收藏
点赞7
打赏
分享
最新回复 (2)
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
薄荷 2006-2-6 17:04
2
0
强啊,居然k32都不链接...
雪    币: 217
活跃值: (99)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
dwing 1 2006-2-6 22:30
3
0
crinkler壳就是用无导入表+CRC32值导入函数名.
游客
登录 | 注册 方可回帖
返回