02 - Un poco de arquitectura para hacer algo de daño en RE
Los ejemplos de estas notas se pueden descargar en: http://ccom.uprrp.edu/~rarce/ccom4702c22-clase01.tgz
Estos son los registros de uso general que tienen las arquitecturas x86 (32 bits) y x86-64 (64 bits)
Para x86 (32 bits)

Nota que podemos referirnos a algunos registros por:
- su nombre completo (ejemplo EAX): 32 bits (a.k.a. DWORD)
- su nombre sin la
E(ejemplo AX): significa los dos bytes menos significativos de EAX - su byte menos significativo (AL en el caso de EAX)
- su segundo byte menos significativo (AH en el caso de EAX)
Para x86-64 (64 bits)

- También aplica lo de acceder todo (64), mitad (32 bits), dos bytes, bytes para ciertos registros.
- Provee muchos más registros, pero para RE la mayoría del tiempo solo nos importan los GPR.
En ambas arquitecturas
El stack
Hay dos registros que se utilizan para llevar cuenta de las direccciones top y el bottom del stack.
- en x86: ESP (top) y EBP (bottom)
- en x86-64: RSP (top) y RBP (bottom)
Ejemplo de un stack frame
RSP = 0x7fffffffe090
RBP = 0x7fffffffe0b0
^
| stack crece hacia
| direcciones menores
|
+------------+
0x7fffffffe090 | | TOP
+------------+ \
0x7fffffffe098 | | |
+------------+ |
0x7fffffffe0a0 | | | : StackFrame
+------------+ |
0x7fffffffe0a8 | | |
+------------+ /
0x7fffffffe0b0 | | BOTTOM
+------------+
El stack crece hacia direcciones más bajas, i.e. el contenido del RSP es menor que el RBP.
Instrucciones básicas
-
Se pueden expresar en dos formatos: Intel y AT&T
-
Intel: ejemplo:
MOV RAX, QWORD PTR [rbp+010h]- Datos se mueven <===
- Incluyen "size directives": BYTE, WORD(16 bits), DWORD (32 bits), QWORD(64 bits)
-
AT&T:
MOV 0x10(%rbp), %rax- Datos se mueven ===>
- Desplazamientos expresados de una forma extraña
Los operandos de las instrucciones en Assembly pueden ser de diversos tipos y en las referencias se utilizan las siguientes abreviaturas para describirlos:
<reg64>Any 64-bit register<reg32>Any 32-bit register (EAX, EBX, ECX, EDX, ESI, EDI, ESP, or EBP)<reg16>Any 16-bit register (AX, BX, CX, or DX)<reg8>Any 8-bit register (AH, BH, CH, DH, AL, BL, CL, or DL)<reg>Any register<mem>A memory address (e.g., [eax], [var + 4], or dword ptr [eax+ebx])<con32>Any 32-bit constant<con16>Any 16-bit constant<con8>Any 8-bit constant<con>Any 8-, 16-, or 32-bit constant
Una referencia de instrucciones x86-64: https://www.felixcloutier.com/x86/index.html
Instrucciones de transferencia de datos
MOV (https://www.felixcloutier.com/x86/mov) y sus primos
MOV ofrece muchas variaciones sobre desde dónde y hacia donde mover un dato. Veamos un ejemplo.
mov rax, 0789ADEADBEEDh ; de inmediato a registro
mov rbx, rax ; de registro a registro
push rbx
xor ebx, ebx
mov rax, rsp ; de registro a registro
mov rax, [rax] ; de MEM a registro
mov rbx, [rsp+010h] ; de MEM a registro
El operador [ ] se utiliza para desreferenciar los operandos. Por ejemplo,
si rcx = 0x7ffff8080 y el contenido de la dirección 0x7ffff8080 es 0xbabababa
mov rax, rcx- copiar el contenido de rcx a rax: rax será0x7ffff8080mov rax, [rcx]- copiar lo apuntado por rcx a rax: rax será0xbabababa
Por default la transferencia se asume que es del tamaño del registro. Si deseamos transferir una cantidad menor, usamos otras variantes de MOV, como MOVZX (https://www.felixcloutier.com/x86/movzx)
mov rax, 0789ADEADBEEDh
movzx rbx, al
No se permite transferencias de memoria a memoria. O sea nunca verás instrucciones como esta: `mov [eax], [ebp+0x10]
A las variaciones sobre desde dónde y hacia donde mover un dato se les llama addressing modes https://www.geeksforgeeks.org/addressing-modes/
gdb con gdb-peda
Para correr gdb sobre un programa sin argumentos basta con: gdb ./programa
Para correr gdb sobre un programa con argumentos : gdb --args ./programa uno dos tres
Cuando entras a gdb eres recibida con el prompt gdb-peda$. La gran mayoría de las veces los primeros comandos que darás serán:
b main: para establecer un breakpoint al comienzo de la funciónmainr: para comenzar a ajecutar el programa.
gdb va a detener la ejecución al principio de main (tal y como le pediste)
gdb-peda facilita la visualización de varias cosas interesantes durante la ejecución de un programa: registros, disassembly, stack.
En la parte de los registros, el valor al lado del registro indica su contenido. Por ejemplo, cuando RAX contiene 0x789adeadbeed.
RAX: 0x789adeadbeed
Si el valor del registro tiene un --> al lado, significa que gdb-peda te esta mostrando la desreferencia del valor del registro, i.e. asumiendo que el registro contiene una dirección, y mostrándote el contenido de esa dirección de memoria.
En este ejemplo, RBX contiene 0x7fffffffe1a8. gdb-peda te muestra que la dirección de memoria 0x7fffffffe1a8 contiene el valor 0x7fffffffe4a4. Tambien te dice que en la dirección 0x7fffffffe4a4 está el comienzo del string "calabaza".
RBX: 0x7fffffffe1a8 --> 0x7fffffffe4a4 ("calabaza")
Aquí está diciendo que RCX contiene 0x7fffffffd4a4, que es la dirección donde comienza el string "zanahoria"
RCX: 0x7fffffffd4a4 ("zanahoria")
- El comando que más utilizo para caminar por el programa es
ni(next instruction). - Si quiero establecer otros breakpoints lo puedo hacer:
b nombre_de_la_funciónb *dirección_de_la_instrucción- Para desplegar un valor de memoria uso el comando
x: x 0x7fffffffe4ee: me muestra el valor que está en la dirección0x7fffffffe4ee-
Puedo modicar el formato de despliegue usando
/x(hexadecimal),/d(decimal),/s(string). -
x /s 0x7fffffffe4ee: me muestra el valor que está en la dirección0x7fffffffe4eecomo string. -
x /d 0x7fffffffe898: me muestra el valor que está en la dirección0x7fffffffe898como decimal. -
Otros ejemplos de
x: _x /s $rcx: Muestra lo que está en la dirección que contiene rcx, formatealo como stirng _x /2wx 0x7fffffffe1b8: el /2wx especifica que queremos ver dos words de información a partir de la dirección de memoria0x7fffffffe1b8_x /s _(unsigned long \*)0x7fffffffe1b8: imrpime como string el contenido de la dirección a la que apunta0x7fffffffe1b8
Aritmética
Las operaciones aritméticas implican el destino y el segundo operando y tienden a modificar los status flags. Ejemplos...
mov rax, 0FFFFFFFFFFFFFEEDh
add rax, 01h
add rax, 01987h
xor rax, 0FFFFh
El adjust flag es un carry de la mitad del word a la otra mitad.
Calling conventions
We will use the standard Linux function calling convention. The calling convention is defined in detail in System V Application Binary Interface—AMD64 Architecture Processor Supplement.
The caller uses registers to pass the first 6 arguments to the callee. Given the arguments in left-to-right order, the order of registers used is: %rdi, %rsi, %rdx, %rcx, %r8, and %r9. Any remaining arguments are passed on the stack in reverse order so that they can be popped off the stack in order.
The callee (la función invocada) is responsible for perserving the value of registers %rbp %rbx, and %r12-r15, as these registers are owned by the caller. The remaining registers are owned by the callee.
The callee places its return value in %rax and is responsible for cleaning up its local variables as well as for removing the return address from the stack.
Un ejemplo,
int foo(int a, int b) {
int c = a - b;
int d = a + b;
return c * d;
}
int main() {
unsigned int r = rand();
int g = foo(r, r + 10);
return g;
}
0000000000001149 <foo>:
1149: endbr64
114d: push rbp ; storing rbp and
114e: mov rbp,rsp ; moving foo's rbp to where main's rsp was
1151: mov DWORD PTR [rbp-0x14],edi ; reading argument a
1154: mov DWORD PTR [rbp-0x18],esi ; reading argument b
1157: mov eax,DWORD PTR [rbp-0x14]
115a: sub eax,DWORD PTR [rbp-0x18]
115d: mov DWORD PTR [rbp-0x8],eax
1160: mov edx,DWORD PTR [rbp-0x14]
1163: mov eax,DWORD PTR [rbp-0x18]
1166: add eax,edx
1168: mov DWORD PTR [rbp-0x4],eax
116b: mov eax,DWORD PTR [rbp-0x8]
116e: imul eax,DWORD PTR [rbp-0x4]
1172: pop rbp ; returning the rbp to where it was
1173: ret
0000000000001174 <main>:
1174: endbr64
1178: push rbp ; storing rbp and was
1179: mov rbp,rsp ; moving main's rbp to where its caller's rsp was
117c: sub rsp,0x10 ; oppening up the stack frame
1180: call 1050 <rand@plt>
1185: mov DWORD PTR [rbp-0x8],eax
1188: mov eax,DWORD PTR [rbp-0x8]
118b: add eax,0xa
118e: mov edx,eax
1190: mov eax,DWORD PTR [rbp-0x8]
1193: mov esi,edx ; cargando los valores de los params
1195: mov edi,eax ; a esi y edi
1197: call 1149 <foo>
119c: mov DWORD PTR [rbp-0x4],eax
119f: mov eax,DWORD PTR [rbp-0x4]
11a2: leave
11a3: ret
Push y pop:
Ejemplo 05
Ret vs leave? https://stackoverflow.com/questions/29790175/assembly-x86-leave-instruction
Find, using gdb peda
Puedes ver el memory map del programa usando info proc map
gdb-peda$ info proc map
process 3812290
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /home/rarce/ccom4702/code/lab01/ejemplo05
0x401000 0x402000 0x1000 0x1000 /home/rarce/ccom4702/code/lab01/ejemplo05
0x402000 0x403000 0x1000 0x2000 /home/rarce/ccom4702/code/lab01/ejemplo05
0x403000 0x404000 0x1000 0x3000 /home/rarce/ccom4702/code/lab01/ejemplo05
0x7ffff7dba000 0x7ffff7ddc000 0x22000 0x0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7ddc000 0x7ffff7f54000 0x178000 0x22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7f54000 0x7ffff7fa2000 0x4e000 0x19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fa2000 0x7ffff7fa6000 0x4000 0x1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fa6000 0x7ffff7fa8000 0x2000 0x1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fa8000 0x7ffff7fae000 0x6000 0x0
0x7ffff7fc9000 0x7ffff7fcd000 0x4000 0x0 [vvar]
0x7ffff7fcd000 0x7ffff7fcf000 0x2000 0x0 [vdso]
0x7ffff7fcf000 0x7ffff7fd0000 0x1000 0x0 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7fd0000 0x7ffff7ff3000 0x23000 0x1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ff3000 0x7ffff7ffb000 0x8000 0x24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
Si deseas buscar un string en el espación de memoría correspondiente al stack puedes usar searchmem:
searchmem "elstring" stack