utiliza servidores seguridad que monitoreo herramientas linux assembly build x86 att

linux - servidores - Ensamblar binarios de 32 bits en un sistema de 64 bits(cadena de herramientas GNU)



herramientas que utiliza linux (2)

Estoy aprendiendo el ensamblaje x86 (en Ubuntu 18.04 de 64 bits) y tuve un problema similar con el mismo ejemplo (es de Programación desde cero , en el capítulo 4 [ http://savannah.nongnu.org/projects/pgubook/ ]).

Después de hurgar, encontré las siguientes dos líneas ensambladas y unidas:

as power.s -o power.o --32 ld power.o -o power -m elf_i386

Estos le dicen a la computadora que solo está trabajando en 32 bits (a pesar de la arquitectura de 64 bits).

Si desea utilizar la gdb debugging , use la línea de ensamblador:

as --gstabs power.s -o power.o --32.

El .code32 parece ser innecesario.

No lo he intentado a tu manera, pero el ensamblador de GNU (gas) también parece estar bien con:
inicio .globl
# (es decir, no ''a'' en global).

Además, sugeriría que probablemente desee mantener los comentarios del código original, ya que parece que se recomienda hacer comentarios profundos en el ensamblaje. (Incluso si usted es el único que mira el código, le resultará más fácil darse cuenta de lo que estaba haciendo si lo mira meses o años después).

Sin embargo, sería bueno saber cómo alterar esto para usar los registros RSP 64-bit R*X y RBP .

Escribo el código de ensamblaje que se puede compilar:

as power.s -o power.o

hay un problema cuando enlace el archivo de objeto power.o:

ld power.o -o power

Para ejecutar en el sistema operativo de 64 bits (Ubuntu 14.04), agregué .code32 al comienzo del archivo power.s , sin embargo, todavía recibo un error:

Falla de segmentación (núcleo descargado)

power.s :

.code32 .section .data .section .text .global _start _start: pushl $3 pushl $2 call power addl $8, %esp pushl %eax pushl $2 pushl $5 call power addl $8, %esp popl %ebx addl %eax, %ebx movl $1, %eax int $0x80 .type power, @function power: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %ebx movl 12(%ebp), %ecx movl %ebx, -4(%ebp) power_loop_start: cmpl $1, %ecx je end_power movl -4(%ebp), %eax imull %ebx, %eax movl %eax, -4(%ebp) decl %ecx jmp power_loop_start end_power: movl -4(%ebp), %eax movl %ebp, %esp popl %ebp ret


TL: DR : use gcc -m32 .

.code32 no cambia el formato del archivo de salida, y eso es lo que determina el modo en que se ejecutará su programa. .code32 de usted no intentar ejecutar el código de 32 bits en el modo de 64 bits. .code32 es para ensamblar código de máquina "extraño" que pueda desear como datos, o para exportar en un segmento de memoria compartida. Si eso no es lo que está haciendo, evítelo para que obtenga errores de tiempo de compilación cuando construya un .S en el modo incorrecto si tiene alguna instrucción push o pop , por ejemplo.

Sugerencia: use la extensión .S para ensamblador escrito a mano. ( gcc foo.S lo ejecutará a través del preprocesador C antes as , por lo que puede #incluir un encabezado con números de syscall, por ejemplo). Además, lo distingue de la salida del compilador .s (de gcc foo.c -O3 -S ).

Para construir binarios de 32 bits, use uno de estos comandos

gcc -g foo.S -o foo -m32 -nostdlib -static # static binary with absolutely no libraries or startup code # -nostdlib by itself makes static executables on Linux, but not OS X. gcc -g foo.S -o foo -m32 # dynamic binary including the startup boilerplate code. Use with code that defines a main() but not a _start

Documentación para nostdlib , -nostartfiles y -static .

Usando funciones libc de _start (vea el final de esta respuesta para ver un ejemplo)

Algunas funciones, como malloc(3) o funciones stdio, incluida printf(3) , dependen de algunos datos globales que se inicializan (por ejemplo, FILE *stdout y el objeto al que realmente apunta).

gcc -nostartfiles el código CRT _start repetitivo, pero aún vincula libc (dinámicamente, por defecto). En Linux, las bibliotecas compartidas pueden tener secciones de inicializador que ejecuta el vinculador dinámico cuando las carga, antes de saltar a su punto de entrada _start . Entonces gcc -nostartfiles hello.S todavía te permite llamar a printf . Para un ejecutable dinámico, el núcleo ejecuta /lib/ld-linux.so.2 en él en lugar de ejecutarlo directamente (use readelf -a para ver la cadena "ELF interpreter" en su binario). Cuando su _start finalmente se ejecuta, no todos los registros se pondrán a cero, porque el vinculador dinámico ejecutó el código en su proceso.

Sin embargo, gcc -nostartfiles -static hello.S se vinculará, pero se bloqueará en tiempo de ejecución si llama a printf o algo sin llamar a las funciones de inicio internas de glibc. (Ver el comentario de Michael Petch).

Por supuesto, puede colocar cualquier combinación de .c , .S y .o en la misma línea de comando para vincularlos a todos en un solo ejecutable. Si tiene alguna C, no olvide -Og -Wall -Wextra : no desea depurar su asm cuando el problema era algo simple en la C que lo llama de lo que el compilador podría haberle advertido.

Use -v para que gcc le muestre los comandos que ejecuta para ensamblar y vincular. Para hacerlo "manualmente" :

as foo.S -o foo.o -g --32 && # skips the preprocessor ld -o foo foo.o -m elf_i386 file foo foo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

gcc -nostdlib -m32 es más fácil de recordar y escribir que las dos opciones diferentes para as y ld ( --32 y -m elf_i386 ). Además, funciona en todas las plataformas, incluidas aquellas en las que el formato ejecutable no es ELF. ( Pero los ejemplos de Linux no funcionarán en OS X, porque los números de llamada del sistema son diferentes , o en Windows porque ni siquiera usa el int 0x80 ABI).

NASM / YASM

gcc no puede manejar la sintaxis NASM. ( -masm=intel se parece más a la sintaxis MASM que a la NASM, donde necesita un offset symbol para obtener la dirección de forma inmediata). Y, por supuesto, las directivas son diferentes (por ejemplo .globl vs global ).

Puede construir con nasm o yasm , luego vincular .o con gcc como se gcc arriba, o ld directamente.

Utilizo un script de envoltura para evitar la escritura repetitiva del mismo nombre de archivo con tres extensiones diferentes. (nasm y yasm predeterminan a file.asm -> file.o , a diferencia de GNU como salida predeterminada de a.out ). Use esto con -m32 para ensamblar y vincular ejecutables ELF de 32 bits. No todos los sistemas operativos usan ELF, por lo que este script es menos portátil que usar gcc -nostdlib -m32 para enlazar.

#!/bin/sh # usage: asm-link [-q] [-m32] foo.asm [assembler options ...] # Just use a Makefile for anything non-trivial. This script is intentionally minimal and doesn''t handle multiple source files verbose=1 # defaults fmt=-felf64 #ldopt=-melf_i386 while getopts ''m:vq'' opt; do case "$opt" in m) if [ "m$OPTARG" = "m32" ]; then fmt=-felf32 ldopt=-melf_i386 fi if [ "m$OPTARG" = "mx32" ]; then fmt=-felfx32 ldopt=-melf32_x86_64 fi # default is -m64 ;; q) verbose=0 ;; v) verbose=1 ;; esac done shift "$((OPTIND-1))" # Shift off the options and optional -- src=$1 base=${src%.*} shift [ "$verbose" = 1 ] && set -x # print commands as they''re run, like make #yasm "$fmt" -Worphan-labels -gdwarf2 "$src" "$@" && nasm "$fmt" -Worphan-labels -g -Fdwarf "$src" "$@" && ld $ldopt -o "$base" "$base.o" # yasm -gdwarf2 includes even .local labels so they show up in objdump output # nasm defaults to that behaviour of including even .local labels # nasm defaults to STABS debugging format, but -g is not the default

Prefiero yasm por algunas razones, incluyendo que por defecto hace largos nop s en lugar de rellenar con muchos nop solo byte. Eso hace que la salida de desmontaje sea desordenada, además de ser más lenta si los nops alguna vez se ejecutan. (En NASM, debe usar el paquete de macro smartalign ).

Ejemplo: un programa que utiliza funciones libc de _start

# hello32.S #include <asm/unistd_32.h> // syscall numbers. only #defines, no C declarations left after CPP to cause asm syntax errors .text #.global main # uncomment these to let this code work as _start, or as main called by glibc _start #main: #.weak _start .global _start _start: mov $__NR_gettimeofday, %eax # make a syscall that we can see in strace output so we know when we get here int $0x80 push %esp push $print_fmt call printf #xor %ebx,%ebx # _exit(0) #mov $__NR_exit_group, %eax # same as glibc''s _exit(2) wrapper #int $0x80 # won''t flush the stdio buffer movl $0, (%esp) # reuse the stack slots we set up for printf, instead of popping call exit # exit(3) does an fflush and other cleanup #add $8, %esp # pop the space reserved by the two pushes #ret # only works in main, not _start .section .rodata print_fmt: .asciz "Hello, World!/n%%esp at startup = %#lx/n"

$ gcc -m32 -nostdlib hello32.S /tmp/ccHNGx24.o: In function `_start'': (.text+0x7): undefined reference to `printf'' ... $ gcc -m32 hello32.S /tmp/ccQ4SOR8.o: In function `_start'': (.text+0x0): multiple definition of `_start'' ...

Falla en tiempo de ejecución, porque nada llama a las funciones de inicio glibc. ( __libc_init_first , __dl_tls_setup y __libc_csu_init en ese orden, según el comentario de Michael Petch. Existen otras implementaciones de libc , incluido MUSL que está diseñado para enlaces estáticos y funciona sin llamadas de inicialización).

$ gcc -m32 -nostartfiles -static hello32.S # fails at run-time $ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, BuildID[sha1]=ef4b74b1c29618d89ad60dbc6f9517d7cdec3236, not stripped $ strace -s128 ./a.out execve("./a.out", ["./a.out"], [/* 70 vars */]) = 0 [ Process PID=29681 runs in 32 bit mode. ] gettimeofday(NULL, NULL) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- +++ killed by SIGSEGV (core dumped) +++ Segmentation fault (core dumped)

También puede gdb ./a.out y ejecutar b _start , layout reg , run y ver qué sucede.

$ gcc -m32 -nostartfiles hello32.S # Correct command line $ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=7b0a731f9b24a77bee41c13ec562ba2a459d91c7, not stripped $ ./a.out Hello, World! %esp at startup = 0xffdf7460 $ ltrace -s128 ./a.out > /dev/null printf("Hello, World!/n%%esp at startup = %#lx/n", 0xff937510) = 43 # note the different address: Address-space layout randomization at work exit(0 <no return ...> +++ exited (status 0) +++ $ strace -s128 ./a.out > /dev/null # redirect stdout so we don''t see a mix of normal output and trace output execve("./a.out", ["./a.out"], [/* 70 vars */]) = 0 [ Process PID=29729 runs in 32 bit mode. ] brk(0) = 0x834e000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) .... more syscalls from dynamic linker code open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 mmap2(NULL, 1814236, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfffffffff7556000 # map the executable text section of the library ... more stuff # end of dynamic linker''s code, finally jumps to our _start gettimeofday({1461874556, 431117}, NULL) = 0 fstat64(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0 # stdio is figuring out whether stdout is a terminal or not ioctl(1, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0xff938870) = -1 ENOTTY (Inappropriate ioctl for device) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7743000 # 4k buffer for stdout write(1, "Hello, World!/n%esp at startup = 0xff938fb0/n", 43) = 43 exit_group(0) = ? +++ exited with 0 +++

Si hubiéramos usado _exit(0) , o si el sistema sys_exit nos llamara con int 0x80 , la write(2) no habría sucedido . Con stdout redirigido a un no-tty, el valor predeterminado es de búfer completo (no de búfer de línea), por lo que la write(2) solo se activa mediante fflush(3) como parte de la exit(3) . Sin redirección, llamar a printf(3) con una cadena que contenga nuevas líneas se vaciará inmediatamente.

Puede ser deseable comportarse de manera diferente dependiendo de si stdout es un terminal, pero solo si lo hace a propósito, no por error.