Coverage for gef.py: 71.6387%

7578 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-12 03:36 +0000

1####################################################################################### 

2# GEF - Multi-Architecture GDB Enhanced Features for Exploiters & Reverse-Engineers 

3# 

4# by @_hugsy_ 

5####################################################################################### 

6# 

7# GEF is a kick-ass set of commands for X86, ARM, MIPS, PowerPC and SPARC to 

8# make GDB cool again for exploit dev. It is aimed to be used mostly by exploit 

9# devs and reversers, to provides additional features to GDB using the Python 

10# API to assist during the process of dynamic analysis. 

11# 

12# GEF fully relies on GDB API and other Linux-specific sources of information 

13# (such as /proc/<pid>). As a consequence, some of the features might not work 

14# on custom or hardened systems such as GrSec. 

15# 

16# Since January 2020, GEF solely support GDB compiled with Python3 and was tested on 

17# * x86-32 & x86-64 

18# * arm v5,v6,v7 

19# * aarch64 (armv8) 

20# * mips & mips64 

21# * powerpc & powerpc64 

22# * sparc & sparc64(v9) 

23# 

24# For GEF with Python2 (only) support was moved to the GEF-Legacy 

25# (https://github.com/hugsy/gef-legacy) 

26# 

27# To start: in gdb, type `source /path/to/gef.py` 

28# 

29####################################################################################### 

30# 

31# gef is distributed under the MIT License (MIT) 

32# Copyright (c) 2013-2023 crazy rabbidz 

33# 

34# Permission is hereby granted, free of charge, to any person obtaining a copy 

35# of this software and associated documentation files (the "Software"), to deal 

36# in the Software without restriction, including without limitation the rights 

37# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

38# copies of the Software, and to permit persons to whom the Software is 

39# furnished to do so, subject to the following conditions: 

40# 

41# The above copyright notice and this permission notice shall be included in all 

42# copies or substantial portions of the Software. 

43# 

44# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

45# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

46# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

47# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

48# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

49# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 

50# SOFTWARE. 

51 

52import abc 

53import argparse 

54import ast 

55import atexit 

56import binascii 

57import codecs 

58import collections 

59import configparser 

60import ctypes 

61import enum 

62import functools 

63import hashlib 

64import importlib 

65import importlib.util 

66import inspect 

67import itertools 

68import os 

69import pathlib 

70import platform 

71import re 

72import shutil 

73import site 

74import socket 

75import string 

76import struct 

77import subprocess 

78import sys 

79import tempfile 

80import time 

81import traceback 

82import warnings 

83from functools import lru_cache 

84from io import StringIO, TextIOWrapper 

85from types import ModuleType 

86from typing import (Any, ByteString, Callable, Dict, Generator, Iterable, 

87 Iterator, List, NoReturn, Optional, Sequence, Set, Tuple, Type, 

88 Union) 

89from urllib.request import urlopen 

90 

91GEF_DEFAULT_BRANCH = "main" 

92GEF_EXTRAS_DEFAULT_BRANCH = "main" 

93 

94def http_get(url: str) -> Optional[bytes]: 

95 """Basic HTTP wrapper for GET request. Return the body of the page if HTTP code is OK, 

96 otherwise return None.""" 

97 try: 

98 http = urlopen(url) 

99 return http.read() if http.getcode() == 200 else None 

100 except Exception: 

101 return None 

102 

103 

104def update_gef(argv: List[str]) -> int: 

105 """Try to update `gef` to the latest version pushed on GitHub main branch. 

106 Return 0 on success, 1 on failure. """ 

107 ver = GEF_DEFAULT_BRANCH 

108 latest_gef_data = http_get(f"https://raw.githubusercontent.com/hugsy/gef/{ver}/scripts/gef.sh") 

109 if not latest_gef_data: 

110 print("[-] Failed to get remote gef") 

111 return 1 

112 with tempfile.NamedTemporaryFile(suffix=".sh") as fd: 

113 fd.write(latest_gef_data) 

114 fd.flush() 

115 fpath = pathlib.Path(fd.name) 

116 return subprocess.run(["bash", fpath, ver], stdout=subprocess.DEVNULL, 

117 stderr=subprocess.DEVNULL).returncode 

118 

119 

120try: 

121 import gdb # type:ignore 

122except ImportError: 

123 # if out of gdb, the only action allowed is to update gef.py 

124 if len(sys.argv) >= 2 and sys.argv[1].lower() in ("--update", "--upgrade"): 

125 sys.exit(update_gef(sys.argv[2:])) 

126 print("[-] gef cannot run as standalone") 

127 sys.exit(0) 

128 

129 

130GDB_MIN_VERSION = (8, 0) 

131GDB_VERSION = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) 

132PYTHON_MIN_VERSION = (3, 6) 

133PYTHON_VERSION = sys.version_info[0:2] 

134 

135DEFAULT_PAGE_ALIGN_SHIFT = 12 

136DEFAULT_PAGE_SIZE = 1 << DEFAULT_PAGE_ALIGN_SHIFT 

137 

138GEF_RC = (pathlib.Path(os.getenv("GEF_RC", "")).absolute() 

139 if os.getenv("GEF_RC") 

140 else pathlib.Path().home() / ".gef.rc") 

141GEF_TEMP_DIR = os.path.join(tempfile.gettempdir(), "gef") 

142GEF_MAX_STRING_LENGTH = 50 

143 

144LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME = "main_arena" 

145ANSI_SPLIT_RE = r"(\033\[[\d;]*m)" 

146 

147LEFT_ARROW = " ← " 

148RIGHT_ARROW = " → " 

149DOWN_ARROW = "↳" 

150HORIZONTAL_LINE = "─" 

151VERTICAL_LINE = "│" 

152CROSS = "✘ " 

153TICK = "✓ " 

154BP_GLYPH = "●" 

155GEF_PROMPT = "gef➤ " 

156GEF_PROMPT_ON = f"\001\033[1;32m\002{GEF_PROMPT}\001\033[0m\002" 

157GEF_PROMPT_OFF = f"\001\033[1;31m\002{GEF_PROMPT}\001\033[0m\002" 

158 

159PATTERN_LIBC_VERSION = re.compile(rb"glibc (\d+)\.(\d+)") 

160 

161gef : "Gef" 

162__registered_commands__ : Set[Type["GenericCommand"]] = set() 

163__registered_functions__ : Set[Type["GenericFunction"]] = set() 

164__registered_architectures__ : Dict[Union["Elf.Abi", str], Type["Architecture"]] = {} 

165__registered_file_formats__ : Set[ Type["FileFormat"] ] = set() 

166 

167 

168def reset_all_caches() -> None: 

169 """Free all caches. If an object is cached, it will have a callable attribute `cache_clear` 

170 which will be invoked to purge the function cache.""" 

171 

172 for mod in dir(sys.modules["__main__"]): 

173 obj = getattr(sys.modules["__main__"], mod) 

174 if hasattr(obj, "cache_clear"): 

175 obj.cache_clear() 

176 

177 gef.reset_caches() 

178 return 

179 

180 

181def reset() -> None: 

182 global gef 

183 

184 arch = None 

185 if "gef" in locals().keys(): 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true

186 reset_all_caches() 

187 arch = gef.arch 

188 del gef 

189 

190 gef = Gef() 

191 gef.setup() 

192 

193 if arch: 193 ↛ 194line 193 didn't jump to line 194, because the condition on line 193 was never true

194 gef.arch = arch 

195 return 

196 

197 

198def highlight_text(text: str) -> str: 

199 """ 

200 Highlight text using `gef.ui.highlight_table` { match -> color } settings. 

201 

202 If RegEx is enabled it will create a match group around all items in the 

203 `gef.ui.highlight_table` and wrap the specified color in the `gef.ui.highlight_table` 

204 around those matches. 

205 

206 If RegEx is disabled, split by ANSI codes and 'colorify' each match found 

207 within the specified string. 

208 """ 

209 global gef 

210 

211 if not gef.ui.highlight_table: 

212 return text 

213 

214 if gef.config["highlight.regex"]: 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true

215 for match, color in gef.ui.highlight_table.items(): 

216 text = re.sub("(" + match + ")", Color.colorify("\\1", color), text) 

217 return text 

218 

219 ansiSplit = re.split(ANSI_SPLIT_RE, text) 

220 

221 for match, color in gef.ui.highlight_table.items(): 

222 for index, val in enumerate(ansiSplit): 222 ↛ 227line 222 didn't jump to line 227, because the loop on line 222 didn't complete

223 found = val.find(match) 

224 if found > -1: 

225 ansiSplit[index] = val.replace(match, Color.colorify(match, color)) 

226 break 

227 text = "".join(ansiSplit) 

228 ansiSplit = re.split(ANSI_SPLIT_RE, text) 

229 

230 return "".join(ansiSplit) 

231 

232 

233def gef_print(*args: str, end="\n", sep=" ", **kwargs: Any) -> None: 

234 """Wrapper around print(), using string buffering feature.""" 

235 parts = [highlight_text(a) for a in args] 

236 if buffer_output() and gef.ui.stream_buffer and not is_debug(): 236 ↛ 237line 236 didn't jump to line 237, because the condition on line 236 was never true

237 gef.ui.stream_buffer.write(sep.join(parts) + end) 

238 return 

239 

240 print(*parts, sep=sep, end=end, **kwargs) 

241 return 

242 

243 

244def bufferize(f: Callable) -> Callable: 

245 """Store the content to be printed for a function in memory, and flush it on function exit.""" 

246 

247 @functools.wraps(f) 

248 def wrapper(*args: Any, **kwargs: Any) -> Any: 

249 global gef 

250 

251 if gef.ui.stream_buffer: 

252 return f(*args, **kwargs) 

253 

254 gef.ui.stream_buffer = StringIO() 

255 try: 

256 rv = f(*args, **kwargs) 

257 finally: 

258 redirect = gef.config["context.redirect"] 

259 if redirect.startswith("/dev/pts/"): 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true

260 if not gef.ui.redirect_fd: 

261 # if the FD has never been open, open it 

262 fd = open(redirect, "wt") 

263 gef.ui.redirect_fd = fd 

264 elif redirect != gef.ui.redirect_fd.name: 

265 # if the user has changed the redirect setting during runtime, update the state 

266 gef.ui.redirect_fd.close() 

267 fd = open(redirect, "wt") 

268 gef.ui.redirect_fd = fd 

269 else: 

270 # otherwise, keep using it 

271 fd = gef.ui.redirect_fd 

272 else: 

273 fd = sys.stdout 

274 gef.ui.redirect_fd = None 

275 

276 if gef.ui.redirect_fd and fd.closed: 276 ↛ 278line 276 didn't jump to line 278, because the condition on line 276 was never true

277 # if the tty was closed, revert back to stdout 

278 fd = sys.stdout 

279 gef.ui.redirect_fd = None 

280 gef.config["context.redirect"] = "" 

281 

282 fd.write(gef.ui.stream_buffer.getvalue()) 

283 fd.flush() 

284 gef.ui.stream_buffer = None 

285 return rv 

286 

287 return wrapper 

288 

289 

290# 

291# Helpers 

292# 

293 

294def p8(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: 

295 """Pack one byte respecting the current architecture endianness.""" 

296 endian = e or gef.arch.endianness 

297 return struct.pack(f"{endian}B", x) if not s else struct.pack(f"{endian:s}b", x) 

298 

299 

300def p16(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: 

301 """Pack one word respecting the current architecture endianness.""" 

302 endian = e or gef.arch.endianness 

303 return struct.pack(f"{endian}H", x) if not s else struct.pack(f"{endian:s}h", x) 

304 

305 

306def p32(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: 

307 """Pack one dword respecting the current architecture endianness.""" 

308 endian = e or gef.arch.endianness 

309 return struct.pack(f"{endian}I", x) if not s else struct.pack(f"{endian:s}i", x) 

310 

311 

312def p64(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: 

313 """Pack one qword respecting the current architecture endianness.""" 

314 endian = e or gef.arch.endianness 

315 return struct.pack(f"{endian}Q", x) if not s else struct.pack(f"{endian:s}q", x) 

316 

317 

318def u8(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: 

319 """Unpack one byte respecting the current architecture endianness.""" 

320 endian = e or gef.arch.endianness 

321 return struct.unpack(f"{endian}B", x)[0] if not s else struct.unpack(f"{endian:s}b", x)[0] 

322 

323 

324def u16(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: 

325 """Unpack one word respecting the current architecture endianness.""" 

326 endian = e or gef.arch.endianness 

327 return struct.unpack(f"{endian}H", x)[0] if not s else struct.unpack(f"{endian:s}h", x)[0] 

328 

329 

330def u32(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: 

331 """Unpack one dword respecting the current architecture endianness.""" 

332 endian = e or gef.arch.endianness 

333 return struct.unpack(f"{endian}I", x)[0] if not s else struct.unpack(f"{endian:s}i", x)[0] 

334 

335 

336def u64(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: 

337 """Unpack one qword respecting the current architecture endianness.""" 

338 endian = e or gef.arch.endianness 

339 return struct.unpack(f"{endian}Q", x)[0] if not s else struct.unpack(f"{endian:s}q", x)[0] 

340 

341 

342def is_ascii_string(address: int) -> bool: 

343 """Helper function to determine if the buffer pointed by `address` is an ASCII string (in GDB)""" 

344 try: 

345 return gef.memory.read_ascii_string(address) is not None 

346 except Exception: 

347 return False 

348 

349 

350def is_alive() -> bool: 

351 """Check if GDB is running.""" 

352 try: 

353 return gdb.selected_inferior().pid > 0 

354 except Exception: 

355 return False 

356 

357 

358def calling_function() -> Optional[str]: 

359 """Return the name of the calling function""" 

360 try: 

361 stack_info = traceback.extract_stack()[-3] 

362 return stack_info.name 

363 except: 

364 return None 

365 

366 

367# 

368# Decorators 

369# 

370def only_if_gdb_running(f: Callable) -> Callable: 

371 """Decorator wrapper to check if GDB is running.""" 

372 

373 @functools.wraps(f) 

374 def wrapper(*args: Any, **kwargs: Any) -> Any: 

375 if is_alive(): 

376 return f(*args, **kwargs) 

377 else: 

378 warn("No debugging session active") 

379 

380 return wrapper 

381 

382 

383def only_if_gdb_target_local(f: Callable) -> Callable: 

384 """Decorator wrapper to check if GDB is running locally (target not remote).""" 

385 

386 @functools.wraps(f) 

387 def wrapper(*args: Any, **kwargs: Any) -> Any: 

388 if not is_remote_debug(): 388 ↛ 391line 388 didn't jump to line 391, because the condition on line 388 was never false

389 return f(*args, **kwargs) 

390 else: 

391 warn("This command cannot work for remote sessions.") 

392 

393 return wrapper 

394 

395 

396def deprecated(solution: str = "") -> Callable: 

397 """Decorator to add a warning when a command is obsolete and will be removed.""" 

398 def decorator(f: Callable) -> Callable: 

399 @functools.wraps(f) 

400 def wrapper(*args: Any, **kwargs: Any) -> Any: 

401 caller = inspect.stack()[1] 

402 caller_file = pathlib.Path(caller.filename) 

403 caller_loc = caller.lineno 

404 msg = f"{caller_file.name}:L{caller_loc} '{f.__name__}' is deprecated and will be removed in a feature release. " 

405 if not gef: 405 ↛ 406line 405 didn't jump to line 406, because the condition on line 405 was never true

406 print(msg) 

407 elif gef.config["gef.show_deprecation_warnings"] is True: 407 ↛ 411line 407 didn't jump to line 411, because the condition on line 407 was never false

408 if solution: 408 ↛ 410line 408 didn't jump to line 410, because the condition on line 408 was never false

409 msg += solution 

410 warn(msg) 

411 return f(*args, **kwargs) 

412 

413 if not wrapper.__doc__: 

414 wrapper.__doc__ = "" 

415 wrapper.__doc__ += f"\r\n`{f.__name__}` is **DEPRECATED** and will be removed in the future.\r\n{solution}" 

416 return wrapper 

417 return decorator 

418 

419 

420def experimental_feature(f: Callable) -> Callable: 

421 """Decorator to add a warning when a feature is experimental.""" 

422 

423 @functools.wraps(f) 

424 def wrapper(*args: Any, **kwargs: Any) -> Any: 

425 warn("This feature is under development, expect bugs and unstability...") 

426 return f(*args, **kwargs) 

427 

428 return wrapper 

429 

430 

431def only_if_events_supported(event_type: str) -> Callable: 

432 """Checks if GDB supports events without crashing.""" 

433 def wrap(f: Callable) -> Callable: 

434 def wrapped_f(*args: Any, **kwargs: Any) -> Any: 

435 if getattr(gdb, "events") and getattr(gdb.events, event_type): 435 ↛ 437line 435 didn't jump to line 437, because the condition on line 435 was never false

436 return f(*args, **kwargs) 

437 warn("GDB events cannot be set") 

438 return wrapped_f 

439 return wrap 

440 

441 

442class classproperty(property): 

443 """Make the attribute a `classproperty`.""" 

444 def __get__(self, cls, owner): 

445 return classmethod(self.fget).__get__(None, owner)() 

446 

447 

448def FakeExit(*args: Any, **kwargs: Any) -> NoReturn: 

449 raise RuntimeWarning 

450 

451 

452sys.exit = FakeExit 

453 

454 

455def parse_arguments(required_arguments: Dict[Union[str, Tuple[str, str]], Any], 

456 optional_arguments: Dict[Union[str, Tuple[str, str]], Any]) -> Callable: 

457 """Argument parsing decorator.""" 

458 

459 def int_wrapper(x: str) -> int: return int(x, 0) 

460 

461 def decorator(f: Callable) -> Optional[Callable]: 

462 def wrapper(*args: Any, **kwargs: Any) -> Callable: 

463 parser = argparse.ArgumentParser(prog=args[0]._cmdline_, add_help=True) 

464 for argname in required_arguments: 

465 argvalue = required_arguments[argname] 

466 argtype = type(argvalue) 

467 if argtype is int: 

468 argtype = int_wrapper 

469 

470 argname_is_list = not isinstance(argname, str) 

471 assert not argname_is_list and isinstance(argname, str) 

472 if not argname_is_list and argname.startswith("-"): 472 ↛ 474line 472 didn't jump to line 474, because the condition on line 472 was never true

473 # optional args 

474 if argtype is bool: 

475 parser.add_argument(argname, action="store_true" if argvalue else "store_false") 

476 else: 

477 parser.add_argument(argname, type=argtype, required=True, default=argvalue) 

478 else: 

479 if argtype in (list, tuple): 

480 nargs = "*" 

481 argtype = type(argvalue[0]) 

482 else: 

483 nargs = "?" 

484 # positional args 

485 parser.add_argument(argname, type=argtype, default=argvalue, nargs=nargs) 

486 

487 for argname in optional_arguments: 

488 if isinstance(argname, str) and not argname.startswith("-"): 488 ↛ 490line 488 didn't jump to line 490, because the condition on line 488 was never true

489 # refuse positional arguments 

490 continue 

491 argvalue = optional_arguments[argname] 

492 argtype = type(argvalue) 

493 if isinstance(argname, str): 

494 argname = [argname,] 

495 if argtype is int: 

496 argtype = int_wrapper 

497 if argtype is bool: 

498 parser.add_argument(*argname, action="store_true" if argvalue else "store_false") 

499 else: 

500 parser.add_argument(*argname, type=argtype, default=argvalue) 

501 

502 parsed_args = parser.parse_args(*(args[1:])) 

503 kwargs["arguments"] = parsed_args 

504 return f(*args, **kwargs) 

505 return wrapper 

506 return decorator 

507 

508 

509class Color: 

510 """Used to colorify terminal output.""" 

511 colors = { 

512 "normal" : "\033[0m", 

513 "gray" : "\033[1;38;5;240m", 

514 "light_gray" : "\033[0;37m", 

515 "red" : "\033[31m", 

516 "green" : "\033[32m", 

517 "yellow" : "\033[33m", 

518 "blue" : "\033[34m", 

519 "pink" : "\033[35m", 

520 "cyan" : "\033[36m", 

521 "bold" : "\033[1m", 

522 "underline" : "\033[4m", 

523 "underline_off" : "\033[24m", 

524 "highlight" : "\033[3m", 

525 "highlight_off" : "\033[23m", 

526 "blink" : "\033[5m", 

527 "blink_off" : "\033[25m", 

528 } 

529 

530 @staticmethod 

531 def redify(msg: str) -> str: return Color.colorify(msg, "red") 

532 @staticmethod 

533 def greenify(msg: str) -> str: return Color.colorify(msg, "green") 

534 @staticmethod 

535 def blueify(msg: str) -> str: return Color.colorify(msg, "blue") 

536 @staticmethod 

537 def yellowify(msg: str) -> str: return Color.colorify(msg, "yellow") 

538 @staticmethod 

539 def grayify(msg: str) -> str: return Color.colorify(msg, "gray") 539 ↛ exitline 539 didn't return from function 'grayify', because the return on line 539 wasn't executed

540 @staticmethod 

541 def light_grayify(msg: str) -> str: return Color.colorify(msg, "light_gray") 541 ↛ exitline 541 didn't return from function 'light_grayify', because the return on line 541 wasn't executed

542 @staticmethod 

543 def pinkify(msg: str) -> str: return Color.colorify(msg, "pink") 

544 @staticmethod 

545 def cyanify(msg: str) -> str: return Color.colorify(msg, "cyan") 545 ↛ exitline 545 didn't return from function 'cyanify', because the return on line 545 wasn't executed

546 @staticmethod 

547 def boldify(msg: str) -> str: return Color.colorify(msg, "bold") 

548 @staticmethod 

549 def underlinify(msg: str) -> str: return Color.colorify(msg, "underline") 549 ↛ exitline 549 didn't return from function 'underlinify', because the return on line 549 wasn't executed

550 @staticmethod 

551 def highlightify(msg: str) -> str: return Color.colorify(msg, "highlight") 551 ↛ exitline 551 didn't return from function 'highlightify', because the return on line 551 wasn't executed

552 @staticmethod 

553 def blinkify(msg: str) -> str: return Color.colorify(msg, "blink") 553 ↛ exitline 553 didn't return from function 'blinkify', because the return on line 553 wasn't executed

554 

555 @staticmethod 

556 def colorify(text: str, attrs: str) -> str: 

557 """Color text according to the given attributes.""" 

558 if gef.config["gef.disable_color"] is True: return text 558 ↛ exitline 558 didn't return from function 'colorify', because the return on line 558 wasn't executed

559 

560 colors = Color.colors 

561 msg = [colors[attr] for attr in attrs.split() if attr in colors] 

562 msg.append(str(text)) 

563 if colors["highlight"] in msg: msg.append(colors["highlight_off"]) 

564 if colors["underline"] in msg: msg.append(colors["underline_off"]) 

565 if colors["blink"] in msg: msg.append(colors["blink_off"]) 

566 msg.append(colors["normal"]) 

567 return "".join(msg) 

568 

569 

570class Address: 

571 """GEF representation of memory addresses.""" 

572 def __init__(self, **kwargs: Any) -> None: 

573 self.value: int = kwargs.get("value", 0) 

574 self.section: "Section" = kwargs.get("section", None) 

575 self.info: "Zone" = kwargs.get("info", None) 

576 return 

577 

578 def __str__(self) -> str: 

579 value = format_address(self.value) 

580 code_color = gef.config["theme.address_code"] 

581 stack_color = gef.config["theme.address_stack"] 

582 heap_color = gef.config["theme.address_heap"] 

583 if self.is_in_text_segment(): 

584 return Color.colorify(value, code_color) 

585 if self.is_in_heap_segment(): 585 ↛ 586line 585 didn't jump to line 586, because the condition on line 585 was never true

586 return Color.colorify(value, heap_color) 

587 if self.is_in_stack_segment(): 

588 return Color.colorify(value, stack_color) 

589 return value 

590 

591 def __int__(self) -> int: 

592 return self.value 

593 

594 def is_in_text_segment(self) -> bool: 

595 return (hasattr(self.info, "name") and ".text" in self.info.name) or \ 

596 (hasattr(self.section, "path") and get_filepath() == self.section.path and self.section.is_executable()) 

597 

598 def is_in_stack_segment(self) -> bool: 

599 return hasattr(self.section, "path") and "[stack]" == self.section.path 

600 

601 def is_in_heap_segment(self) -> bool: 

602 return hasattr(self.section, "path") and "[heap]" == self.section.path 

603 

604 def dereference(self) -> Optional[int]: 

605 addr = align_address(int(self.value)) 

606 derefed = dereference(addr) 

607 return None if derefed is None else int(derefed) 

608 

609 @property 

610 def valid(self) -> bool: 

611 return any(map(lambda x: x.page_start <= self.value < x.page_end, gef.memory.maps)) 

612 

613 

614class Permission(enum.Flag): 

615 """GEF representation of Linux permission.""" 

616 NONE = 0 

617 EXECUTE = 1 

618 WRITE = 2 

619 READ = 4 

620 ALL = 7 

621 

622 def __str__(self) -> str: 

623 perm_str = "" 

624 perm_str += "r" if self & Permission.READ else "-" 

625 perm_str += "w" if self & Permission.WRITE else "-" 

626 perm_str += "x" if self & Permission.EXECUTE else "-" 

627 return perm_str 

628 

629 @classmethod 

630 def from_info_sections(cls, *args: str) -> "Permission": 

631 perm = cls(0) 

632 for arg in args: 

633 if "READONLY" in arg: perm |= Permission.READ 

634 if "DATA" in arg: perm |= Permission.WRITE 

635 if "CODE" in arg: perm |= Permission.EXECUTE 

636 return perm 

637 

638 @classmethod 

639 def from_process_maps(cls, perm_str: str) -> "Permission": 

640 perm = cls(0) 

641 if perm_str[0] == "r": perm |= Permission.READ 

642 if perm_str[1] == "w": perm |= Permission.WRITE 

643 if perm_str[2] == "x": perm |= Permission.EXECUTE 

644 return perm 

645 

646 @classmethod 

647 def from_info_mem(cls, perm_str: str) -> "Permission": 

648 perm = cls(0) 

649 # perm_str[0] shows if this is a user page, which 

650 # we don't track 

651 if perm_str[1] == "r": perm |= Permission.READ 

652 if perm_str[2] == "w": perm |= Permission.WRITE 

653 return perm 

654 

655 

656class Section: 

657 """GEF representation of process memory sections.""" 

658 

659 def __init__(self, **kwargs: Any) -> None: 

660 self.page_start: int = kwargs.get("page_start", 0) 

661 self.page_end: int = kwargs.get("page_end", 0) 

662 self.offset: int = kwargs.get("offset", 0) 

663 self.permission: Permission = kwargs.get("permission", Permission(0)) 

664 self.inode: int = kwargs.get("inode", 0) 

665 self.path: str = kwargs.get("path", "") 

666 return 

667 

668 def is_readable(self) -> bool: 

669 return (self.permission & Permission.READ) != 0 

670 

671 def is_writable(self) -> bool: 

672 return (self.permission & Permission.WRITE) != 0 

673 

674 def is_executable(self) -> bool: 

675 return (self.permission & Permission.EXECUTE) != 0 

676 

677 @property 

678 def size(self) -> int: 

679 if self.page_end is None or self.page_start is None: 

680 return -1 

681 return self.page_end - self.page_start 

682 

683 @property 

684 def realpath(self) -> str: 

685 # when in a `gef-remote` session, realpath returns the path to the binary on the local disk, not remote 

686 return self.path if gef.session.remote is None else f"/tmp/gef/{gef.session.remote:d}/{self.path}" 

687 

688 def __str__(self) -> str: 

689 return (f"Section(page_start={self.page_start:#x}, page_end={self.page_end:#x}, " 

690 f"permissions={self.permission!s})") 

691 

692 

693Zone = collections.namedtuple("Zone", ["name", "zone_start", "zone_end", "filename"]) 

694 

695 

696class Endianness(enum.Enum): 

697 LITTLE_ENDIAN = 1 

698 BIG_ENDIAN = 2 

699 

700 def __str__(self) -> str: 

701 return "<" if self == Endianness.LITTLE_ENDIAN else ">" 

702 

703 def __repr__(self) -> str: 

704 return self.name 

705 

706 def __int__(self) -> int: 

707 return self.value 

708 

709 

710class FileFormatSection: 

711 misc: Any 

712 

713 

714class FileFormat: 

715 name: str 

716 path: pathlib.Path 

717 entry_point: int 

718 checksec: Dict[str, bool] 

719 sections: List[FileFormatSection] 

720 

721 def __init__(self, path: Union[str, pathlib.Path]) -> None: 

722 raise NotImplemented 

723 

724 def __init_subclass__(cls: Type["FileFormat"], **kwargs): 

725 global __registered_file_formats__ 

726 super().__init_subclass__(**kwargs) 

727 required_attributes = ("name", "entry_point", "is_valid", "checksec",) 

728 for attr in required_attributes: 

729 if not hasattr(cls, attr): 729 ↛ 730line 729 didn't jump to line 730, because the condition on line 729 was never true

730 raise NotImplementedError(f"File format '{cls.__name__}' is invalid: missing attribute '{attr}'") 

731 __registered_file_formats__.add(cls) 

732 return 

733 

734 @classmethod 

735 def is_valid(cls, path: pathlib.Path) -> bool: 

736 raise NotImplemented 

737 

738 def __str__(self) -> str: 

739 return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point:#x})" 

740 

741 

742class Elf(FileFormat): 

743 """Basic ELF parsing. 

744 Ref: 

745 - http://www.skyfree.org/linux/references/ELF_Format.pdf 

746 - https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf 

747 - https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html 

748 """ 

749 class Class(enum.Enum): 

750 ELF_32_BITS = 0x01 

751 ELF_64_BITS = 0x02 

752 

753 ELF_MAGIC = 0x7f454c46 

754 

755 class Abi(enum.Enum): 

756 X86_64 = 0x3e 

757 X86_32 = 0x03 

758 ARM = 0x28 

759 MIPS = 0x08 

760 POWERPC = 0x14 

761 POWERPC64 = 0x15 

762 SPARC = 0x02 

763 SPARC64 = 0x2b 

764 AARCH64 = 0xb7 

765 RISCV = 0xf3 

766 IA64 = 0x32 

767 M68K = 0x04 

768 

769 class Type(enum.Enum): 

770 ET_RELOC = 1 

771 ET_EXEC = 2 

772 ET_DYN = 3 

773 ET_CORE = 4 

774 

775 class OsAbi(enum.Enum): 

776 SYSTEMV = 0x00 

777 HPUX = 0x01 

778 NETBSD = 0x02 

779 LINUX = 0x03 

780 SOLARIS = 0x06 

781 AIX = 0x07 

782 IRIX = 0x08 

783 FREEBSD = 0x09 

784 OPENBSD = 0x0C 

785 

786 e_magic: int = ELF_MAGIC 

787 e_class: "Elf.Class" = Class.ELF_32_BITS 

788 e_endianness: Endianness = Endianness.LITTLE_ENDIAN 

789 e_eiversion: int 

790 e_osabi: "Elf.OsAbi" 

791 e_abiversion: int 

792 e_pad: bytes 

793 e_type: "Elf.Type" = Type.ET_EXEC 

794 e_machine: Abi = Abi.X86_32 

795 e_version: int 

796 e_entry: int 

797 e_phoff: int 

798 e_shoff: int 

799 e_flags: int 

800 e_ehsize: int 

801 e_phentsize: int 

802 e_phnum: int 

803 e_shentsize: int 

804 e_shnum: int 

805 e_shstrndx: int 

806 

807 path: pathlib.Path 

808 phdrs : List["Phdr"] 

809 shdrs : List["Shdr"] 

810 name: str = "ELF" 

811 

812 __checksec : Dict[str, bool] 

813 

814 def __init__(self, path: Union[str, pathlib.Path]) -> None: 

815 """Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown.""" 

816 

817 if isinstance(path, str): 

818 self.path = pathlib.Path(path).expanduser() 

819 elif isinstance(path, pathlib.Path): 819 ↛ 822line 819 didn't jump to line 822, because the condition on line 819 was never false

820 self.path = path 

821 else: 

822 raise TypeError 

823 

824 if not self.path.exists(): 824 ↛ 825line 824 didn't jump to line 825, because the condition on line 824 was never true

825 raise FileNotFoundError(f"'{self.path}' not found/readable, most gef features will not work") 

826 

827 self.__checksec = {} 

828 

829 with self.path.open("rb") as self.fd: 

830 # off 0x0 

831 self.e_magic, e_class, e_endianness, self.e_eiversion = self.read_and_unpack(">IBBB") 

832 if self.e_magic != Elf.ELF_MAGIC: 832 ↛ 834line 832 didn't jump to line 834, because the condition on line 832 was never true

833 # The ELF is corrupted, GDB won't handle it, no point going further 

834 raise RuntimeError("Not a valid ELF file (magic)") 

835 

836 self.e_class, self.e_endianness = Elf.Class(e_class), Endianness(e_endianness) 

837 

838 if self.e_endianness != gef.arch.endianness: 838 ↛ 839line 838 didn't jump to line 839, because the condition on line 838 was never true

839 warn("Unexpected endianness for architecture") 

840 

841 endian = self.e_endianness 

842 

843 # off 0x7 

844 e_osabi, self.e_abiversion = self.read_and_unpack(f"{endian}BB") 

845 self.e_osabi = Elf.OsAbi(e_osabi) 

846 

847 # off 0x9 

848 self.e_pad = self.read(7) 

849 

850 # off 0x10 

851 e_type, e_machine, self.e_version = self.read_and_unpack(f"{endian}HHI") 

852 self.e_type, self.e_machine = Elf.Type(e_type), Elf.Abi(e_machine) 

853 

854 # off 0x18 

855 if self.e_class == Elf.Class.ELF_64_BITS: 855 ↛ 858line 855 didn't jump to line 858, because the condition on line 855 was never false

856 self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}QQQ") 

857 else: 

858 self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}III") 

859 

860 self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum = self.read_and_unpack(f"{endian}IHHH") 

861 self.e_shentsize, self.e_shnum, self.e_shstrndx = self.read_and_unpack(f"{endian}HHH") 

862 

863 self.phdrs = [] 

864 for i in range(self.e_phnum): 

865 self.phdrs.append(Phdr(self, self.e_phoff + self.e_phentsize * i)) 

866 

867 self.shdrs = [] 

868 for i in range(self.e_shnum): 

869 self.shdrs.append(Shdr(self, self.e_shoff + self.e_shentsize * i)) 

870 return 

871 

872 def read(self, size: int) -> bytes: 

873 return self.fd.read(size) 

874 

875 def read_and_unpack(self, fmt: str) -> Tuple[Any, ...]: 

876 size = struct.calcsize(fmt) 

877 data = self.fd.read(size) 

878 return struct.unpack(fmt, data) 

879 

880 def seek(self, off: int) -> None: 

881 self.fd.seek(off, 0) 

882 

883 def __str__(self) -> str: 

884 return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})" 

885 

886 def __repr__(self) -> str: 

887 return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})" 

888 

889 @property 

890 def entry_point(self) -> int: 

891 return self.e_entry 

892 

893 @classmethod 

894 def is_valid(cls, path: pathlib.Path) -> bool: 

895 return u32(path.open("rb").read(4), e = Endianness.BIG_ENDIAN) == Elf.ELF_MAGIC 

896 

897 @property 

898 def checksec(self) -> Dict[str, bool]: 

899 """Check the security property of the ELF binary. The following properties are: 

900 - Canary 

901 - NX 

902 - PIE 

903 - Fortify 

904 - Partial/Full RelRO. 

905 Return a dict() with the different keys mentioned above, and the boolean 

906 associated whether the protection was found.""" 

907 if not self.__checksec: 907 ↛ 930line 907 didn't jump to line 930, because the condition on line 907 was never false

908 def __check_security_property(opt: str, filename: str, pattern: str) -> bool: 

909 cmd = [readelf,] 

910 cmd += opt.split() 

911 cmd += [filename,] 

912 lines = gef_execute_external(cmd, as_list=True) 

913 for line in lines: 

914 if re.search(pattern, line): 

915 return True 

916 return False 

917 

918 abspath = str(self.path.absolute()) 

919 readelf = gef.session.constants["readelf"] 

920 self.__checksec["Canary"] = __check_security_property("-rs", abspath, r"__stack_chk_fail") is True 

921 has_gnu_stack = __check_security_property("-W -l", abspath, r"GNU_STACK") is True 

922 if has_gnu_stack: 922 ↛ 925line 922 didn't jump to line 925, because the condition on line 922 was never false

923 self.__checksec["NX"] = __check_security_property("-W -l", abspath, r"GNU_STACK.*RWE") is False 

924 else: 

925 self.__checksec["NX"] = False 

926 self.__checksec["PIE"] = __check_security_property("-h", abspath, r":.*EXEC") is False 

927 self.__checksec["Fortify"] = __check_security_property("-s", abspath, r"_chk@GLIBC") is True 

928 self.__checksec["Partial RelRO"] = __check_security_property("-l", abspath, r"GNU_RELRO") is True 

929 self.__checksec["Full RelRO"] = self.__checksec["Partial RelRO"] and __check_security_property("-d", abspath, r"BIND_NOW") is True 

930 return self.__checksec 

931 

932 @classproperty 

933 @deprecated("use `Elf.Abi.X86_64`") 

934 def X86_64(cls) -> int: return Elf.Abi.X86_64.value # pylint: disable=no-self-argument 

935 

936 @classproperty 

937 @deprecated("use `Elf.Abi.X86_32`") 

938 def X86_32(cls) -> int : return Elf.Abi.X86_32.value # pylint: disable=no-self-argument 

939 

940 @classproperty 

941 @deprecated("use `Elf.Abi.ARM`") 

942 def ARM(cls) -> int : return Elf.Abi.ARM.value # pylint: disable=no-self-argument 

943 

944 @classproperty 

945 @deprecated("use `Elf.Abi.MIPS`") 

946 def MIPS(cls) -> int : return Elf.Abi.MIPS.value # pylint: disable=no-self-argument 

947 

948 @classproperty 

949 @deprecated("use `Elf.Abi.POWERPC`") 

950 def POWERPC(cls) -> int : return Elf.Abi.POWERPC.value # pylint: disable=no-self-argument 

951 

952 @classproperty 

953 @deprecated("use `Elf.Abi.POWERPC64`") 

954 def POWERPC64(cls) -> int : return Elf.Abi.POWERPC64.value # pylint: disable=no-self-argument 

955 

956 @classproperty 

957 @deprecated("use `Elf.Abi.SPARC`") 

958 def SPARC(cls) -> int : return Elf.Abi.SPARC.value # pylint: disable=no-self-argument 

959 

960 @classproperty 

961 @deprecated("use `Elf.Abi.SPARC64`") 

962 def SPARC64(cls) -> int : return Elf.Abi.SPARC64.value # pylint: disable=no-self-argument 

963 

964 @classproperty 

965 @deprecated("use `Elf.Abi.AARCH64`") 

966 def AARCH64(cls) -> int : return Elf.Abi.AARCH64.value # pylint: disable=no-self-argument 

967 

968 @classproperty 

969 @deprecated("use `Elf.Abi.RISCV`") 

970 def RISCV(cls) -> int : return Elf.Abi.RISCV.value # pylint: disable=no-self-argument 

971 

972 

973class Phdr: 

974 class Type(enum.IntEnum): 

975 PT_NULL = 0 

976 PT_LOAD = 1 

977 PT_DYNAMIC = 2 

978 PT_INTERP = 3 

979 PT_NOTE = 4 

980 PT_SHLIB = 5 

981 PT_PHDR = 6 

982 PT_TLS = 7 

983 PT_LOOS = 0x60000000 

984 PT_GNU_EH_FRAME = 0x6474e550 

985 PT_GNU_STACK = 0x6474e551 

986 PT_GNU_RELRO = 0x6474e552 

987 PT_GNU_PROPERTY = 0x6474e553 

988 PT_LOSUNW = 0x6ffffffa 

989 PT_SUNWBSS = 0x6ffffffa 

990 PT_SUNWSTACK = 0x6ffffffb 

991 PT_HISUNW = PT_HIOS = 0x6fffffff 

992 PT_LOPROC = 0x70000000 

993 PT_ARM_EIDX = 0x70000001 

994 PT_MIPS_ABIFLAGS= 0x70000003 

995 PT_HIPROC = 0x7fffffff 

996 UNKNOWN_PHDR = 0xffffffff 

997 

998 @classmethod 

999 def _missing_(cls, _:int) -> Type: 

1000 return cls.UNKNOWN_PHDR 

1001 

1002 class Flags(enum.IntFlag): 

1003 PF_X = 1 

1004 PF_W = 2 

1005 PF_R = 4 

1006 

1007 p_type: "Phdr.Type" 

1008 p_flags: "Phdr.Flags" 

1009 p_offset: int 

1010 p_vaddr: int 

1011 p_paddr: int 

1012 p_filesz: int 

1013 p_memsz: int 

1014 p_align: int 

1015 

1016 def __init__(self, elf: Elf, off: int) -> None: 

1017 if not elf: 1017 ↛ 1018line 1017 didn't jump to line 1018, because the condition on line 1017 was never true

1018 return 

1019 elf.seek(off) 

1020 self.offset = off 

1021 endian = elf.e_endianness 

1022 if elf.e_class == Elf.Class.ELF_64_BITS: 1022 ↛ 1027line 1022 didn't jump to line 1027, because the condition on line 1022 was never false

1023 p_type, p_flags, self.p_offset = elf.read_and_unpack(f"{endian}IIQ") 

1024 self.p_vaddr, self.p_paddr = elf.read_and_unpack(f"{endian}QQ") 

1025 self.p_filesz, self.p_memsz, self.p_align = elf.read_and_unpack(f"{endian}QQQ") 

1026 else: 

1027 p_type, self.p_offset = elf.read_and_unpack(f"{endian}II") 

1028 self.p_vaddr, self.p_paddr = elf.read_and_unpack(f"{endian}II") 

1029 self.p_filesz, self.p_memsz, p_flags, self.p_align = elf.read_and_unpack(f"{endian}IIII") 

1030 

1031 self.p_type, self.p_flags = Phdr.Type(p_type), Phdr.Flags(p_flags) 

1032 return 

1033 

1034 def __str__(self) -> str: 

1035 return (f"Phdr(offset={self.offset}, type={self.p_type.name}, flags={self.p_flags.name}, " 

1036 f"vaddr={self.p_vaddr}, paddr={self.p_paddr}, filesz={self.p_filesz}, " 

1037 f"memsz={self.p_memsz}, align={self.p_align})") 

1038 

1039 

1040class Shdr: 

1041 class Type(enum.IntEnum): 

1042 SHT_NULL = 0 

1043 SHT_PROGBITS = 1 

1044 SHT_SYMTAB = 2 

1045 SHT_STRTAB = 3 

1046 SHT_RELA = 4 

1047 SHT_HASH = 5 

1048 SHT_DYNAMIC = 6 

1049 SHT_NOTE = 7 

1050 SHT_NOBITS = 8 

1051 SHT_REL = 9 

1052 SHT_SHLIB = 10 

1053 SHT_DYNSYM = 11 

1054 SHT_NUM = 12 

1055 SHT_INIT_ARRAY = 14 

1056 SHT_FINI_ARRAY = 15 

1057 SHT_PREINIT_ARRAY = 16 

1058 SHT_GROUP = 17 

1059 SHT_SYMTAB_SHNDX = 18 

1060 SHT_LOOS = 0x60000000 

1061 SHT_GNU_ATTRIBUTES = 0x6ffffff5 

1062 SHT_GNU_HASH = 0x6ffffff6 

1063 SHT_GNU_LIBLIST = 0x6ffffff7 

1064 SHT_CHECKSUM = 0x6ffffff8 

1065 SHT_LOSUNW = 0x6ffffffa 

1066 SHT_SUNW_move = 0x6ffffffa 

1067 SHT_SUNW_COMDAT = 0x6ffffffb 

1068 SHT_SUNW_syminfo = 0x6ffffffc 

1069 SHT_GNU_verdef = 0x6ffffffd 

1070 SHT_GNU_verneed = 0x6ffffffe 

1071 SHT_GNU_versym = 0x6fffffff 

1072 SHT_LOPROC = 0x70000000 

1073 SHT_ARM_EXIDX = 0x70000001 

1074 SHT_X86_64_UNWIND = 0x70000001 

1075 SHT_ARM_ATTRIBUTES = 0x70000003 

1076 SHT_MIPS_OPTIONS = 0x7000000d 

1077 DT_MIPS_INTERFACE = 0x7000002a 

1078 SHT_HIPROC = 0x7fffffff 

1079 SHT_LOUSER = 0x80000000 

1080 SHT_HIUSER = 0x8fffffff 

1081 UNKNOWN_SHDR = 0xffffffff 

1082 

1083 @classmethod 

1084 def _missing_(cls, _:int) -> Type: 

1085 return cls.UNKNOWN_SHDR 

1086 

1087 class Flags(enum.IntFlag): 

1088 WRITE = 1 

1089 ALLOC = 2 

1090 EXECINSTR = 4 

1091 MERGE = 0x10 

1092 STRINGS = 0x20 

1093 INFO_LINK = 0x40 

1094 LINK_ORDER = 0x80 

1095 OS_NONCONFORMING = 0x100 

1096 GROUP = 0x200 

1097 TLS = 0x400 

1098 COMPRESSED = 0x800 

1099 RELA_LIVEPATCH = 0x00100000 

1100 RO_AFTER_INIT = 0x00200000 

1101 ORDERED = 0x40000000 

1102 EXCLUDE = 0x80000000 

1103 UNKNOWN_FLAG = 0xffffffff 

1104 

1105 @classmethod 

1106 def _missing_(cls, _:int): 

1107 return cls.UNKNOWN_FLAG 

1108 

1109 sh_name: int 

1110 sh_type: "Shdr.Type" 

1111 sh_flags: "Shdr.Flags" 

1112 sh_addr: int 

1113 sh_offset: int 

1114 sh_size: int 

1115 sh_link: int 

1116 sh_info: int 

1117 sh_addralign: int 

1118 sh_entsize: int 

1119 name: str 

1120 

1121 def __init__(self, elf: Optional[Elf], off: int) -> None: 

1122 if elf is None: 1122 ↛ 1123line 1122 didn't jump to line 1123, because the condition on line 1122 was never true

1123 return 

1124 elf.seek(off) 

1125 endian = elf.e_endianness 

1126 if elf.e_class == Elf.Class.ELF_64_BITS: 1126 ↛ 1132line 1126 didn't jump to line 1132, because the condition on line 1126 was never false

1127 self.sh_name, sh_type, sh_flags = elf.read_and_unpack(f"{endian}IIQ") 

1128 self.sh_addr, self.sh_offset = elf.read_and_unpack(f"{endian}QQ") 

1129 self.sh_size, self.sh_link, self.sh_info = elf.read_and_unpack(f"{endian}QII") 

1130 self.sh_addralign, self.sh_entsize = elf.read_and_unpack(f"{endian}QQ") 

1131 else: 

1132 self.sh_name, sh_type, sh_flags = elf.read_and_unpack(f"{endian}III") 

1133 self.sh_addr, self.sh_offset = elf.read_and_unpack(f"{endian}II") 

1134 self.sh_size, self.sh_link, self.sh_info = elf.read_and_unpack(f"{endian}III") 

1135 self.sh_addralign, self.sh_entsize = elf.read_and_unpack(f"{endian}II") 

1136 

1137 self.sh_type = Shdr.Type(sh_type) 

1138 self.sh_flags = Shdr.Flags(sh_flags) 

1139 stroff = elf.e_shoff + elf.e_shentsize * elf.e_shstrndx 

1140 

1141 if elf.e_class == Elf.Class.ELF_64_BITS: 1141 ↛ 1145line 1141 didn't jump to line 1145, because the condition on line 1141 was never false

1142 elf.seek(stroff + 16 + 8) 

1143 offset = u64(elf.read(8)) 

1144 else: 

1145 elf.seek(stroff + 12 + 4) 

1146 offset = u32(elf.read(4)) 

1147 elf.seek(offset + self.sh_name) 

1148 self.name = "" 

1149 while True: 

1150 c = u8(elf.read(1)) 

1151 if c == 0: 

1152 break 

1153 self.name += chr(c) 

1154 return 

1155 

1156 def __str__(self) -> str: 

1157 return (f"Shdr(name={self.name}, type={self.sh_type.name}, flags={self.sh_flags.name}, " 

1158 f"addr={self.sh_addr:#x}, offset={self.sh_offset}, size={self.sh_size}, link={self.sh_link}, " 

1159 f"info={self.sh_info}, addralign={self.sh_addralign}, entsize={self.sh_entsize})") 

1160 

1161 

1162class Instruction: 

1163 """GEF representation of a CPU instruction.""" 

1164 

1165 def __init__(self, address: int, location: str, mnemo: str, operands: List[str], opcodes: bytes) -> None: 

1166 self.address, self.location, self.mnemonic, self.operands, self.opcodes = \ 

1167 address, location, mnemo, operands, opcodes 

1168 return 

1169 

1170 # Allow formatting an instruction with {:o} to show opcodes. 

1171 # The number of bytes to display can be configured, e.g. {:4o} to only show 4 bytes of the opcodes 

1172 def __format__(self, format_spec: str) -> str: 

1173 if len(format_spec) == 0 or format_spec[-1] != "o": 1173 ↛ 1174line 1173 didn't jump to line 1174, because the condition on line 1173 was never true

1174 return str(self) 

1175 

1176 if format_spec == "o": 1176 ↛ 1177line 1176 didn't jump to line 1177, because the condition on line 1176 was never true

1177 opcodes_len = len(self.opcodes) 

1178 else: 

1179 opcodes_len = int(format_spec[:-1]) 

1180 

1181 opcodes_text = "".join(f"{b:02x}" for b in self.opcodes[:opcodes_len]) 

1182 if opcodes_len < len(self.opcodes): 

1183 opcodes_text += "..." 

1184 return (f"{self.address:#10x} {opcodes_text:{opcodes_len * 2 + 3:d}s} {self.location:16} " 

1185 f"{self.mnemonic:6} {', '.join(self.operands)}") 

1186 

1187 def __str__(self) -> str: 

1188 return f"{self.address:#10x} {self.location:16} {self.mnemonic:6} {', '.join(self.operands)}" 

1189 

1190 def is_valid(self) -> bool: 

1191 return "(bad)" not in self.mnemonic 

1192 

1193 def size(self) -> int: 

1194 return len(self.opcodes) 

1195 

1196 def next(self) -> "Instruction": 

1197 address = self.address + self.size() 

1198 return gef_get_instruction_at(address) 

1199 

1200 

1201@deprecated("Use GefHeapManager.find_main_arena_addr()") 

1202def search_for_main_arena() -> int: 

1203 return GefHeapManager.find_main_arena_addr() 

1204 

1205class GlibcHeapInfo: 

1206 """Glibc heap_info struct""" 

1207 

1208 @staticmethod 

1209 def heap_info_t() -> Type[ctypes.Structure]: 

1210 assert gef.libc.version 

1211 class heap_info_cls(ctypes.Structure): 

1212 pass 

1213 pointer = ctypes.c_uint64 if gef.arch.ptrsize == 8 else ctypes.c_uint32 

1214 pad_size = -5 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1) 

1215 fields = [ 

1216 ("ar_ptr", ctypes.POINTER(GlibcArena.malloc_state_t())), 

1217 ("prev", ctypes.POINTER(heap_info_cls)), 

1218 ("size", pointer) 

1219 ] 

1220 if gef.libc.version >= (2, 5): 1220 ↛ 1225line 1220 didn't jump to line 1225, because the condition on line 1220 was never false

1221 fields += [ 

1222 ("mprotect_size", pointer) 

1223 ] 

1224 pad_size = -6 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1) 

1225 if gef.libc.version >= (2, 34): 1225 ↛ 1230line 1225 didn't jump to line 1230, because the condition on line 1225 was never false

1226 fields += [ 

1227 ("pagesize", pointer) 

1228 ] 

1229 pad_size = -3 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1) 

1230 fields += [ 

1231 ("pad", ctypes.c_uint8*pad_size) 

1232 ] 

1233 heap_info_cls._fields_ = fields 

1234 return heap_info_cls 

1235 

1236 def __init__(self, addr: Union[str, int]) -> None: 

1237 self.__address : int = parse_address(f"&{addr}") if isinstance(addr, str) else addr 

1238 self.reset() 

1239 return 

1240 

1241 def reset(self): 

1242 self._sizeof = ctypes.sizeof(GlibcHeapInfo.heap_info_t()) 

1243 self._data = gef.memory.read(self.__address, ctypes.sizeof(GlibcHeapInfo.heap_info_t())) 

1244 self._heap_info = GlibcHeapInfo.heap_info_t().from_buffer_copy(self._data) 

1245 return 

1246 

1247 def __getattr__(self, item: Any) -> Any: 

1248 if item in dir(self._heap_info): 1248 ↛ 1250line 1248 didn't jump to line 1250, because the condition on line 1248 was never false

1249 return ctypes.cast(getattr(self._heap_info, item), ctypes.c_void_p).value 

1250 return getattr(self, item) 

1251 

1252 def __abs__(self) -> int: 

1253 return self.__address 

1254 

1255 def __int__(self) -> int: 

1256 return self.__address 

1257 

1258 @property 

1259 def address(self) -> int: 

1260 return self.__address 

1261 

1262 @property 

1263 def sizeof(self) -> int: 

1264 return self._sizeof 

1265 

1266 @property 

1267 def addr(self) -> int: 

1268 return int(self) 

1269 

1270 @property 

1271 def heap_start(self) -> int: 

1272 # check special case: first heap of non-main-arena 

1273 if self.ar_ptr - self.address < 0x60: 1273 ↛ 1285line 1273 didn't jump to line 1285, because the condition on line 1273 was never false

1274 # the first heap of a non-main-arena starts with a `heap_info` 

1275 # struct, which should fit easily into 0x60 bytes throughout 

1276 # all architectures and glibc versions. If this check succeeds 

1277 # then we are currently looking at such a "first heap" 

1278 arena = GlibcArena(f"*{self.ar_ptr:#x}") 

1279 heap_addr = arena.heap_addr() 

1280 if heap_addr: 1280 ↛ 1283line 1280 didn't jump to line 1283, because the condition on line 1280 was never false

1281 return heap_addr 

1282 else: 

1283 err(f"Cannot find heap address for arena {self.ar_ptr:#x}") 

1284 return 0 

1285 return self.address + self.sizeof 

1286 

1287 @property 

1288 def heap_end(self) -> int: 

1289 return self.address + self.size 

1290 

1291 

1292class GlibcArena: 

1293 """Glibc arena class""" 

1294 

1295 NFASTBINS = 10 

1296 NBINS = 128 

1297 NSMALLBINS = 64 

1298 BINMAPSHIFT = 5 

1299 BITSPERMAP = 1 << BINMAPSHIFT 

1300 BINMAPSIZE = NBINS // BITSPERMAP 

1301 

1302 @staticmethod 

1303 def malloc_state_t() -> Type[ctypes.Structure]: 

1304 pointer = ctypes.c_uint64 if gef and gef.arch.ptrsize == 8 else ctypes.c_uint32 

1305 fields = [ 

1306 ("mutex", ctypes.c_uint32), 

1307 ("flags", ctypes.c_uint32), 

1308 ] 

1309 if gef and gef.libc.version and gef.libc.version >= (2, 27): 1309 ↛ 1315line 1309 didn't jump to line 1315, because the condition on line 1309 was never false

1310 # https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L1684 

1311 fields += [ 

1312 ("have_fastchunks", ctypes.c_uint32), 

1313 ("UNUSED_c", ctypes.c_uint32), # padding to align to 0x10 

1314 ] 

1315 fields += [ 

1316 ("fastbinsY", GlibcArena.NFASTBINS * pointer), 

1317 ("top", pointer), 

1318 ("last_remainder", pointer), 

1319 ("bins", (GlibcArena.NBINS * 2 - 2) * pointer), 

1320 ("binmap", GlibcArena.BINMAPSIZE * ctypes.c_uint32), 

1321 ("next", pointer), 

1322 ("next_free", pointer) 

1323 ] 

1324 if gef and gef.libc.version and gef.libc.version >= (2, 23): 1324 ↛ 1329line 1324 didn't jump to line 1329, because the condition on line 1324 was never false

1325 # https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1719 

1326 fields += [ 

1327 ("attached_threads", pointer) 

1328 ] 

1329 fields += [ 

1330 ("system_mem", pointer), 

1331 ("max_system_mem", pointer), 

1332 ] 

1333 class malloc_state_cls(ctypes.Structure): 

1334 _fields_ = fields 

1335 return malloc_state_cls 

1336 

1337 def __init__(self, addr: str) -> None: 

1338 try: 

1339 self.__address : int = parse_address(f"&{addr}") 

1340 except gdb.error: 

1341 self.__address : int = GefHeapManager.find_main_arena_addr() 

1342 # if `find_main_arena_addr` throws `gdb.error` on symbol lookup: 

1343 # it means the session is not started, so just propagate the exception 

1344 self.reset() 

1345 return 

1346 

1347 def reset(self): 

1348 self._sizeof = ctypes.sizeof(GlibcArena.malloc_state_t()) 

1349 self._data = gef.memory.read(self.__address, ctypes.sizeof(GlibcArena.malloc_state_t())) 

1350 self.__arena = GlibcArena.malloc_state_t().from_buffer_copy(self._data) 

1351 return 

1352 

1353 def __abs__(self) -> int: 

1354 return self.__address 

1355 

1356 def __int__(self) -> int: 

1357 return self.__address 

1358 

1359 def __iter__(self) -> Generator["GlibcArena", None, None]: 

1360 main_arena = int(gef.heap.main_arena) 

1361 

1362 current_arena = self 

1363 yield current_arena 

1364 

1365 while True: 

1366 if current_arena.next == 0 or current_arena.next == main_arena: 

1367 break 

1368 

1369 current_arena = GlibcArena(f"*{current_arena.next:#x} ") 

1370 yield current_arena 

1371 return 

1372 

1373 def __eq__(self, other: "GlibcArena") -> bool: 

1374 return self.__address == int(other) 

1375 

1376 def __str__(self) -> str: 

1377 properties = f"base={self.__address:#x}, top={self.top:#x}, " \ 

1378 f"last_remainder={self.last_remainder:#x}, next={self.next:#x}" 

1379 return (f"{Color.colorify('Arena', 'blue bold underline')}({properties})") 

1380 

1381 def __repr__(self) -> str: 

1382 return f"GlibcArena(address={self.__address:#x}, size={self._sizeof})" 

1383 

1384 @property 

1385 def address(self) -> int: 

1386 return self.__address 

1387 

1388 @property 

1389 def sizeof(self) -> int: 

1390 return self._sizeof 

1391 

1392 @property 

1393 def addr(self) -> int: 

1394 return int(self) 

1395 

1396 @property 

1397 def top(self) -> int: 

1398 return self.__arena.top 

1399 

1400 @property 

1401 def last_remainder(self) -> int: 

1402 return self.__arena.last_remainder 

1403 

1404 @property 

1405 def fastbinsY(self) -> ctypes.Array: 

1406 return self.__arena.fastbinsY 

1407 

1408 @property 

1409 def bins(self) -> ctypes.Array: 

1410 return self.__arena.bins 

1411 

1412 @property 

1413 def binmap(self) -> ctypes.Array: 

1414 return self.__arena.binmap 

1415 

1416 @property 

1417 def next(self) -> int: 

1418 return self.__arena.next 

1419 

1420 @property 

1421 def next_free(self) -> int: 

1422 return self.__arena.next_free 

1423 

1424 @property 

1425 def attached_threads(self) -> int: 

1426 return self.__arena.attached_threads 

1427 

1428 @property 

1429 def system_mem(self) -> int: 

1430 return self.__arena.system_mem 

1431 

1432 @property 

1433 def max_system_mem(self) -> int: 

1434 return self.__arena.max_system_mem 

1435 

1436 def fastbin(self, i: int) -> Optional["GlibcFastChunk"]: 

1437 """Return head chunk in fastbinsY[i].""" 

1438 addr = int(self.fastbinsY[i]) 

1439 if addr == 0: 

1440 return None 

1441 return GlibcFastChunk(addr + 2 * gef.arch.ptrsize) 

1442 

1443 def bin(self, i: int) -> Tuple[int, int]: 

1444 idx = i * 2 

1445 fd = int(self.bins[idx]) 

1446 bk = int(self.bins[idx + 1]) 

1447 return fd, bk 

1448 

1449 def bin_at(self, i) -> int: 

1450 header_sz = 2 * gef.arch.ptrsize 

1451 offset = ctypes.addressof(self.__arena.bins) - ctypes.addressof(self.__arena) 

1452 return self.__address + offset + (i-1) * 2 * gef.arch.ptrsize + header_sz 

1453 

1454 def is_main_arena(self) -> bool: 

1455 return gef.heap.main_arena is not None and int(self) == int(gef.heap.main_arena) 

1456 

1457 def heap_addr(self, allow_unaligned: bool = False) -> Optional[int]: 

1458 if self.is_main_arena(): 

1459 heap_section = gef.heap.base_address 

1460 if not heap_section: 1460 ↛ 1461line 1460 didn't jump to line 1461, because the condition on line 1460 was never true

1461 return None 

1462 return heap_section 

1463 _addr = int(self) + self.sizeof 

1464 if allow_unaligned: 1464 ↛ 1465line 1464 didn't jump to line 1465, because the condition on line 1464 was never true

1465 return _addr 

1466 return gef.heap.malloc_align_address(_addr) 

1467 

1468 def get_heap_info_list(self) -> Optional[List[GlibcHeapInfo]]: 

1469 if self.is_main_arena(): 1469 ↛ 1470line 1469 didn't jump to line 1470, because the condition on line 1469 was never true

1470 return None 

1471 heap_addr = self.get_heap_for_ptr(self.top) 

1472 heap_infos = [GlibcHeapInfo(heap_addr)] 

1473 while heap_infos[-1].prev is not None: 1473 ↛ 1474line 1473 didn't jump to line 1474, because the condition on line 1473 was never true

1474 prev = int(heap_infos[-1].prev) 

1475 heap_info = GlibcHeapInfo(prev) 

1476 heap_infos.append(heap_info) 

1477 return heap_infos[::-1] 

1478 

1479 @staticmethod 

1480 def get_heap_for_ptr(ptr: int) -> int: 

1481 """Find the corresponding heap for a given pointer (int). 

1482 See https://github.com/bminor/glibc/blob/glibc-2.34/malloc/arena.c#L129""" 

1483 if is_32bit(): 1483 ↛ 1484line 1483 didn't jump to line 1484, because the condition on line 1483 was never true

1484 default_mmap_threshold_max = 512 * 1024 

1485 else: # 64bit 

1486 default_mmap_threshold_max = 4 * 1024 * 1024 * cached_lookup_type("long").sizeof 

1487 heap_max_size = 2 * default_mmap_threshold_max 

1488 return ptr & ~(heap_max_size - 1) 

1489 

1490 @staticmethod 

1491 def verify(addr: int) -> bool: 

1492 """Verify that the address matches a possible valid GlibcArena""" 

1493 try: 

1494 test_arena = GlibcArena(f"*{addr:#x}") 

1495 cur_arena = GlibcArena(f"*{test_arena.next:#x}") 

1496 while cur_arena != test_arena: 

1497 if cur_arena == 0: 

1498 return False 

1499 cur_arena = GlibcArena(f"*{cur_arena.next:#x}") 

1500 except Exception as e: 

1501 return False 

1502 return True 

1503 

1504 

1505class GlibcChunk: 

1506 """Glibc chunk class. The default behavior (from_base=False) is to interpret the data starting at the memory 

1507 address pointed to as the chunk data. Setting from_base to True instead treats that data as the chunk header. 

1508 Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/.""" 

1509 

1510 class ChunkFlags(enum.IntFlag): 

1511 PREV_INUSE = 1 

1512 IS_MMAPPED = 2 

1513 NON_MAIN_ARENA = 4 

1514 

1515 def __str__(self) -> str: 

1516 return f" | ".join([ 

1517 Color.greenify("PREV_INUSE") if self.value & self.PREV_INUSE else Color.redify("PREV_INUSE"), 

1518 Color.greenify("IS_MMAPPED") if self.value & self.IS_MMAPPED else Color.redify("IS_MMAPPED"), 

1519 Color.greenify("NON_MAIN_ARENA") if self.value & self.NON_MAIN_ARENA else Color.redify("NON_MAIN_ARENA") 

1520 ]) 

1521 

1522 @staticmethod 

1523 def malloc_chunk_t() -> Type[ctypes.Structure]: 

1524 pointer = ctypes.c_uint64 if gef and gef.arch.ptrsize == 8 else ctypes.c_uint32 

1525 class malloc_chunk_cls(ctypes.Structure): 

1526 pass 

1527 

1528 malloc_chunk_cls._fields_ = [ 

1529 ("prev_size", pointer), 

1530 ("size", pointer), 

1531 ("fd", pointer), 

1532 ("bk", pointer), 

1533 ("fd_nextsize", ctypes.POINTER(malloc_chunk_cls)), 

1534 ("bk_nextsize", ctypes.POINTER(malloc_chunk_cls)), 

1535 ] 

1536 return malloc_chunk_cls 

1537 

1538 def __init__(self, addr: int, from_base: bool = False, allow_unaligned: bool = True) -> None: 

1539 ptrsize = gef.arch.ptrsize 

1540 self.data_address = addr + 2 * ptrsize if from_base else addr 

1541 self.base_address = addr if from_base else addr - 2 * ptrsize 

1542 if not allow_unaligned: 

1543 self.data_address = gef.heap.malloc_align_address(self.data_address) 

1544 self.size_addr = int(self.data_address - ptrsize) 

1545 self.prev_size_addr = self.base_address 

1546 self.reset() 

1547 return 

1548 

1549 def reset(self): 

1550 self._sizeof = ctypes.sizeof(GlibcChunk.malloc_chunk_t()) 

1551 self._data = gef.memory.read( 

1552 self.base_address, ctypes.sizeof(GlibcChunk.malloc_chunk_t())) 

1553 self._chunk = GlibcChunk.malloc_chunk_t().from_buffer_copy(self._data) 

1554 return 

1555 

1556 @property 

1557 def prev_size(self) -> int: 

1558 return self._chunk.prev_size 

1559 

1560 @property 

1561 def size(self) -> int: 

1562 return self._chunk.size & (~0x07) 

1563 

1564 @property 

1565 def flags(self) -> ChunkFlags: 

1566 return GlibcChunk.ChunkFlags(self._chunk.size & 0x07) 

1567 

1568 @property 

1569 def fd(self) -> int: 

1570 return self._chunk.fd 

1571 

1572 @property 

1573 def bk(self) -> int: 

1574 return self._chunk.bk 

1575 

1576 @property 

1577 def fd_nextsize(self) -> int: 

1578 return self._chunk.fd_nextsize 

1579 

1580 @property 

1581 def bk_nextsize(self) -> int: 

1582 return self._chunk.bk_nextsize 

1583 

1584 def get_usable_size(self) -> int: 

1585 # https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L4537 

1586 ptrsz = gef.arch.ptrsize 

1587 cursz = self.size 

1588 if cursz == 0: return cursz 1588 ↛ exitline 1588 didn't return from function 'get_usable_size', because the return on line 1588 wasn't executed

1589 if self.has_m_bit(): return cursz - 2 * ptrsz 1589 ↛ exitline 1589 didn't return from function 'get_usable_size', because the return on line 1589 wasn't executed

1590 return cursz - ptrsz 

1591 

1592 @property 

1593 def usable_size(self) -> int: 

1594 return self.get_usable_size() 

1595 

1596 def get_prev_chunk_size(self) -> int: 

1597 return gef.memory.read_integer(self.prev_size_addr) 

1598 

1599 def __iter__(self) -> Generator["GlibcChunk", None, None]: 

1600 current_chunk = self 

1601 top = gef.heap.main_arena.top 

1602 

1603 while True: 

1604 yield current_chunk 

1605 

1606 if current_chunk.base_address == top: 1606 ↛ 1607line 1606 didn't jump to line 1607, because the condition on line 1606 was never true

1607 break 

1608 

1609 if current_chunk.size == 0: 1609 ↛ 1610line 1609 didn't jump to line 1610, because the condition on line 1609 was never true

1610 break 

1611 

1612 next_chunk_addr = current_chunk.get_next_chunk_addr() 

1613 

1614 if not Address(value=next_chunk_addr).valid: 1614 ↛ 1615line 1614 didn't jump to line 1615, because the condition on line 1614 was never true

1615 break 

1616 

1617 next_chunk = current_chunk.get_next_chunk() 

1618 if next_chunk is None: 1618 ↛ 1619line 1618 didn't jump to line 1619, because the condition on line 1618 was never true

1619 break 

1620 

1621 current_chunk = next_chunk 

1622 return 

1623 

1624 def get_next_chunk(self, allow_unaligned: bool = False) -> "GlibcChunk": 

1625 addr = self.get_next_chunk_addr() 

1626 return GlibcChunk(addr, allow_unaligned=allow_unaligned) 

1627 

1628 def get_next_chunk_addr(self) -> int: 

1629 return self.data_address + self.size 

1630 

1631 def has_p_bit(self) -> bool: 

1632 return bool(self.flags & GlibcChunk.ChunkFlags.PREV_INUSE) 

1633 

1634 def has_m_bit(self) -> bool: 

1635 return bool(self.flags & GlibcChunk.ChunkFlags.IS_MMAPPED) 

1636 

1637 def has_n_bit(self) -> bool: 

1638 return bool(self.flags & GlibcChunk.ChunkFlags.NON_MAIN_ARENA) 

1639 

1640 def is_used(self) -> bool: 

1641 """Check if the current block is used by: 

1642 - checking the M bit is true 

1643 - or checking that next chunk PREV_INUSE flag is true""" 

1644 if self.has_m_bit(): 1644 ↛ 1645line 1644 didn't jump to line 1645, because the condition on line 1644 was never true

1645 return True 

1646 

1647 next_chunk = self.get_next_chunk() 

1648 return True if next_chunk.has_p_bit() else False 

1649 

1650 def __str_sizes(self) -> str: 

1651 msg = [] 

1652 failed = False 

1653 

1654 try: 

1655 msg.append("Chunk size: {0:d} ({0:#x})".format(self.size)) 

1656 msg.append("Usable size: {0:d} ({0:#x})".format(self.usable_size)) 

1657 failed = True 

1658 except gdb.MemoryError: 

1659 msg.append(f"Chunk size: Cannot read at {self.size_addr:#x} (corrupted?)") 

1660 

1661 try: 

1662 msg.append("Previous chunk size: {0:d} ({0:#x})".format(self.get_prev_chunk_size())) 

1663 failed = True 

1664 except gdb.MemoryError: 

1665 msg.append(f"Previous chunk size: Cannot read at {self.base_address:#x} (corrupted?)") 

1666 

1667 if failed: 1667 ↛ 1670line 1667 didn't jump to line 1670, because the condition on line 1667 was never false

1668 msg.append(str(self.flags)) 

1669 

1670 return "\n".join(msg) 

1671 

1672 def _str_pointers(self) -> str: 

1673 fwd = self.data_address 

1674 bkw = self.data_address + gef.arch.ptrsize 

1675 

1676 msg = [] 

1677 try: 

1678 msg.append(f"Forward pointer: {self.fd:#x}") 

1679 except gdb.MemoryError: 

1680 msg.append(f"Forward pointer: {fwd:#x} (corrupted?)") 

1681 

1682 try: 

1683 msg.append(f"Backward pointer: {self.bk:#x}") 

1684 except gdb.MemoryError: 

1685 msg.append(f"Backward pointer: {bkw:#x} (corrupted?)") 

1686 

1687 return "\n".join(msg) 

1688 

1689 def __str__(self) -> str: 

1690 return (f"{Color.colorify('Chunk', 'yellow bold underline')}(addr={self.data_address:#x}, " 

1691 f"size={self.size:#x}, flags={self.flags!s})") 

1692 

1693 def psprint(self) -> str: 

1694 msg = [ 

1695 str(self), 

1696 self.__str_sizes(), 

1697 ] 

1698 if not self.is_used(): 1698 ↛ 1699line 1698 didn't jump to line 1699, because the condition on line 1698 was never true

1699 msg.append(f"\n\n{self._str_pointers()}") 

1700 return "\n".join(msg) + "\n" 

1701 

1702 

1703class GlibcFastChunk(GlibcChunk): 

1704 

1705 @property 

1706 def fd(self) -> int: 

1707 assert(gef and gef.libc.version) 

1708 if gef.libc.version < (2, 32): 1708 ↛ 1709line 1708 didn't jump to line 1709, because the condition on line 1708 was never true

1709 return self._chunk.fd 

1710 return self.reveal_ptr(self.data_address) 

1711 

1712 def protect_ptr(self, pos: int, pointer: int) -> int: 

1713 """https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L339""" 

1714 assert(gef and gef.libc.version) 

1715 if gef.libc.version < (2, 32): 

1716 return pointer 

1717 return (pos >> 12) ^ pointer 

1718 

1719 def reveal_ptr(self, pointer: int) -> int: 

1720 """https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L341""" 

1721 assert(gef and gef.libc.version) 

1722 if gef.libc.version < (2, 32): 1722 ↛ 1723line 1722 didn't jump to line 1723, because the condition on line 1722 was never true

1723 return pointer 

1724 return gef.memory.read_integer(pointer) ^ (pointer >> 12) 

1725 

1726class GlibcTcacheChunk(GlibcFastChunk): 

1727 

1728 pass 

1729 

1730@deprecated("Use GefLibcManager.find_libc_version()") 

1731def get_libc_version() -> Tuple[int, ...]: 

1732 return GefLibcManager.find_libc_version() 

1733 

1734def titlify(text: str, color: Optional[str] = None, msg_color: Optional[str] = None) -> str: 

1735 """Print a centered title.""" 

1736 _, cols = get_terminal_size() 

1737 nb = (cols - len(text) - 2) // 2 

1738 line_color = color or gef.config["theme.default_title_line"] 

1739 text_color = msg_color or gef.config["theme.default_title_message"] 

1740 

1741 msg = [Color.colorify(f"{HORIZONTAL_LINE * nb} ", line_color), 

1742 Color.colorify(text, text_color), 

1743 Color.colorify(f" {HORIZONTAL_LINE * nb}", line_color)] 

1744 return "".join(msg) 

1745 

1746 

1747def dbg(msg: str) -> None: 

1748 if gef.config["gef.debug"] is True: 1748 ↛ 1750line 1748 didn't jump to line 1750, because the condition on line 1748 was never false

1749 gef_print(f"{Color.colorify('[=]', 'bold cyan')} {msg}") 

1750 return 

1751 

1752 

1753def err(msg: str) -> None: 

1754 gef_print(f"{Color.colorify('[!]', 'bold red')} {msg}") 

1755 return 

1756 

1757 

1758def warn(msg: str) -> None: 

1759 gef_print(f"{Color.colorify('[*]', 'bold yellow')} {msg}") 

1760 return 

1761 

1762 

1763def ok(msg: str) -> None: 

1764 gef_print(f"{Color.colorify('[+]', 'bold green')} {msg}") 

1765 return 

1766 

1767 

1768def info(msg: str) -> None: 

1769 gef_print(f"{Color.colorify('[+]', 'bold blue')} {msg}") 

1770 return 

1771 

1772 

1773def push_context_message(level: str, message: str) -> None: 

1774 """Push the message to be displayed the next time the context is invoked.""" 

1775 if level not in ("error", "warn", "ok", "info"): 1775 ↛ 1776line 1775 didn't jump to line 1776, because the condition on line 1775 was never true

1776 err(f"Invalid level '{level}', discarding message") 

1777 return 

1778 gef.ui.context_messages.append((level, message)) 

1779 return 

1780 

1781 

1782def show_last_exception() -> None: 

1783 """Display the last Python exception.""" 

1784 

1785 def _show_code_line(fname: str, idx: int) -> str: 

1786 fname = os.path.expanduser(os.path.expandvars(fname)) 

1787 with open(fname, "r") as f: 

1788 _data = f.readlines() 

1789 return _data[idx - 1] if 0 < idx < len(_data) else "" 

1790 

1791 gef_print("") 

1792 exc_type, exc_value, exc_traceback = sys.exc_info() 

1793 

1794 gef_print(" Exception raised ".center(80, HORIZONTAL_LINE)) 

1795 gef_print(f"{Color.colorify(exc_type.__name__, 'bold underline red')}: {exc_value}") 

1796 gef_print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE)) 

1797 

1798 for fs in traceback.extract_tb(exc_traceback)[::-1]: 

1799 filename, lineno, method, code = fs 

1800 

1801 if not code or not code.strip(): 1801 ↛ 1802line 1801 didn't jump to line 1802, because the condition on line 1801 was never true

1802 code = _show_code_line(filename, lineno) 

1803 

1804 gef_print(f"""{DOWN_ARROW} File "{Color.yellowify(filename)}", line {lineno:d}, in {Color.greenify(method)}()""") 

1805 gef_print(f" {RIGHT_ARROW} {code}") 

1806 

1807 gef_print(" Version ".center(80, HORIZONTAL_LINE)) 

1808 gdb.execute("version full") 

1809 gef_print(" Last 10 GDB commands ".center(80, HORIZONTAL_LINE)) 

1810 gdb.execute("show commands") 

1811 gef_print(" Runtime environment ".center(80, HORIZONTAL_LINE)) 

1812 gef_print(f"* GDB: {gdb.VERSION}") 

1813 gef_print(f"* Python: {sys.version_info.major:d}.{sys.version_info.minor:d}.{sys.version_info.micro:d} - {sys.version_info.releaselevel}") 

1814 gef_print(f"* OS: {platform.system()} - {platform.release()} ({platform.machine()})") 

1815 

1816 try: 

1817 lsb_release = which("lsb_release") 

1818 gdb.execute(f"!'{lsb_release}' -a") 

1819 except FileNotFoundError: 

1820 gef_print("lsb_release is missing, cannot collect additional debug information") 

1821 

1822 gef_print(HORIZONTAL_LINE*80) 

1823 gef_print("") 

1824 return 

1825 

1826 

1827def gef_pystring(x: bytes) -> str: 

1828 """Returns a sanitized version as string of the bytes list given in input.""" 

1829 res = str(x, encoding="utf-8") 

1830 substs = [("\n", "\\n"), ("\r", "\\r"), ("\t", "\\t"), ("\v", "\\v"), ("\b", "\\b"), ] 

1831 for x, y in substs: res = res.replace(x, y) 

1832 return res 

1833 

1834 

1835def gef_pybytes(x: str) -> bytes: 

1836 """Returns an immutable bytes list from the string given as input.""" 

1837 return bytes(str(x), encoding="utf-8") 

1838 

1839 

1840@lru_cache() 

1841def which(program: str) -> pathlib.Path: 

1842 """Locate a command on the filesystem.""" 

1843 for path in os.environ["PATH"].split(os.pathsep): 

1844 dirname = pathlib.Path(path) 

1845 fpath = dirname / program 

1846 if os.access(fpath, os.X_OK): 

1847 return fpath 

1848 

1849 raise FileNotFoundError(f"Missing file `{program}`") 

1850 

1851 

1852def style_byte(b: int, color: bool = True) -> str: 

1853 style = { 

1854 "nonprintable": "yellow", 

1855 "printable": "white", 

1856 "00": "gray", 

1857 "0a": "blue", 

1858 "ff": "green", 

1859 } 

1860 sbyte = f"{b:02x}" 

1861 if not color or gef.config["highlight.regex"]: 

1862 return sbyte 

1863 

1864 if sbyte in style: 

1865 st = style[sbyte] 

1866 elif chr(b) in (string.ascii_letters + string.digits + string.punctuation + " "): 

1867 st = style.get("printable") 

1868 else: 

1869 st = style.get("nonprintable") 

1870 if st: 1870 ↛ 1872line 1870 didn't jump to line 1872, because the condition on line 1870 was never false

1871 sbyte = Color.colorify(sbyte, st) 

1872 return sbyte 

1873 

1874 

1875def hexdump(source: ByteString, length: int = 0x10, separator: str = ".", show_raw: bool = False, show_symbol: bool = True, base: int = 0x00) -> str: 

1876 """Return the hexdump of `src` argument. 

1877 @param source *MUST* be of type bytes or bytearray 

1878 @param length is the length of items per line 

1879 @param separator is the default character to use if one byte is not printable 

1880 @param show_raw if True, do not add the line nor the text translation 

1881 @param base is the start address of the block being hexdump 

1882 @return a string with the hexdump""" 

1883 result = [] 

1884 align = gef.arch.ptrsize * 2 + 2 if is_alive() else 18 

1885 

1886 for i in range(0, len(source), length): 

1887 chunk = bytearray(source[i : i + length]) 

1888 hexa = " ".join([style_byte(b, color=not show_raw) for b in chunk]) 

1889 

1890 if show_raw: 

1891 result.append(hexa) 

1892 continue 

1893 

1894 text = "".join([chr(b) if 0x20 <= b < 0x7F else separator for b in chunk]) 

1895 if show_symbol: 1895 ↛ 1899line 1895 didn't jump to line 1899, because the condition on line 1895 was never false

1896 sym = gdb_get_location_from_symbol(base + i) 

1897 sym = "<{:s}+{:04x}>".format(*sym) if sym else "" 

1898 else: 

1899 sym = "" 

1900 

1901 result.append(f"{base + i:#0{align}x} {sym} {hexa:<{3 * length}} {text}") 

1902 return "\n".join(result) 

1903 

1904 

1905def is_debug() -> bool: 

1906 """Check if debug mode is enabled.""" 

1907 return gef.config["gef.debug"] is True 

1908 

1909 

1910def buffer_output() -> bool: 

1911 """Check if output should be buffered until command completion.""" 

1912 return gef.config["gef.buffer"] is True 

1913 

1914 

1915def hide_context() -> bool: 

1916 """Helper function to hide the context pane.""" 

1917 gef.ui.context_hidden = True 

1918 return True 

1919 

1920 

1921def unhide_context() -> bool: 

1922 """Helper function to unhide the context pane.""" 

1923 gef.ui.context_hidden = False 

1924 return True 

1925 

1926 

1927class DisableContextOutputContext: 

1928 def __enter__(self) -> None: 

1929 hide_context() 

1930 return 

1931 

1932 def __exit__(self, *exc: Any) -> None: 

1933 unhide_context() 

1934 return 

1935 

1936 

1937class RedirectOutputContext: 

1938 def __init__(self, to_file: str = "/dev/null") -> None: 

1939 if " " in to_file: raise ValueEror("Target filepath cannot contain spaces") 1939 ↛ exitline 1939 didn't except from function '__init__', because the raise on line 1939 wasn't executed

1940 self.redirection_target_file = to_file 

1941 return 

1942 

1943 def __enter__(self) -> None: 

1944 """Redirect all GDB output to `to_file` parameter. By default, `to_file` redirects to `/dev/null`.""" 

1945 gdb.execute("set logging overwrite") 

1946 gdb.execute(f"set logging file {self.redirection_target_file}") 

1947 gdb.execute("set logging redirect on") 

1948 gdb.execute("set logging on") 

1949 return 

1950 

1951 def __exit__(self, *exc: Any) -> None: 

1952 """Disable the output redirection, if any.""" 

1953 gdb.execute("set logging off") 

1954 gdb.execute("set logging redirect off") 

1955 return 

1956 

1957 

1958def enable_redirect_output(to_file: str = "/dev/null") -> None: 

1959 """Redirect all GDB output to `to_file` parameter. By default, `to_file` redirects to `/dev/null`.""" 

1960 if " " in to_file: raise ValueEror("Target filepath cannot contain spaces") 

1961 gdb.execute("set logging overwrite") 

1962 gdb.execute(f"set logging file {to_file}") 

1963 gdb.execute("set logging redirect on") 

1964 gdb.execute("set logging on") 

1965 return 

1966 

1967 

1968def disable_redirect_output() -> None: 

1969 """Disable the output redirection, if any.""" 

1970 gdb.execute("set logging off") 

1971 gdb.execute("set logging redirect off") 

1972 return 

1973 

1974 

1975def gef_makedirs(path: str, mode: int = 0o755) -> pathlib.Path: 

1976 """Recursive mkdir() creation. If successful, return the absolute path of the directory created.""" 

1977 fpath = pathlib.Path(path) 

1978 if not fpath.is_dir(): 

1979 fpath.mkdir(mode=mode, exist_ok=True, parents=True) 

1980 return fpath.absolute() 

1981 

1982 

1983@lru_cache() 

1984def gdb_lookup_symbol(sym: str) -> Optional[Tuple[Optional[str], Optional[Tuple[gdb.Symtab_and_line, ...]]]]: 

1985 """Fetch the proper symbol or None if not defined.""" 

1986 try: 

1987 return gdb.decode_line(sym)[1] 

1988 except gdb.error: 

1989 return None 

1990 

1991 

1992@lru_cache(maxsize=512) 

1993def gdb_get_location_from_symbol(address: int) -> Optional[Tuple[str, int]]: 

1994 """Retrieve the location of the `address` argument from the symbol table. 

1995 Return a tuple with the name and offset if found, None otherwise.""" 

1996 # this is horrible, ugly hack and shitty perf... 

1997 # find a *clean* way to get gdb.Location from an address 

1998 sym = str(gdb.execute(f"info symbol {address:#x}", to_string=True)) 

1999 if sym.startswith("No symbol matches"): 

2000 return None 

2001 

2002 i = sym.find(" in section ") 

2003 sym = sym[:i].split() 

2004 name, offset = sym[0], 0 

2005 if len(sym) == 3 and sym[2].isdigit(): 

2006 offset = int(sym[2]) 

2007 return name, offset 

2008 

2009 

2010def gdb_disassemble(start_pc: int, **kwargs: int) -> Generator[Instruction, None, None]: 

2011 """Disassemble instructions from `start_pc` (Integer). Accepts the following named 

2012 parameters: 

2013 - `end_pc` (Integer) only instructions whose start address fall in the interval from 

2014 start_pc to end_pc are returned. 

2015 - `count` (Integer) list at most this many disassembled instructions 

2016 If `end_pc` and `count` are not provided, the function will behave as if `count=1`. 

2017 Return an iterator of Instruction objects 

2018 """ 

2019 frame = gdb.selected_frame() 

2020 arch = frame.architecture() 

2021 

2022 for insn in arch.disassemble(start_pc, **kwargs): 

2023 assert isinstance(insn["addr"], int) 

2024 assert isinstance(insn["length"], int) 

2025 assert isinstance(insn["asm"], str) 

2026 address = insn["addr"] 

2027 asm = insn["asm"].rstrip().split(None, 1) 

2028 if len(asm) > 1: 

2029 mnemo, operands = asm 

2030 operands = operands.split(",") 

2031 else: 

2032 mnemo, operands = asm[0], [] 

2033 

2034 loc = gdb_get_location_from_symbol(address) 

2035 location = "<{}+{}>".format(*loc) if loc else "" 

2036 

2037 opcodes = gef.memory.read(insn["addr"], insn["length"]) 

2038 

2039 yield Instruction(address, location, mnemo, operands, opcodes) 

2040 

2041 

2042def gdb_get_nth_previous_instruction_address(addr: int, n: int) -> Optional[int]: 

2043 """Return the address (Integer) of the `n`-th instruction before `addr`.""" 

2044 # fixed-length ABI 

2045 if gef.arch.instruction_length: 2045 ↛ 2046line 2045 didn't jump to line 2046, because the condition on line 2045 was never true

2046 return max(0, addr - n * gef.arch.instruction_length) 

2047 

2048 # variable-length ABI 

2049 cur_insn_addr = gef_current_instruction(addr).address 

2050 

2051 # we try to find a good set of previous instructions by "guessing" disassembling backwards 

2052 # the 15 comes from the longest instruction valid size 

2053 for i in range(15 * n, 0, -1): 2053 ↛ 2076line 2053 didn't jump to line 2076, because the loop on line 2053 didn't complete

2054 try: 

2055 insns = list(gdb_disassemble(addr - i, end_pc=cur_insn_addr)) 

2056 except gdb.MemoryError: 

2057 # this is because we can hit an unmapped page trying to read backward 

2058 break 

2059 

2060 # 1. check that the disassembled instructions list size can satisfy 

2061 if len(insns) < n + 1: # we expect the current instruction plus the n before it 2061 ↛ 2062line 2061 didn't jump to line 2062, because the condition on line 2061 was never true

2062 continue 

2063 

2064 # If the list of instructions is longer than what we need, then we 

2065 # could get lucky and already have more than what we need, so slice down 

2066 insns = insns[-n - 1 :] 

2067 

2068 # 2. check that the sequence ends with the current address 

2069 if insns[-1].address != cur_insn_addr: 2069 ↛ 2070line 2069 didn't jump to line 2070, because the condition on line 2069 was never true

2070 continue 

2071 

2072 # 3. check all instructions are valid 

2073 if all(insn.is_valid() for insn in insns): 2073 ↛ 2053line 2073 didn't jump to line 2053, because the condition on line 2073 was never false

2074 return insns[0].address 

2075 

2076 return None 

2077 

2078 

2079@deprecated(solution="Use `gef_instruction_n().address`") 

2080def gdb_get_nth_next_instruction_address(addr: int, n: int) -> int: 

2081 """Return the address of the `n`-th instruction after `addr`. """ 

2082 return gef_instruction_n(addr, n).address 

2083 

2084 

2085def gef_instruction_n(addr: int, n: int) -> Instruction: 

2086 """Return the `n`-th instruction after `addr` as an Instruction object. Note that `n` is treated as 

2087 an positive index, starting from 0 (current instruction address)""" 

2088 return list(gdb_disassemble(addr, count=n + 1))[n] 

2089 

2090 

2091def gef_get_instruction_at(addr: int) -> Instruction: 

2092 """Return the full Instruction found at the specified address.""" 

2093 insn = next(gef_disassemble(addr, 1)) 

2094 return insn 

2095 

2096 

2097def gef_current_instruction(addr: int) -> Instruction: 

2098 """Return the current instruction as an Instruction object.""" 

2099 return gef_instruction_n(addr, 0) 

2100 

2101 

2102def gef_next_instruction(addr: int) -> Instruction: 

2103 """Return the next instruction as an Instruction object.""" 

2104 return gef_instruction_n(addr, 1) 

2105 

2106 

2107def gef_disassemble(addr: int, nb_insn: int, nb_prev: int = 0) -> Generator[Instruction, None, None]: 

2108 """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr`. 

2109 Return an iterator of Instruction objects.""" 

2110 nb_insn = max(1, nb_insn) 

2111 

2112 if nb_prev: 

2113 try: 

2114 start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) 

2115 if start_addr: 

2116 for insn in gdb_disassemble(start_addr, count=nb_prev): 

2117 if insn.address == addr: break 2117 ↛ 2123line 2117 didn't jump to line 2123, because the break on line 2117 wasn't executed

2118 yield insn 

2119 except gdb.MemoryError: 

2120 # If the address pointing to the previous instruction(s) is not mapped, simply skip them 

2121 pass 

2122 

2123 for insn in gdb_disassemble(addr, count=nb_insn): 

2124 yield insn 

2125 

2126 

2127def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[str, List[str]]: 

2128 """Execute an external command and return the result.""" 

2129 res = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=kwargs.get("shell", False)) 

2130 return [gef_pystring(_) for _ in res.splitlines()] if as_list else gef_pystring(res) 

2131 

2132 

2133def gef_execute_gdb_script(commands: str) -> None: 

2134 """Execute the parameter `source` as GDB command. This is done by writing `commands` to 

2135 a temporary file, which is then executed via GDB `source` command. The tempfile is then deleted.""" 

2136 fd, fname = tempfile.mkstemp(suffix=".gdb", prefix="gef_") 

2137 with os.fdopen(fd, "w") as f: 

2138 f.write(commands) 

2139 f.flush() 

2140 

2141 fname = pathlib.Path(fname) 

2142 if fname.is_file() and os.access(fname, os.R_OK): 

2143 gdb.execute(f"source {fname}") 

2144 fname.unlink() 

2145 return 

2146 

2147 

2148@deprecated("Use Elf(fname).checksec()") 

2149def checksec(filename: str) -> Dict[str, bool]: 

2150 return Elf(filename).checksec 

2151 

2152 

2153@lru_cache() 

2154def get_arch() -> str: 

2155 """Return the binary's architecture.""" 

2156 if is_alive(): 

2157 arch = gdb.selected_frame().architecture() 

2158 return arch.name() 

2159 

2160 arch_str = gdb.execute("show architecture", to_string=True).strip() 

2161 pat = "The target architecture is set automatically (currently " 

2162 if arch_str.startswith(pat): 2162 ↛ 2163line 2162 didn't jump to line 2163, because the condition on line 2162 was never true

2163 arch_str = arch_str[len(pat):].rstrip(")") 

2164 return arch_str 

2165 

2166 pat = "The target architecture is assumed to be " 

2167 if arch_str.startswith(pat): 2167 ↛ 2168line 2167 didn't jump to line 2168, because the condition on line 2167 was never true

2168 return arch_str[len(pat):] 

2169 

2170 pat = "The target architecture is set to " 

2171 if arch_str.startswith(pat): 2171 ↛ 2178line 2171 didn't jump to line 2178, because the condition on line 2171 was never false

2172 # GDB version >= 10.1 

2173 if '"auto"' in arch_str: 2173 ↛ 2175line 2173 didn't jump to line 2175, because the condition on line 2173 was never false

2174 return re.findall(r"currently \"(.+)\"", arch_str)[0] 

2175 return re.findall(r"\"(.+)\"", arch_str)[0] 

2176 

2177 # Unknown, we throw an exception to be safe 

2178 raise RuntimeError(f"Unknown architecture: {arch_str}") 

2179 

2180 

2181@deprecated("Use `gef.binary.entry_point` instead") 

2182def get_entry_point() -> Optional[int]: 

2183 """Return the binary entry point.""" 

2184 return gef.binary.entry_point if gef.binary else None 

2185 

2186 

2187def is_pie(fpath: str) -> bool: 

2188 return Elf(fpath).checksec["PIE"] 

2189 

2190 

2191@deprecated("Prefer `gef.arch.endianness == Endianness.BIG_ENDIAN`") 

2192def is_big_endian() -> bool: 

2193 return gef.arch.endianness == Endianness.BIG_ENDIAN 

2194 

2195 

2196@deprecated("gef.arch.endianness == Endianness.LITTLE_ENDIAN") 

2197def is_little_endian() -> bool: 

2198 return gef.arch.endianness == Endianness.LITTLE_ENDIAN 

2199 

2200 

2201def flags_to_human(reg_value: int, value_table: Dict[int, str]) -> str: 

2202 """Return a human readable string showing the flag states.""" 

2203 flags = [] 

2204 for bit_index, name in value_table.items(): 

2205 flags.append(Color.boldify(name.upper()) if reg_value & (1<<bit_index) != 0 else name.lower()) 

2206 return f"[{' '.join(flags)}]" 

2207 

2208 

2209@lru_cache() 

2210def get_section_base_address(name: str) -> Optional[int]: 

2211 section = process_lookup_path(name) 

2212 return section.page_start if section else None 

2213 

2214 

2215@lru_cache() 

2216def get_zone_base_address(name: str) -> Optional[int]: 

2217 zone = file_lookup_name_path(name, get_filepath()) 

2218 return zone.zone_start if zone else None 

2219 

2220 

2221# 

2222# Architecture classes 

2223# 

2224@deprecated("Using the decorator `register_architecture` is unecessary") 

2225def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: 

2226 return cls 

2227 

2228class ArchitectureBase: 

2229 """Class decorator for declaring an architecture to GEF.""" 

2230 aliases: Union[Tuple[()], Tuple[Union[str, Elf.Abi], ...]] = () 

2231 

2232 def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): 

2233 global __registered_architectures__ 

2234 super().__init_subclass__(**kwargs) 

2235 for key in getattr(cls, "aliases"): 

2236 if issubclass(cls, Architecture): 2236 ↛ 2235line 2236 didn't jump to line 2235, because the condition on line 2236 was never false

2237 __registered_architectures__[key] = cls 

2238 return 

2239 

2240 

2241class Architecture(ArchitectureBase): 

2242 """Generic metaclass for the architecture supported by GEF.""" 

2243 

2244 # Mandatory defined attributes by inheriting classes 

2245 arch: str 

2246 mode: str 

2247 all_registers: Union[Tuple[()], Tuple[str, ...]] 

2248 nop_insn: bytes 

2249 return_register: str 

2250 flag_register: Optional[str] 

2251 instruction_length: Optional[int] 

2252 flags_table: Dict[int, str] 

2253 syscall_register: Optional[str] 

2254 syscall_instructions: Union[Tuple[()], Tuple[str, ...]] 

2255 function_parameters: Union[Tuple[()], Tuple[str, ...]] 

2256 

2257 # Optionally defined attributes 

2258 _ptrsize: Optional[int] = None 

2259 _endianness: Optional[Endianness] = None 

2260 special_registers: Union[Tuple[()], Tuple[str, ...]] = () 

2261 

2262 def __init_subclass__(cls, **kwargs): 

2263 super().__init_subclass__(**kwargs) 

2264 attributes = ("arch", "mode", "aliases", "all_registers", "nop_insn", 

2265 "return_register", "flag_register", "instruction_length", "flags_table", 

2266 "function_parameters",) 

2267 if not all(map(lambda x: hasattr(cls, x), attributes)): 2267 ↛ 2268line 2267 didn't jump to line 2268, because the condition on line 2267 was never true

2268 raise NotImplementedError 

2269 

2270 def __str__(self) -> str: 

2271 return f"Architecture({self.arch}, {self.mode or 'None'}, {repr(self.endianness)})" 

2272 

2273 @staticmethod 

2274 def supports_gdb_arch(gdb_arch: str) -> Optional[bool]: 

2275 """If implemented by a child `Architecture`, this function dictates if the current class 

2276 supports the loaded ELF file (which can be accessed via `gef.binary`). This callback 

2277 function will override any assumption made by GEF to determine the architecture.""" 

2278 return None 

2279 

2280 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

2281 raise NotImplementedError 

2282 

2283 def is_call(self, insn: Instruction) -> bool: 

2284 raise NotImplementedError 

2285 

2286 def is_ret(self, insn: Instruction) -> bool: 

2287 raise NotImplementedError 

2288 

2289 def is_conditional_branch(self, insn: Instruction) -> bool: 

2290 raise NotImplementedError 

2291 

2292 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

2293 raise NotImplementedError 

2294 

2295 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: 

2296 raise NotImplementedError 

2297 

2298 def canary_address(self) -> int: 

2299 raise NotImplementedError 

2300 

2301 @classmethod 

2302 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2303 raise NotImplementedError 

2304 

2305 def reset_caches(self) -> None: 

2306 self.__get_register_for_selected_frame.cache_clear() 

2307 return 

2308 

2309 def __get_register(self, regname: str) -> int: 

2310 """Return a register's value.""" 

2311 curframe = gdb.selected_frame() 

2312 key = curframe.pc() ^ int(curframe.read_register('sp')) # todo: check when/if gdb.Frame implements `level()` 

2313 return self.__get_register_for_selected_frame(regname, key) 

2314 

2315 @lru_cache() 

2316 def __get_register_for_selected_frame(self, regname: str, hash_key: int) -> int: 

2317 # 1st chance 

2318 try: 

2319 return parse_address(regname) 

2320 except gdb.error: 

2321 pass 

2322 

2323 # 2nd chance - if an exception, propagate it 

2324 regname = regname.lstrip("$") 

2325 value = gdb.selected_frame().read_register(regname) 

2326 return int(value) 

2327 

2328 def register(self, name: str) -> int: 

2329 if not is_alive(): 

2330 raise gdb.error("No debugging session active") 

2331 return self.__get_register(name) 

2332 

2333 @property 

2334 def registers(self) -> Generator[str, None, None]: 

2335 yield from self.all_registers 

2336 

2337 @property 

2338 def pc(self) -> int: 

2339 return self.register("$pc") 

2340 

2341 @property 

2342 def sp(self) -> int: 

2343 return self.register("$sp") 

2344 

2345 @property 

2346 def fp(self) -> int: 

2347 return self.register("$fp") 

2348 

2349 @property 

2350 def ptrsize(self) -> int: 

2351 if not self._ptrsize: 2351 ↛ 2352line 2351 didn't jump to line 2352, because the condition on line 2351 was never true

2352 res = cached_lookup_type("size_t") 

2353 if res is not None: 

2354 self._ptrsize = res.sizeof 

2355 else: 

2356 self._ptrsize = gdb.parse_and_eval("$pc").type.sizeof 

2357 return self._ptrsize 

2358 

2359 @property 

2360 def endianness(self) -> Endianness: 

2361 if not self._endianness: 

2362 output = gdb.execute("show endian", to_string=True).strip().lower() 

2363 if "little endian" in output: 2363 ↛ 2365line 2363 didn't jump to line 2365, because the condition on line 2363 was never false

2364 self._endianness = Endianness.LITTLE_ENDIAN 

2365 elif "big endian" in output: 

2366 self._endianness = Endianness.BIG_ENDIAN 

2367 else: 

2368 raise OSError(f"No valid endianess found in '{output}'") 

2369 return self._endianness 

2370 

2371 def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: 

2372 """Retrieves the correct parameter used for the current function call.""" 

2373 reg = self.function_parameters[i] 

2374 val = self.register(reg) 

2375 key = reg 

2376 return key, val 

2377 

2378 

2379class GenericArchitecture(Architecture): 

2380 arch = "Generic" 

2381 mode = "" 

2382 aliases = ("GenericArchitecture",) 

2383 all_registers = () 

2384 instruction_length = 0 

2385 return_register = "" 

2386 function_parameters = () 

2387 syscall_register = "" 

2388 syscall_instructions = () 

2389 nop_insn = b"" 

2390 flag_register = None 

2391 flags_table = {} 

2392 

2393 

2394class RISCV(Architecture): 

2395 arch = "RISCV" 

2396 mode = "RISCV" 

2397 aliases = ("RISCV", Elf.Abi.RISCV) 

2398 all_registers = ("$zero", "$ra", "$sp", "$gp", "$tp", "$t0", "$t1", 

2399 "$t2", "$fp", "$s1", "$a0", "$a1", "$a2", "$a3", 

2400 "$a4", "$a5", "$a6", "$a7", "$s2", "$s3", "$s4", 

2401 "$s5", "$s6", "$s7", "$s8", "$s9", "$s10", "$s11", 

2402 "$t3", "$t4", "$t5", "$t6",) 

2403 return_register = "$a0" 

2404 function_parameters = ("$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7") 

2405 syscall_register = "$a7" 

2406 syscall_instructions = ("ecall",) 

2407 nop_insn = b"\x00\x00\x00\x13" 

2408 # RISC-V has no flags registers 

2409 flag_register = None 

2410 flags_table = {} 

2411 

2412 @property 

2413 def instruction_length(self) -> int: 

2414 return 4 

2415 

2416 def is_call(self, insn: Instruction) -> bool: 

2417 return insn.mnemonic == "call" 

2418 

2419 def is_ret(self, insn: Instruction) -> bool: 

2420 mnemo = insn.mnemonic 

2421 if mnemo == "ret": 

2422 return True 

2423 elif (mnemo == "jalr" and insn.operands[0] == "zero" and 

2424 insn.operands[1] == "ra" and insn.operands[2] == 0): 

2425 return True 

2426 elif (mnemo == "c.jalr" and insn.operands[0] == "ra"): 

2427 return True 

2428 return False 

2429 

2430 @classmethod 

2431 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2432 raise OSError(f"Architecture {cls.arch} not supported yet") 

2433 

2434 @property 

2435 def ptrsize(self) -> int: 

2436 if self._ptrsize is not None: 

2437 return self._ptrsize 

2438 if is_alive(): 

2439 self._ptrsize = gdb.parse_and_eval("$pc").type.sizeof 

2440 return self._ptrsize 

2441 return 4 

2442 

2443 def is_conditional_branch(self, insn: Instruction) -> bool: 

2444 return insn.mnemonic.startswith("b") 

2445 

2446 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

2447 def long_to_twos_complement(v: int) -> int: 

2448 """Convert a python long value to its two's complement.""" 

2449 if is_32bit(): 

2450 if v & 0x80000000: 

2451 return v - 0x100000000 

2452 elif is_64bit(): 

2453 if v & 0x8000000000000000: 

2454 return v - 0x10000000000000000 

2455 else: 

2456 raise OSError("RISC-V: ELF file is not ELF32 or ELF64. This is not currently supported") 

2457 return v 

2458 

2459 mnemo = insn.mnemonic 

2460 condition = mnemo[1:] 

2461 

2462 if condition.endswith("z"): 

2463 # r2 is the zero register if we are comparing to 0 

2464 rs1 = gef.arch.register(insn.operands[0]) 

2465 rs2 = gef.arch.register("$zero") 

2466 condition = condition[:-1] 

2467 elif len(insn.operands) > 2: 

2468 # r2 is populated with the second operand 

2469 rs1 = gef.arch.register(insn.operands[0]) 

2470 rs2 = gef.arch.register(insn.operands[1]) 

2471 else: 

2472 raise OSError(f"RISC-V: Failed to get rs1 and rs2 for instruction: `{insn}`") 

2473 

2474 # If the conditional operation is not unsigned, convert the python long into 

2475 # its two's complement 

2476 if not condition.endswith("u"): 

2477 rs2 = long_to_twos_complement(rs2) 

2478 rs1 = long_to_twos_complement(rs1) 

2479 else: 

2480 condition = condition[:-1] 

2481 

2482 if condition == "eq": 

2483 if rs1 == rs2: taken, reason = True, f"{rs1}={rs2}" 

2484 else: taken, reason = False, f"{rs1}!={rs2}" 

2485 elif condition == "ne": 

2486 if rs1 != rs2: taken, reason = True, f"{rs1}!={rs2}" 

2487 else: taken, reason = False, f"{rs1}={rs2}" 

2488 elif condition == "lt": 

2489 if rs1 < rs2: taken, reason = True, f"{rs1}<{rs2}" 

2490 else: taken, reason = False, f"{rs1}>={rs2}" 

2491 elif condition == "le": 

2492 if rs1 <= rs2: taken, reason = True, f"{rs1}<={rs2}" 

2493 else: taken, reason = False, f"{rs1}>{rs2}" 

2494 elif condition == "ge": 

2495 if rs1 < rs2: taken, reason = True, f"{rs1}>={rs2}" 

2496 else: taken, reason = False, f"{rs1}<{rs2}" 

2497 else: 

2498 raise OSError(f"RISC-V: Conditional instruction `{insn}` not supported yet") 

2499 

2500 return taken, reason 

2501 

2502 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: 

2503 ra = None 

2504 if self.is_ret(insn): 

2505 ra = gef.arch.register("$ra") 

2506 elif frame.older(): 

2507 ra = frame.older().pc() 

2508 return ra 

2509 

2510 

2511class ARM(Architecture): 

2512 aliases = ("ARM", Elf.Abi.ARM) 

2513 arch = "ARM" 

2514 all_registers = ("$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", 

2515 "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", 

2516 "$lr", "$pc", "$cpsr",) 

2517 

2518 nop_insn = b"\x00\xf0\x20\xe3" # hint #0 

2519 return_register = "$r0" 

2520 flag_register: str = "$cpsr" 

2521 flags_table = { 

2522 31: "negative", 

2523 30: "zero", 

2524 29: "carry", 

2525 28: "overflow", 

2526 7: "interrupt", 

2527 6: "fast", 

2528 5: "thumb", 

2529 } 

2530 function_parameters = ("$r0", "$r1", "$r2", "$r3") 

2531 syscall_register = "$r7" 

2532 syscall_instructions = ("swi 0x0", "swi NR") 

2533 _endianness = Endianness.LITTLE_ENDIAN 

2534 

2535 def is_thumb(self) -> bool: 

2536 """Determine if the machine is currently in THUMB mode.""" 

2537 return is_alive() and (self.cpsr & (1 << 5) == 1) 

2538 

2539 @property 

2540 def pc(self) -> Optional[int]: 

2541 pc = gef.arch.register("$pc") 

2542 if self.is_thumb(): 

2543 pc += 1 

2544 return pc 

2545 

2546 @property 

2547 def cpsr(self) -> int: 

2548 if not is_alive(): 

2549 raise RuntimeError("Cannot get CPSR, program not started?") 

2550 return gef.arch.register(self.flag_register) 

2551 

2552 @property 

2553 def mode(self) -> str: 

2554 return "THUMB" if self.is_thumb() else "ARM" 

2555 

2556 @property 

2557 def instruction_length(self) -> Optional[int]: 

2558 # Thumb instructions have variable-length (2 or 4-byte) 

2559 return None if self.is_thumb() else 4 

2560 

2561 @property 

2562 def ptrsize(self) -> int: 

2563 return 4 

2564 

2565 def is_call(self, insn: Instruction) -> bool: 

2566 mnemo = insn.mnemonic 

2567 call_mnemos = {"bl", "blx"} 

2568 return mnemo in call_mnemos 

2569 

2570 def is_ret(self, insn: Instruction) -> bool: 

2571 pop_mnemos = {"pop"} 

2572 branch_mnemos = {"bl", "bx"} 

2573 write_mnemos = {"ldr", "add"} 

2574 if insn.mnemonic in pop_mnemos: 

2575 return insn.operands[-1] == " pc}" 

2576 if insn.mnemonic in branch_mnemos: 

2577 return insn.operands[-1] == "lr" 

2578 if insn.mnemonic in write_mnemos: 

2579 return insn.operands[0] == "pc" 

2580 return False 

2581 

2582 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

2583 # https://www.botskool.com/user-pages/tutorials/electronics/arm-7-tutorial-part-1 

2584 if val is None: 

2585 reg = self.flag_register 

2586 val = gef.arch.register(reg) 

2587 return flags_to_human(val, self.flags_table) 

2588 

2589 def is_conditional_branch(self, insn: Instruction) -> bool: 

2590 conditions = {"eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cc", "cs"} 

2591 return insn.mnemonic[-2:] in conditions 

2592 

2593 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

2594 mnemo = insn.mnemonic 

2595 # ref: https://www.davespace.co.uk/arm/introduction-to-arm/conditional.html 

2596 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

2597 val = gef.arch.register(self.flag_register) 

2598 taken, reason = False, "" 

2599 

2600 if mnemo.endswith("eq"): taken, reason = bool(val&(1<<flags["zero"])), "Z" 

2601 elif mnemo.endswith("ne"): taken, reason = not bool(val&(1<<flags["zero"])), "!Z" 

2602 elif mnemo.endswith("lt"): 

2603 taken, reason = bool(val&(1<<flags["negative"])) != bool(val&(1<<flags["overflow"])), "N!=V" 

2604 elif mnemo.endswith("le"): 

2605 taken, reason = bool(val&(1<<flags["zero"])) or \ 

2606 bool(val&(1<<flags["negative"])) != bool(val&(1<<flags["overflow"])), "Z || N!=V" 

2607 elif mnemo.endswith("gt"): 

2608 taken, reason = bool(val&(1<<flags["zero"])) == 0 and \ 

2609 bool(val&(1<<flags["negative"])) == bool(val&(1<<flags["overflow"])), "!Z && N==V" 

2610 elif mnemo.endswith("ge"): 

2611 taken, reason = bool(val&(1<<flags["negative"])) == bool(val&(1<<flags["overflow"])), "N==V" 

2612 elif mnemo.endswith("vs"): taken, reason = bool(val&(1<<flags["overflow"])), "V" 

2613 elif mnemo.endswith("vc"): taken, reason = not val&(1<<flags["overflow"]), "!V" 

2614 elif mnemo.endswith("mi"): 

2615 taken, reason = bool(val&(1<<flags["negative"])), "N" 

2616 elif mnemo.endswith("pl"): 

2617 taken, reason = not val&(1<<flags["negative"]), "N==0" 

2618 elif mnemo.endswith("hi"): 

2619 taken, reason = bool(val&(1<<flags["carry"])) and not bool(val&(1<<flags["zero"])), "C && !Z" 

2620 elif mnemo.endswith("ls"): 

2621 taken, reason = not val&(1<<flags["carry"]) or bool(val&(1<<flags["zero"])), "!C || Z" 

2622 elif mnemo.endswith("cs"): taken, reason = bool(val&(1<<flags["carry"])), "C" 

2623 elif mnemo.endswith("cc"): taken, reason = not val&(1<<flags["carry"]), "!C" 

2624 return taken, reason 

2625 

2626 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int: 

2627 ra = None 

2628 if self.is_ret(insn): 

2629 # If it's a pop, we have to peek into the stack, otherwise use lr 

2630 if insn.mnemonic == "pop": 

2631 ra_addr = gef.arch.sp + (len(insn.operands)-1) * self.ptrsize 

2632 ra = to_unsigned_long(dereference(ra_addr)) 

2633 elif insn.mnemonic == "ldr": 

2634 return to_unsigned_long(dereference(gef.arch.sp)) 

2635 else: # 'bx lr' or 'add pc, lr, #0' 

2636 return gef.arch.register("$lr") 

2637 elif frame.older(): 

2638 ra = frame.older().pc() 

2639 return ra 

2640 

2641 @classmethod 

2642 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2643 _NR_mprotect = 125 

2644 insns = [ 

2645 "push {r0-r2, r7}", 

2646 f"mov r1, {addr & 0xffff:d}", 

2647 f"mov r0, {(addr & 0xffff0000) >> 16:d}", 

2648 "lsl r0, r0, 16", 

2649 "add r0, r0, r1", 

2650 f"mov r1, {size & 0xffff:d}", 

2651 f"mov r2, {perm.value & 0xff:d}", 

2652 f"mov r7, {_NR_mprotect:d}", 

2653 "svc 0", 

2654 "pop {r0-r2, r7}", 

2655 ] 

2656 return "; ".join(insns) 

2657 

2658 

2659class AARCH64(ARM): 

2660 aliases = ("ARM64", "AARCH64", Elf.Abi.AARCH64) 

2661 arch = "ARM64" 

2662 mode: str = "" 

2663 

2664 all_registers = ( 

2665 "$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7", 

2666 "$x8", "$x9", "$x10", "$x11", "$x12", "$x13", "$x14","$x15", 

2667 "$x16", "$x17", "$x18", "$x19", "$x20", "$x21", "$x22", "$x23", 

2668 "$x24", "$x25", "$x26", "$x27", "$x28", "$x29", "$x30", "$sp", 

2669 "$pc", "$cpsr", "$fpsr", "$fpcr",) 

2670 return_register = "$x0" 

2671 flag_register = "$cpsr" 

2672 flags_table = { 

2673 31: "negative", 

2674 30: "zero", 

2675 29: "carry", 

2676 28: "overflow", 

2677 7: "interrupt", 

2678 9: "endian", 

2679 6: "fast", 

2680 5: "t32", 

2681 4: "m[4]", 

2682 } 

2683 nop_insn = b"\x1f\x20\x03\xd5" # hint #0 

2684 function_parameters = ("$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7",) 

2685 syscall_register = "$x8" 

2686 syscall_instructions = ("svc $x0",) 

2687 

2688 def is_call(self, insn: Instruction) -> bool: 

2689 mnemo = insn.mnemonic 

2690 call_mnemos = {"bl", "blr"} 

2691 return mnemo in call_mnemos 

2692 

2693 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

2694 # https://events.linuxfoundation.org/sites/events/files/slides/KoreaLinuxForum-2014.pdf 

2695 reg = self.flag_register 

2696 if not val: 

2697 val = gef.arch.register(reg) 

2698 return flags_to_human(val, self.flags_table) 

2699 

2700 def is_aarch32(self) -> bool: 

2701 """Determine if the CPU is currently in AARCH32 mode from runtime.""" 

2702 return (self.cpsr & (1 << 4) != 0) and (self.cpsr & (1 << 5) == 0) 

2703 

2704 def is_thumb32(self) -> bool: 

2705 """Determine if the CPU is currently in THUMB32 mode from runtime.""" 

2706 return (self.cpsr & (1 << 4) == 1) and (self.cpsr & (1 << 5) == 1) 

2707 

2708 @property 

2709 def ptrsize(self) -> int: 

2710 """Determine the size of pointer from the current CPU mode""" 

2711 if not is_alive(): 

2712 return 8 

2713 if self.is_aarch32(): 

2714 return 4 

2715 if self.is_thumb32(): 

2716 return 2 

2717 return 8 

2718 

2719 @classmethod 

2720 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2721 _NR_mprotect = 226 

2722 insns = [ 

2723 "str x8, [sp, -16]!", 

2724 "str x0, [sp, -16]!", 

2725 "str x1, [sp, -16]!", 

2726 "str x2, [sp, -16]!", 

2727 f"mov x8, {_NR_mprotect:d}", 

2728 f"movz x0, {addr & 0xFFFF:#x}", 

2729 f"movk x0, {(addr >> 16) & 0xFFFF:#x}, lsl 16", 

2730 f"movk x0, {(addr >> 32) & 0xFFFF:#x}, lsl 32", 

2731 f"movk x0, {(addr >> 48) & 0xFFFF:#x}, lsl 48", 

2732 f"movz x1, {size & 0xFFFF:#x}", 

2733 f"movk x1, {(size >> 16) & 0xFFFF:#x}, lsl 16", 

2734 f"mov x2, {perm.value:d}", 

2735 "svc 0", 

2736 "ldr x2, [sp], 16", 

2737 "ldr x1, [sp], 16", 

2738 "ldr x0, [sp], 16", 

2739 "ldr x8, [sp], 16", 

2740 ] 

2741 return "; ".join(insns) 

2742 

2743 def is_conditional_branch(self, insn: Instruction) -> bool: 

2744 # https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf 

2745 # sect. 5.1.1 

2746 mnemo = insn.mnemonic 

2747 branch_mnemos = {"cbnz", "cbz", "tbnz", "tbz"} 

2748 return mnemo.startswith("b.") or mnemo in branch_mnemos 

2749 

2750 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

2751 mnemo, operands = insn.mnemonic, insn.operands 

2752 taken, reason = False, "" 

2753 

2754 if mnemo in {"cbnz", "cbz", "tbnz", "tbz"}: 

2755 reg = f"${operands[0]}" 

2756 op = gef.arch.register(reg) 

2757 if mnemo == "cbnz": 

2758 if op!=0: taken, reason = True, f"{reg}!=0" 

2759 else: taken, reason = False, f"{reg}==0" 

2760 elif mnemo == "cbz": 

2761 if op == 0: taken, reason = True, f"{reg}==0" 

2762 else: taken, reason = False, f"{reg}!=0" 

2763 elif mnemo == "tbnz": 

2764 # operands[1] has one or more white spaces in front, then a #, then the number 

2765 # so we need to eliminate them 

2766 i = int(operands[1].strip().lstrip("#")) 

2767 if (op & 1<<i) != 0: taken, reason = True, f"{reg}&1<<{i}!=0" 

2768 else: taken, reason = False, f"{reg}&1<<{i}==0" 

2769 elif mnemo == "tbz": 

2770 # operands[1] has one or more white spaces in front, then a #, then the number 

2771 # so we need to eliminate them 

2772 i = int(operands[1].strip().lstrip("#")) 

2773 if (op & 1<<i) == 0: taken, reason = True, f"{reg}&1<<{i}==0" 

2774 else: taken, reason = False, f"{reg}&1<<{i}!=0" 

2775 

2776 if not reason: 

2777 taken, reason = super().is_branch_taken(insn) 

2778 return taken, reason 

2779 

2780 

2781class X86(Architecture): 

2782 aliases: Tuple[Union[str, Elf.Abi], ...] = ("X86", Elf.Abi.X86_32) 

2783 arch = "X86" 

2784 mode = "32" 

2785 

2786 nop_insn = b"\x90" 

2787 flag_register: str = "$eflags" 

2788 special_registers = ("$cs", "$ss", "$ds", "$es", "$fs", "$gs", ) 

2789 gpr_registers = ("$eax", "$ebx", "$ecx", "$edx", "$esp", "$ebp", "$esi", "$edi", "$eip", ) 

2790 all_registers = gpr_registers + ( flag_register,) + special_registers 

2791 instruction_length = None 

2792 return_register = "$eax" 

2793 function_parameters = ("$esp", ) 

2794 flags_table = { 

2795 6: "zero", 

2796 0: "carry", 

2797 2: "parity", 

2798 4: "adjust", 

2799 7: "sign", 

2800 8: "trap", 

2801 9: "interrupt", 

2802 10: "direction", 

2803 11: "overflow", 

2804 16: "resume", 

2805 17: "virtualx86", 

2806 21: "identification", 

2807 } 

2808 syscall_register = "$eax" 

2809 syscall_instructions = ("sysenter", "int 0x80") 

2810 _ptrsize = 4 

2811 _endianness = Endianness.LITTLE_ENDIAN 

2812 

2813 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

2814 reg = self.flag_register 

2815 if not val: 2815 ↛ 2817line 2815 didn't jump to line 2817, because the condition on line 2815 was never false

2816 val = gef.arch.register(reg) 

2817 return flags_to_human(val, self.flags_table) 

2818 

2819 def is_call(self, insn: Instruction) -> bool: 

2820 mnemo = insn.mnemonic 

2821 call_mnemos = {"call", "callq"} 

2822 return mnemo in call_mnemos 

2823 

2824 def is_ret(self, insn: Instruction) -> bool: 

2825 return insn.mnemonic == "ret" 

2826 

2827 def is_conditional_branch(self, insn: Instruction) -> bool: 

2828 mnemo = insn.mnemonic 

2829 branch_mnemos = { 

2830 "ja", "jnbe", "jae", "jnb", "jnc", "jb", "jc", "jnae", "jbe", "jna", 

2831 "jcxz", "jecxz", "jrcxz", "je", "jz", "jg", "jnle", "jge", "jnl", 

2832 "jl", "jnge", "jle", "jng", "jne", "jnz", "jno", "jnp", "jpo", "jns", 

2833 "jo", "jp", "jpe", "js" 

2834 } 

2835 return mnemo in branch_mnemos 

2836 

2837 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

2838 mnemo = insn.mnemonic 

2839 # all kudos to fG! (https://github.com/gdbinit/Gdbinit/blob/master/gdbinit#L1654) 

2840 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

2841 val = gef.arch.register(self.flag_register) 

2842 

2843 taken, reason = False, "" 

2844 

2845 if mnemo in ("ja", "jnbe"): 

2846 taken, reason = not val&(1<<flags["carry"]) and not bool(val&(1<<flags["zero"])), "!C && !Z" 

2847 elif mnemo in ("jae", "jnb", "jnc"): 

2848 taken, reason = not val&(1<<flags["carry"]), "!C" 

2849 elif mnemo in ("jb", "jc", "jnae"): 

2850 taken, reason = bool(val&(1<<flags["carry"])) != 0, "C" 

2851 elif mnemo in ("jbe", "jna"): 

2852 taken, reason = bool(val&(1<<flags["carry"])) or bool(val&(1<<flags["zero"])), "C || Z" 

2853 elif mnemo in ("jcxz", "jecxz", "jrcxz"): 

2854 cx = gef.arch.register("$rcx") if is_x86_64() else gef.arch.register("$ecx") 

2855 taken, reason = cx == 0, "!$CX" 

2856 elif mnemo in ("je", "jz"): 

2857 taken, reason = bool(val&(1<<flags["zero"])), "Z" 

2858 elif mnemo in ("jne", "jnz"): 

2859 taken, reason = not bool(val&(1<<flags["zero"])), "!Z" 

2860 elif mnemo in ("jg", "jnle"): 

2861 taken, reason = not bool(val&(1<<flags["zero"])) and bool(val&(1<<flags["overflow"])) == bool(val&(1<<flags["sign"])), "!Z && S==O" 

2862 elif mnemo in ("jge", "jnl"): 

2863 taken, reason = bool(val&(1<<flags["sign"])) == bool(val&(1<<flags["overflow"])), "S==O" 

2864 elif mnemo in ("jl", "jnge"): 

2865 taken, reason = bool(val&(1<<flags["overflow"]) != val&(1<<flags["sign"])), "S!=O" 

2866 elif mnemo in ("jle", "jng"): 

2867 taken, reason = bool(val&(1<<flags["zero"])) or bool(val&(1<<flags["overflow"])) != bool(val&(1<<flags["sign"])), "Z || S!=O" 

2868 elif mnemo in ("jo",): 

2869 taken, reason = bool(val&(1<<flags["overflow"])), "O" 

2870 elif mnemo in ("jno",): 

2871 taken, reason = not val&(1<<flags["overflow"]), "!O" 

2872 elif mnemo in ("jpe", "jp"): 

2873 taken, reason = bool(val&(1<<flags["parity"])), "P" 

2874 elif mnemo in ("jnp", "jpo"): 

2875 taken, reason = not val&(1<<flags["parity"]), "!P" 

2876 elif mnemo in ("js",): 

2877 taken, reason = bool(val&(1<<flags["sign"])) != 0, "S" 

2878 elif mnemo in ("jns",): 

2879 taken, reason = not val&(1<<flags["sign"]), "!S" 

2880 return taken, reason 

2881 

2882 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: 

2883 ra = None 

2884 if self.is_ret(insn): 2884 ↛ 2886line 2884 didn't jump to line 2886, because the condition on line 2884 was never false

2885 ra = to_unsigned_long(dereference(gef.arch.sp)) 

2886 if frame.older(): 2886 ↛ 2889line 2886 didn't jump to line 2889, because the condition on line 2886 was never false

2887 ra = frame.older().pc() 

2888 

2889 return ra 

2890 

2891 @classmethod 

2892 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2893 _NR_mprotect = 125 

2894 insns = [ 

2895 "pushad", 

2896 "pushfd", 

2897 f"mov eax, {_NR_mprotect:d}", 

2898 f"mov ebx, {addr:d}", 

2899 f"mov ecx, {size:d}", 

2900 f"mov edx, {perm.value:d}", 

2901 "int 0x80", 

2902 "popfd", 

2903 "popad", 

2904 ] 

2905 return "; ".join(insns) 

2906 

2907 def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: 

2908 if in_func: 

2909 i += 1 # Account for RA being at the top of the stack 

2910 sp = gef.arch.sp 

2911 sz = gef.arch.ptrsize 

2912 loc = sp + (i * sz) 

2913 val = gef.memory.read_integer(loc) 

2914 key = f"[sp + {i * sz:#x}]" 

2915 return key, val 

2916 

2917 

2918class X86_64(X86): 

2919 aliases = ("X86_64", Elf.Abi.X86_64, "i386:x86-64") 

2920 arch = "X86" 

2921 mode = "64" 

2922 

2923 gpr_registers = ( 

2924 "$rax", "$rbx", "$rcx", "$rdx", "$rsp", "$rbp", "$rsi", "$rdi", "$rip", 

2925 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", ) 

2926 all_registers = gpr_registers + ( X86.flag_register, ) + X86.special_registers 

2927 return_register = "$rax" 

2928 function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] 

2929 syscall_register = "$rax" 

2930 syscall_instructions = ["syscall"] 

2931 # We don't want to inherit x86's stack based param getter 

2932 get_ith_parameter = Architecture.get_ith_parameter 

2933 _ptrsize = 8 

2934 

2935 @classmethod 

2936 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

2937 _NR_mprotect = 10 

2938 insns = [ 

2939 "pushfq", 

2940 "push rax", 

2941 "push rdi", 

2942 "push rsi", 

2943 "push rdx", 

2944 "push rcx", 

2945 "push r11", 

2946 f"mov rax, {_NR_mprotect:d}", 

2947 f"mov rdi, {addr:d}", 

2948 f"mov rsi, {size:d}", 

2949 f"mov rdx, {perm.value:d}", 

2950 "syscall", 

2951 "pop r11", 

2952 "pop rcx", 

2953 "pop rdx", 

2954 "pop rsi", 

2955 "pop rdi", 

2956 "pop rax", 

2957 "popfq", 

2958 ] 

2959 return "; ".join(insns) 

2960 

2961 def canary_address(self) -> int: 

2962 return self.register("fs_base") + 0x28 

2963 

2964class PowerPC(Architecture): 

2965 aliases = ("PowerPC", Elf.Abi.POWERPC, "PPC") 

2966 arch = "PPC" 

2967 mode = "PPC32" 

2968 

2969 all_registers = ( 

2970 "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", 

2971 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", 

2972 "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", 

2973 "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", 

2974 "$pc", "$msr", "$cr", "$lr", "$ctr", "$xer", "$trap",) 

2975 instruction_length = 4 

2976 nop_insn = b"\x60\x00\x00\x00" # https://developer.ibm.com/articles/l-ppc/ 

2977 return_register = "$r0" 

2978 flag_register: str = "$cr" 

2979 flags_table = { 

2980 3: "negative[0]", 

2981 2: "positive[0]", 

2982 1: "equal[0]", 

2983 0: "overflow[0]", 

2984 # cr7 

2985 31: "less[7]", 

2986 30: "greater[7]", 

2987 29: "equal[7]", 

2988 28: "overflow[7]", 

2989 } 

2990 function_parameters = ("$i0", "$i1", "$i2", "$i3", "$i4", "$i5") 

2991 syscall_register = "$r0" 

2992 syscall_instructions = ("sc",) 

2993 ptrsize = 4 

2994 

2995 

2996 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

2997 # https://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3) 

2998 if not val: 

2999 reg = self.flag_register 

3000 val = gef.arch.register(reg) 

3001 return flags_to_human(val, self.flags_table) 

3002 

3003 def is_call(self, insn: Instruction) -> bool: 

3004 return False 

3005 

3006 def is_ret(self, insn: Instruction) -> bool: 

3007 return insn.mnemonic == "blr" 

3008 

3009 def is_conditional_branch(self, insn: Instruction) -> bool: 

3010 mnemo = insn.mnemonic 

3011 branch_mnemos = {"beq", "bne", "ble", "blt", "bgt", "bge"} 

3012 return mnemo in branch_mnemos 

3013 

3014 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

3015 mnemo = insn.mnemonic 

3016 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

3017 val = gef.arch.register(self.flag_register) 

3018 taken, reason = False, "" 

3019 if mnemo == "beq": taken, reason = bool(val&(1<<flags["equal[7]"])), "E" 

3020 elif mnemo == "bne": taken, reason = val&(1<<flags["equal[7]"]) == 0, "!E" 

3021 elif mnemo == "ble": taken, reason = bool(val&(1<<flags["equal[7]"])) or bool(val&(1<<flags["less[7]"])), "E || L" 

3022 elif mnemo == "blt": taken, reason = bool(val&(1<<flags["less[7]"])), "L" 

3023 elif mnemo == "bge": taken, reason = bool(val&(1<<flags["equal[7]"])) or bool(val&(1<<flags["greater[7]"])), "E || G" 

3024 elif mnemo == "bgt": taken, reason = bool(val&(1<<flags["greater[7]"])), "G" 

3025 return taken, reason 

3026 

3027 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: 

3028 ra = None 

3029 if self.is_ret(insn): 

3030 ra = gef.arch.register("$lr") 

3031 elif frame.older(): 

3032 ra = frame.older().pc() 

3033 return ra 

3034 

3035 @classmethod 

3036 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

3037 # Ref: https://developer.ibm.com/articles/l-ppc/ 

3038 _NR_mprotect = 125 

3039 insns = [ 

3040 "addi 1, 1, -16", # 1 = r1 = sp 

3041 "stw 0, 0(1)", 

3042 "stw 3, 4(1)", # r0 = syscall_code | r3, r4, r5 = args 

3043 "stw 4, 8(1)", 

3044 "stw 5, 12(1)", 

3045 f"li 0, {_NR_mprotect:d}", 

3046 f"lis 3, {addr:#x}@h", 

3047 f"ori 3, 3, {addr:#x}@l", 

3048 f"lis 4, {size:#x}@h", 

3049 f"ori 4, 4, {size:#x}@l", 

3050 f"li 5, {perm.value:d}", 

3051 "sc", 

3052 "lwz 0, 0(1)", 

3053 "lwz 3, 4(1)", 

3054 "lwz 4, 8(1)", 

3055 "lwz 5, 12(1)", 

3056 "addi 1, 1, 16", 

3057 ] 

3058 return ";".join(insns) 

3059 

3060 

3061class PowerPC64(PowerPC): 

3062 aliases = ("PowerPC64", Elf.Abi.POWERPC64, "PPC64") 

3063 arch = "PPC" 

3064 mode = "PPC64" 

3065 ptrsize = 8 

3066 

3067 

3068class SPARC(Architecture): 

3069 """ Refs: 

3070 - https://www.cse.scu.edu/~atkinson/teaching/sp05/259/sparc.pdf 

3071 """ 

3072 aliases = ("SPARC", Elf.Abi.SPARC) 

3073 arch = "SPARC" 

3074 mode = "" 

3075 

3076 all_registers = ( 

3077 "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", 

3078 "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7", 

3079 "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", 

3080 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i7", 

3081 "$pc", "$npc", "$sp ", "$fp ", "$psr",) 

3082 instruction_length = 4 

3083 nop_insn = b"\x00\x00\x00\x00" # sethi 0, %g0 

3084 return_register = "$i0" 

3085 flag_register: str = "$psr" 

3086 flags_table = { 

3087 23: "negative", 

3088 22: "zero", 

3089 21: "overflow", 

3090 20: "carry", 

3091 7: "supervisor", 

3092 5: "trap", 

3093 } 

3094 function_parameters = ("$o0 ", "$o1 ", "$o2 ", "$o3 ", "$o4 ", "$o5 ", "$o7 ",) 

3095 syscall_register = "%g1" 

3096 syscall_instructions = ("t 0x10",) 

3097 

3098 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

3099 # https://www.gaisler.com/doc/sparcv8.pdf 

3100 reg = self.flag_register 

3101 if not val: 

3102 val = gef.arch.register(reg) 

3103 return flags_to_human(val, self.flags_table) 

3104 

3105 def is_call(self, insn: Instruction) -> bool: 

3106 return False 

3107 

3108 def is_ret(self, insn: Instruction) -> bool: 

3109 return insn.mnemonic == "ret" 

3110 

3111 def is_conditional_branch(self, insn: Instruction) -> bool: 

3112 mnemo = insn.mnemonic 

3113 # http://moss.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/condbranch.html 

3114 branch_mnemos = { 

3115 "be", "bne", "bg", "bge", "bgeu", "bgu", "bl", "ble", "blu", "bleu", 

3116 "bneg", "bpos", "bvs", "bvc", "bcs", "bcc" 

3117 } 

3118 return mnemo in branch_mnemos 

3119 

3120 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

3121 mnemo = insn.mnemonic 

3122 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

3123 val = gef.arch.register(self.flag_register) 

3124 taken, reason = False, "" 

3125 

3126 if mnemo == "be": taken, reason = bool(val&(1<<flags["zero"])), "Z" 

3127 elif mnemo == "bne": taken, reason = bool(val&(1<<flags["zero"])) == 0, "!Z" 

3128 elif mnemo == "bg": taken, reason = bool(val&(1<<flags["zero"])) == 0 and (val&(1<<flags["negative"]) == 0 or val&(1<<flags["overflow"]) == 0), "!Z && (!N || !O)" 

3129 elif mnemo == "bge": taken, reason = val&(1<<flags["negative"]) == 0 or val&(1<<flags["overflow"]) == 0, "!N || !O" 

3130 elif mnemo == "bgu": taken, reason = val&(1<<flags["carry"]) == 0 and val&(1<<flags["zero"]) == 0, "!C && !Z" 

3131 elif mnemo == "bgeu": taken, reason = val&(1<<flags["carry"]) == 0, "!C" 

3132 elif mnemo == "bl": taken, reason = bool(val&(1<<flags["negative"])) and bool(val&(1<<flags["overflow"])), "N && O" 

3133 elif mnemo == "blu": taken, reason = bool(val&(1<<flags["carry"])), "C" 

3134 elif mnemo == "ble": taken, reason = bool(val&(1<<flags["zero"])) or bool(val&(1<<flags["negative"]) or val&(1<<flags["overflow"])), "Z || (N || O)" 

3135 elif mnemo == "bleu": taken, reason = bool(val&(1<<flags["carry"])) or bool(val&(1<<flags["zero"])), "C || Z" 

3136 elif mnemo == "bneg": taken, reason = bool(val&(1<<flags["negative"])), "N" 

3137 elif mnemo == "bpos": taken, reason = val&(1<<flags["negative"]) == 0, "!N" 

3138 elif mnemo == "bvs": taken, reason = bool(val&(1<<flags["overflow"])), "O" 

3139 elif mnemo == "bvc": taken, reason = val&(1<<flags["overflow"]) == 0, "!O" 

3140 elif mnemo == "bcs": taken, reason = bool(val&(1<<flags["carry"])), "C" 

3141 elif mnemo == "bcc": taken, reason = val&(1<<flags["carry"]) == 0, "!C" 

3142 return taken, reason 

3143 

3144 def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: 

3145 ra = None 

3146 if self.is_ret(insn): 

3147 ra = gef.arch.register("$o7") 

3148 elif frame.older(): 

3149 ra = frame.older().pc() 

3150 return ra 

3151 

3152 @classmethod 

3153 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

3154 hi = (addr & 0xffff0000) >> 16 

3155 lo = (addr & 0x0000ffff) 

3156 _NR_mprotect = 125 

3157 insns = ["add %sp, -16, %sp", 

3158 "st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]", 

3159 "st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]", 

3160 f"sethi %hi({hi}), %o0", 

3161 f"or %o0, {lo}, %o0", 

3162 "clr %o1", 

3163 "clr %o2", 

3164 f"mov {_NR_mprotect}, %g1", 

3165 "t 0x10", 

3166 "ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0", 

3167 "ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2", 

3168 "add %sp, 16, %sp",] 

3169 return "; ".join(insns) 

3170 

3171 

3172class SPARC64(SPARC): 

3173 """Refs: 

3174 - http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_sparc.pdf 

3175 - https://cr.yp.to/2005-590/sparcv9.pdf 

3176 """ 

3177 aliases = ("SPARC64", Elf.Abi.SPARC64) 

3178 arch = "SPARC" 

3179 mode = "V9" 

3180 

3181 all_registers = [ 

3182 "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", 

3183 "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7", 

3184 "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", 

3185 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i7", 

3186 "$pc", "$npc", "$sp", "$fp", "$state", ] 

3187 

3188 flag_register = "$state" # sparcv9.pdf, 5.1.5.1 (ccr) 

3189 flags_table = { 

3190 35: "negative", 

3191 34: "zero", 

3192 33: "overflow", 

3193 32: "carry", 

3194 } 

3195 

3196 syscall_instructions = ["t 0x6d"] 

3197 

3198 @classmethod 

3199 def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: 

3200 hi = (addr & 0xffff0000) >> 16 

3201 lo = (addr & 0x0000ffff) 

3202 _NR_mprotect = 125 

3203 insns = ["add %sp, -16, %sp", 

3204 "st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]", 

3205 "st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]", 

3206 f"sethi %hi({hi}), %o0", 

3207 f"or %o0, {lo}, %o0", 

3208 "clr %o1", 

3209 "clr %o2", 

3210 f"mov {_NR_mprotect}, %g1", 

3211 "t 0x6d", 

3212 "ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0", 

3213 "ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2", 

3214 "add %sp, 16, %sp",] 

3215 return "; ".join(insns) 

3216 

3217 

3218class MIPS(Architecture): 

3219 aliases: Tuple[Union[str, Elf.Abi], ...] = ("MIPS", Elf.Abi.MIPS) 

3220 arch = "MIPS" 

3221 mode = "MIPS32" 

3222 

3223 # https://vhouten.home.xs4all.nl/mipsel/r3000-isa.html 

3224 all_registers = ( 

3225 "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", 

3226 "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", 

3227 "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", 

3228 "$t8", "$t9", "$k0", "$k1", "$s8", "$pc", "$sp", "$hi", 

3229 "$lo", "$fir", "$ra", "$gp", ) 

3230 instruction_length = 4 

3231 _ptrsize = 4 

3232 nop_insn = b"\x00\x00\x00\x00" # sll $0,$0,0 

3233 return_register = "$v0" 

3234 flag_register = "$fcsr" 

3235 flags_table = {} 

3236 function_parameters = ("$a0", "$a1", "$a2", "$a3") 

3237 syscall_register = "$v0" 

3238 syscall_instructions = ("syscall",) 

3239 

3240 def flag_register_to_human(self, val: Optional[int] = None) -> str: 

3241 return Color.colorify("No flag register", "yellow underline") 

3242 

3243 def is_call(self, insn: Instruction) -> bool: 

3244 return False 

3245 

3246 def is_ret(self, insn: Instruction) -> bool: 

3247 return insn.mnemonic == "jr" and insn.operands[0] == "ra" 

3248 

3249 def is_conditional_branch(self, insn: Instruction) -> bool: 

3250 mnemo = insn.mnemonic 

3251 branch_mnemos = {"beq", "bne", "beqz", "bnez", "bgtz", "bgez", "bltz", "blez"} 

3252 return mnemo in branch_mnemos 

3253 

3254 def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: 

3255 mnemo, ops = insn.mnemonic, insn.operands 

3256 taken, reason = False, "" 

3257 

3258 if mnemo == "beq": 

3259 taken, reason = gef.arch.register(ops[0])