Delphi是最适合编写ShellCode的工具

本文是写给经常写ShellCode或者给PE文件添加代码的人的.
题目这样写有点不精确.不过Borland的编译器确实是算得上最适合编写ShellCode工具.
编写ShellCode的朋友们知道.字符串常量应该是处理起来非常麻烦.
例如:
调用GetProcAddress函数吧.参数有一个是字符串.很多编译器字符串常量是在数据段中.只读的属性.
那么编写ShellCode的时候GetProcAddress(hm,”Function”);就不行了.因为”Function”实际上是在数据段中,传递的只是指针.如果是DLL的话可能还要重定位.
所以见到有很多人编写ShellCode自己实现GetProcAddress.用的是函数名的CRC.

Borland的编译器有一个特点.正好可以利用.它编译的时候字符串常量不是放在数据段里面.而是放到所在函数的后面.以Delphi为例.

Function A():PChar;
begin
Result := 'abc';
end;

实际上在编译后变成
00453BF0 B8F83B4500       mov eax,$00453bf8 //把字符串abc的地址付给eax,eax是返回值
00453BF5 C3               ret               //返回
00453BF6 0000                               //实际上是没用.作用是对齐代码
00453BF8 61626300         字符串abc

但是现在你还不能把这份代码作为ShellCode拷贝到任意地方使用.应为 mov eax,$00453bf8 的参数是绝对地址.我们怎么把它转化成相对的呢?
这需要一个技巧.

Function FixPChar(Value : PChar):PChar;forward;

Function A():PChar;
begin
Result := FixPChar('abc');
end;

Function FixPChar(Value : PChar):PChar; register;//register;加不加都无所谓.默认就是register;
asm
call   @next                //
@next:    pop    ecx                  //ecx里面装的就是@next的相对地址,当前执行时
mov    ebx, offset @next    //ebx里面装的就是@next的绝对地址,编译时生成的
add    eax, ecx             //返回Value的地址+(相对地址-绝对地址)
sub    eax, ebx
end;

procedure _end();
begin

end;

现在我们可以把A函数和FixChar拷贝到任何地方使用了.如何拷贝呢?

我们只要拷贝A函数的地址为起始,_end函数的地址为终点的数据块就可以用了.

例如

Type
TFunc = Function():PChar;
var
M : String;
begin
//
SetLength(M, Integer(@_end) - Integer(@A)); //为M分配长度
CopyMemory(PChar(M), @A, Length(M));        //把A函数和_end之间的代码拷贝到M
ShowMessage(TFunc(PChar(M))());             //把M强制转换为TFunc类型执行
end;

网上流传很广的一个API搜索就是用CRC来避开字符串的.如果用这样的技术完全可以省略计算CRC的步骤.

原函数如下:

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 FixPChar(Value: PAnsiChar): PChar; forward;

function strcmp(p1, p2: PAnsiChar): boolean; forward;

function GetProcAddress(Module: Cardinal; ProcessName: PAnsiChar): Pointer;
var
ExportName        : pAnsiChar;
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 strcmp(ExportName, ProcessName) 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 strcmp(p1, p2: PAnsiChar): boolean;
begin
Result := False;
while (p1^ = p2^) do
begin
if (P1^ = #0) or (P2^ = #0) then
begin
Result := True;
Exit;
end;

Inc(P1);
Inc(P2);
end;

end;

function FixPChar(Value: PAnsiChar): PAnsiChar; register; //register;加不加都无所谓.默认就是register;
asm
call   @next                //
@next:    pop    ecx                  //ecx里面装的就是@next的相对地址,当前执行时
mov    ebx, offset @next    //ebx里面装的就是@next的绝对地址,编译时生成的
add    eax, ecx             //返回Value的地址+(相对地址-绝对地址)
sub    eax, ebx
end;

procedure _end();
begin

end;

调用的时候直接  GetProcAddress(hm,FixPChar(‘函数名’));就可以了.免了事先计算函数名CRC的麻烦.

此条目发表在未分类分类目录。将固定链接加入收藏夹。