老实说,写shellcode还是Delphi最舒服。比如说,写一个SDK窗口显示:
procedure _Start;
var
pSDKForms: array[0..5] of LPSDKForm;
begin
for i := Low(pSDKForms) to High(pSDKForms) do
begin
pSDKForms[i] := LPSDKForm(fnLocalAlloc(LMEM_ZEROINIT, sizeof(TSDKForm)));
if pSDKForms[i] = nil then Exit;
fnRtlMoveMemory(pSDKForms[i], @SDKForm, sizeof(TSDKForm));
pSDKForms[i]^.InitAPI;
pSDKForms[i]^.CreateWindow;
end;
MessageLoop;
end;
{ TSDKForm }
procedure TSDKForm.CreateWindow;
var
Inst: HMODULE;
tmpWndClass: WNDCLASSA;
p: Pointer;
begin
Inst := fnGetModuleHandleA(nil);
if Inst = 0 then Exit;
if not fnGetClassInfoA(Inst, FixPAnsiChar('TFormx'), tmpWndClass) then
begin
with WinClass do
begin
style := CS_CLASSDC or CS_PARENTDC;
lpfnWndProc := FixPAnsiChar(@WindowProc);
cbClsExtra := 0;
cbWndExtra := sizeof(Pointer);
hInstance := Inst;
hIcon := fnLoadIconA(Inst, FixPAnsiChar('MAINICON'));
hCursor := fnLoadCursorA(0, (MakeIntResourceA(32512)));
lpszMenuName := nil;
hbrBackground := COLOR_BTNFACE + 1;
lpszClassname := FixPAnsiChar('TFormx');
end; { with }
fnRegisterClassA(WinClass);
end;
.....
end;
function TSDKForm.InitAPI: Boolean;
begin
...
{$IFDEF WIN64}
@fnGetWindowLongA := GetWindowAPI(FixPAnsiChar('user32.dll'), FixPAnsiChar('GetWindowLongPtrA'));
{$ELSE}
@fnGetWindowLongA := GetWindowAPI(FixPAnsiChar('user32.dll'), FixPAnsiChar('GetWindowLongA'));
{$ENDIF}
...
end;
procedure _End;
begin
//空函数,只是用来计算Shellcode的体积
end; 就是说,跟平时写程序没什么区别的。需要保存的时候:
procedure TForm1.Button1Click(Sender: TObject);
var
ShellCodeSize: Integer;
ShellCodeAddr: Pointer;
StartRunAddr: procedure;
MS: TMemoryStream;
begin
ShellCodeSize := PAnsiChar(@_End) - PAnsiChar(@_Start); //计算大小
ShellCodeAddr := VirtualAlloc(nil, ShellCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //申请运行内存
MoveMemory(ShellCodeAddr, Pointer(@_Start), ShellCodeSize);
MS := TMemoryStream.Create;
MS.Write(Pointer(@_Start)^, ShellCodeSize);
{$IFDEF WIN64}
MS.SaveToFile(ExtractFilePath(Application.ExeName) + 'ShellCode64.bin');
{$ELSE}
MS.SaveToFile(ExtractFilePath(Application.ExeName) + 'ShellCode32.bin');
{$ENDIF}
MS.Free;
@StartRunAddr := ShellCodeAddr; //计算拷贝后函数的运行地址
StartRunAddr;//直接测试shellcode
end; 当然,也可以直接这样测试:
program Test;
{$R *.res}
procedure ShellCode;
asm
.....................................
DB $00,$00,$48,$89,$C6,$48,$85,$C0,$0F,$84,$A1,$00,$00,$00,$41,$BE
DB $06,$00,$00,$00,$4C,$8D,$6D,$38,$B9,$40,$00,$00,$00,$BA,$98,$01
DB $00,$00,$48,$8B,$45,$28,$FF,$D0,$49,$89,$45,$00,$48,$85,$C0,$74
..................................
DB $8D,$75,$38,$48,$8B,$06,$48,$8B,$48,$50,$BA,$02,$00,$00,$00,$4D
DB $33,$C0,$4D,$33,$C9,$FF,$D7,$48,$83,$C6,$08,$41,$83,$ED,$01,$45
DB $85,$ED,$75,$DF,$90,$BF,$06,$00,$00,$00,$48,$8D,$75,$38,$48,$8B
..............................
end;
begin
ShellCode;
end.
总的来说,优点就是:
1、不用第三方工具。
2、不需要编译后再后期处理。所见即所得。
a、开始函数
b、其它函数
c、结束函数(空函数)
编译后直接保存a-c的内容即可,编译器的特点决定的。当然,有些地址需要运行时调整,比如说字符串:
@fnGetWindowLongA := GetWindowAPI(FixPAnsiChar('user32.dll'), FixPAnsiChar('GetWindowLongPtrA')); 不过也就是32位的需要调用FixPAnsiChar,64位可以直接写成:
@fnGetWindowLongA := GetWindowAPI('user32.dll', 'GetWindowLongPtrA');
3、32位和64位通杀(64位也可以直接內镶汇编)。
缺点:Delphi不流行了。
最后于 2023-4-16 17:31
被bestbird编辑
,原因:
上传的附件: