前提假设您已经有了一定的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:<a href="http://www.Anskya.Net" target=_blank>www.Anskya.Net</a>
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 starsoul!',0);
END.
到此程序基本上完成了~测试了Win2k,XP,2k3都可以正常运行
(实在找不到Win9x测试了~~不好意思)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!