Tradutor

Mostrando postagens com marcador c. Mostrar todas as postagens
Mostrando postagens com marcador c. Mostrar todas as postagens

quinta-feira, 18 de setembro de 2014

A book to study C, or NOT!

As promised I'm back to talk about the Programming in C (4th Edition) book. Well, I'll paste my amazon's review, to be short.

"When I started this reading soon I became absorbed by the formation of the writer's words. Unfortunately that feeling did not persist throughout the work. I still read until functions chapter, but the book is not renewed in style. There's much prolonged talk for bit subjects... For me a boring approach. The author uses many almost useless, in day-to-day, mathematical examples. A beginner will give up of C language or seek another book that is more aimed.
I still prefer the Expert C Programming: Deep C Secrets by Peter van der Linden that invites you to understand language with real facts about C, and The C Programming Language by the creators of the Language that despite being old and summarized, informs the reader about the language accurately."

It's yours choice.

Cheers.

terça-feira, 26 de agosto de 2014

A book to study C

I received my copy of Programming in C (4th Edition) (Developer's Library) from Pearson today.



The book is dense with 548 pages and, at first glance, a great resource for beginners and advanced C coders. I'll let you know more about my reading. Just wait.

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)

sexta-feira, 12 de abril de 2013

SLAE - 4th Assignment - Encoding and Decoding Gollums

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
   SLAE - 1st Assignment - Shell Bind TCP
   Hacking do Dia - Shell Bind TCP Random Port
   SLAE - 2nd Assignment - Shell Reverse TCP
   O menor do mundo? Yeah? So Beat the Bits!  
   SLAE - 3rd Assignment - Caçando Ovos?
*/

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/4th.assignment

Shell-Storm:


Olá pessoal!

Com este post prosseguiremos as missões do curso SLAE.

Gostaria de ressaltar que, no último vídeo do treinamento, Vivek determinou que o aluno que reduzir os shellcodes e/ou tê-los aceitos em repositórios receberá pontuação extra para a certificação.

Eu venho tentando, além de reduzir o tamanho, criar versões dos shellcodes com propriedades singulares; exemplos são o Shell Bind TCP (GetPC) e o Shell Bind TCP (com REUSE), este que acabou originando o Tiny Shell Bind TCP Random Port (57 bytes).

Porém, nesta missão, Vivek foi claro quanto à originalidade do shellcode. Ou seja, neste payload, o que importará realmente é o engendramento de um método de inserção divergente do que ele demonstrou em aula.


Insertion Decoder

Um Shellcode Insertion Decoder realiza o realinhamento de um shellcode devidamente encodado com inserção de garbage bytes. Sua utilização é voltada para a tentativa de bypass de ferramentas que detectam injeção de shellcode.

Na respectiva aula ele cria um decoder sequencial que substitui gargabe bytes ao copiar o byte verdadeiro na sequencia correta. Inicialmente, pensei em apenas remodelar o padrão de inserção da versão dele para, em vez de fazer o decoding de apenas um garbage byte (x_x_x_), fazer de dois (x__x__x__). Mas nem cheguei a amadurecer a ideia, desisti ao realizar que tal modificação não seria nem original muito menos útil num caso concreto.


Cachola para que te quero?!

Após alguns dias da resignação à ideia anterior, eis que me acendeu uma luz acima da cabeça! Por que não criar um decoder com análise para qualquer padrão? Deixando nas mãos do usuário a possibilidade de inserir o lixo no shellcode da forma que melhor lhe aprouver? Isso sim seria original!

Why not?!


Assembly e C (perfect couple)

O shellcode exigido na missão era o do execve_stack já corretamente encodado. Como um plus, reduzi o tamanho dele engendrando o Tiny Execve Sh (21 bytes), cujos código e shellcode podem ser visualizados no github.

Inseri o garbage byte em posições aleatórias dentro desse shellcode e, após um estudo algorítmico concretizei o decoder em asm como podem ver abaixo.



O que ele faz é percorrer a área da memória na qual o shellcode está inserido, comparando o byte lido com o byte lixo, e reordenando-os quando resolvidas as condições. Dúvidas? Leia o código fonte, está bem comentado.


Pronto para publicar!

Logo após ter feito push no git, já me preparando para escrever este post, tweetei para o Vivek!

Wow!

Ele não só apenas gostou do decoder, pediu também que eu fizesse um extra:

Well, lets go!


An Unexpected Journey

Nunca tinha programado em python, a não ser modificado algum pedaço de código ou lido rapidamente. Entretanto, sempre ouvi falar que é uma ótima linguagem para aprender, assim como para diversos outros fins.

The campaign begins...

Um obstáculo:
- Uso de argumentos para receber o shellcode e demais parâmetros.

Não estava conseguindo preencher a variável shellcode da forma correta... descobri, após garimpar os pergaminhos do python, que era por conta da necessidade do uso de encoding. Mas mesmo usando o encode(), este baggins não conseguia fazer com que a string shellcode ficasse como bytearray. Até que o wizard sigsegv empunhou o seu cajado proclamando: error handler surrogateescape. Valeu, sig!

Outro obstáculo:
- Bytes, Strings...

Continuando na campanha, precisei comparar, em alguns casos, string a byte... como era meu primeiro contato com a linguagem quase fui cozido por alguns trolls até me lembrar de umas dicas dadas pelo Pedro Fausto, um Dwarf que guerreia distante do Reino perdido de Erebor. hex() e int() foram suas armas. Valeu, Pedro!


Epílogo


Uso:
# ./insertion_encoder.py -h
# ./insertion_encoder.py -g f3 -p xxbbxb -e f1f1 -s $'\x31...\x80'

@geyslangb @felipensp @pedrofaustojr and the code really looked 133t on my iPhone screen ... ;)

Not at all, Vivek.


Galadriel: Why the Halfling?

Gandalf: Saruman believes it is only great power that can hold evil in check, but that is not what I have found. I found it is the small everyday deeds of ordinary folk that keep the darkness at bay... small acts of kindness and love. Why Bilbo Baggins? That's because I am afraid and it gives me courage.


#\o/

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, 27 de março de 2013

SLAE - 3rd Assignment - Caçando Ovos?

/*
   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:


Terceira Missão (Caça aos bits)

Olá! Continuemos com a terceira missão do SLAE.

Nesta empreitada, vamos caçar ovos... não, cooler, não vai ser um elefante engolido por uma jiboia, muito menos achocolatados. :D []'s

3rd assignment:
- estudar sobre shellcodes egg hunter
- criar uma demonstração funcional de um
- essa demonstração deve ser configurável para diferentes payloads

O resultado da pesquisa sobre egg hunting foi escasso. Encontrei apenas um shellcode do tipo para Linux. Fiz alguns testes mas logo vi que ele não era funcional, pois tentava ler toda a VAS do usuário (0x0 a 0xffffffff) resultando em SIGSEGV quando batia na porta de uma área protegida. A ideia principal que era percorrer a memória num looping eu já tinha abstraído, entretanto, como fazer isso de forma segura?

Perseverei no google e mudei a pesquisa de linux egg hunter para "windows" egg hunter. Bingo! Encontrei um paper fantástico escrito por skape em 2004, Safely Searching Process Virtual Address Space. Esse artigo, felizmente, além de abordar o windows, descreve duas formas no linux de se verificar se o usuário (aplicativo) tem permissões de leitura a dado offset. A primeira, a qual optei, é através da syscall access e a segunda é através da sigaction. Não sei por que tive que colocar "windows" na pesquisa; o que importa é que encontrei o que precisava.

Ok! Mas você não disse para que serve mesmo um egg hunter!

Perdão, vamos lá! Em alguns casos de exploração encontramos pequenas janelas de buffer disponíveis para injeção de código, nas quais um shellcode de tamanho maior seria inútil. Nessa mesma janela poderíamos injetar um Egg Hunter, obviamente menor que o shellcode principal, para vasculharmos a memória em busca do nosso shellcode (egg). Em suma, o Egg Hunter é um tipo de stager shellcode.

Mas como nosso shellcode principal (egg) estaria na memória?

Isso vai depender da aplicação explorada, das formas de entrada de dados etc. Como exemplos poderíamos ter um browser que carregaria o egg de um html, um mp3 player que carregaria o egg de um mp3 ou m3u, um programa genérico que leria todo o conteúdo de um arquivo de configuração com o egg implantado.

Compreendendo as explicações do skape a respeito do seu Egg Hunter (access), complementei-o com controle de lixo dos registers e limpando o DF (Direction Flag) para se garantir o incremento de EDI no uso da SCASD.

O resultado foi este.


Fluxograma gerado no r2 (radare).


Para facilitar a exemplificação, o Egg já se encontra no próprio Egg Hunter Launcher.


A configuração da assinatura do Egg é feita mudando-se os seus oito primeiros bytes; já no Egg Hunter, do 25° ao 28° byte. Optei por usar opcodes que não interferem no fluxo (nops, pushs), mesmo eles sendo ignorados no último JMP.

É importante ressaltar que os quatro últimos bytes, no caso do Egg, são uma mera repetição dos quatro primeiros; tal medida visa a fortificar a identificação evitando-se falso-positivos e faz parte do algoritmo de identificação do Egg Hunter, assim, se desejarem modificar os quatro primeiros bytes, repliquem a mesma mudança nos últimos.


Testando

# gcc -m32 -fno-stack-protector -z execstack egg_hunter_shellcode.c -o egg_hunter_shellcode
# ./egg_hunter_shellcode
Egg Mark



Missão Cumprida

Bom pessoal, é isso. Boa caça aos ovos!


Mais Informações

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