Skip to content

Commit 636b3b2

Browse files
authored
ROP: fix ROP(ELF(exe)).leave is None in some ELF (Gallopsled#2506)
* ROP: fix `self.leave is None` in some libc When `pop rbp; pop rsp; ret;` exists in libc, it is chosen instead of `leave`. Add a specific `order` to filter out the former one. * add changelog for Gallopsled#2506 * ROP: add sp_move when `pop rsp` * CHANGELOG: mv Gallopsled#2506 to `5.0.0`
1 parent 447ac94 commit 636b3b2

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ The table below shows which release corresponds to each branch, and what date th
8383
- [#2527][2527] Allow setting debugger path via `context.gdb_binary`
8484
- [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool.
8585
- [#2529][2529] Add LoongArch64 support
86+
- [#2506][2506] ROP: fix `ROP(ELF(exe)).leave` is `None` in some ELF
8687
- [#2504][2504] doc: add example case for `tuple` (host, port pair) in `gdb.attach`
8788

8889
[2519]: https://github.com/Gallopsled/pwntools/pull/2519
@@ -94,6 +95,7 @@ The table below shows which release corresponds to each branch, and what date th
9495
[2527]: https://github.com/Gallopsled/pwntools/pull/2527
9596
[2530]: https://github.com/Gallopsled/pwntools/pull/2530
9697
[2529]: https://github.com/Gallopsled/pwntools/pull/2529
98+
[2506]: https://github.com/Gallopsled/pwntools/pull/2506
9799
[2504]: https://github.com/Gallopsled/pwntools/pull/2504
98100

99101
## 4.15.0 (`beta`)

pwnlib/rop/rop.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,8 @@ def __getattr__(self, k):
13911391
if pop.match(insn):
13921392
regs.append(pop.match(insn).group(1))
13931393
sp_move += context.bytes
1394+
if 'sp' in insn:
1395+
sp_move += 9999999
13941396
elif add.match(insn):
13951397
arg = int(add.match(insn).group(1), 16)
13961398
sp_move += arg
@@ -1418,7 +1420,7 @@ def __getattr__(self, k):
14181420
if not set(['rsp', 'esp']) & set(regs):
14191421
self.pivots[sp_move] = addr
14201422

1421-
leave = self.search(regs=frame_regs, order='regs')
1423+
leave = self.search(regs=frame_regs, order='leav')
14221424
if leave and leave.regs != frame_regs:
14231425
leave = None
14241426
self.leave = leave
@@ -1451,15 +1453,17 @@ def search(self, move = 0, regs = None, order = 'size'):
14511453
pointer is adjusted.
14521454
regs(list): Minimum list of registers which are popped off the
14531455
stack.
1454-
order(str): Either the string 'size' or 'regs'. Decides how to
1456+
order(str): Either the string 'size', 'leav' or 'regs'. Decides how to
14551457
order multiple gadgets the fulfill the requirements.
14561458
14571459
The search will try to minimize the number of bytes popped more than
14581460
requested, the number of registers touched besides the requested and
14591461
the address.
14601462
14611463
If ``order == 'size'``, then gadgets are compared lexicographically
1462-
by ``(total_moves, total_regs, addr)``, otherwise by ``(total_regs, total_moves, addr)``.
1464+
by ``(total_moves, total_regs, addr)``, if ``order == 'regs'``,
1465+
then by ``(total_regs, total_moves, addr)``. ``order == 'leav'``
1466+
is specifically for ``leave`` insn.
14631467
14641468
Returns:
14651469
A :class:`.Gadget` object
@@ -1471,8 +1475,9 @@ def search(self, move = 0, regs = None, order = 'size'):
14711475
# Search for an exact match, save the closest match
14721476
key = {
14731477
'size': lambda g: (g.move, len(g.regs), g.address),
1474-
'regs': lambda g: (len(g.regs), g.move, g.address)
1475-
}[order]
1478+
'regs': lambda g: (len(g.regs), g.move, g.address),
1479+
'leav': lambda g: ('leave' not in g.insns, len(g.regs), g.address)
1480+
}[order] # False is prior than True
14761481

14771482
try:
14781483
result = min(matches, key=key)

0 commit comments

Comments
 (0)