I really don’t know if this is the result of previous ignorance or that my RE focus has shifted, but I feel like the rate at which hexrays errors out with ‘incorrect function frame’ seems to be rather high with IDA 9.2.
Supporting most of my cases, I wrote this script to auto-fix function frames:
import idc
import idautils
import ida_frame
import ida_funcs
def analyze_frame(ea):
func = ida_funcs.get_func(ea)
if not func:
return None
locals_size = 0
gpr_count = 0
xmm_count = 0
gprs = {'rbp','rbx','rsi','rdi','r12','r13','r14','r15'}
for head in idautils.Heads(func.start_ea, min(func.start_ea + 0x100, func.end_ea)):
mnem = idc.print_insn_mnem(head)
op0 = idc.print_operand(head, 0)
op1 = idc.print_operand(head, 1)
# Count GPR pushes
if mnem == 'push' and op0.lower() in gprs:
gpr_count += 1
# Count XMM saves (vmovaps [...], xmmN)
if mnem == 'vmovaps' and 'xmm' in op1.lower() and '[' in op0:
xmm_count += 1
# Pattern 1: lea rbp, [rax - LOCALS] or lea rbp, [rsp + X]
if mnem == 'lea' and 'rbp' in op0.lower():
# Extract the constant
if '-' in op1:
# lea rbp, [rax-0D8h] → locals = 0xD8
val = op1.split('-')[-1].rstrip('h]').strip()
locals_size = int(val, 16)
break
elif '+' in op1 and 'rsp' in op1.lower():
# lea rbp, [rsp+80h] → need post-AND [rbp+NNNN+var] pattern
# Check for AND alignment next
continue
# Pattern 2: Check for aligned frame, extract from var access
if mnem == 'and' and 'rbp' in op0.lower() and 'FFFFFFE0' in op1.upper():
# AVX-aligned frame - scan forward for [rbp+NNNN+var_XXX]
for h2 in idautils.Heads(head, min(head + 0x50, func.end_ea)):
disasm = idc.generate_disasm_line(h2, 0)
if 'rbp+' in disasm and 'var_' in disasm:
# Extract NNNN from [rbp+NNNNh+var_XXX]
import re
match = re.search(r'\[rbp\+([0-9A-Fa-f]+)h\+var_', disasm)
if match:
locals_size = int(match.group(1), 16)
break
break
saved_regs = (gpr_count * 8) + (xmm_count * 16)
return {
'locals': locals_size,
'saved_regs': saved_regs,
'gpr_count': gpr_count,
'xmm_count': xmm_count
}
def fix_frame(ea):
result = analyze_frame(ea)
if not result:
print("Failed to analyze")
return
print(f"Locals: 0x{result['locals']:X}")
print(f"Saved: 0x{result['saved_regs']:X} ({result['gpr_count']} GPRs, {result['xmm_count']} XMMs)")
func = ida_funcs.get_func(ea)
ida_frame.del_frame(func)
ida_frame.add_frame(func, result['locals'], result['saved_regs'], 0)
print("Frame fixed")
# Usage:
# i.e. fix_frame(0x146CD3840)
What breaks IDA’s ability to do this properly?