No Habré existem muitos artigos sobre o tema ELF - que só as pessoas não fazem com eles - explicam seus dispositivos, montam-nos manual e automaticamente, editam, criptografam e muito mais. Eu, por sua vez, gostaria de compartilhar um caso interessante, na minha opinião, que me apresentou de uma vez a muitos aspectos da programação de baixo nível na prática:
compilação de programas,
uma espécie de engenharia reversa e portabilidade de bibliotecas de tempo de execução,
arquivos executáveis do dispositivo Windows e Linux,
montar e editar esses arquivos manualmente
Esses e alguns outros aspectos, bem como muitos movimentos não padrão, serão abordados ao portar o compilador . Ao mesmo tempo, a parte do trabalho relacionada aos arquivos executáveis (e ELFs em particular) me pareceu a mais interessante, por isso se tornará o fio condutor do artigo.
Este artigo não é um tutorial completo, mas pode ser do interesse de leitores interessados em uma ou mais das áreas acima e prontos para descobrir (ou aperfeiçoar) abordagens não padronizadas para resolver problemas nessas áreas.
No decorrer do trabalho, precisamos:
compilador gcc, vinculador ld, depurador gdb
utilitários do binutils (readelf, strip, hexdump)
compreensão básica de executáveis portáteis (PE) e dispositivos ELF
Pascal
, Bero TinyPascal Compiler (, BTPC) 2016 - Pascal, (Benjamin Rosseaux). Pascal' (Delphi 7-XE7 FreePascal >= 3) Windows x32. , .
github . , , :
BTPC ( btpc.exe) self-contained - - ~3 . . - , , , . , , .
Pascal', , - -, , -, . , . " ".
Seg fault
BTPC - PE (Portable Executable, Mircosoft), .. "ELF Windows". , ELF, , - .
, PE 2
Portable Executable (PE) — , , Microsoft Windows. PE , , PE- . , API- ..
PE COFF Unix. «» PE — ELF ( Linux Unix) Mach-O ( Mac OS X).
, , Pascal, , , -:
-
- "-"
- "" PE-
PE- , "" .
- . , (Runtime Library, RTL).
Runtime Library 2
RTL- - CRT C/C++. , , , .. , , .
- pre-start post-exit "", main' . , , main, ( ), main .
PE- RTL 9 :
RTLHalt —
RTLWriteChar — char’ stdout
RTLWriteInteger — integer' stdout
RTLWriteLn — linebreak' stdout
RTLReadChar — EAX STDIN
RTLReadInteger — EAX integer' STDOUT
RTLReadLn — STDIN EOF ( )
RTLEOF — EAX EOF. 0 — .
RTLEOLN — 1 DL, - \n, 0 —
- , :
.ENTRYPOINT
JMP StubEntryPoint #
RTLHalt:
... # RTLHalt
RTLWriteChar:
...
... # RTL
RTLFunctionTable: #
DD OFFSET RTLHalt
DD OFFSET RTLWriteChar
DD OFFSET RTLWriteInteger
...
StubEntryPoint:
INVOKE HeapAlloc ... #
MOV ESI, OFFSET RTLFunctionTable #
ProgramEntryPoint:
, RTL, :
StubEntryPoint
ESI
… , ProgramEntryPoint !
, , BTPC ProgramEntryPoint.
, 2 - .
: btpc.pas rtl.asm . btpc.pas blob, :
{ }
procedure EmitStubCode;
begin
OutputCodeDataSize := 0;
OutputCodeString(#77#90#82#195#66#101#82#111#94#102#114#0#80#69#0#0#76#1#1#0#0#0#0#0#...
OutputCodeString(#0#0#0#0#0#0#0#0#0#0#0#0#0#0#16#0#0#0#16#0#0#143##16#0#0#0#0...
OutputCodeString(#0#0#0#0#0#0#0#0#0#0#255#255#255#255#40#16#0#0#53#0#0#0...
OutputCodeString(#101#110#106#97#109#105#110#32#39#66#101#82#111#...
OutputCodeDataSize := 1423;
end;
{ }
- Pascal- , rtl.asm.
, PE .
, :
RTL ( ) , PE- ( , - )
PE- - ( , 255 )
, - NOP
- ( )
BTPC :
stdin
, -
, -
( - PE-)
, "" , ,
PE32
: - . , , . , , ( ). : , , , .
: 4 , 156 . 100 . , 4 , 100 .
, RTL, Excagena, - PE-. / , Excagena , .
-, PE. , , ProgramEntryPoint - , .
- - PE-. - :
(OptionalHeader.SizeOfCode)
(SectionTable.VirtualSize)
(OptionalHeader.SizeOfImage),
- , , :
{ }
CodeSize := OutputCodeGetInt32($29) + (OutputCodeDataSize - CodeStart);
OutputCodePutInt32($29, CodeSize);
{ }
SectionAlignment := OutputCodeGetInt32($45);
{ }
SectionVirtualSize := CodeSize;
Value := CodeSize mod SectionAlignment;
SectionVirtualSize := SectionVirtualSize + (SectionAlignment - Value);
OutputCodePutInt32($10d, SectionVirtualSize);
{ }
OutputCodePutInt32($5d, SectionVirtualSize + OutputCodeGetInt32($39));
, $29, $45, $115 …
, - Linux x64 . , :
RTL Linux x64
ELF-
ELF-,
- 3
- "" WinApi .
, , . . - , Win32 API, :
ReadCharBuffer: DB 0x90
ReadCharBytesRead: DB 0x90,0x8D,0x40,0x00
ReadCharEx:
PUSHAD
INVOKE ReadFile, DWORD PTR StdHandleInput, OFFSET ReadCharBuffer, 1, OFFSET ReadCharBytesRead, BYTE 0
TEST EAX, EAX
SETZ AL
OR BYTE PTR IsEOF, AL
CMP DWORD PTR [ReadCharBytesRead], 0
SETZ AL
OR BYTE PTR IsEOF, AL
POPAD
RET
, , . , - " " - ReadCharBuffer ReadCharBytesRead. , …
- , , . ( - , - , - ), .
, 64- syscall ( ). , RDI, RSI, RDX . RAX.
x64 pusha, pushad, popa, popad. pushall, popall, . - bss.
, - data.
:
.section .data # -
ReadCharBuffer:
.byte 0x3c
.section .text # -
ReadCharEx:
PUSHALL # - bss
XORQ %RAX, %RAX # syscall #0: read(int fd, void *buf, size_t count)
XORQ %RDI, %RDI # fd : 0 == stdin
MOVQ $ReadCharBuffer, %RSI # buf : ReadCharBuffer
MOVQ $1, %RDX # count : 1 byte
SYSCALL
CMPQ $0, %RAX
SETZ %BL
ORB %BL, (IsEOF)
POPALL
RET
, - . , - , Guard Page.
, , Linux , , , Segmentation fault. , , . , . Guard Page . , , , , .
:
$ gcc -c rtl64.s $ ld rtl64.o -g --output rtl64
ELF - BTPC . .
, BTPC - EmitByte:
procedure OCPopESI;
begin
EmitByte($5e);
LastOutputCodeValue := locPopESI;
end;
procedure OCMovzxEAXAL;
begin
EmitByte($0f);
EmitByte($b6);
EmitByte($c0);
LastOutputCodeValue := locMovzxEAXAL;
end;
, , x64. - ,
1 -
MOV R10, [R12 + R13]
, (, ) "Intel 64 and IA-32 Architectures Developer’s Manual"
. - – R10 "R?".
MOV i8086+. "r/m →R?". , 0x8B.
R10 4 , 3 Rn ModR/M.
"" " REX".
R10 R REX Rn ModR/M.
ModR/M 32- , R/M ModR/M = 100, Mod = 00. SIB.
R12, 1, X REX SIB.
SIB , [#Base + #Index2^(Scale)]. Base R12. 3 = 100 3 Base SIB.
(Index) SIB 3 R13 = 101.
(1 + 1 ), Scale SIB = 00 2^(Scale) = 2^0 = 1.
R13 B REX. SIB.
REX: "0100" + "W:1" + "R:1" + "X:1" + "B:1" = 01001111, hex 0x4F. REX .
ModR/M: "Mod:00" + "Rn:010" + "R/M:100" = 00010100, 0x14.
SIB: "Scale:00" + "Index:101" + "Base:100" = 00101100, 0x2C.
MOV R10, (R12 + R13)
0x4F 0x8B 0x14 0x2C
.
, PE- , , . , BTPC 70 .
: , , .
.
ELF'
, , BTPC - , , "" , , , . .
, , - ELF' - ELF'.
, . 2 :
ELF64 PE32
PE32 ,
readelf' rtl64:
$ readelf --section-headers rtl64 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 00000000004000b0 000000b0 0000000000000317 0000000000000000 AX 0 0 1 [ 2] .data PROGBITS 00000000006003c7 000003c7 00000000000000bf 0000000000000000 WA 0 0 1 [ 3] .symtab SYMTAB 0000000000000000 00000488 0000000000000408 0000000000000018 4 39 8 [ 4] .strtab STRTAB 0000000000000000 00000890 0000000000000248 0000000000000000 0 0 1 [ 5] .shstrtab STRTAB 0000000000000000 00000ad8 0000000000000027 0000000000000000 0 0 1
- ELF' 6 !:
( )
— text
— data
— shstrtab (Section header string table)
symtab
strtab
, . - . ProgramEntryPoint , " ".
? . , 4 , , ELF'.
ld (--nostdlib, --strip-all):
$ ld rtl64.o -g --output rtl64-min -nostdlib --strip-all
ELF 2 - 1.4 . :
$ readelf --section-headers rtl64-min Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 00000000004000b0 000000b0 0000000000000317 0000000000000000 AX 0 0 1 [ 2] .data PROGBITS 00000000006003c7 000003c7 00000000000000bf 0000000000000000 WA 0 0 1 [ 3] .shstrtab STRTAB 0000000000000000 00000486 0000000000000017 0000000000000000 0 0 1
2 . , shstrtab . , binutils strip, . shstrtab :
$ strip -R shstrtab rtl64-min $ readelf --section-headers rtl64-min Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 00000000004000b0 000000b0 0000000000000317 0000000000000000 AX 0 0 1 [ 2] .data PROGBITS 00000000006003c7 000003c7 00000000000000bf 0000000000000000 WA 0 0 1 [ 3] .shstrtab STRTAB 0000000000000000 00000486 0000000000000017 0000000000000000 0 0 1
Shstrtab . , :
$ hexdump -C rtl64-min 00000480 40 00 00 00 00 00 00 2e 73 68 73 74 72 74 61 62 |@.......shstrtab| 00000490 00 2e 74 65 78 74 00 2e 64 61 74 61 00 00 00 00 |..text..data....| 000004a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
… ! . , .shstrtab . , , - , .
, ( ). . , .
, :
ENTRY(_start) /* */
SECTIONS
{
. = 0x4000b0; /* */
.data : { *(.data) }
.bss : { *(.bss) *(COMMON) }
. = 0x6000d3; /* */
.text : { *(.text) } /* */
}
ld - . , , ld --verbose
- , , . .
. :
$ ld rtl64.o -g --output rtl64-custom-ld -T linkerScript.ld -nostdlib --strip-all $ readelf --section-headers rtl64-custom-ld Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .data PROGBITS 00000000006000b0 000000b0 00000000000000bf 0000000000000000 WA 0 0 1 [ 2] .text PROGBITS 0000000000a00170 00000170 0000000000000317 0000000000000000 AX 0 0 1 [ 3] .shstrtab STRTAB 0000000000000000 00000487 0000000000000017 0000000000000000 0 0 1
, ! text, , data.
, , . gdb, - , , , , .
, - gdb . , …
, , , symtab strtab , . , - - ( , .. )
, . , , . stackoverflow , , . :
, , :
|
|
Elf__hdr.e__shoff |
0x28 |
Text_phdr.p_filesz |
sizeof(Elf__hdr) + sizeof(p_hdr) + 0x20 |
Text_phdr.p_memsz |
sizeof(Elf_hdr) + sizeof(p_hdr) + 0x28 |
Text_shdr.sh_size |
Elf_hdr.e_shoff + sizeof(injection) + 2*sizeof(s_hdr) + 0x20 |
Shstrtab_shdr.sh_offs |
Elf_hdr.e_shoff + sizeof(injection) + 3*sizeof(s_hdr) + 0x18 |
Symtab_shdr.sh_offs |
Elf_hdr.e_shoff + sizeof(injection) + 4*sizeof(s_hdr) + 0x18 |
Strtab_shdr.sh_offs |
Elf_hdr.e_shoff + sizeof(injection) + 5*sizeof(s_hdr) + 0x18 |
: sizeof(injection). .
, , ELF', - , , , .
, , "", "" "", .
. , ELF, , . - . , RTL.
.
- bootstrapping
#
$ btpc.exe < btpc64.pas > btpcCrossWin.exe
# Linux–
$ btpcCrossWin.exe < btpc64.pas > btpc64Linux
# , «» , Linux
$ btpc64Linux < btpc64.pas > btpc64Check
Pascal, Linux x64, .
, , . :
BTPC
, Pascal Windows
(RTL)
RTL ELF
, , , . , - . , ,
P.S.
, , . , , 9 . , . . , , , . , ( " ?") . , , , "", , ("" ).
Além disso, não posso deixar de agradecer ao meu orientador científico, Alexander Konovalov.
Conforme observado no início, este artigo não teve como objetivo explicar o dispositivo ELF, ensinar como executar truques malucos com ele ou portar programas. Mas, usando um exemplo real, ela, espero, mostrou como podem ser soluções atípicas de problemas comuns, que coisas interessantes podem ser observadas no curso de resolvê-los e quais descobertas fazer por si mesmo ... E talvez ela encoraje alguém a dar o primeiro passo em direção ao próximo desafio desconhecido, mas emocionante ...