Fix pwndbg.disasm.near with disabled caching (#465)

Before this changes `context_disasm` produced different display based on memoization settings.

The bug can be seen below:

```
[dc@dc:pwndbg|dev $%]$ gdb ~/test/a.out
pwndbg: loaded 166 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from /home/dc/test/a.out...(no debugging symbols found)...done.
pwndbg> set context-sections disasm
Set which context sections are displayed (controls order) to 'disasm'
pwndbg> entry
Temporary breakpoint 1 at 0x400080

Temporary breakpoint 1, 0x0000000000400080 in _start ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>

Breakpoint *0x400080
pwndbg> python import pwndbg; pwndbg.memoize.memoize.caching=False
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
    ↓
 ► 0x400080 <_start>    jmp    _start <0x400080>
Breakpoint *0x400080
pwndbg>
```

The tested binary can be reproduced with this assembly code:

```asm
global _start

_start:
jmp $
```

Compiled as `nasm -f elf64 code.asm && ld code.o`.

---

About the bug:

The check for multiple identical loops or rets is done using `set(insns[-3:])`. Before this hapens the `insns` is filled with the results of `one(address)`. This calls `get_one_instruction(address)` which is cached until `reset_on_cont`. As a result, when caching is enabled the `get_one_instruction` returns the same `capstone.CsInsn` instances for given address. When it is disabled, we return other instances which are identical.

The problem was that `set(insns[-3:])` creates a set based on `capstone.CsInsn` instances and not on the instruction addresses.

The fix changes this behavior so that we compare last 3 instruction addresses.
pull/466/head
Disconnect3d 8 years ago committed by GitHub
parent 7d77bf7004
commit 812186aeef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -165,7 +165,7 @@ def near(address, instructions=1, emulate=False, show_prev_insns=True):
if current is None or not pwndbg.memory.peek(address):
return []
insns = []
insns = []
# Try to go backward by seeing which instructions we've returned
# before, which were followed by this one.
@ -238,7 +238,7 @@ def near(address, instructions=1, emulate=False, show_prev_insns=True):
# but any repeats after that are removed.
#
# This helps with infinite loops and RET sleds.
while insns and len(insns) > 2 and len(set(insns[-3:])) == 1:
while insns and len(insns) > 2 and insns[-3].address == insns[-2].address == insns[-1].address:
del insns[-1]
return insns

Loading…
Cancel
Save