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¶
- Busca el tamano total primero:
malloc(sizeof(X))ooperator new(size)te da una cota fuerte.- Mapea offsets de acceso:
- lecturas/escrituras tipo
[base+0x4],[base+0x38],[base+0x3C]. - Inferi tipos por uso:
mov eax, [base+off]+%d=>int%u=>unsigned int%c=>char%scon direccion de campo => array decharochar*segun contexto.- Detecta padding:
- si ves salto de offset no explicable (ej.
0x36a0x38), hay relleno. - Valida con flujo completo:
- constructor/input -> procesamiento -> output.
- 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¶
- Abrir la funcion que usa el puntero al struct (
main,printEmployeeInfo). - Asegurar pseudocodigo disponible (
F5). - 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 Conflictspara 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
Finalizeen Structure Builder. - Generar definicion C-like del struct.
- Aplicar tipo al argumento de
printEmployeeInfoy al puntero enmain.
7) Validar con herramientas nativas de tipos (IDA)¶
En Local Types (Shift + F1), segun docs oficiales:
Inspara agregar tipos.Ctrl + Epara 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:
- coincide con
sizeofobservado (malloc(0x40)), - todos los offsets usados en asm tienen campo valido,
- no quedan accesos "raros" sin tipar.
Qué hace el programa¶
- Reserva
0x40bytes conmallocpara una estructuraEmployee. - Si falla la reserva, imprime error y retorna
1. - Si la reserva es exitosa:
- pide ID (
%d), - pide nombre (
%s), - pide género (
%c), - pide edad (
%u), - pide salario mensual (
%d). - Llama a
printEmployeeInfopara mostrar el registro. - Libera memoria con
freey retorna0.
Diferencias entre pseudocódigo y ASM original¶
- Offsets explícitos de campos
- El pseudocódigo muestra
emp->campo. -
En ASM se ven desplazamientos concretos:
+0x4(name),+0x36(gender),+0x38(age),+0x3C(monthlySalary). -
Cálculo de salario anual materializado
- En pseudo suele verse
12 * emp->monthlySalaryinline. -
En ASM aparece
imul eax, [rax+3Ch], 0Ch(multiplicación por 12) y guardado temporal en stack enmain. -
Control de flujo if/else
- Pseudo:
if (emp) ... else .... -
ASM:
cmp [emp],0+jnzal camino éxito y salto al epílogo para el error. -
Calling convention x64 (MSVC ABI)
- Pseudo no muestra registros.
-
ASM usa
RCXpara primer argumento (formato enprintf/scanf_s) yRDXpara siguiente argumento. -
Prolog/epilog explícitos
- ASM de
main:sub rsp, 38h/add rsp, 38h, másretn.
Cambios aplicados en IDA¶
Comentarios ASM línea por línea¶
- Se agregaron comentarios en todas las instrucciones de
main(54instrucciones), explicando cada operación.
Renombres de funciones¶
?printEmployee@@YAXPEAUEmployee@@@Z→printEmployeeInfo.- Thunks de import con sufijo
_0renombrados a prefijothunk_(p.ej.memset_0→thunk_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 mostrandoEmployee *por inferencia previa/caché, pero la llamada ya refleja cast aEmployeeData.
Renombres de variables¶
- Local de stack en
main:emp→employee. - Local temporal:
annualSalary→annual_salary_calc.
Nota: en vista decompilada puede seguir apareciendo
empen 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;
}