Can anyone tell me how to clean up the code which contains the following blocks:

call $+5
pop reg
add reg, 08BE96h

In other words I need to change all such blocks to:

mov reg, offset

Edit # 1

List all block EA's:

ea = ScreenEA()
for funcea in Functions(SegStart(ea), SegEnd(ea)):
    E = list(FuncItems(funcea))
    for e in E:
        if idc.GetMnem(e) == "call" and idc.GetOpnd(e, 0) == "$+5" and idc.GetMnem(e + 5) == "pop":
            print "%x" % e
  • Are you asking for a full idc/idapython script?
    – DCoder
    May 22 '13 at 10:39
  • Well, if such script exists... May 22 '13 at 11:05
  • It is definitely possible to patch IDA database content through IDC (PatchByte). If nobody else replies, I will try to come up with my own version later.
    – DCoder
    May 22 '13 at 13:38

Here's a rough implementation that scans the currently focused function for this pattern:

call $+5
pop <reg>
op <reg>, imm32

And changes it to

mov <reg>, <computed imm32>

Adapting it to scan different ranges and support more instructions is left as an exercise to the reader.

def simplify(tgtEA):
  ins1 = tgtEA
  if not idc.isHead(idc.GetFlags(ins1)):
    ins1 = idaapi.prev_head(ins1, limits.startEA)

  ins2 = idaapi.next_head(ins1, limits.endEA)
  if ins2 == 0xFFFFFFFF:
    return False

  ins3 = idaapi.next_head(ins2, limits.endEA)
  if ins3 == 0xFFFFFFFF:
    return False

  print "Scanning instructions at %d, %d, %d" % (ins1, ins2, ins3)

  result = ins2
  matched = False
  reg = 0

  if idc.GetMnem(ins1) == "call":
    if idc.GetOpnd(ins1, 0) == "$+5":
      if idc.GetMnem(ins2) == "pop":
        if idc.GetOpType(ins3, 0) == 1:
          reg = GetOperandValue(ins2, 0)
          if idc.GetOperandValue(ins3, 0) == reg:
            op = idc.GetMnem(ins3)
            optype = idc.GetOpType(ins3, 1)

            if op == "inc":
              matched = True
              result = result + 1
            elif op == "dec":
              matched = True
              result = result - 1
            elif op == "add":
              if optype == 5:
                # optype 5 = immediate
                matched = True
                offset = idc.GetOperandValue(ins3, 1)
                result = result + offset

  if matched:
    # patch the bytes by hand - there's no nice API to assemble("foo bar")?
    # MOV r32, imm32 = 0xB8 /rd 
    # reg is the register number acquired from IDA operand value
    # IDA uses the same register numbering as Intel
    regbyte = 0xB8 + reg;
    idaapi.patch_byte(ins1, regbyte)
    idaapi.patch_long(ins1 + 1, result)
    idaapi.patch_word(ins1 + 5, 0x9090) # NOP NOP 
    return True

  return False

# ----

curEA = ScreenEA()
tgtEA = idaapi.askaddr(curEA, "Enter target address")
#tgtEA = curEA
if tgtEA is None:

f = idaapi.get_func(tgtEA)
if not f:
  print "No function at 0x%x" % (tgtEA)

limits = idaapi.area_t()
if not idaapi.get_func_limits(f, limits):
  print "No limits in this function?!"

print "Scanning function from %x to %x" % (limits.startEA, limits.endEA)

itEA = limits.startEA
while itEA < limits.endEA:
  if simplify(itEA):
    itEA = idaapi.next_head(itEA + 7, limits.endEA)
    itEA = idaapi.next_head(itEA, limits.endEA)
  • Thanks, but I found that your script needs to be corrected as it breaks the code in my case. I'll post the result in a few days. May 23 '13 at 19:32

call $+5 is 5 byte (e8 00000000) pop reg is 1 byte add reg will be 3 and more bytes

if you require call $+5 to become mov reg , const

you first need to compute the return address + the value of constant

in your case the address of pop reg + 0xbe96

and then use a 5 byte mov REG, computed value

that leaves 4 + bytes which you need to nop out so that the execution path remains the same if you did not nop the bytes then the left out garbage bytes can alter the execution path

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.