Saltar a contenido

Análisis estático - binario actual

Contexto

Se analizó de forma 100% estática el binario actualmente cargado en IDA, centrado en main() (0x140001160) y en printEmployeeInfo() (0x140001000).

Además, se trabajó explícitamente sobre Local Types para dejar un struct más legible:

  • EmployeeData (nuevo tipo local definido)
  • aplicado en la firma de printEmployeeInfo(): EmployeeData *employee

Mini apunte extra: detectar structs y padding en binario

  1. Busca el tamano total primero:
  2. malloc(sizeof(X)) o operator new(size) te da una cota fuerte.
  3. Mapea offsets de acceso:
  4. lecturas/escrituras tipo [base+0x4], [base+0x38], [base+0x3C].
  5. Inferi tipos por uso:
  6. mov eax, [base+off] + %d => int
  7. %u => unsigned int
  8. %c => char
  9. %s con direccion de campo => array de char o char* segun contexto.
  10. Detecta padding:
  11. si ves salto de offset no explicable (ej. 0x36 a 0x38), hay relleno.
  12. Valida con flujo completo:
  13. constructor/input -> procesamiento -> output.
  14. si todos los accesos cierran, el layout es consistente.

Plugins/herramientas recomendadas en IDA para acelerar:

  • HexRaysPyTools (si es compatible con tu version): ayuda a propagar tipos y reconstruir estructuras desde uso en decompilado.
  • Class Informer: mas orientado a C++/RTTI, util cuando hay clases y vtables.
  • ida-pro-mcp (MCP): sirve para asistir renombres/comentarios/tipos, pero siempre validar manualmente offsets y padding.

Plugin HRT (HexRaysPyTools) aplicado

Cuando encontramos un struct con campos claros, es buena idea usar un plugin como HexRaysPyTools para propagar tipos y ayudar a reconstruir la estructura en IDA. Repositorio oficial
Documentacion de tipos de IDA

Nota de compatibilidad

El README original del plugin indica soporte para IDA 7.x.
Si estas en IDA 9.x, puede requerir fork/parches o no funcionar completo.

Mini tutorial: usar HRT para reconstruir EmployeeData

1) Instalacion basica

Segun el README:

  • copiar HexRaysPyTools.py
  • copiar carpeta HexRaysPyTools
  • pegar ambos en la carpeta de plugins de IDA
  • reiniciar IDA

2) Preparar el terreno en IDA

  1. Abrir la funcion que usa el puntero al struct (main, printEmployeeInfo).
  2. Asegurar pseudocodigo disponible (F5).
  3. Identificar la variable base (emp / employee).

3) Abrir Structure Builder

  • Hotkey: Alt + F8

Este panel concentra offsets, tipos candidatos y conflictos.

4) Escanear variable

En pseudocodigo, clic derecho sobre el puntero:

  • Scan Variable: levanta campos usados en la funcion actual.
  • Deep Scan Variable: recorre funciones relacionadas y agrega mas evidencia.

Si tienes funciones que devuelven el puntero:

  • clic derecho en funcion -> Deep Scan Returned Value.

5) Resolver conflictos y padding

En Structure Builder:

  • revisar campos con mismo offset pero tipos distintos.
  • usar Resolve Conflicts para limpiar inferencias debiles.
  • ajustar manualmente donde haga falta.

Para este caso EmployeeData, resultado esperado:

  • +0x00 -> int id
  • +0x04 -> char name[50]
  • +0x36 -> char gender
  • +0x37 -> padding
  • +0x38 -> unsigned int age
  • +0x3C -> int monthlySalary

6) Finalizar y aplicar tipo

  • Boton Finalize en Structure Builder.
  • Generar definicion C-like del struct.
  • Aplicar tipo al argumento de printEmployeeInfo y al puntero en main.

7) Validar con herramientas nativas de tipos (IDA)

En Local Types (Shift + F1), segun docs oficiales:

  • Ins para agregar tipos.
  • Ctrl + E para editar.
  • Insert gap... / Remove gap... para ajustar padding.

En esta etapa valida que el decompilado quede coherente en:

  • lecturas/escrituras por offset,
  • formatos de printf/scanf,
  • operaciones sobre campos (imul, comparaciones, etc.).

8) Criterio de calidad

Da por bueno el struct cuando:

  1. coincide con sizeof observado (malloc(0x40)),
  2. todos los offsets usados en asm tienen campo valido,
  3. no quedan accesos "raros" sin tipar.

Qué hace el programa

  1. Reserva 0x40 bytes con malloc para una estructura Employee.
  2. Si falla la reserva, imprime error y retorna 1.
  3. Si la reserva es exitosa:
  4. pide ID (%d),
  5. pide nombre (%s),
  6. pide género (%c),
  7. pide edad (%u),
  8. pide salario mensual (%d).
  9. Llama a printEmployeeInfo para mostrar el registro.
  10. Libera memoria con free y retorna 0.

Diferencias entre pseudocódigo y ASM original

  1. Offsets explícitos de campos
  2. El pseudocódigo muestra emp->campo.
  3. En ASM se ven desplazamientos concretos: +0x4 (name), +0x36 (gender), +0x38 (age), +0x3C (monthlySalary).

  4. Cálculo de salario anual materializado

  5. En pseudo suele verse 12 * emp->monthlySalary inline.
  6. En ASM aparece imul eax, [rax+3Ch], 0Ch (multiplicación por 12) y guardado temporal en stack en main.

  7. Control de flujo if/else

  8. Pseudo: if (emp) ... else ....
  9. ASM: cmp [emp],0 + jnz al camino éxito y salto al epílogo para el error.

  10. Calling convention x64 (MSVC ABI)

  11. Pseudo no muestra registros.
  12. ASM usa RCX para primer argumento (formato en printf/scanf_s) y RDX para siguiente argumento.

  13. Prolog/epilog explícitos

  14. ASM de main: sub rsp, 38h / add rsp, 38h, más retn.

Cambios aplicados en IDA

Comentarios ASM línea por línea

  • Se agregaron comentarios en todas las instrucciones de main (54 instrucciones), explicando cada operación.

Renombres de funciones

  • ?printEmployee@@YAXPEAUEmployee@@@ZprintEmployeeInfo.
  • Thunks de import con sufijo _0 renombrados a prefijo thunk_ (p.ej. memset_0thunk_memset).

Structs / Local Types

  • Se definió en Local Types:
typedef struct EmployeeData {
    int id;
    char name[50];
    char gender;
    unsigned char _pad_37;
    unsigned int age;
    int monthlySalary;
} EmployeeData;
00000000 struct Employee // sizeof=0x40
00000000 {
00000000     int id;
00000004     char name[50];
00000036     char gender;
00000037     // padding byte
00000038     unsigned int age;
0000003C     int monthlySalary;
00000040 };
  • Se aplicó en la firma de printEmployeeInfo().
  • En main(), el decompilador puede seguir mostrando Employee * por inferencia previa/caché, pero la llamada ya refleja cast a EmployeeData.

Renombres de variables

  • Local de stack en main: empemployee.
  • Local temporal: annualSalaryannual_salary_calc.

Nota: en vista decompilada puede seguir apareciendo emp en algunos puntos por caché/limitación de refresco del decompilador, aunque el stack frame ya quedó renombrado.

unk_* / tipos

  • En este binario no se detectaron símbolos unk_* relevantes equivalentes al caso anterior de chars sueltos.

Nota importante sobre scanf_s (pseudo vs código fuente)

Comparado con el código fuente de referencia en ANALISIS_ESTATICO_BINARIO_ACTUAL.md, el binario muestra llamadas a scanf_s de 2 argumentos para %s y %c en main(), sin tamaño explícito adicional en la llamada observada en ASM.

Esto puede pasar por diferencias de compilación/configuración (o simplificación del decompilador), pero en C seguro la forma recomendada para scanf_s con %s/%c incluye longitud.

Pseudocódigo final

int __fastcall main(int argc, const char **argv, const char **envp)
{
  Employee *employee; // [rsp+28h] [rbp-10h]

  employee = (Employee *)malloc(0x40u);
  if ( employee )
  {
    printf("Enter employee ID: ");
    scanf_s("%d", employee);
    printf("Enter name: ");
    scanf_s("%s", employee->name);
    printf("Enter gender (M/F): ");
    scanf_s(" %c", &employee->gender);
    printf("Enter age: ");
    scanf_s("%u", &employee->age);
    printf("Enter monthly salary: ");
    scanf_s("%d", &employee->monthlySalary);
    printEmployeeInfo((struct EmployeeData *)employee);
    free(employee);
    return 0;
  }
  else
  {
    printf("Memory allocation failed!\n");
    return 1;
  }
}

Codigo original

#include <stdio.h>
#include <stdlib.h>
#define _CRT_SECURE_NO_WARNINGS

struct Employee {
    int id;
    char name[50];
    char gender;
    unsigned int age;
    int monthlySalary;
};

void printEmployee(struct Employee* emp) {
    printf("\n--- Employee Information ---\n");
    printf("ID: %d\n", emp->id);
    printf("Name: %s\n", emp->name);
    printf("Gender: %c\n", emp->gender);
    printf("Age: %u\n", emp->age);
    printf("Monthly Salary: %d\n", emp->monthlySalary);
    printf("Annual Salary: %d\n", emp->monthlySalary * 12);
}

int main() {
    struct Employee* emp = (struct Employee*)malloc(sizeof(struct Employee));
    if (emp == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }

    printf("Enter employee ID: ");
    scanf_s("%d", &emp->id);

    printf("Enter name: ");
    scanf_s("%s", emp->name, (unsigned)_countof(emp->name));

    printf("Enter gender (M/F): ");
    scanf_s(" %c", &emp->gender, 1);

    printf("Enter age: ");
    scanf_s("%u", &emp->age);

    printf("Enter monthly salary: ");
    scanf_s("%d", &emp->monthlySalary);

    printEmployee(emp);
    free(emp);
    return 0;
}