Recuperación de datos y tipos: estructuras, matrices, objetos de C++ y DWARF
a->balancecuando tiene una tabla de tipo y *(int *)(param_1 + 4)Esta lección construye la tabla a partir de los accesos, lee la verdadera de DWARF 5, y muestra dónde están de acuerdo los dos.Type: Build
Languages: Python, C, C++, x86-64 assembly
Prerequisites: Phase 13 · 33 (disassembly and control flow), Phase 13 · 34 (lifting to pseudocode)
Time: ~140 minutes
Objetivos de aprendizaje
- Lea un acceso de miembro de estructura como registro base más desplazamiento, y un acceso de matriz como base más índice veces paso, de las reglas de dirección de Intel SDM.
- Aplicar las reglas de alineación del Sistema V a la mano a
struct accounty explicar cada byte de relleno. - Construye un pase de recuperación de tipo que convierta los accesos en una lista en offsets de campo, anchos, tipos, un paso y un tamaño, con la evidencia para cada uno.
- Escriba un lector DWARF 5 para
.debug_info¿ Qué ?.debug_abbrev¿ Qué ?.debug_str_offsets, y.debug_addrde un objeto relocable, y comprobarlo conllvm-dwarfdump¿ Qué ? - Comparar el diseño recuperado con el diseño DWARF y nombrar cada diferencia como evidencia faltante, un acceso fusionado, o un tipo no comprobable.
- Lea un vtable de Itanium C++, su tipo de información y sus nombres desfigurados de las reubicaciones, y resuelve una llamada virtual a una ranura.
El problema
Usted compiladoaccount.cSin-gy preguntó a un agente qué.bumpEl agente envió el objeto a Ghidra 12.1.4 y recibió esto:
void bump(long param_1,int param_2)
{
*(int *)(param_1 + 4) = param_2 + *(int *)(param_1 + 4);
return;
}La misma fuente compilada con -gda esto desde el mismo decompiler:
int bump(account *a,int by)
{
a->balance = by + a->balance;
return a->balance;
}Las instrucciones son idénticas en ambos objetos. La diferencia es una tabla de tipo. Con la tabla el descompilador sabe que el primer parámetro apunta a una estructura de 32 bytes cuyo campo en el offset 4 es un 4 byte intnombrebalanceSin él , el decompiler sólo sabe que algo de 8 bytes de ancho llegó .%rdiy que 4 bytes en +4se leían y se escribían.
Todo en esa tabla tiene una fuente.addl 0x4(%rax), %ecxEl ancho 4 proviene de lalEl nombre proviene de unDW_TAG_memberEntrada en .debug_infoEsta lección construye ambas mitades: un pase que recupera lo que las instrucciones demuestran y un lector que decodifica lo que el compilador grabó.
Un acceso de miembro es un desplazamiento
El volumen 1 del SDM de Intel, sección 3.7.5, define la dirección efectiva como Offset = Base + (Index * Scale) + Displacement, con una escala de 1, 2, 4 o 8 y un desplazamiento de 8, 16 o 32 bits.RIP + Displacement. Un acceso a miembros de estructuras utiliza la primera forma sin índice: el registro base contiene la dirección del objeto y el desplazamiento es el desplazamiento de miembros.
¿ Qué ?bumpEn el-O0. La lista de llvm-objdump -d -rSe dice:
4: 48 89 7d f8 movq %rdi, -0x8(%rbp)
e: 48 8b 45 f8 movq -0x8(%rbp), %rax
12: 03 48 04 addl 0x4(%rax), %ecx
15: 89 48 04 movl %ecx, 0x4(%rax)Descifrar03 48 04con las reglas de la lección 33. 03¿ Es verdad ?ADD r32, r/m32- ModRM .48¿ Es verdad ?01 001 000¿ Qué es esto ?mod = 01se refiere a un operando de memoria con un desplazamiento de 8 bits, reg = 001¿ Es verdad ?%ecx¿ Qué ?r/m = 000¿ Es verdad ?%raxEl byte de desplazamiento es 04La instrucción lee 4 bytes en %rax + 4y los agrega a %ecx- El .lel sufijo que llvm-objdumpImpresiones es el tamaño de la operanda, así que el campo es de 4 bytes de ancho.%raxvino de la ranura del marco en -0x8(%rbp), y ese espacio recibido%rdi, el primer argumento de un número entero bajo la convención de llamadas del Sistema V. Sigue tres hechos: el primer parámetro es un puntero, el objeto al que apunta tiene un campo de 4 bytes en el offset 4, y bumplee y escribe ese campo.
Nada en estos bytes diceint¿ Qué ?balance, ostruct accountNo hay nada que diga cuánto es el objeto. Los 28 bytes restantes de la estructura son invisibles para elbump¿ Qué ?
Las reglas de diseño del sistema V
El x86-64 psABI, sección 3.1.2, da tres reglas para los agregados. "Las estructuras y las uniones asumen la alineación de su componente más estrictamente alineado. Cada miembro se asigna al offset disponible más bajo con la alineación apropiada. El tamaño de cualquier objeto es siempre un múltiplo de la alineación del objeto".char1 y 1, short2 y 2, int4 y 4, double8 y 8, cualquier puntero 8 y 8.
Aplicar las normas a las estructuras enaccount.c¿Qué es esto ?
struct account {
char kind;
int balance;
short code;
double rate;
const char *label;
};| Member | Size | Alignment | Lowest offset that fits | Padding before |
|---|---|---|---|---|
kind | 1 | 1 | 0 | 0 |
balance | 4 | 4 | 4 | 3 (bytes 1 to 3) |
code | 2 | 2 | 8 | 0 |
rate | 8 | 8 | 16 | 6 (bytes 10 to 15) |
label | 8 | 8 | 24 | 0 |
El último miembro termina en 32. La alineación más estricta es 8, y 32 es un múltiplo de 8, por lo que el tamaño es 32 sin relleno de cola. Nueve bytes de los 32 son relleno, y el psABI dice "El contenido de cualquier relleno es indefinido".
El orden de los miembros determina el tamaño.double, pointer, int, short, charel relleno compensa 0, 8, 16, 20, 22 y termina en 23, que ronda hasta 24. sysv_layout()En elcode/main.pyaplica las tres reglas y produce ambos resultados.ctypesel módulo aplica las mismas reglas a través de la convención del compilador host, y ctypes.sizeof¿ Qué ?ctypes.alignment, y el .offsetde cada campo dar 32, 8, y 0, 4, 8, 16, 24El programa mismo imprime la tercera copia cuando se construye y se ejecuta en esta máquina:
sizeof 32 align 8
kind 0 balance 4 code 8 rate 16 label 24
sum 315 tag -3 projected 318.75 label_length 7La arm AAPCS64 establece las mismas reglas agregadas en su sección 5.10.1, y el documento arm64 de Apple mantiene estos cinco tamaños escalares, por lo que los números coinciden en los dos objetivos.-target x86_64-unknown-linux-gnupara que el lector ELF de la lección 33 y el lector DWARF de abajo trabajen en ellos.
Lea el resto de la estructura a mano
bumpVe un campo. Las otras cuatro funciones en account.cEn el caso de las direcciones de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de la dirección de laaccount-x86_64-O0-g.objdump.txt¿ Qué ?
sum_balancesindexa una matriz de la estructura:
51: 48 8b 45 f8 movq -0x8(%rbp), %rax
55: 48 63 4d ec movslq -0x14(%rbp), %rcx
59: 48 c1 e1 05 shlq $0x5, %rcx
5d: 48 01 c8 addq %rcx, %rax
60: 8b 40 04 movl 0x4(%rax), %eax%rax¿ Es verdad ?list¿ Qué ?%rcx¿ Es verdad ?ide signo extendido a 64 bits, y shlq $0x5y la multiplicamos por 32.addq¿ Qué ?%rax¿ Es verdad ?list + 32 * i, y la carga lee 4 bytes en el desplazamiento 4 del elemento i. El conteo de cambios es el único lugar en este objeto donde el tamaño de la estructura aparece como un número.0x49a la jmp 0x49En el0x72¿ Qué ?
tagSe lee dos campos estrechos:
8c: 0f be 00 movsbl (%rax), %eax
98: 0f bf 48 08 movswl 0x8(%rax), %ecxmovsblcarga un byte en el desplazamiento 0 y se extiende de signo, por lo que el campo en 0 es un número entero de 1 byte firmado. movswlcarga dos bytes en el offset 8 con extensión de signo, por lo que el campo en 8 es un número entero de 2 bytes firmado.-O1El primer acceso se hace cmpb $0x6c, (%rdi), que todavía dice 1 byte en el desplazamiento 0 y no dice nada sobre el signo.
projecteddice un flotante:
cc: f2 0f 2a 40 04 cvtsi2sdl 0x4(%rax), %xmm0
d5: f2 0f 59 40 10 mulsd 0x10(%rax), %xmm0cvtsi2sdlConverte un número entero de 32 bits firmado de la memoria a un doble, lo que confirma que el offset 4 tiene un número entero de 4 bytes firmado. mulsdse multiplica por el doble de la memoria, por lo que el offset 16 tiene un valor de punto flotante de 8 bytes.
label_lengthlee un puntero y luego lo sigue:
f3: 48 8b 40 18 movq 0x18(%rax), %rax
f7: 48 63 4d f4 movslq -0xc(%rbp), %rcx
fb: 80 3c 08 00 cmpb $0x0, (%rax,%rcx)El movqSe lee 8 bytes en el offset 24. Por sí solo que es un integer de 8 bytes o un puntero.cmpbEl valor de la memoria que se utiliza como dirección es un puntero.cmpbse lee 1 byte en el índice %rcxde la misma, y en -O1el puntero está avanzado con leaq 0x1(%rcx), %rcxdentro del bucle, por lo que los elementos apuntados están a 1 byte de distancia.
Si juntamos las cinco funciones, la estructura es{ i8 at 0, i32 at 4, i16 at 8, f64 at 16, pointer at 24 }Las diferencias entre 1 y 3 y 10 y 15 son exactamente el relleno previsto por las reglas de psABI.const char *, el tipo declarado del byte firmado en 0, que la fuente escribe char, y que las cinco funciones reciben el mismo tipo.main, que pasa un objeto de marco a los cinco.-O0Las páginas de llamadas hacen que sea visible: leaq -0x70(%rbp), %rdiseguido por addq $0x20, %rdiantes decallq bump, y addq $0x40, %rdiantes decallq tagLos desventajos son 0,0x20, y 0x40son tres elementos de una matriz, y el memcpyde 0x60los bytes que lo llenan es 96 bytes, que es tres elementos de 32.
El pase
trace_function()En elcode/main.pyEl control de la función de un registro de 64 bits o de un espacio de marco como -0x8(%rbp)Cada titular tiene uno de los tres valores.Pointeres una raíz más una offset constante más un conjunto de pasos, donde un paso registra que un múltiple desconocido de tantos bytes se agregó.Indexes un número entero de valor desconocido que es un múltiplo de una escala.
El estado comienza con los seis registros de argumentos del Sistema V .Pointer("param0", 0)por elPointer("param5", 0)Entonces cada instrucción aplica una regla:
- ¿ Qué es esto ?
movqUna escritura de 32 bits o más estrecha en un registro lo hace unIndexcon escala 1. - ¿ Qué es esto ?
leaqcon una base de puntero produce un puntero con el desplazamiento añadido, y con un registro de índices añadescale * index_scaleA los pasos.leaqcuya base es%rbpo%rspnombra un objeto de marco, que es cómomainPasesrows¿ Qué ? shlq $nen unIndexmultiplica su escala por2^n, yimulq $kse multiplica pork¿ Qué ?addqde un puntero e índice mantiene el puntero y añade la escala del índice a los pasos.addq $ka un indicador se mueve su descenso y se registra también como incremento de dicho registro.- Una llamada hace que cada registro guardado por el llamador sea desconocido, y los registros de argumentos que contienen punteros se registran como un sitio de llamada.
- Cualquier operando de memoria cuyo registro base contiene un puntero se convierte en un
Accesscon la raíz y el desplazamiento del puntero, el ancho y el tipo de la mnemónica, y una bandera de la tienda.
La amplitud y la bondad vienen del mnemónico solo.access_width()mapas de la b¿ Qué ?w¿ Qué ?l, y qlos sufijos a 1, 2, 4 y 8 bytes, movsbly movswlpara las cargas estrechas firmadas, movzbla los no firmados, el sdy ssinstrucciones de SSE escalar para flotadores de 8 y 4 bytes, cvtsi2sdla una fuente de un número entero de 4 bytes firmada, y el de 16 bytes movapsfamilia a un vector. leaqy el noplos formularios no son accesos.
if memory.base in FRAME_REGISTERS:
return Pointer(f"frame({memory.base}{format_signed(memory.displacement) if memory.displacement else ''})", 0)
base = env.get(memory.base)
if not isinstance(base, Pointer):
return Unknown
strides = set(base.strides)
if memory.index is not None:
index_value = env.get(memory.index)
strides.add((index_value.scale if isinstance(index_value, Index) else 1) * memory.scale)
return Pointer(base.root, base.offset + memory.displacement, frozenset(strides), base.origin)Una carga de 8 bytes a través de un puntero produce un nuevo puntero cuyos nombres raíces el campo de que proviene, como param0->0x18. Cuando ese valor se utiliza más tarde como base, o avanzado con leaq, el campo de que viene se marca como un puntero probado. Esa es la regla que convierte el descalzo 24 de "8 bytes" en "puntero".
Cuatro tipos de pruebas de pasos
infer_layout()fusiona los accesos de cada función de testigo y recoge el paso de cuatro fuentes, cada una con la instrucción que lo produjo.
| Kind | Where it comes from | Build that shows it |
|---|---|---|
scale | an Index with scale above 1 reached a pointer, from shlq $0x5 or imulq | -O0, shlq $0x5, %rcx at 0x59 |
loop-step | a 64-bit increment of the base or index register inside a loop that also contains the access | -O1, addq $0x20, %rdx at 0x24 |
offset-delta | same-width accesses through one base register inside one loop, at offsets a constant apart | -O2, eight loads 32 apart |
call-site | the offsets at which one frame object is passed to the witness functions | -O0, main passes rows, rows+0x20, rows+0x40 |
Un bucle es un retroceso .jmpEn el caso de los datos de acceso, el incremento se calcula sólo cuando el rango de bucle contiene tanto el incremento como el acceso.addl $0x1, %eaxque avanza.iEn el-O0, porque es un 32 bits de adición y porque el acceso no pasa por %eaxEl paso final es el mayor divisor común de todos los valores encontrados. El tamaño es el paso cuando el paso es al menos tan grande como el final del campo final.
El bucle vectorizado en -O2
En el-O2Clang vectorizado sum_balances. El cuerpo del bucle de0x50¿ Qué ?0xaaSe lee ocho elementos por pase:
3b: 48 8d 87 e4 00 00 00 leaq 0xe4(%rdi), %rax
50: 66 0f 6e 50 80 movd -0x80(%rax), %xmm2
55: 66 0f 6e 98 60 ff ff ff movd -0xa0(%rax), %xmm3
61: 66 0f 6e 90 40 ff ff ff movd -0xc0(%rax), %xmm2
69: 66 0f 6e a0 20 ff ff ff movd -0xe0(%rax), %xmm4
7d: 66 0f 6e 10 movd (%rax), %xmm2
81: 66 0f 6e 58 e0 movd -0x20(%rax), %xmm3
8a: 66 0f 6e 50 c0 movd -0x40(%rax), %xmm2
8f: 66 0f 6e 60 a0 movd -0x60(%rax), %xmm4
a0: 48 05 00 01 00 00 addq $0x100, %rax
aa: 75 a4 jne 0x50 <sum_balances+0x40>%raxcomienza enlist + 0xe4Los ocho desplazamientos colocan las cargas en compensaciones .0x04, 0x24, 0x44, 0x64, 0x84, 0xa4, 0xc4, 0xe4de lalist, que son4, 36, 68, 100, 132, 164, 196, 228El incremento es0x100Un pase que registra compensaciones al verlas informa ocho campos y una estructura de al menos 232 bytes.
fold_unrolled()En un bucle, toma los accesos de la misma anchura a través de un registro base. Sus compensaciones sondaparte, aquí 32. Si el paso del bucle es k * dY hay exactamentekLos cambios de la velocidad de la velocidad de los motores de carga son distintos, el bucle se desarrolla.kveces, y cada acceso es el mismo elemento de acceso en el offset (offset - lowest) mod dAquí tienes .256 = 8 * 32con ocho desplazamientos, así que todas las ocho cargas se doblan en el desplazamiento 4, y los registros de pruebas de pasooffset-delta 32¿ Qué ?unrolled 32, y loop-step 256El ciclo de residuos escalares en 0xe0añade scale 32de lashlq $0x5, %rdxy loop-step 32de laaddq $0x20, %rdxEl divisor común más grande de los cinco valores es 32.
La figura tiene un botón que desactiva el plegado.-O2Este estado es lo que un lector ve que confía en el desplazamiento sin el bucle.
DWARF 5 Desde los bytes
El -gconstrucción de account.ccontiene ocho secciones de desactivación. .debug_infoes 493 bytes, .debug_abbrev179, .debug_str230, .debug_str_offsets124, .debug_addrEl número 112,.debug_line312, .debug_line_str12, y .debug_frame264. La introducción de las listas de normas DWARF 5 .debug_str_offsets¿ Qué ?.debug_addr, y .debug_line_strcomo nuevo en la Versión 5. El lector en code/main.pyNecesita los primeros cinco.
El encabezado de la unidad
La sección 7.5.1.1 de la norma da el título completo de la unidad de compilación: unit_length¿ Qué ?version¿ Qué ?unit_type¿ Qué ?address_size, y debug_abbrev_offsetLos primeros doce bytes de .debug_infoson:
e9 01 00 00 unit_length = 0x1e9 = 489
05 00 version = 5
01 unit_type = DW_UT_compile
08 address_size = 8
00 00 00 00 debug_abbrev_offset = 0El unit_typeel byte es nuevo en la versión 5, y el estándar lo dice en la misma sección. La longitud cuenta los bytes después de sí mismo, por lo que la siguiente unidad comenzaría en 4 + 489 = 493, el final de la sección. llvm-dwarfdump --debug-infoImprime los mismos cinco valores en su Compile Unit:la línea, y el primer ensayo analiza esa línea y compara.
Las abreviaturas
La sección 2.1 dice que cada entrada de información de depuración "se compone de una etiqueta de identificación y una serie de atributos". La entrada en sí solo almacena un código de abreviatura..debug_abbrev(sección 7.5.3) que nombra la etiqueta, dice si los niños siguen, y enumera cada atributo con su forma. Este objeto tiene 14 declaraciones. Dos cosas aquí:
[12] DW_TAG_structure_type DW_CHILDREN_yes
DW_AT_name DW_FORM_strx1
DW_AT_byte_size DW_FORM_data1
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
[13] DW_TAG_member DW_CHILDREN_no
DW_AT_name DW_FORM_strx1
DW_AT_type DW_FORM_ref4
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
DW_AT_data_member_location DW_FORM_data1parse_abbreviations()Se lee código ULEB128, etiqueta ULEB128, un byte de niños, luego atributo y forma pares hasta que un 0, 0En la sección 7.5.6 se dan los códigos de formulario: data1es un byte, ref4es un descenso de 4 bytes en relación con la unidad, strx1es un índice de 1 byte en la tabla de compensaciones de cadenas, sec_offsety data4son 4 bytes, addrxes un índice ULEB128 en la tabla de direcciones, exprloces una longitud ULEB128 y que muchos bytes, y flag_presentno tiene bytes en absoluto.
Descifrar el miembro del balance a mano
La entrada de la estructura para accountcomienza en el offset 0x195con los bytes 0c 15 20 00 03El código abreviado es 12, la estructura.DW_AT_namees el índice de cadenas 0x15¿ Qué ?DW_AT_byte_size¿ Es verdad ?0x20= 32, DW_AT_decl_filees 0, y DW_AT_decl_linees 3, que es la línea de struct account {en la fuente.
La segunda entrada de miembros comienza en 0x1a3con0d 10 88 01 00 00 00 05 04¿Qué es esto ?
0des la abreviatura 13, un miembro.10¿ Es verdad ?DW_AT_name¿ Cómo ?strx1: índice de cuerdas 16.88 01 00 00¿ Es verdad ?DW_AT_type¿ Cómo ?ref4: compensación0x188desde el inicio de la unidad, que es elDW_TAG_base_typeEntrada nombradaint¿ Qué ?00¿ Es verdad ?DW_AT_decl_filey05¿ Es verdad ?DW_AT_decl_line: línea 5 deaccount.c¿ Qué ?04¿ Es verdad ?DW_AT_data_member_location: compensación 4.
En la sección 5.7.6 se dice que una entrada miembro "puede tener un atributo DW_AT_data_member_location o un atributo DW_AT_data_bit_offset" y que una constante entera "es el desplazamiento en bytes desde el principio de la entidad que contiene". Ese byte es el mismo número que el desplazamiento en addl 0x4(%rax), %ecx, y compare_layouts()es la función que pone los dos lados a la vez.
Las cadenas y direcciones pasan a través de dos tablas
El índice de cuerdas 16 no es un descenso en .debug_strEl lector tomaDW_AT_str_offsets_basede la entrada de unidad, que es 8, y lee la entrada de 4 bytes en 8 + 4 * 16En el.debug_str_offsetsEsa entrada contiene el descenso de"balance"En el.debug_strLa misma indirección se aplica a las direcciones.sum_balancescomienza con los bytes 07 07: abreviatura 7, un subprograma, entonces DW_AT_low_pc¿ Cómo ?addrxIndice 7. La entrada de 8 bytes en 8 + 8 * 7En el.debug_addr¿ Es verdad ?0x30, la dirección de la función en .textLas primeras seis entradas de esa tabla son las direcciones de las literalmente de seis cuerdas.
En un objeto relocable ninguna tabla tiene sus valores finales..debug_str_offsetses cero en el archivo, y .rela.debug_str_offsetstiene 29 R_X86_64_32las reubicaciones contra .debug_strcon el real de compensación en el adjunto. .debug_addrtiene 13 R_X86_64_64las reubicaciones contra .texty .rodata.str1.1- ¿ Qué ?.debug_infotiene cuatro reubicaciones propias: la abreviatura de compensación, DW_AT_str_offsets_base¿ Cómo ?.debug_str_offsets+0x8¿ Qué ?DW_AT_stmt_list, y DW_AT_addr_base¿ Cómo ?.debug_addr+0x8- ¿ Qué ?ElfObject.relocated()se aplica S + APara los tipos de 32 bits y 64 bits antes de que el lector analice una sección. Un símbolo de sección tiene valor 0 en un archivo objeto, por lo que el campo recibe el agregado. llvm-dwarfdumpEl índice de resolución de la información de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de datos de la fuente de referencia de la fuente de datos de la fuente de referencia de la fuente de referencia de la fuente de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de referencia de la fuente de referencia de la fuente de referencia de referencia de la fuente de referencia de la fuente de referencia de la fuente de referencia de la fuente de referencia de la fuente de referencia de referencia de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de referencia de referencia de la fuente de la fuente de referencia de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de la fuente de referencia de la fuente de referencia de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de referencia de la fuente de la fuente de referencia de la fuente de la fuente de la fuente de la fuente de referencia de la fuente de la
Tipos, funciones y ubicaciones
DwarfInfo.type_name()sigueDW_AT_typeLas referencias.labelpuntos de los miembros en un DW_TAG_pointer_type, que apunta a una DW_TAG_const_type, que señalan lachartipo base, por lo que el nombre se compone de const char *- ¿ Qué ?type_size()retorno DW_AT_byte_sizepara tipos y estructuras de base, 8 para un puntero, y contar veces el tamaño de un elemento para una matriz. type_kind()reduce el DW_AT_encodingde un tipo base a intcon un signo, o float, y un tipo de puntero para pointer, que es el vocabulario que utiliza la comparación.
DwarfInfo.functions()Se lee seis .DW_TAG_subprogramLas entradas.bumpse ejecuta desde 0x0¿ Qué ?0x21, que es DW_AT_low_pcmás el DW_AT_high_pcSu base de marco esDW_OP_reg6 RBP, y su parámetro atiene la expresión de ubicación 91 78¿ Qué es esto ?DW_OP_fbregcon el valor SLEB128 0x78, que es -8. Ese es el espacio del marco -0x8(%rbp)en el que se almacena la lista %rdien la dirección 0x4La información de descomposición y las instrucciones describen la misma ranura desde dos lados.
Comparar las dos estructuras
compare_layouts()El miembro que no tiene campo es no-evidenceUn campo de un tamaño diferente essize-mismatchUn miembro de puntero cuyo campo nunca fue desreferenciado espointer-unproven. Un tipo que no esté de acuerdo, como un float donde las instrucciones utilizan aritmética de números enteros, es kind-mismatch. Los campos inferidos que comienzan dentro de un miembro o dentro de los relleno son unexpectedEl tamaño compara el paso conDW_AT_byte_size¿ Qué ?
Los números de las tres construcciones, desde python3 code/main.py¿Qué es esto ?
| Build | Accesses through the first parameter | Fields | Stride evidence | Members matched | Size |
|---|---|---|---|---|---|
-O0 | 11 | 5 | scale 32, call-site 32, call-site 64 | 5 of 5 | 32, match |
-O1 | 9 | 5 | loop-step 32 | 5 of 5 | 32, match |
-O2 | 17 | 5 after folding, 12 before | offset-delta 32, unrolled 32, loop-step 256, scale 32, loop-step 32 | 5 of 5 | 32, match |
Los huecos inferidos son bytes de 1 a 3 y 10 a 15 en cada construcción, y esos son exactamente los bytes de relleno del diseño DWARF. Cada campo inferido se encuentra en un múltiplo de su propio tamaño, que es lo que requieren las reglas de psABI, y natural_alignment_ok()- Eso lo registra.
Añadir las funciones una a la vez y la imagen cambia.bumpsolo el diseño es un campo en el offset 4, el tamaño es desconocido, y el límite inferior es 8. sum_balancesañade el paso. tagañade los desventajas 0 y 8. projectedañade el flotador a 16. label_lengthañade el puntero a 24 y demuestra que es un puntero. mainañade los sitios de llamadas en -O0y no añade nada a -O1y -O2La figura pasa por este orden para cada construcción.
Tres cosas permanecen fuera de alcance.constNo está en las instrucciones.labeles sólo "elementos 1 byte aparte" de la leaq 0x1(%rcx), %rcxen el bucle de cuerdas, que es consistente con chary con unsigned chary _Bool- En el-O1El signo del byte en el desplazamiento 0 es desconocido, porque cmpb $0x6c, (%rdi)Las pruebas de aprobación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de evaluación de los resultados de los estudios de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de la investigación de los Estados Unidosint8allí yi8En el-O0, donde movsblEstas son las lecciones de hechos 35 registros como desconocidos.
Objetos de C++
shapes.cppdefine Shapecon un destructor virtual, un puro virtual area(), y un virtual name(), y Rect, que se deriva de Shapey añade dos intEl Itanium C++ ABI rige cómo Clang establece estos en x86-64 Linux.
Nombres equivocados
La sección 5.1.2 del ABI dice que "un nombre se corrompe con prefijos _ZEl objeto tiene 19 símbolos de este tipo._ZNK4Rect4areaEvSe lee como _Z, entoncesNpara un nombre anidado, Kpara una función de miembro const, 4Recty 4areacomo nombres de fuentes con prefijo de longitud, Epara cerrar el nombre anidado, y vpara una lista de parámetros vacíos. llvm-cxxfilthuellasRect::area() const- ¿ Qué ?_Z8describeRK5Shapei¿ Es verdad ?describe(Shape const&, int)¿ Qué es esto ?Res una referencia, Kes const, 5Shapees la clase,i¿ Es verdad ?intLos nombres especiales en la sección 5.1.4 son:_ZTVpara una mesa de trabajo, _ZTIpara información de tipo, y _ZTSpara la cadena de nombres de tipoinfo.C1¿ Qué ?C2¿ Qué ?D0¿ Qué ?D1, y D2, así que_ZN4RectD0Evy _ZN4RectD2Evambas impresas como Rect::~Rect(): el destructor de eliminación y el destructor de objeto base comparten un nombre de fuente.
DemanglerEn elcode/main.pycubre ese subconjunto más los códigos de tipo de construcción de la sección 5.1.5.2 y el S_Las pruebas se realizan en los 19 nombres y se comparan con los capturados.llvm-cxxfilt --no-strip-underscoreLa bandera es importante en macOS, donde la herramienta elimina un subtítulo principal por defecto y luego no reconoce Z8describe...¿ Qué ?
El vtable de sus reubicaciones
La sección 2.5.2 de la ABI enumera los componentes de una tabla virtual en orden: offsets de llamada virtual, offsets de base virtual, offset a arriba, el puntero typeinfo y luego los punteros de función virtual. El punto de dirección, el valor que tiene vptr de un objeto, es la dirección del primer puntero de función (sección 2.6.2). Rectno tiene bases virtuales, por lo que su tabla se compensa a arriba, tipo de información y funciones.
El símbolo_ZTV4RectEn el objeto cada byte es cero. El contenido está en .rela.data.rel.ro._ZTV4Rect¿Qué es esto ?
0000000000000008 R_X86_64_64 _ZTI4Rect
0000000000000010 R_X86_64_64 _ZN4RectD2Ev
0000000000000018 R_X86_64_64 _ZN4RectD0Ev
0000000000000020 R_X86_64_64 _ZNK4Rect4areaEv
0000000000000028 R_X86_64_64 _ZNK4Rect4nameEvLa ranura 0 mantiene el desplazamiento hasta arriba, que es 0 y no necesita una reubicación.+16De ahí, la ranura 0 es el destructor de objetos completos, la ranura 1 es el destructor de borrado, la ranura 2area, apartado 3 name- ¿ Qué ?read_vtable()se lee el tamaño del símbolo, las reubicaciones en su rango y el cero en el desplazamiento 0. ShapeLa mesa tiene la misma forma que la de__cxa_pure_virtualen la ranura 2, que es la función de tiempo de ejecución que una llamada virtual pura alcanza.
RTTI
En la sección 2.9.4 se describe std::type_infocomo un vptr seguido de un indicador al nombre de tipo de descomposición, y enumera las clases derivadas. _ZTI4Recttiene tres traslados: _ZTVN10__cxxabiv120__si_class_type_infoE+0x10¿ Qué ?_ZTS4Rect, y _ZTI5ShapeEl primero es el vtable de__si_class_type_info, la clase que utiliza el ABI para un tipo con una única base pública no virtual en el punto de compensación cero, y el +0x10Es el punto de dirección de esa tabla.4Rect, que es el nombre del tipo de mangled, y name()en un type_infoLa tercera es la base._ZTI5Shapeusos __class_type_infoy no tiene puntero de base. read_typeinfo()devuelve estos tres hechos.
La llamada virtual
describeEn el-O0hace una llamada virtual:
f: 48 8b 7d f8 movq -0x8(%rbp), %rdi
13: 48 8b 07 movq (%rdi), %rax
16: ff 50 10 callq *0x10(%rax)El primero .movqLa segunda lectura de 8 bytes en el desplazamiento 0 del objeto, que es el vptr, porque el ABI lo coloca primero en una clase con funciones virtuales.callqpasa por aquí .0x10pasado el VPT, y 0x10 / 8es la ranura 2.find_virtual_calls()coincide con el patrón y informa a ambos candidatos: Rect::area() constde la_ZTV4Recty __cxa_pure_virtualde la_ZTV5ShapeEl que se ejecuta depende del objeto, por lo que la llamada es indirecta.
DWARF registra la misma ranura.DW_TAG_subprogramlas entradas para areaen ambas clases de transporteDW_AT_vtable_elem_location (DW_OP_constu 0x2)¿ Qué ?namelleva la ranura 3, y los destructores llevan la ranura 0. Rectes un DW_TAG_structure_typede 16 bytes con una DW_TAG_inheritanceEntrada para Shapeen compensación 0 y miembros wa las 8 y hA las 12 .Shapees 8 bytes con un miembro artificial _vptr$ShapeDos derivadas independientes dan la ranura 2 para area: la tabla de reubicación y las entradas de desactivación.
En el-O1El objeto no tiene nada de vtable. mainconstrucciones Rectcomo local, así que el compilador resolvió r.name()y describe(r, 2)No se puede tomar la dirección de la tabla, por lo que la tabla no se emite.describetodavía contiene movq (%rdi), %raxy callq *0x10(%rax), porque su parámetro puede ser cualquierShape. Una tabla v que falta es un hecho sobre un archivo de objeto, y la prueba test_the_optimised_object_keeps_the_call_pattern_but_drops_the_vtables- ¿Qué? - Comprueba ambas mitades.
El ABI de Microsoft difiere en cada uno de estos detalles.?y codificar la convención de llamada, así que void test(void)Compilado como C++ __cdecl¿ Es verdad ??test@@ZAXXZEl localizador RTTI se encuentra en vfptr[-1]como un _RTTICompleteObjectLocator, y los símbolos son??_7para un vftable y ??_R4La documentación de Microsoft dice que "no hay estándares para nombrar en C++ entre los proveedores de compiladores".
Cómo las herramientas presentan los tipos recuperados
Cada fila que tiene un nombre de salida fue producida en esta máquina y capturada encode/fixtures/tools/Las filas para las herramientas que no están instaladas aquí describen su documentación y lo dicen.
| Tool | Version, licence | Where types live | On our fixtures |
|---|---|---|---|
| Ghidra | 12.1.4, 2026-09-21, Apache-2.0 | Data Type Manager with built-in, user-defined, and derived types; .gdt archives; types used by a program are copied into it | With DWARF: /DWARF/account.c/account size=32 align=8 and a->balance = by + a->balance;. Without: *(int *)(param_1 + 4) with long param_1, tag(char *param_1), and *(char **)(param_1 + 0x18) |
| IDA Pro | 9.4, 2026-07-13, commercial; IDA Free is non-commercial with a cloud decompiler for x86 only | Local Types window, .til type libraries, DWARF and PDB import into a dwarf or pdb folder | Not installed. Documentation only |
| Binary Ninja | 6.0, 2026-09-03, commercial with a Free edition | Type editing, header import and export, type archives, debug info from DWARF and PDB, type libraries, platform types | Not installed. Documentation only |
| radare2 | 6.2.4, 2026-10-05, LGPLv3 | ts, tsc, afvd | ts and tsc account printed nothing for the relocatable object with bin.dbginfo=true; pdf shows mov dword [rax + 4], ecx with source line comments from DWARF |
| rizin | 0.9.1, 2026-06-29, LGPL core | ts, tsc, afvd | tsc account printed struct account { char kind; int balance; short code; double rate; const char *label; }; from DWARF; the build without -g lists only its bundled header types |
| lldb | lldb-1703.0.236.21 | image lookup -t | byte-size = 32 and the full compiler_type text from the ELF object |
| gdb | not installed here | ptype /o prints "the offsets and sizes of fields in a struct" with holes marked | Documentation only |
| pyelftools | 0.33, public domain | Python API over .debug_info | A 43-line script prints the same five members and unit version 5 |
| llvm-dwarfdump | Apple LLVM 17.0.0 | --debug-info, --debug-abbrev, --debug-str-offsets | The oracle for every reader test |
La salida de Ghidra sin DWARF muestra las mismas dos reglas en funcionamiento.char *al parámetro de tagporque la función compara un byte a través de ella, y char **al campo en 0x18Porque ...label_lengthDesreferencia el valor cargado y compara los bytes. Esas son las mismas dos reglas que el paso en esta lección aplica: ancho de la instrucción, puntero de la dereferencia. La documentación del decompiler de Ghidra dice que los varnodos de código p "no tienen necesariamente un tipo de datos asociado con ellos" y que el decompiler "al final asigna un tipo de datos formal" por propagación. La opción "Inferir indicadores constantes" extiende la misma propagación a las constantes. Ghidra 12.1 añadió la recuperación de bitfield al descompilador, que las fichas de esta lección no ejercen.
La tabla hecha a mano es la otra mitad de la historia. Ghidra's Data Type Manager, IDA's Local Types, Binary Ninja's type archives, y rizin's tdEl pase te dice qué escribir y qué campos estás adivinando.
rea 6.5.0 con su proveedor Ghidra devuelve el texto del descompilador como un registro. Todo lo que está en la fila Ghidra se aplica a él, y el registro lleva los nombres de tipos sin la evidencia que los produjo.python3 code/main.py inferen la misma lista le da esa evidencia.
Investigación de la interferencia de tipo
El paso aquí es la mitad de generación de restricciones de un sistema de inferencia de tipo, restringido a un estruto alcanzado desde un parámetro.
- TIE (Lee, Avgerinos, Brumley, NDSS 2011) enmarcó la reconstrucción de tipo como generación de restricciones y resolución sobre una IR binaria, midió la conservaduría y precisión, y comparó con un descompilador comercial en 87 programas de núcleos.
- Retypd (Noonan, Loginov, Cok, PLDI 2016) añadió subtipo, polimorfismo y tipos recursivos, y informó reconstrucción del puntero
constUna implementación de código abierto existe bajo el nombre de GrammaTech. - OSPREY (Zhang y otros, IEEE S&P 2021) trata cada ubicación de memoria como una variable aleatoria e inferen los tipos más probables.
- DIRTY (Chen y otros, USENIX Security 2022) y ReSym (Xie y otros, CCS 2024) producen descompiladores postproceso con modelos aprendidos, y ReSym informa con una precisión del 72,9% en diseños estructurales completos.
- RecStruct (Chen y otros, USENIX Security 2026) recupera estructos anidados sin un descompilador, con ejecución simbólica insensible al flujo y unión-encuentra sobre ranuras de pila.
La estructura de la pila es lo primero que salta el pase de esta lección.-O1¿ Qué ?main¿ Qué es ?rowsen la pila y el código en línea dice 0x24(%rsp)¿ Qué ?0x40(%rsp)¿ Qué ?0x48(%rsp)¿ Qué ?0x30(%rsp), y 0x38(%rsp)Los modulos 32 son 4, 0, 8, 16 y 24: la misma estructura, sin parámetros para sembrar.
Construye el mismo
Ejecutar la demostración:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.pyLa salida es un informe JSON con cinco partes. dwarfEl título de la unidad, las estructuras y las funciones de la-O0Objeto.oraclestiene el diseño de las reglas de psABI, el ctypesLa distribución, la distribución DWARF, la ejecución nativa y un agreebandera.buildscontiene los accesos, la disposición inferida y la comparación para cada uno de los tres objetos. cxxcontiene los nombres desfigurados, ambos vtables, ambos registros de tipoinfo, la llamada virtual y las ranuras DWARF. toolsenumera las salidas de la herramienta capturadas.
Forma esperada:
{
"dwarf": {"unit": {"unitLength": 489, "version": 5, "unitType": "DW_UT_compile", "addressSize": 8, "abbrevOffset": 0}, "...": "..."},
"oracles": {"agree": true, "psabiRules": {"size": 32, "align": 8, "...": "..."}, "...": "..."},
"builds": {
"O0": {"layout": {"stride": 32, "size": 32, "gaps": [1, 2, 3, 10, 11, 12, 13, 14, 15], "...": "..."}, "comparison": {"matches": 5, "members": 5, "...": "..."}},
"O1": {"...": "..."},
"O2": {"...": "..."}
},
"cxx": {"virtualCalls": [{"slot": 2, "candidates": {"Shape": "__cxa_pure_virtual", "Rect": "_ZNK4Rect4areaEv"}, "...": "..."}], "...": "..."}
}Leer .code/main.pyen este orden:
ElfObjectSe leían secciones, símbolos y reubicaciones, yrelocated()se aplicaS + Aen una sección de descomposición.parse_abbreviations()yDwarfInfo._parse_unit()Lea el encabezado, la tabla de abreviaturas y las entradas, luego resuelvastrxyaddrxa través de las dos mesas.DwarfInfo.struct_layouts()yfunctions()convertir las entradas enStructLayoutyFunctionInforegistros, contype_name()¿ Qué ?type_size(), ytype_kind()¿ Qué ?parse_listing()¿ Qué ?parse_memory(), yaccess_width()Leer elllvm-objdump¿Qué es eso?trace_function()es la máquina del estado abstracto, yfold_unrolled()yinfer_layout()convertir los accesos en un diseño.compare_layouts()produce las filas de estado, ysysv_layout()yctypes_layout()son los otros dos oráculos.Demangler¿ Qué ?read_vtable()¿ Qué ?read_typeinfo(), yfind_virtual_calls()cubrir el objeto C++.
Hay cuatro órdenes más.python3 code/main.py dwarf FILE.oImprime las estructuras y funciones de cualquier objeto relocable de DWARF 5. python3 code/main.py infer LISTING.txt [FILE.o]ejecuta el pase en una lista y, con un objeto, compara con su DWARF. python3 code/main.py demangle NAME...y python3 code/main.py vtable FILE.ocubrir la mitad de C ++.
Usalo
En un nuevo objeto que construiste tú mismo:
- Captura .
llvm-objdump -d -rEl pase lee ese texto. - - ¿ Qué ?
python3 code/main.py inferEn el listado. Leer primero la línea de testigos: nombra las funciones cuyo primer parámetro se utilizó como base de memoria. Si una función falta, su parámetro nunca alcanzó un operando de memoria, o llegó a uno a través de un camino que el paso no rastrea. - Lea cada campo con su número de acceso. Un acceso es un testigo.
- Lea la evidencia de los pasos.
call-siteoscaleLa entrada es fuerte.loop-stepLa entrada sin otro tipo es débil, porque un bucle desarrollado informa el paso de varios elementos.unrolledaparece, compruebe el pliegue a mano. - Si un
-gconstruir existe, ejecutar la comparación y leer cada fila que no esmatch- ¿ Qué ?no-evidencefila nombra un miembro no se ha tocado función.unexpectednombre de filas a un acceso que el pase no pudo colocar. - Escriba el diseño en el administrador de tipos de su herramienta como un estructurado, y registre qué campos, nombres y tipos son suposiciones.
Mantenga los tres oráculos a su alcance.ctypes, y DWARF no está de acuerdo en un diseño que usted escribió, usted ha encontrado un estructurado, un campo de bits, o un objetivo con reglas diferentes.
Laboratorio interactivo
Laboratorio A: decodificar la entrada de miembro
Desde elcodedirectorio, Python abierto:
cd phases/13-tools-and-protocols/41-recovering-data-and-types/code
python3 -q- ¿Qué quieres decir ?
from main import *
elf = load_object("account-x86_64-O0-g.o")
dwarf = DwarfInfo(elf)
dwarf.header()
elf.section(".debug_info").data[0x1a3:0x1ac].hex(" ")
dwarf.dies[0x1a3].attributes
dwarf.type_name(dwarf.type_of(dwarf.dies[0x1a3]))Decodifica los nueve bytes en papel con la abreviatura 13 antes de mirar el diccionario.elf.section(".debug_str_offsets").data[8:16].hex(), y el trasladado,elf.relocated(".debug_str_offsets")[8:16].hex(), y explicar la diferencia.
Laboratorio B: rastrear una función
listing = load_listing("account-x86_64-O0-g.objdump.txt")
trace = trace_function("label_length", listing["label_length"])
[(hex(a.address), a.root, a.offset, a.width, a.kind) for a in trace.accesses]
trace.pointer_uses
trace_function("sum_balances", listing["sum_balances"]).loopsEscriba, antes de ejecutar, que acceso prueba que el offset 24 es un puntero.-O1lista y encontrar el leaq 0x1(%rcx), %rcxEn eltrace.increments¿ Qué ?
Laboratorio C: doblar el bucle vectorial
o2 = load_listing("account-x86_64-O2-g.objdump.txt")
trace = trace_function("sum_balances", o2["sum_balances"])
sorted({a.offset for a in trace.accesses})
folded, evidence = fold_unrolled(trace, trace.accesses)
sorted({a.offset for a in folded}), [(e.kind, e.value, e.text) for e in evidence]Entonces corre .analyze_build("O2", fold=False)["comparison"]y contar los unexpectedCambiar la regla de plegado para que requiere que el paso del bucle sea exactamentek * dconkal menos 2, y confirmen que el -O1el bucle, con un acceso y el paso 32, se deja solo.
Laboratorio D: el tablero y los nombres
shapes = load_object("shapes-x86_64-O0-g.o")
read_vtable(shapes, "_ZTV4Rect")
read_typeinfo(shapes, "_ZTI4Rect")
demangle("_ZNK4Rect4areaEv"), demangle("_Z8describeRK5Shapei")
find_virtual_calls(load_listing("shapes-x86_64-O0-g.objdump.txt"), [read_vtable(shapes, "_ZTV5Shape"), read_vtable(shapes, "_ZTV4Rect")])Calcule la ranura desde callq *0x10(%rax)en papel, luego confirme que contraDwarfInfo(shapes).struct("Rect").virtual_methodsEntonces cargue .shapes-x86_64-O1-g.oy tratarread_vtableLeer el libro.ElfErrory explicar por qué el símbolo está ausente.
Laboratorio de práctica
Recupera la estructura de la pila en mainEn el-O1¿ Qué ?
Requisitos:
- Extenderse
trace_function()Así que una región de ranura de marco puede ser una raíz.movapsTiendas que llenan0x0(%rsp)¿ Qué ?0x5f(%rsp)de la.data.rel.ro, que es el inicializador de 96 bytes derows¿ Qué ? - Atribuye los accesos en línea
0x24(%rsp)¿ Qué ?0x40(%rsp)¿ Qué ?0x48(%rsp)¿ Qué ?0x30(%rsp), y0x38(%rsp)y reportar sus compensaciones modulo un candidato paso. - Derivar el paso 32 del bucle en
0x120¿ Qué ?0x12c, donde%r14comienza a las 4 y avanza a las0x20hasta que0x64, y mostrar que el bucle se une0x64es tres elementos más el campo de compensación. - Comparar el resultado con el diseño de DWARF y alcanzar cinco coincidencias, y registrar qué tipos permanecen no probados.
- Añadir una prueba que falla cuando la raíz del marco se atribuye a un parámetro.
El éxito significapython3 code/main.py inferen el -O1listas de listados maincomo un testigo con una raíz de marco, y la comparación muestra cinco coincidencias demain¿Por qué no?
Lección Artefacto
Esta lección incluye outputs/skill-recovering-data-and-types.md. Utilice para convertir una lista y una configuración opcional de defecto en un diseño estructural con evidencia por campo, un paso con sus fuentes, una comparación con DWARF y una lista de lo que permaneció desconocido. Requiere la tabla de acceso, la prueba de paso, la verificación de plegado, el oráculo DWARF cuando existe, y una declaración de nombres y calificadores que ninguna instrucción puede proporcionar.
Verifique el hecho
Ejecutar la demostración y la suite determinista:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
python3 -m unittest discover -s code/tests -vLa verificación deberá demostrar:
- El título de la unidad, las 14 abreviaturas y los cinco elementos de compensación están de acuerdo con
llvm-dwarfdump - Los nombres llegan a través de los relocalizados
.debug_str_offsetsmesa y la unidadstr_offsets_baseyaddr_baseson 8 - Seis funciones llevan los rangos,
DW_OP_reg6 RBP, yDW_OP_fbreglas ubicaciones que muestra el vertedero, y sus direcciones bajas son iguales a la función de la lista comienza - un objeto sin
.debug_infoeleva, y el decodificador LEB128 maneja0x78como -8 pyelftoolsylldbimprime el mismo diseño que el lector- las reglas de psABI,
ctypes, DWARF, y la carrera nativa dan compensaciones0, 4, 8, 16, 24y tamaño 32, y el estruct reordenado es de 24 bytes - el parser de la lista lee 13 instrucciones en
bump, adjuntos .R_X86_64_PLT32 bump-0x4a la llamada en0x194, y encuentra el salto hacia atrás en0x72 - operandos de memoria, anchos, tipos y signos decodificados del texto AT&T y el mnemónico
bumpEn el-O0lee su puntero hacia atrás desde la ranura del marco y hace tres accesos en el despacho 4- El paso viene de
shlq $0x5En el-O0, desdeaddq $0x20, %rdxEn el-O1, y de ocho cargas dobladas por un paso de 256 en-O2 label_lengthprueba el puntero en el desplazamiento 24 desreferenciándolomainpasa un objeto de marco en compensaciones 0,0x20, y0x40En el-O0y no llama nada más queprintfEn el-O1- Cada construcción coincide con cinco de los cinco miembros con tamaño 32 y huecos iguales al relleno DWARF
- sin doblar el
-O2crear informes siete campos más allá de byte 32 y no tamaño - El desangler reproduce todos los 19
llvm-cxxfiltcuerdas - tanto las tablas de vtables, tanto los registros de tipoinfo, y la llamada virtual en
0x16Resolver a la ranura 2, y DWARF está de acuerdo - el
-O1Objeto C++ mantiene el patrón de llamada y no tiene vtable - Las capturas de Ghidra, rizin y radar2 muestran la estructura con DWARF y los desplazamientos crudos sin ella
- La figura interactiva incorpora los mismos datos que el análisis produce
Modos de falla de producción
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
| Unrolled loop | Eight fields 32 bytes apart and a 232-byte struct | Loop step 256 equals 8 times the offset delta 32, so one field at offset 4 |
| Stride read from one increment | sizeof 256 from addq $0x100 | The greatest common divisor of every stride source, with the scalar tail's addq $0x20 |
| 8-byte field called a pointer | void * at offset 24 | A later access uses the loaded value as a base, or the kind stays int64 |
| Pointer called an integer | long at offset 24 | The dereference at 0xfb, recorded as pointer proof |
| Narrow compare | char with a sign | cmpb carries no sign; only movsbl or movzbl does |
| Five functions assumed to share a type | One struct with no evidence of aliasing | Call sites in main that pass one frame object, or an explicit analyst assertion |
Strings read from .debug_str by index | Garbage names from a relocatable object | str_offsets_base plus the relocated .debug_str_offsets entry |
| Addresses read as numbers | low_pc of 0 for every function | addrx through the relocated .debug_addr table |
| DWARF 4 object | A reader that misparses the header | Version check on the second header field; 4 has no unit_type byte |
| Missing vtable | "the class has no virtual functions" | The -O1 object inlined every call; the -O0 object has the table and its relocations |
| Vtable slot read from the wrong base | Off-by-two slot numbers | The address point is 16 bytes past the symbol, after offset-to-top and typeinfo |
| Mangled name stripped | Z8describeRK5Shapei left as is | --no-strip-underscore on macOS, or a demangler that checks for _Z |
| Struct typed from one tool's guess | char *param_1 taken as the field type | The width and the dereference, written as evidence, and the name marked as a guess |
Regla de funcionamiento
Recupere un campo de un acceso y un paso de un bucle, y adjunta la instrucción a cada uno. Doble los bucles desarrollados antes de confiar en un desplazamiento que pase el paso. Lea DWARF a través de sus abreviaturas y sus tablas reubicadas, nunca indexando en una sección de cadena. Comparar campo por campo y escribir cada miembro sin evidencia, cada puntero no probado, y cada nombre como desconocido. En un objeto de C ++, lea la tabla de sus reubicaciones y la ranura de la llamada, luego use DWARF para confirmar la ranura que ha leído.
Leer más
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, section 3.1.2: Data Representation
- Procedure Call Standard for the Arm 64-bit Architecture (AAPCS64), section 5.10: Composite Types
- Writing ARM64 code for Apple platforms
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1, section 3.7.5: Specifying an Offset
- DWARF Debugging Information Format Version 5, sections 2.1, 5.7.6, 7.5.1.1, 7.5.3, 7.5.6
- Itanium C++ ABI, sections 2.5 (Virtual Table Layout), 2.9 (Run-Time Type Information), 5.1 (External Names)
- Microsoft Learn: Decorated namesy Name Decoration
- llvm-dwarfdump command guidey llvm-cxxfilt command guide
- Python ctypes: Structure layout, sizeof, alignment
- Ghidra Data Type Manager¿ Qué ?Decompiler Concepts¿ Qué ?Decompiler Options, y Ghidra 12.1.4 release
- IDA 9.1 user guide: Types¿ Qué ?IDA 9.4 release post, y IDA Free
- Binary Ninja user guide: Typesy Binary Ninja 6.0 Krypton
- radare2 6.2.4 releasey rizin v0.9.1 release
- GDB manual: Examining the Symbol Table, ptype /o
- pyelftools on PyPI
- Lee, Avgerinos, Brumley, "TIE: Principled Reverse Engineering of Types in Binary Programs" (NDSS 2011)
- Noonan, Loginov, Cok, "Polymorphic Type Inference for Machine Code" (PLDI 2016)y the retypd repository
- Zhang et al., "OSPREY: Recovery of Variable and Data Structure via Probabilistic Analysis for Stripped Binary" (IEEE S&P 2021)
- Chen et al., "Augmenting Decompiler Output with Learned Variable Names and Types" (USENIX Security 2022)
- Xie et al., "ReSym: Harnessing LLMs to Recover Variable and Data Structure Symbols from Stripped Binaries" (CCS 2024)
- Chen et al., "RecStruct" (USENIX Security 2026)
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.