Coverage for gef.py: 71.6798%

7944 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2024-06-09 19:35 +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 socket 

74import string 

75import struct 

76import subprocess 

77import sys 

78import tempfile 

79import time 

80import traceback 

81import warnings 

82from functools import lru_cache 

83from io import StringIO, TextIOWrapper 

84from types import ModuleType 

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

86 Iterator, List, NoReturn, Optional, Sequence, Set, Tuple, Type, TypeVar, 

87 Union, cast) 

88from urllib.request import urlopen 

89 

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 """Obsolete. Use `gef.sh`.""" 

106 return -1 

107 

108 

109try: 

110 import gdb # type:ignore 

111except ImportError: 

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

113 print("[-] `update_gef` is obsolete. Use the `gef.sh` script to update gef from the command line.") 

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

115 sys.exit(1) 

116 

117 

118GDB_MIN_VERSION: Tuple[int, int] = (8, 0) 

119PYTHON_MIN_VERSION: Tuple[int, int] = (3, 6) 

120PYTHON_VERSION: Tuple[int, int] = sys.version_info[0:2] 

121GDB_VERSION: Tuple[int, int] = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) # type:ignore 

122 

123DEFAULT_PAGE_ALIGN_SHIFT = 12 

124DEFAULT_PAGE_SIZE = 1 << DEFAULT_PAGE_ALIGN_SHIFT 

125 

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

127 if os.getenv("GEF_RC") 

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

129GEF_TEMP_DIR = pathlib.Path(tempfile.gettempdir())/ "gef" 

130GEF_MAX_STRING_LENGTH = 50 

131 

132LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME = "main_arena" 

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

134 

135LEFT_ARROW = " ← " 

136RIGHT_ARROW = " → " 

137DOWN_ARROW = "↳" 

138HORIZONTAL_LINE = "─" 

139VERTICAL_LINE = "│" 

140CROSS = "✘ " 

141TICK = "✓ " 

142BP_GLYPH = "●" 

143GEF_PROMPT = "gef➤ " 

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

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

146 

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

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

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

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

151 

152GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] 

153 

154 

155def reset_all_caches() -> None: 

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

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

158 

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

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

161 if hasattr(obj, "cache_clear"): 

162 obj.cache_clear() 

163 

164 gef.reset_caches() 

165 return 

166 

167 

168def reset() -> None: 

169 global gef 

170 

171 arch = None 

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

173 reset_all_caches() 

174 arch = gef.arch 

175 del gef 

176 

177 gef = Gef() 

178 gef.setup() 

179 

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

181 gef.arch = arch 

182 return 

183 

184 

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

186 """ 

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

188 

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

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

191 around those matches. 

192 

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

194 within the specified string. 

195 """ 

196 global gef 

197 

198 if not gef.ui.highlight_table: 

199 return text 

200 

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

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

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

204 return text 

205 

206 ansiSplit = re.split(ANSI_SPLIT_RE, text) 

207 

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

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

210 found = val.find(match) 

211 if found > -1: 

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

213 break 

214 text = "".join(ansiSplit) 

215 ansiSplit = re.split(ANSI_SPLIT_RE, text) 

216 

217 return "".join(ansiSplit) 

218 

219 

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

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

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

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

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

225 return 

226 

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

228 return 

229 

230 

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

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

233 

234 @functools.wraps(f) 

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

236 global gef 

237 

238 if gef.ui.stream_buffer: 

239 return f(*args, **kwargs) 

240 

241 gef.ui.stream_buffer = StringIO() 

242 try: 

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

244 finally: 

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

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

247 if not gef.ui.redirect_fd: 

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

249 fd = open(redirect, "wt") 

250 gef.ui.redirect_fd = fd 

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

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

253 gef.ui.redirect_fd.close() 

254 fd = open(redirect, "wt") 

255 gef.ui.redirect_fd = fd 

256 else: 

257 # otherwise, keep using it 

258 fd = gef.ui.redirect_fd 

259 else: 

260 fd = sys.stdout 

261 gef.ui.redirect_fd = None 

262 

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

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

265 fd = sys.stdout 

266 gef.ui.redirect_fd = None 

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

268 

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

270 fd.flush() 

271 gef.ui.stream_buffer = None 

272 return rv 

273 

274 return wrapper 

275 

276 

277class ValidationError(Exception): pass 

278 

279# 

280# Helpers 

281# 

282 

283class ObsoleteException(Exception): pass 

284 

285class AlreadyRegisteredException(Exception): pass 

286 

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

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

289 endian = e or gef.arch.endianness 

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

291 

292 

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

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

295 endian = e or gef.arch.endianness 

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

297 

298 

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

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

301 endian = e or gef.arch.endianness 

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

303 

304 

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

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

307 endian = e or gef.arch.endianness 

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

309 

310 

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

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

313 endian = e or gef.arch.endianness 

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

315 

316 

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

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

319 endian = e or gef.arch.endianness 

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

321 

322 

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

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

325 endian = e or gef.arch.endianness 

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

327 

328 

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

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

331 endian = e or gef.arch.endianness 

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

333 

334 

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

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

337 try: 

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

339 except Exception: 

340 return False 

341 

342 

343def is_alive() -> bool: 

344 """Check if GDB is running.""" 

345 try: 

346 return gdb.selected_inferior().pid > 0 

347 except Exception: 

348 return False 

349 

350 

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

352 """Return the name of the calling function""" 

353 try: 

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

355 return stack_info.name 

356 except: 

357 return None 

358 

359 

360# 

361# Decorators 

362# 

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

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

365 

366 @functools.wraps(f) 

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

368 if is_alive(): 

369 return f(*args, **kwargs) 

370 else: 

371 warn("No debugging session active") 

372 

373 return wrapper 

374 

375 

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

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

378 

379 @functools.wraps(f) 

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

381 if not is_remote_debug(): 381 ↛ 384line 381 didn't jump to line 384, because the condition on line 381 was always true

382 return f(*args, **kwargs) 

383 else: 

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

385 

386 return wrapper 

387 

388 

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

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

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

392 @functools.wraps(f) 

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

394 caller = inspect.stack()[1] 

395 caller_file = pathlib.Path(caller.filename) 

396 caller_loc = caller.lineno 

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

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

399 print(msg) 

400 elif gef.config["gef.show_deprecation_warnings"] is True: 400 ↛ 404line 400 didn't jump to line 404, because the condition on line 400 was always true

401 if solution: 

402 msg += solution 

403 warn(msg) 

404 return f(*args, **kwargs) 

405 

406 if not wrapper.__doc__: 

407 wrapper.__doc__ = "" 

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

409 return wrapper 

410 return decorator 

411 

412 

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

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

415 

416 @functools.wraps(f) 

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

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

419 return f(*args, **kwargs) 

420 

421 return wrapper 

422 

423 

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

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

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

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

428 if getattr(gdb, "events") and getattr(gdb.events, event_type): 428 ↛ 430line 428 didn't jump to line 430, because the condition on line 428 was always true

429 return f(*args, **kwargs) 

430 warn("GDB events cannot be set") 

431 return wrapped_f 

432 return wrap 

433 

434 

435class classproperty(property): 

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

437 def __get__(self, cls, owner): 

438 assert self.fget 

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

440 

441 

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

443 raise RuntimeWarning 

444 

445 

446sys.exit = FakeExit 

447 

448 

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

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

451 """Argument parsing decorator.""" 

452 

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

454 

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

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

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

458 for argname in required_arguments: 

459 argvalue = required_arguments[argname] 

460 argtype = type(argvalue) 

461 if argtype is int: 

462 argtype = int_wrapper 

463 

464 argname_is_list = not isinstance(argname, str) 

465 assert not argname_is_list and isinstance(argname, str) 

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

467 # optional args 

468 if argtype is bool: 

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

470 else: 

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

472 else: 

473 if argtype in (list, tuple): 

474 nargs = "*" 

475 argtype = type(argvalue[0]) 

476 else: 

477 nargs = "?" 

478 # positional args 

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

480 

481 for argname in optional_arguments: 

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

483 # refuse positional arguments 

484 continue 

485 argvalue = optional_arguments[argname] 

486 argtype = type(argvalue) 

487 if isinstance(argname, str): 

488 argname = [argname,] 

489 if argtype is int: 

490 argtype = int_wrapper 

491 elif argtype is bool: 

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

493 continue 

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

495 

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

497 kwargs["arguments"] = parsed_args 

498 return f(*args, **kwargs) 

499 return wrapper 

500 return decorator 

501 

502 

503class Color: 

504 """Used to colorify terminal output.""" 

505 

506 ### Special chars: 

507 # \001 -> Tell the readline library that we start a special sequence 

508 # which won't be displayed (takes no column in the output) 

509 # \002 -> Tell the readline library that we end a special sequence 

510 # started with \001 

511 # \033 -> Start an ANSI escape code for displaying colors 

512 colors = { 

513 "normal" : "\001\033[0m\002", 

514 "gray" : "\001\033[1;38;5;240m\002", 

515 "light_gray" : "\001\033[0;37m\002", 

516 "red" : "\001\033[31m\002", 

517 "green" : "\001\033[32m\002", 

518 "yellow" : "\001\033[33m\002", 

519 "blue" : "\001\033[34m\002", 

520 "pink" : "\001\033[35m\002", 

521 "cyan" : "\001\033[36m\002", 

522 "bold" : "\001\033[1m\002", 

523 "underline" : "\001\033[4m\002", 

524 "underline_off" : "\001\033[24m\002", 

525 "highlight" : "\001\033[3m\002", 

526 "highlight_off" : "\001\033[23m\002", 

527 "blink" : "\001\033[5m\002", 

528 "blink_off" : "\001\033[25m\002", 

529 } 

530 

531 @staticmethod 

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

533 @staticmethod 

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

535 @staticmethod 

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

537 @staticmethod 

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

539 @staticmethod 

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

541 @staticmethod 

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

543 @staticmethod 

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

545 @staticmethod 

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

547 @staticmethod 

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

549 @staticmethod 

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

551 @staticmethod 

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

553 @staticmethod 

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

555 

556 @staticmethod 

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

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

559 if gef.config["gef.disable_color"] is True: return text 

560 

561 colors = Color.colors 

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

563 msg.append(str(text)) 

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

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

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

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

568 return "".join(msg) 

569 

570 

571class Address: 

572 """GEF representation of memory addresses.""" 

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

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

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

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

577 return 

578 

579 def __str__(self) -> str: 

580 value = format_address(self.value) 

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

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

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

584 if self.is_in_text_segment(): 

585 return Color.colorify(value, code_color) 

586 if self.is_in_heap_segment(): 

587 return Color.colorify(value, heap_color) 

588 if self.is_in_stack_segment(): 

589 return Color.colorify(value, stack_color) 

590 return value 

591 

592 def __int__(self) -> int: 

593 return self.value 

594 

595 def is_in_text_segment(self) -> bool: 

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

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

598 

599 def is_in_stack_segment(self) -> bool: 

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

601 

602 def is_in_heap_segment(self) -> bool: 

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

604 

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

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

607 derefed = dereference(addr) 

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

609 

610 @property 

611 def valid(self) -> bool: 

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

613 

614 

615class Permission(enum.Flag): 

616 """GEF representation of Linux permission.""" 

617 NONE = 0 

618 EXECUTE = 1 

619 WRITE = 2 

620 READ = 4 

621 ALL = 7 

622 

623 def __str__(self) -> str: 

624 perm_str = "" 

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

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

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

628 return perm_str 

629 

630 @classmethod 

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

632 perm = cls(0) 

633 for arg in args: 

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

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

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

637 return perm 

638 

639 @classmethod 

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

641 perm = cls(0) 

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

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

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

645 return perm 

646 

647 @classmethod 

648 def from_monitor_info_mem(cls, perm_str: str) -> "Permission": 

649 perm = cls(0) 

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

651 # we don't track 

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

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

654 return perm 

655 

656 @classmethod 

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

658 perm = cls(0) 

659 if "r" in perm_str: perm |= Permission.READ 

660 if "w" in perm_str: perm |= Permission.WRITE 

661 if "x" in perm_str: perm |= Permission.EXECUTE 

662 return perm 

663 

664 

665class Section: 

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

667 

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

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

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

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

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

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

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

675 return 

676 

677 def is_readable(self) -> bool: 

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

679 

680 def is_writable(self) -> bool: 

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

682 

683 def is_executable(self) -> bool: 

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

685 

686 @property 

687 def size(self) -> int: 

688 if self.page_end is None or self.page_start is None: 688 ↛ 689line 688 didn't jump to line 689, because the condition on line 688 was never true

689 raise AttributeError 

690 return self.page_end - self.page_start 

691 

692 @property 

693 def realpath(self) -> str: 

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

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

696 

697 def __str__(self) -> str: 

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

699 f"perm={self.permission!s})") 

700 

701 def __repr__(self) -> str: 

702 return str(self) 

703 

704 def __eq__(self, other: "Section") -> bool: 

705 return other and \ 

706 self.page_start == other.page_start and \ 

707 self.size == other.size and \ 

708 self.permission == other.permission and \ 

709 self.path == other.path 

710 

711 def overlaps(self, other: "Section") -> bool: 

712 return max(self.page_start, other.page_start) <= min(self.page_end, other.page_end) 

713 

714 def contains(self, addr: int) -> bool: 

715 return addr in range(self.page_start, self.page_end) 

716 

717 

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

719 

720 

721class Endianness(enum.Enum): 

722 LITTLE_ENDIAN = 1 

723 BIG_ENDIAN = 2 

724 

725 def __str__(self) -> str: 

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

727 

728 def __repr__(self) -> str: 

729 return self.name 

730 

731 def __int__(self) -> int: 

732 return self.value 

733 

734 

735class FileFormatSection: 

736 misc: Any 

737 

738 

739class FileFormat: 

740 name: str 

741 path: pathlib.Path 

742 entry_point: int 

743 checksec: Dict[str, bool] 

744 sections: List[FileFormatSection] 

745 

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

747 raise NotImplementedError 

748 

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

750 global __registered_file_formats__ 

751 super().__init_subclass__(**kwargs) 

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

753 for attr in required_attributes: 

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

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

756 __registered_file_formats__.add(cls) 

757 return 

758 

759 @classmethod 

760 def is_valid(cls, _: pathlib.Path) -> bool: 

761 raise NotImplementedError 

762 

763 def __str__(self) -> str: 

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

765 

766 

767class Elf(FileFormat): 

768 """Basic ELF parsing. 

769 Ref: 

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

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

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

773 """ 

774 class Class(enum.Enum): 

775 ELF_32_BITS = 0x01 

776 ELF_64_BITS = 0x02 

777 

778 ELF_MAGIC = 0x7f454c46 

779 

780 class Abi(enum.Enum): 

781 X86_64 = 0x3e 

782 X86_32 = 0x03 

783 ARM = 0x28 

784 MIPS = 0x08 

785 POWERPC = 0x14 

786 POWERPC64 = 0x15 

787 SPARC = 0x02 

788 SPARC64 = 0x2b 

789 AARCH64 = 0xb7 

790 RISCV = 0xf3 

791 IA64 = 0x32 

792 M68K = 0x04 

793 

794 class Type(enum.Enum): 

795 ET_RELOC = 1 

796 ET_EXEC = 2 

797 ET_DYN = 3 

798 ET_CORE = 4 

799 

800 class OsAbi(enum.Enum): 

801 SYSTEMV = 0x00 

802 HPUX = 0x01 

803 NETBSD = 0x02 

804 LINUX = 0x03 

805 SOLARIS = 0x06 

806 AIX = 0x07 

807 IRIX = 0x08 

808 FREEBSD = 0x09 

809 OPENBSD = 0x0C 

810 

811 e_magic: int = ELF_MAGIC 

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

813 e_endianness: Endianness = Endianness.LITTLE_ENDIAN 

814 e_eiversion: int 

815 e_osabi: "Elf.OsAbi" 

816 e_abiversion: int 

817 e_pad: bytes 

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

819 e_machine: Abi = Abi.X86_32 

820 e_version: int 

821 e_entry: int 

822 e_phoff: int 

823 e_shoff: int 

824 e_flags: int 

825 e_ehsize: int 

826 e_phentsize: int 

827 e_phnum: int 

828 e_shentsize: int 

829 e_shnum: int 

830 e_shstrndx: int 

831 

832 path: pathlib.Path 

833 phdrs : List["Phdr"] 

834 shdrs : List["Shdr"] 

835 name: str = "ELF" 

836 

837 __checksec : Dict[str, bool] 

838 

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

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

841 

842 if isinstance(path, str): 

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

844 elif isinstance(path, pathlib.Path): 844 ↛ 847line 844 didn't jump to line 847, because the condition on line 844 was always true

845 self.path = path 

846 else: 

847 raise TypeError 

848 

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

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

851 

852 self.__checksec = {} 

853 

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

855 # off 0x0 

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

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

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

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

860 

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

862 

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

864 warn("Unexpected endianness for architecture") 

865 

866 endian = self.e_endianness 

867 

868 # off 0x7 

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

870 self.e_osabi = Elf.OsAbi(e_osabi) 

871 

872 # off 0x9 

873 self.e_pad = self.read(7) 

874 

875 # off 0x10 

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

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

878 

879 # off 0x18 

880 if self.e_class == Elf.Class.ELF_64_BITS: 880 ↛ 883line 880 didn't jump to line 883, because the condition on line 880 was always true

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

882 else: 

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

884 

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

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

887 

888 self.phdrs = [] 

889 for i in range(self.e_phnum): 

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

891 

892 self.shdrs = [] 

893 for i in range(self.e_shnum): 

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

895 return 

896 

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

898 return self.fd.read(size) 

899 

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

901 size = struct.calcsize(fmt) 

902 data = self.fd.read(size) 

903 return struct.unpack(fmt, data) 

904 

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

906 self.fd.seek(off, 0) 

907 

908 def __str__(self) -> str: 

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

910 

911 def __repr__(self) -> str: 

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

913 

914 @property 

915 def entry_point(self) -> int: 

916 return self.e_entry 

917 

918 @classmethod 

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

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

921 

922 @property 

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

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

925 - Canary 

926 - NX 

927 - PIE 

928 - Fortify 

929 - Partial/Full RelRO. 

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

931 associated whether the protection was found.""" 

932 if not self.__checksec: 932 ↛ 955line 932 didn't jump to line 955, because the condition on line 932 was always true

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

934 cmd = [readelf,] 

935 cmd += opt.split() 

936 cmd += [filename,] 

937 lines = gef_execute_external(cmd, as_list=True) 

938 for line in lines: 

939 if re.search(pattern, line): 

940 return True 

941 return False 

942 

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

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

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

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

947 if has_gnu_stack: 947 ↛ 950line 947 didn't jump to line 950, because the condition on line 947 was always true

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

949 else: 

950 self.__checksec["NX"] = False 

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

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

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

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

955 return self.__checksec 

956 

957 @classproperty 

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

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

960 

961 @classproperty 

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

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

964 

965 @classproperty 

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

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

968 

969 @classproperty 

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

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

972 

973 @classproperty 

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

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

976 

977 @classproperty 

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

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

980 

981 @classproperty 

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

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

984 

985 @classproperty 

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

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

988 

989 @classproperty 

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

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

992 

993 @classproperty 

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

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

996 

997 

998class Phdr: 

999 class Type(enum.IntEnum): 

1000 PT_NULL = 0 

1001 PT_LOAD = 1 

1002 PT_DYNAMIC = 2 

1003 PT_INTERP = 3 

1004 PT_NOTE = 4 

1005 PT_SHLIB = 5 

1006 PT_PHDR = 6 

1007 PT_TLS = 7 

1008 PT_LOOS = 0x60000000 

1009 PT_GNU_EH_FRAME = 0x6474e550 

1010 PT_GNU_STACK = 0x6474e551 

1011 PT_GNU_RELRO = 0x6474e552 

1012 PT_GNU_PROPERTY = 0x6474e553 

1013 PT_LOSUNW = 0x6ffffffa 

1014 PT_SUNWBSS = 0x6ffffffa 

1015 PT_SUNWSTACK = 0x6ffffffb 

1016 PT_HISUNW = PT_HIOS = 0x6fffffff 

1017 PT_LOPROC = 0x70000000 

1018 PT_ARM_EIDX = 0x70000001 

1019 PT_MIPS_ABIFLAGS= 0x70000003 

1020 PT_HIPROC = 0x7fffffff 

1021 UNKNOWN_PHDR = 0xffffffff 

1022 

1023 @classmethod 

1024 def _missing_(cls, _:int) -> "Phdr.Type": 

1025 return cls.UNKNOWN_PHDR 

1026 

1027 class Flags(enum.IntFlag): 

1028 PF_X = 1 

1029 PF_W = 2 

1030 PF_R = 4 

1031 

1032 p_type: "Phdr.Type" 

1033 p_flags: "Phdr.Flags" 

1034 p_offset: int 

1035 p_vaddr: int 

1036 p_paddr: int 

1037 p_filesz: int 

1038 p_memsz: int 

1039 p_align: int 

1040 

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

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

1043 return 

1044 elf.seek(off) 

1045 self.offset = off 

1046 endian = elf.e_endianness 

1047 if elf.e_class == Elf.Class.ELF_64_BITS: 1047 ↛ 1052line 1047 didn't jump to line 1052, because the condition on line 1047 was always true

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

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

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

1051 else: 

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

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

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

1055 

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

1057 return 

1058 

1059 def __str__(self) -> str: 

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

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

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

1063 

1064 

1065class Shdr: 

1066 class Type(enum.IntEnum): 

1067 SHT_NULL = 0 

1068 SHT_PROGBITS = 1 

1069 SHT_SYMTAB = 2 

1070 SHT_STRTAB = 3 

1071 SHT_RELA = 4 

1072 SHT_HASH = 5 

1073 SHT_DYNAMIC = 6 

1074 SHT_NOTE = 7 

1075 SHT_NOBITS = 8 

1076 SHT_REL = 9 

1077 SHT_SHLIB = 10 

1078 SHT_DYNSYM = 11 

1079 SHT_NUM = 12 

1080 SHT_INIT_ARRAY = 14 

1081 SHT_FINI_ARRAY = 15 

1082 SHT_PREINIT_ARRAY = 16 

1083 SHT_GROUP = 17 

1084 SHT_SYMTAB_SHNDX = 18 

1085 SHT_LOOS = 0x60000000 

1086 SHT_GNU_ATTRIBUTES = 0x6ffffff5 

1087 SHT_GNU_HASH = 0x6ffffff6 

1088 SHT_GNU_LIBLIST = 0x6ffffff7 

1089 SHT_CHECKSUM = 0x6ffffff8 

1090 SHT_LOSUNW = 0x6ffffffa 

1091 SHT_SUNW_move = 0x6ffffffa 

1092 SHT_SUNW_COMDAT = 0x6ffffffb 

1093 SHT_SUNW_syminfo = 0x6ffffffc 

1094 SHT_GNU_verdef = 0x6ffffffd 

1095 SHT_GNU_verneed = 0x6ffffffe 

1096 SHT_GNU_versym = 0x6fffffff 

1097 SHT_LOPROC = 0x70000000 

1098 SHT_ARM_EXIDX = 0x70000001 

1099 SHT_X86_64_UNWIND = 0x70000001 

1100 SHT_ARM_ATTRIBUTES = 0x70000003 

1101 SHT_MIPS_OPTIONS = 0x7000000d 

1102 DT_MIPS_INTERFACE = 0x7000002a 

1103 SHT_HIPROC = 0x7fffffff 

1104 SHT_LOUSER = 0x80000000 

1105 SHT_HIUSER = 0x8fffffff 

1106 UNKNOWN_SHDR = 0xffffffff 

1107 

1108 @classmethod 

1109 def _missing_(cls, _:int) -> "Shdr.Type": 

1110 return cls.UNKNOWN_SHDR 

1111 

1112 class Flags(enum.IntFlag): 

1113 WRITE = 1 

1114 ALLOC = 2 

1115 EXECINSTR = 4 

1116 MERGE = 0x10 

1117 STRINGS = 0x20 

1118 INFO_LINK = 0x40 

1119 LINK_ORDER = 0x80 

1120 OS_NONCONFORMING = 0x100 

1121 GROUP = 0x200 

1122 TLS = 0x400 

1123 COMPRESSED = 0x800 

1124 RELA_LIVEPATCH = 0x00100000 

1125 RO_AFTER_INIT = 0x00200000 

1126 ORDERED = 0x40000000 

1127 EXCLUDE = 0x80000000 

1128 UNKNOWN_FLAG = 0xffffffff 

1129 

1130 @classmethod 

1131 def _missing_(cls, _:int): 

1132 return cls.UNKNOWN_FLAG 

1133 

1134 sh_name: int 

1135 sh_type: "Shdr.Type" 

1136 sh_flags: "Shdr.Flags" 

1137 sh_addr: int 

1138 sh_offset: int 

1139 sh_size: int 

1140 sh_link: int 

1141 sh_info: int 

1142 sh_addralign: int 

1143 sh_entsize: int 

1144 name: str 

1145 

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

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

1148 return 

1149 elf.seek(off) 

1150 endian = elf.e_endianness 

1151 if elf.e_class == Elf.Class.ELF_64_BITS: 1151 ↛ 1157line 1151 didn't jump to line 1157, because the condition on line 1151 was always true

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

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

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

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

1156 else: 

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

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

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

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

1161 

1162 self.sh_type = Shdr.Type(sh_type) 

1163 self.sh_flags = Shdr.Flags(sh_flags) 

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

1165 

1166 if elf.e_class == Elf.Class.ELF_64_BITS: 1166 ↛ 1170line 1166 didn't jump to line 1170, because the condition on line 1166 was always true

1167 elf.seek(stroff + 16 + 8) 

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

1169 else: 

1170 elf.seek(stroff + 12 + 4) 

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

1172 elf.seek(offset + self.sh_name) 

1173 self.name = "" 

1174 while True: 

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

1176 if c == 0: 

1177 break 

1178 self.name += chr(c) 

1179 return 

1180 

1181 def __str__(self) -> str: 

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

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

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

1185 

1186 

1187class Instruction: 

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

1189 

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

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

1192 address, location, mnemo, operands, opcodes 

1193 return 

1194 

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

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

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

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

1199 return str(self) 

1200 

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

1202 opcodes_len = len(self.opcodes) 

1203 else: 

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

1205 

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

1207 if opcodes_len < len(self.opcodes): 

1208 opcodes_text += "..." 

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

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

1211 

1212 def __str__(self) -> str: 

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

1214 

1215 def is_valid(self) -> bool: 

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

1217 

1218 def size(self) -> int: 

1219 return len(self.opcodes) 

1220 

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

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

1223 return gef_get_instruction_at(address) 

1224 

1225 

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

1227def search_for_main_arena() -> int: 

1228 return GefHeapManager.find_main_arena_addr() 

1229 

1230class GlibcHeapInfo: 

1231 """Glibc heap_info struct""" 

1232 

1233 @staticmethod 

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

1235 assert gef.libc.version 

1236 class heap_info_cls(ctypes.Structure): 

1237 pass 

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

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

1240 fields = [ 

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

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

1243 ("size", pointer) 

1244 ] 

1245 if gef.libc.version >= (2, 5): 1245 ↛ 1250line 1245 didn't jump to line 1250, because the condition on line 1245 was always true

1246 fields += [ 

1247 ("mprotect_size", pointer) 

1248 ] 

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

1250 if gef.libc.version >= (2, 34): 1250 ↛ 1255line 1250 didn't jump to line 1255, because the condition on line 1250 was always true

1251 fields += [ 

1252 ("pagesize", pointer) 

1253 ] 

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

1255 fields += [ 

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

1257 ] 

1258 heap_info_cls._fields_ = fields 

1259 return heap_info_cls 

1260 

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

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

1263 self.reset() 

1264 return 

1265 

1266 def reset(self): 

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

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

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

1270 return 

1271 

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

1273 if item in dir(self._heap_info): 1273 ↛ 1275line 1273 didn't jump to line 1275, because the condition on line 1273 was always true

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

1275 return getattr(self, item) 

1276 

1277 def __abs__(self) -> int: 

1278 return self.__address 

1279 

1280 def __int__(self) -> int: 

1281 return self.__address 

1282 

1283 @property 

1284 def address(self) -> int: 

1285 return self.__address 

1286 

1287 @property 

1288 def sizeof(self) -> int: 

1289 return self._sizeof 

1290 

1291 @property 

1292 def addr(self) -> int: 

1293 return int(self) 

1294 

1295 @property 

1296 def heap_start(self) -> int: 

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

1298 if self.ar_ptr - self.address < 0x60: 1298 ↛ 1310line 1298 didn't jump to line 1310, because the condition on line 1298 was always true

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

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

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

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

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

1304 heap_addr = arena.heap_addr() 

1305 if heap_addr: 1305 ↛ 1308line 1305 didn't jump to line 1308, because the condition on line 1305 was always true

1306 return heap_addr 

1307 else: 

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

1309 return 0 

1310 return self.address + self.sizeof 

1311 

1312 @property 

1313 def heap_end(self) -> int: 

1314 return self.address + self.size 

1315 

1316 

1317class GlibcArena: 

1318 """Glibc arena class""" 

1319 

1320 NFASTBINS = 10 

1321 NBINS = 128 

1322 NSMALLBINS = 64 

1323 BINMAPSHIFT = 5 

1324 BITSPERMAP = 1 << BINMAPSHIFT 

1325 BINMAPSIZE = NBINS // BITSPERMAP 

1326 

1327 @staticmethod 

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

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

1330 fields = [ 

1331 ("mutex", ctypes.c_uint32), 

1332 ("flags", ctypes.c_uint32), 

1333 ] 

1334 if gef and gef.libc.version and gef.libc.version >= (2, 27): 1334 ↛ 1340line 1334 didn't jump to line 1340, because the condition on line 1334 was always true

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

1336 fields += [ 

1337 ("have_fastchunks", ctypes.c_uint32), 

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

1339 ] 

1340 fields += [ 

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

1342 ("top", pointer), 

1343 ("last_remainder", pointer), 

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

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

1346 ("next", pointer), 

1347 ("next_free", pointer) 

1348 ] 

1349 if gef and gef.libc.version and gef.libc.version >= (2, 23): 1349 ↛ 1354line 1349 didn't jump to line 1354, because the condition on line 1349 was always true

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

1351 fields += [ 

1352 ("attached_threads", pointer) 

1353 ] 

1354 fields += [ 

1355 ("system_mem", pointer), 

1356 ("max_system_mem", pointer), 

1357 ] 

1358 class malloc_state_cls(ctypes.Structure): 

1359 _fields_ = fields 

1360 return malloc_state_cls 

1361 

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

1363 try: 

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

1365 except gdb.error: 

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

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

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

1369 self.reset() 

1370 return 

1371 

1372 def reset(self): 

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

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

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

1376 return 

1377 

1378 def __abs__(self) -> int: 

1379 return self.__address 

1380 

1381 def __int__(self) -> int: 

1382 return self.__address 

1383 

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

1385 assert gef.heap.main_arena 

1386 main_arena = int(gef.heap.main_arena) 

1387 

1388 current_arena = self 

1389 yield current_arena 

1390 

1391 while True: 

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

1393 break 

1394 

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

1396 yield current_arena 

1397 return 

1398 

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

1400 return self.__address == int(other) 

1401 

1402 def __str__(self) -> str: 

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

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

1405 f"mem={self.system_mem}, mempeak={self.max_system_mem}" 

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

1407 

1408 def __repr__(self) -> str: 

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

1410 

1411 @property 

1412 def address(self) -> int: 

1413 return self.__address 

1414 

1415 @property 

1416 def sizeof(self) -> int: 

1417 return self._sizeof 

1418 

1419 @property 

1420 def addr(self) -> int: 

1421 return int(self) 

1422 

1423 @property 

1424 def top(self) -> int: 

1425 return self.__arena.top 

1426 

1427 @property 

1428 def last_remainder(self) -> int: 

1429 return self.__arena.last_remainder 

1430 

1431 @property 

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

1433 return self.__arena.fastbinsY 

1434 

1435 @property 

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

1437 return self.__arena.bins 

1438 

1439 @property 

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

1441 return self.__arena.binmap 

1442 

1443 @property 

1444 def next(self) -> int: 

1445 return self.__arena.next 

1446 

1447 @property 

1448 def next_free(self) -> int: 

1449 return self.__arena.next_free 

1450 

1451 @property 

1452 def attached_threads(self) -> int: 

1453 return self.__arena.attached_threads 

1454 

1455 @property 

1456 def system_mem(self) -> int: 

1457 return self.__arena.system_mem 

1458 

1459 @property 

1460 def max_system_mem(self) -> int: 

1461 return self.__arena.max_system_mem 

1462 

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

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

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

1466 if addr == 0: 

1467 return None 

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

1469 

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

1471 idx = i * 2 

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

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

1474 return fd, bk 

1475 

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

1477 header_sz = 2 * gef.arch.ptrsize 

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

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

1480 

1481 def is_main_arena(self) -> bool: 

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

1483 

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

1485 if self.is_main_arena(): 

1486 heap_section = gef.heap.base_address 

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

1488 return None 

1489 return heap_section 

1490 _addr = int(self) + self.sizeof 

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

1492 return _addr 

1493 return gef.heap.malloc_align_address(_addr) 

1494 

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

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

1497 return None 

1498 heap_addr = self.get_heap_for_ptr(self.top) 

1499 heap_infos = [GlibcHeapInfo(heap_addr)] 

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

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

1502 heap_info = GlibcHeapInfo(prev) 

1503 heap_infos.append(heap_info) 

1504 return heap_infos[::-1] 

1505 

1506 @staticmethod 

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

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

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

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

1511 default_mmap_threshold_max = 512 * 1024 

1512 else: # 64bit 

1513 val = cached_lookup_type("long") 

1514 sz = val.sizeof if val else gef.arch.ptrsize 

1515 default_mmap_threshold_max = 4 * 1024 * 1024 * sz 

1516 heap_max_size = 2 * default_mmap_threshold_max 

1517 return ptr & ~(heap_max_size - 1) 

1518 

1519 @staticmethod 

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

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

1522 try: 

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

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

1525 while cur_arena != test_arena: 

1526 if cur_arena == 0: 

1527 return False 

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

1529 except Exception as e: 

1530 dbg(f"GlibcArena.verify({addr:#x}) failed: {str(e)}") 

1531 return False 

1532 return True 

1533 

1534 

1535class GlibcChunk: 

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

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

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

1539 

1540 class ChunkFlags(enum.IntFlag): 

1541 PREV_INUSE = 1 

1542 IS_MMAPPED = 2 

1543 NON_MAIN_ARENA = 4 

1544 

1545 def __str__(self) -> str: 

1546 return f" | ".join([ 

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

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

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

1550 ]) 

1551 

1552 @staticmethod 

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

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

1555 class malloc_chunk_cls(ctypes.Structure): 

1556 pass 

1557 

1558 malloc_chunk_cls._fields_ = [ 

1559 ("prev_size", pointer), 

1560 ("size", pointer), 

1561 ("fd", pointer), 

1562 ("bk", pointer), 

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

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

1565 ] 

1566 return malloc_chunk_cls 

1567 

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

1569 ptrsize = gef.arch.ptrsize 

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

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

1572 if not allow_unaligned: 

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

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

1575 self.prev_size_addr = self.base_address 

1576 self.reset() 

1577 return 

1578 

1579 def reset(self): 

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

1581 self._data = gef.memory.read( 

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

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

1584 return 

1585 

1586 @property 

1587 def prev_size(self) -> int: 

1588 return self._chunk.prev_size 

1589 

1590 @property 

1591 def size(self) -> int: 

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

1593 

1594 @property 

1595 def flags(self) -> ChunkFlags: 

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

1597 

1598 @property 

1599 def fd(self) -> int: 

1600 return self._chunk.fd 

1601 

1602 @property 

1603 def bk(self) -> int: 

1604 return self._chunk.bk 

1605 

1606 @property 

1607 def fd_nextsize(self) -> int: 

1608 return self._chunk.fd_nextsize 

1609 

1610 @property 

1611 def bk_nextsize(self) -> int: 

1612 return self._chunk.bk_nextsize 

1613 

1614 def get_usable_size(self) -> int: 

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

1616 ptrsz = gef.arch.ptrsize 

1617 cursz = self.size 

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

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

1620 return cursz - ptrsz 

1621 

1622 @property 

1623 def usable_size(self) -> int: 

1624 return self.get_usable_size() 

1625 

1626 def get_prev_chunk_size(self) -> int: 

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

1628 

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

1630 assert gef.heap.main_arena 

1631 current_chunk = self 

1632 top = gef.heap.main_arena.top 

1633 

1634 while True: 

1635 yield current_chunk 

1636 

1637 if current_chunk.base_address == top: 

1638 break 

1639 

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

1641 break 

1642 

1643 next_chunk_addr = current_chunk.get_next_chunk_addr() 

1644 

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

1646 break 

1647 

1648 next_chunk = current_chunk.get_next_chunk() 

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

1650 break 

1651 

1652 current_chunk = next_chunk 

1653 return 

1654 

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

1656 addr = self.get_next_chunk_addr() 

1657 return GlibcChunk(addr, allow_unaligned=allow_unaligned) 

1658 

1659 def get_next_chunk_addr(self) -> int: 

1660 return self.data_address + self.size 

1661 

1662 def has_p_bit(self) -> bool: 

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

1664 

1665 def has_m_bit(self) -> bool: 

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

1667 

1668 def has_n_bit(self) -> bool: 

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

1670 

1671 def is_used(self) -> bool: 

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

1673 - checking the M bit is true 

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

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

1676 return True 

1677 

1678 next_chunk = self.get_next_chunk() 

1679 return True if next_chunk.has_p_bit() else False 

1680 

1681 def __str_sizes(self) -> str: 

1682 msg = [] 

1683 failed = False 

1684 

1685 try: 

1686 msg.append(f"Chunk size: {self.size:d} ({self.size:#x})") 

1687 msg.append(f"Usable size: {self.usable_size:d} ({self.usable_size:#x})") 

1688 failed = True 

1689 except gdb.MemoryError: 

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

1691 

1692 try: 

1693 prev_chunk_sz = self.get_prev_chunk_size() 

1694 msg.append(f"Previous chunk size: {prev_chunk_sz:d} ({prev_chunk_sz:#x})") 

1695 failed = True 

1696 except gdb.MemoryError: 

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

1698 

1699 if failed: 1699 ↛ 1702line 1699 didn't jump to line 1702, because the condition on line 1699 was always true

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

1701 

1702 return "\n".join(msg) 

1703 

1704 def _str_pointers(self) -> str: 

1705 fwd = self.data_address 

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

1707 

1708 msg = [] 

1709 try: 

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

1711 except gdb.MemoryError: 

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

1713 

1714 try: 

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

1716 except gdb.MemoryError: 

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

1718 

1719 return "\n".join(msg) 

1720 

1721 def __str__(self) -> str: 

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

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

1724 

1725 def psprint(self) -> str: 

1726 msg = [ 

1727 str(self), 

1728 self.__str_sizes(), 

1729 ] 

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

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

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

1733 

1734 def resolve_type(self) -> str: 

1735 ptr_data = gef.memory.read_integer(self.data_address) 

1736 if ptr_data != 0: 

1737 sym = gdb_get_location_from_symbol(ptr_data) 

1738 if sym is not None and "vtable for" in sym[0]: 

1739 return sym[0].replace("vtable for ", "") 

1740 

1741 return "" 

1742 

1743 

1744class GlibcFastChunk(GlibcChunk): 

1745 

1746 @property 

1747 def fd(self) -> int: 

1748 assert(gef and gef.libc.version) 

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

1750 return self._chunk.fd 

1751 return self.reveal_ptr(self.data_address) 

1752 

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

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

1755 assert(gef and gef.libc.version) 

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

1757 return pointer 

1758 return (pos >> 12) ^ pointer 

1759 

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

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

1762 assert(gef and gef.libc.version) 

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

1764 return pointer 

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

1766 

1767class GlibcTcacheChunk(GlibcFastChunk): 

1768 pass 

1769 

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

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

1772 return GefLibcManager.find_libc_version() 

1773 

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

1775 """Print a centered title.""" 

1776 _, cols = get_terminal_size() 

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

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

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

1780 

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

1782 Color.colorify(text, text_color), 

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

1784 return "".join(msg) 

1785 

1786 

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

1788 if gef.config["gef.debug"] is True: 

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

1790 return 

1791 

1792 

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

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

1795 return 

1796 

1797 

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

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

1800 return 

1801 

1802 

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

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

1805 return 

1806 

1807 

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

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

1810 return 

1811 

1812 

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

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

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

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

1817 return 

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

1819 return 

1820 

1821 

1822def show_last_exception() -> None: 

1823 """Display the last Python exception.""" 

1824 

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

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

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

1828 _data = f.readlines() 

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

1830 

1831 gef_print("") 

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

1833 

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

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

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

1837 

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

1839 filename, lineno, method, code = fs 

1840 

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

1842 code = _show_code_line(filename, lineno) 

1843 

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

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

1846 

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

1848 gdb.execute("version full") 

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

1850 gdb.execute("show commands") 

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

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

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

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

1855 

1856 try: 

1857 lsb_release = which("lsb_release") 

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

1859 except FileNotFoundError: 

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

1861 

1862 gef_print(HORIZONTAL_LINE*80) 

1863 gef_print("") 

1864 return 

1865 

1866 

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

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

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

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

1871 for _x, _y in substs: res = res.replace(_x, _y) 

1872 return res 

1873 

1874 

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

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

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

1878 

1879 

1880@lru_cache() 

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

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

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

1884 dirname = pathlib.Path(path) 

1885 fpath = dirname / program 

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

1887 return fpath 

1888 

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

1890 

1891 

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

1893 style = { 

1894 "nonprintable": "yellow", 

1895 "printable": "white", 

1896 "00": "gray", 

1897 "0a": "blue", 

1898 "ff": "green", 

1899 } 

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

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

1902 return sbyte 

1903 

1904 if sbyte in style: 

1905 st = style[sbyte] 

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

1907 st = style.get("printable") 

1908 else: 

1909 st = style.get("nonprintable") 

1910 if st: 1910 ↛ 1912line 1910 didn't jump to line 1912, because the condition on line 1910 was always true

1911 sbyte = Color.colorify(sbyte, st) 

1912 return sbyte 

1913 

1914 

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

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

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

1918 @param length is the length of items per line 

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

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

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

1922 @return a string with the hexdump""" 

1923 result = [] 

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

1925 

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

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

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

1929 

1930 if show_raw: 

1931 result.append(hexa) 

1932 continue 

1933 

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

1935 if show_symbol: 1935 ↛ 1939line 1935 didn't jump to line 1939, because the condition on line 1935 was always true

1936 sym = gdb_get_location_from_symbol(base + i) 

1937 sym = f"<{sym[0]:s}+{sym[1]:04x}>" if sym else "" 

1938 else: 

1939 sym = "" 

1940 

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

1942 return "\n".join(result) 

1943 

1944 

1945def is_debug() -> bool: 

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

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

1948 

1949 

1950def buffer_output() -> bool: 

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

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

1953 

1954 

1955def hide_context() -> bool: 

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

1957 gef.ui.context_hidden = True 

1958 return True 

1959 

1960 

1961def unhide_context() -> bool: 

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

1963 gef.ui.context_hidden = False 

1964 return True 

1965 

1966 

1967class DisableContextOutputContext: 

1968 def __enter__(self) -> None: 

1969 hide_context() 

1970 return 

1971 

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

1973 unhide_context() 

1974 return 

1975 

1976 

1977class RedirectOutputContext: 

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

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

1980 self.redirection_target_file = to_file 

1981 return 

1982 

1983 def __enter__(self) -> None: 

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

1985 gdb.execute("set logging overwrite") 

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

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

1988 

1989 if GDB_VERSION >= (12, 0): 1989 ↛ 1992line 1989 didn't jump to line 1992, because the condition on line 1989 was always true

1990 gdb.execute("set logging enabled on") 

1991 else: 

1992 gdb.execute("set logging on") 

1993 return 

1994 

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

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

1997 if GDB_VERSION >= (12, 0): 1997 ↛ 2000line 1997 didn't jump to line 2000, because the condition on line 1997 was always true

1998 gdb.execute("set logging enabled off") 

1999 else: 

2000 gdb.execute("set logging off") 

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

2002 return 

2003 

2004 

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

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

2007 if " " in to_file: raise ValueError("Target filepath cannot contain spaces") 

2008 gdb.execute("set logging overwrite") 

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

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

2011 

2012 if GDB_VERSION >= (12, 0): 

2013 gdb.execute("set logging enabled on") 

2014 else: 

2015 gdb.execute("set logging on") 

2016 return 

2017 

2018 

2019def disable_redirect_output() -> None: 

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

2021 if GDB_VERSION >= (12, 0): 

2022 gdb.execute("set logging enabled off") 

2023 else: 

2024 gdb.execute("set logging off") 

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

2026 return 

2027 

2028@deprecated("use `pathlib.Path(...).mkdir()`") 

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

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

2031 fpath = pathlib.Path(path) 

2032 if not fpath.is_dir(): 

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

2034 return fpath.absolute() 

2035 

2036 

2037@lru_cache() 

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

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

2040 try: 

2041 res = gdb.decode_line(sym)[1] # pylint: disable=E1136 

2042 return res 

2043 except gdb.error: 

2044 return None 

2045 

2046@lru_cache(maxsize=512) 

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

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

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

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

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

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

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

2054 return None 

2055 

2056 # gdb outputs symbols with format: "<symbol_name> + <offset> in section <section_name> of <file>", 

2057 # here, we are only interested in symbol name and offset. 

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

2059 sym = sym[:i].split(" + ") 

2060 name, offset = sym[0], 0 

2061 if len(sym) == 2 and sym[1].isdigit(): 

2062 offset = int(sym[1]) 

2063 return name, offset 

2064 

2065 

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

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

2068 parameters: 

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

2070 start_pc to end_pc are returned. 

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

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

2073 Return an iterator of Instruction objects 

2074 """ 

2075 frame = gdb.selected_frame() 

2076 arch = frame.architecture() 

2077 

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

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

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

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

2082 address = insn["addr"] 

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

2084 if len(asm) > 1: 

2085 mnemo, operands = asm 

2086 operands = operands.split(",") 

2087 else: 

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

2089 

2090 loc = gdb_get_location_from_symbol(address) 

2091 location = f"<{loc[0]}+{loc[1]:04x}>" if loc else "" 

2092 

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

2094 

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

2096 

2097 

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

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

2100 # fixed-length ABI 

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

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

2103 

2104 # variable-length ABI 

2105 cur_insn_addr = gef_current_instruction(addr).address 

2106 

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

2108 # the 15 comes from the longest instruction valid size 

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

2110 try: 

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

2112 except gdb.MemoryError: 

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

2114 break 

2115 

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

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

2118 continue 

2119 

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

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

2122 insns = insns[-n - 1 :] 

2123 

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

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

2126 continue 

2127 

2128 # 3. check all instructions are valid 

2129 if all(insn.is_valid() for insn in insns): 2129 ↛ 2109line 2129 didn't jump to line 2109, because the condition on line 2129 was always true

2130 return insns[0].address 

2131 

2132 return None 

2133 

2134 

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

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

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

2138 return gef_instruction_n(addr, n).address 

2139 

2140 

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

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

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

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

2145 

2146 

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

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

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

2150 return insn 

2151 

2152 

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

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

2155 return gef_instruction_n(addr, 0) 

2156 

2157 

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

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

2160 return gef_instruction_n(addr, 1) 

2161 

2162 

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

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

2165 Return an iterator of Instruction objects.""" 

2166 nb_insn = max(1, nb_insn) 

2167 

2168 if nb_prev: 

2169 try: 

2170 start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) 

2171 if start_addr: 

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

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

2174 yield insn 

2175 except gdb.MemoryError: 

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

2177 pass 

2178 

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

2180 yield insn 

2181 

2182 

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

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

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

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

2187 

2188 

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

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

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

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

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

2194 f.write(commands) 

2195 f.flush() 

2196 

2197 fname = pathlib.Path(fname) 

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

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

2200 fname.unlink() 

2201 return 

2202 

2203 

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

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

2206 return Elf(filename).checksec 

2207 

2208 

2209@deprecated("Use `gef.arch` instead") 

2210def get_arch() -> str: 

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

2212 if is_alive(): 

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

2214 return arch.name() 

2215 

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

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

2218 if arch_str.startswith(pat): 

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

2220 return arch_str 

2221 

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

2223 if arch_str.startswith(pat): 

2224 return arch_str[len(pat):] 

2225 

2226 pat = "The target architecture is set to " 

2227 if arch_str.startswith(pat): 

2228 # GDB version >= 10.1 

2229 if '"auto"' in arch_str: 

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

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

2232 

2233 # Unknown, we throw an exception to be safe 

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

2235 

2236 

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

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

2239 """Return the binary entry point.""" 

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

2241 

2242 

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

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

2245 

2246 

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

2248def is_big_endian() -> bool: 

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

2250 

2251 

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

2253def is_little_endian() -> bool: 

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

2255 

2256 

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

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

2259 flags = [] 

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

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

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

2263 

2264 

2265@lru_cache() 

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

2267 section = process_lookup_path(name) 

2268 return section.page_start if section else None 

2269 

2270 

2271@lru_cache() 

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

2273 zone = file_lookup_name_path(name, get_filepath()) 

2274 return zone.zone_start if zone else None 

2275 

2276 

2277# 

2278# Architecture classes 

2279# 

2280 

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

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

2283 return cls 

2284 

2285class ArchitectureBase: 

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

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

2288 

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

2290 global __registered_architectures__ 

2291 super().__init_subclass__(**kwargs) 

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

2293 if issubclass(cls, Architecture): 2293 ↛ 2292line 2293 didn't jump to line 2292, because the condition on line 2293 was always true

2294 if isinstance(key, str): 

2295 __registered_architectures__[key.lower()] = cls 

2296 else: 

2297 __registered_architectures__[key] = cls 

2298 return 

2299 

2300 

2301class Architecture(ArchitectureBase): 

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

2303 

2304 # Mandatory defined attributes by inheriting classes 

2305 arch: str 

2306 mode: str 

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

2308 nop_insn: bytes 

2309 return_register: str 

2310 flag_register: Optional[str] 

2311 instruction_length: Optional[int] 

2312 flags_table: Dict[int, str] 

2313 syscall_register: Optional[str] 

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

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

2316 

2317 # Optionally defined attributes 

2318 _ptrsize: Optional[int] = None 

2319 _endianness: Optional[Endianness] = None 

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

2321 maps: Optional[GefMemoryMapProvider] = None 

2322 

2323 def __init_subclass__(cls, **kwargs): 

2324 super().__init_subclass__(**kwargs) 

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

2326 "return_register", "flag_register", "instruction_length", "flags_table", 

2327 "function_parameters",) 

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

2329 raise NotImplementedError 

2330 

2331 def __str__(self) -> str: 

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

2333 

2334 def __repr__(self) -> str: 

2335 return self.__str__() 

2336 

2337 @staticmethod 

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

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

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

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

2342 return None 

2343 

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

2345 raise NotImplementedError 

2346 

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

2348 raise NotImplementedError 

2349 

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

2351 raise NotImplementedError 

2352 

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

2354 raise NotImplementedError 

2355 

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

2357 raise NotImplementedError 

2358 

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

2360 raise NotImplementedError 

2361 

2362 def canary_address(self) -> int: 

2363 raise NotImplementedError 

2364 

2365 @classmethod 

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

2367 raise NotImplementedError 

2368 

2369 def reset_caches(self) -> None: 

2370 self.__get_register_for_selected_frame.cache_clear() 

2371 return 

2372 

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

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

2375 curframe = gdb.selected_frame() 

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

2377 return self.__get_register_for_selected_frame(regname, int(key)) 

2378 

2379 @lru_cache() 

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

2381 # 1st chance 

2382 try: 

2383 return parse_address(regname) 

2384 except gdb.error: 

2385 pass 

2386 

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

2388 regname = regname.lstrip("$") 

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

2390 return int(value) 

2391 

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

2393 if not is_alive(): 

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

2395 return self.__get_register(name) 

2396 

2397 @property 

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

2399 yield from self.all_registers 

2400 

2401 @property 

2402 def pc(self) -> int: 

2403 return self.register("$pc") 

2404 

2405 @property 

2406 def sp(self) -> int: 

2407 return self.register("$sp") 

2408 

2409 @property 

2410 def fp(self) -> int: 

2411 return self.register("$fp") 

2412 

2413 @property 

2414 def ptrsize(self) -> int: 

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

2416 res = cached_lookup_type("size_t") 

2417 if res is not None: 

2418 self._ptrsize = res.sizeof 

2419 else: 

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

2421 return self._ptrsize 

2422 

2423 @property 

2424 def endianness(self) -> Endianness: 

2425 if not self._endianness: 

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

2427 if "little endian" in output: 2427 ↛ 2429line 2427 didn't jump to line 2429, because the condition on line 2427 was always true

2428 self._endianness = Endianness.LITTLE_ENDIAN 

2429 elif "big endian" in output: 

2430 self._endianness = Endianness.BIG_ENDIAN 

2431 else: 

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

2433 return self._endianness 

2434 

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

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

2437 reg = self.function_parameters[i] 

2438 val = self.register(reg) 

2439 key = reg 

2440 return key, val 

2441 

2442 

2443class GenericArchitecture(Architecture): 

2444 arch = "Generic" 

2445 mode = "" 

2446 aliases = ("GenericArchitecture",) 

2447 all_registers = () 

2448 instruction_length = 0 

2449 return_register = "" 

2450 function_parameters = () 

2451 syscall_register = "" 

2452 syscall_instructions = () 

2453 nop_insn = b"" 

2454 flag_register = None 

2455 flags_table = {} 

2456 

2457 

2458class RISCV(Architecture): 

2459 arch = "RISCV" 

2460 mode = "RISCV" 

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

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

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

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

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

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

2467 return_register = "$a0" 

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

2469 syscall_register = "$a7" 

2470 syscall_instructions = ("ecall",) 

2471 nop_insn = b"\x00\x00\x00\x13" 

2472 # RISC-V has no flags registers 

2473 flag_register = None 

2474 flags_table = {} 

2475 

2476 @property 

2477 def instruction_length(self) -> int: 

2478 return 4 

2479 

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

2481 return insn.mnemonic == "call" 

2482 

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

2484 mnemo = insn.mnemonic 

2485 if mnemo == "ret": 

2486 return True 

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

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

2489 return True 

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

2491 return True 

2492 return False 

2493 

2494 @classmethod 

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

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

2497 

2498 @property 

2499 def ptrsize(self) -> int: 

2500 if self._ptrsize is not None: 

2501 return self._ptrsize 

2502 if is_alive(): 

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

2504 return self._ptrsize 

2505 return 4 

2506 

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

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

2509 

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

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

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

2513 if is_32bit(): 

2514 if v & 0x80000000: 

2515 return v - 0x100000000 

2516 elif is_64bit(): 

2517 if v & 0x8000000000000000: 

2518 return v - 0x10000000000000000 

2519 else: 

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

2521 return v 

2522 

2523 mnemo = insn.mnemonic 

2524 condition = mnemo[1:] 

2525 

2526 if condition.endswith("z"): 

2527 # r2 is the zero register if we are comparing to 0 

2528 rs1 = gef.arch.register(insn.operands[0]) 

2529 rs2 = gef.arch.register("$zero") 

2530 condition = condition[:-1] 

2531 elif len(insn.operands) > 2: 

2532 # r2 is populated with the second operand 

2533 rs1 = gef.arch.register(insn.operands[0]) 

2534 rs2 = gef.arch.register(insn.operands[1]) 

2535 else: 

2536 raise OSError(f"RISC-V: Failed to get rs1 and rs2 for instruction: `{insn}`") 

2537 

2538 # If the conditional operation is not unsigned, convert the python long into 

2539 # its two's complement 

2540 if not condition.endswith("u"): 

2541 rs2 = long_to_twos_complement(rs2) 

2542 rs1 = long_to_twos_complement(rs1) 

2543 else: 

2544 condition = condition[:-1] 

2545 

2546 if condition == "eq": 

2547 if rs1 == rs2: taken, reason = True, f"{rs1}={rs2}" 

2548 else: taken, reason = False, f"{rs1}!={rs2}" 

2549 elif condition == "ne": 

2550 if rs1 != rs2: taken, reason = True, f"{rs1}!={rs2}" 

2551 else: taken, reason = False, f"{rs1}={rs2}" 

2552 elif condition == "lt": 

2553 if rs1 < rs2: taken, reason = True, f"{rs1}<{rs2}" 

2554 else: taken, reason = False, f"{rs1}>={rs2}" 

2555 elif condition == "le": 

2556 if rs1 <= rs2: taken, reason = True, f"{rs1}<={rs2}" 

2557 else: taken, reason = False, f"{rs1}>{rs2}" 

2558 elif condition == "ge": 

2559 if rs1 < rs2: taken, reason = True, f"{rs1}>={rs2}" 

2560 else: taken, reason = False, f"{rs1}<{rs2}" 

2561 else: 

2562 raise OSError(f"RISC-V: Conditional instruction `{insn}` not supported yet") 

2563 

2564 return taken, reason 

2565 

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

2567 ra = None 

2568 if self.is_ret(insn): 

2569 ra = gef.arch.register("$ra") 

2570 else: 

2571 older = frame.older() 

2572 if older: 

2573 ra = to_unsigned_long(older.pc()) 

2574 return ra 

2575 

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

2577 # RISC-V has no flags registers, return an empty string to 

2578 # preserve the Architecture API 

2579 return "" 

2580 

2581class ARM(Architecture): 

2582 aliases = ("ARM", Elf.Abi.ARM) 

2583 arch = "ARM" 

2584 all_registers = ("$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", 

2585 "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", 

2586 "$lr", "$pc", "$cpsr",) 

2587 

2588 nop_insn = b"\x00\xf0\x20\xe3" # hint #0 

2589 return_register = "$r0" 

2590 flag_register: str = "$cpsr" 

2591 flags_table = { 

2592 31: "negative", 

2593 30: "zero", 

2594 29: "carry", 

2595 28: "overflow", 

2596 7: "interrupt", 

2597 6: "fast", 

2598 5: "thumb", 

2599 } 

2600 function_parameters = ("$r0", "$r1", "$r2", "$r3") 

2601 syscall_register = "$r7" 

2602 syscall_instructions = ("swi 0x0", "swi NR") 

2603 _endianness = Endianness.LITTLE_ENDIAN 

2604 

2605 def is_thumb(self) -> bool: 

2606 """Determine if the machine is currently in THUMB mode.""" 

2607 return is_alive() and (self.cpsr & (1 << 5) == 1) 

2608 

2609 @property 

2610 def pc(self) -> Optional[int]: 

2611 pc = gef.arch.register("$pc") 

2612 if self.is_thumb(): 

2613 pc += 1 

2614 return pc 

2615 

2616 @property 

2617 def cpsr(self) -> int: 

2618 if not is_alive(): 

2619 raise RuntimeError("Cannot get CPSR, program not started?") 

2620 return gef.arch.register(self.flag_register) 

2621 

2622 @property 

2623 def mode(self) -> str: 

2624 return "THUMB" if self.is_thumb() else "ARM" 

2625 

2626 @property 

2627 def instruction_length(self) -> Optional[int]: 

2628 # Thumb instructions have variable-length (2 or 4-byte) 

2629 return None if self.is_thumb() else 4 

2630 

2631 @property 

2632 def ptrsize(self) -> int: 

2633 return 4 

2634 

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

2636 mnemo = insn.mnemonic 

2637 call_mnemos = {"bl", "blx"} 

2638 return mnemo in call_mnemos 

2639 

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

2641 pop_mnemos = {"pop"} 

2642 branch_mnemos = {"bl", "bx"} 

2643 write_mnemos = {"ldr", "add"} 

2644 if insn.mnemonic in pop_mnemos: 

2645 return insn.operands[-1] == " pc}" 

2646 if insn.mnemonic in branch_mnemos: 

2647 return insn.operands[-1] == "lr" 

2648 if insn.mnemonic in write_mnemos: 

2649 return insn.operands[0] == "pc" 

2650 return False 

2651 

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

2653 # https://www.botskool.com/user-pages/tutorials/electronics/arm-7-tutorial-part-1 

2654 if val is None: 

2655 reg = self.flag_register 

2656 val = gef.arch.register(reg) 

2657 return flags_to_human(val, self.flags_table) 

2658 

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

2660 conditions = {"eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cc", "cs"} 

2661 return insn.mnemonic[-2:] in conditions 

2662 

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

2664 mnemo = insn.mnemonic 

2665 # ref: https://www.davespace.co.uk/arm/introduction-to-arm/conditional.html 

2666 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

2667 val = gef.arch.register(self.flag_register) 

2668 taken, reason = False, "" 

2669 

2670 if mnemo.endswith("eq"): taken, reason = bool(val&(1<<flags["zero"])), "Z" 

2671 elif mnemo.endswith("ne"): taken, reason = not bool(val&(1<<flags["zero"])), "!Z" 

2672 elif mnemo.endswith("lt"): 

2673 taken, reason = bool(val&(1<<flags["negative"])) != bool(val&(1<<flags["overflow"])), "N!=V" 

2674 elif mnemo.endswith("le"): 

2675 taken, reason = bool(val&(1<<flags["zero"])) or \ 

2676 bool(val&(1<<flags["negative"])) != bool(val&(1<<flags["overflow"])), "Z || N!=V" 

2677 elif mnemo.endswith("gt"): 

2678 taken, reason = bool(val&(1<<flags["zero"])) == 0 and \ 

2679 bool(val&(1<<flags["negative"])) == bool(val&(1<<flags["overflow"])), "!Z && N==V" 

2680 elif mnemo.endswith("ge"): 

2681 taken, reason = bool(val&(1<<flags["negative"])) == bool(val&(1<<flags["overflow"])), "N==V" 

2682 elif mnemo.endswith("vs"): taken, reason = bool(val&(1<<flags["overflow"])), "V" 

2683 elif mnemo.endswith("vc"): taken, reason = not val&(1<<flags["overflow"]), "!V" 

2684 elif mnemo.endswith("mi"): 

2685 taken, reason = bool(val&(1<<flags["negative"])), "N" 

2686 elif mnemo.endswith("pl"): 

2687 taken, reason = not val&(1<<flags["negative"]), "N==0" 

2688 elif mnemo.endswith("hi"): 

2689 taken, reason = bool(val&(1<<flags["carry"])) and not bool(val&(1<<flags["zero"])), "C && !Z" 

2690 elif mnemo.endswith("ls"): 

2691 taken, reason = not val&(1<<flags["carry"]) or bool(val&(1<<flags["zero"])), "!C || Z" 

2692 elif mnemo.endswith("cs"): taken, reason = bool(val&(1<<flags["carry"])), "C" 

2693 elif mnemo.endswith("cc"): taken, reason = not val&(1<<flags["carry"]), "!C" 

2694 return taken, reason 

2695 

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

2697 if not self.is_ret(insn): 

2698 older = frame.older() 

2699 if not older: 

2700 return None 

2701 return int(older.pc()) 

2702 

2703 # If it's a pop, we have to peek into the stack, otherwise use lr 

2704 if insn.mnemonic == "pop": 

2705 ra_addr = gef.arch.sp + (len(insn.operands)-1) * self.ptrsize 

2706 if not ra_addr: 

2707 return None 

2708 ra = dereference(ra_addr) 

2709 if ra is None: 

2710 return None 

2711 return to_unsigned_long(ra) 

2712 elif insn.mnemonic == "ldr": 

2713 ra = dereference(gef.arch.sp) 

2714 if ra is None: 

2715 return None 

2716 return to_unsigned_long(ra) 

2717 else: # 'bx lr' or 'add pc, lr, #0' 

2718 return gef.arch.register("$lr") 

2719 

2720 @classmethod 

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

2722 _NR_mprotect = 125 

2723 insns = [ 

2724 "push {r0-r2, r7}", 

2725 f"mov r1, {addr & 0xffff:d}", 

2726 f"mov r0, {(addr & 0xffff0000) >> 16:d}", 

2727 "lsl r0, r0, 16", 

2728 "add r0, r0, r1", 

2729 f"mov r1, {size & 0xffff:d}", 

2730 f"mov r2, {perm.value & 0xff:d}", 

2731 f"mov r7, {_NR_mprotect:d}", 

2732 "svc 0", 

2733 "pop {r0-r2, r7}", 

2734 ] 

2735 return "; ".join(insns) 

2736 

2737 

2738class AARCH64(ARM): 

2739 aliases = ("ARM64", "AARCH64", Elf.Abi.AARCH64) 

2740 arch = "ARM64" 

2741 mode: str = "" 

2742 

2743 all_registers = ( 

2744 "$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7", 

2745 "$x8", "$x9", "$x10", "$x11", "$x12", "$x13", "$x14","$x15", 

2746 "$x16", "$x17", "$x18", "$x19", "$x20", "$x21", "$x22", "$x23", 

2747 "$x24", "$x25", "$x26", "$x27", "$x28", "$x29", "$x30", "$sp", 

2748 "$pc", "$cpsr", "$fpsr", "$fpcr",) 

2749 return_register = "$x0" 

2750 flag_register = "$cpsr" 

2751 flags_table = { 

2752 31: "negative", 

2753 30: "zero", 

2754 29: "carry", 

2755 28: "overflow", 

2756 7: "interrupt", 

2757 9: "endian", 

2758 6: "fast", 

2759 5: "t32", 

2760 4: "m[4]", 

2761 } 

2762 nop_insn = b"\x1f\x20\x03\xd5" # hint #0 

2763 function_parameters = ("$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7",) 

2764 syscall_register = "$x8" 

2765 syscall_instructions = ("svc $x0",) 

2766 

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

2768 mnemo = insn.mnemonic 

2769 call_mnemos = {"bl", "blr"} 

2770 return mnemo in call_mnemos 

2771 

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

2773 # https://events.linuxfoundation.org/sites/events/files/slides/KoreaLinuxForum-2014.pdf 

2774 reg = self.flag_register 

2775 if not val: 

2776 val = gef.arch.register(reg) 

2777 return flags_to_human(val, self.flags_table) 

2778 

2779 def is_aarch32(self) -> bool: 

2780 """Determine if the CPU is currently in AARCH32 mode from runtime.""" 

2781 return (self.cpsr & (1 << 4) != 0) and (self.cpsr & (1 << 5) == 0) 

2782 

2783 def is_thumb32(self) -> bool: 

2784 """Determine if the CPU is currently in THUMB32 mode from runtime.""" 

2785 return (self.cpsr & (1 << 4) == 1) and (self.cpsr & (1 << 5) == 1) 

2786 

2787 @property 

2788 def ptrsize(self) -> int: 

2789 """Determine the size of pointer from the current CPU mode""" 

2790 if not is_alive(): 

2791 return 8 

2792 if self.is_aarch32(): 

2793 return 4 

2794 if self.is_thumb32(): 

2795 return 2 

2796 return 8 

2797 

2798 @classmethod 

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

2800 _NR_mprotect = 226 

2801 insns = [ 

2802 "str x8, [sp, -16]!", 

2803 "str x0, [sp, -16]!", 

2804 "str x1, [sp, -16]!", 

2805 "str x2, [sp, -16]!", 

2806 f"mov x8, {_NR_mprotect:d}", 

2807 f"movz x0, {addr & 0xFFFF:#x}", 

2808 f"movk x0, {(addr >> 16) & 0xFFFF:#x}, lsl 16", 

2809 f"movk x0, {(addr >> 32) & 0xFFFF:#x}, lsl 32", 

2810 f"movk x0, {(addr >> 48) & 0xFFFF:#x}, lsl 48", 

2811 f"movz x1, {size & 0xFFFF:#x}", 

2812 f"movk x1, {(size >> 16) & 0xFFFF:#x}, lsl 16", 

2813 f"mov x2, {perm.value:d}", 

2814 "svc 0", 

2815 "ldr x2, [sp], 16", 

2816 "ldr x1, [sp], 16", 

2817 "ldr x0, [sp], 16", 

2818 "ldr x8, [sp], 16", 

2819 ] 

2820 return "; ".join(insns) 

2821 

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

2823 # https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf 

2824 # sect. 5.1.1 

2825 mnemo = insn.mnemonic 

2826 branch_mnemos = {"cbnz", "cbz", "tbnz", "tbz"} 

2827 return mnemo.startswith("b.") or mnemo in branch_mnemos 

2828 

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

2830 mnemo, operands = insn.mnemonic, insn.operands 

2831 taken, reason = False, "" 

2832 

2833 if mnemo in {"cbnz", "cbz", "tbnz", "tbz"}: 

2834 reg = f"${operands[0]}" 

2835 op = gef.arch.register(reg) 

2836 if mnemo == "cbnz": 

2837 if op!=0: taken, reason = True, f"{reg}!=0" 

2838 else: taken, reason = False, f"{reg}==0" 

2839 elif mnemo == "cbz": 

2840 if op == 0: taken, reason = True, f"{reg}==0" 

2841 else: taken, reason = False, f"{reg}!=0" 

2842 elif mnemo == "tbnz": 

2843 # operands[1] has one or more white spaces in front, then a #, then the number 

2844 # so we need to eliminate them 

2845 i = int(operands[1].strip().lstrip("#")) 

2846 if (op & 1<<i) != 0: taken, reason = True, f"{reg}&1<<{i}!=0" 

2847 else: taken, reason = False, f"{reg}&1<<{i}==0" 

2848 elif mnemo == "tbz": 

2849 # operands[1] has one or more white spaces in front, then a #, then the number 

2850 # so we need to eliminate them 

2851 i = int(operands[1].strip().lstrip("#")) 

2852 if (op & 1<<i) == 0: taken, reason = True, f"{reg}&1<<{i}==0" 

2853 else: taken, reason = False, f"{reg}&1<<{i}!=0" 

2854 

2855 if not reason: 

2856 taken, reason = super().is_branch_taken(insn) 

2857 return taken, reason 

2858 

2859 

2860class X86(Architecture): 

2861 aliases: Tuple[Union[str, Elf.Abi], ...] = ("X86", Elf.Abi.X86_32) 

2862 arch = "X86" 

2863 mode = "32" 

2864 

2865 nop_insn = b"\x90" 

2866 flag_register: str = "$eflags" 

2867 special_registers = ("$cs", "$ss", "$ds", "$es", "$fs", "$gs", ) 

2868 gpr_registers = ("$eax", "$ebx", "$ecx", "$edx", "$esp", "$ebp", "$esi", "$edi", "$eip", ) 

2869 all_registers = gpr_registers + ( flag_register,) + special_registers 

2870 instruction_length = None 

2871 return_register = "$eax" 

2872 function_parameters = ("$esp", ) 

2873 flags_table = { 

2874 6: "zero", 

2875 0: "carry", 

2876 2: "parity", 

2877 4: "adjust", 

2878 7: "sign", 

2879 8: "trap", 

2880 9: "interrupt", 

2881 10: "direction", 

2882 11: "overflow", 

2883 16: "resume", 

2884 17: "virtualx86", 

2885 21: "identification", 

2886 } 

2887 syscall_register = "$eax" 

2888 syscall_instructions = ("sysenter", "int 0x80") 

2889 _ptrsize = 4 

2890 _endianness = Endianness.LITTLE_ENDIAN 

2891 

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

2893 reg = self.flag_register 

2894 if val is None: 

2895 val = gef.arch.register(reg) 

2896 return flags_to_human(val, self.flags_table) 

2897 

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

2899 mnemo = insn.mnemonic 

2900 call_mnemos = {"call", "callq"} 

2901 return mnemo in call_mnemos 

2902 

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

2904 return insn.mnemonic == "ret" 

2905 

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

2907 mnemo = insn.mnemonic 

2908 branch_mnemos = { 

2909 "ja", "jnbe", "jae", "jnb", "jnc", "jb", "jc", "jnae", "jbe", "jna", 

2910 "jcxz", "jecxz", "jrcxz", "je", "jz", "jg", "jnle", "jge", "jnl", 

2911 "jl", "jnge", "jle", "jng", "jne", "jnz", "jno", "jnp", "jpo", "jns", 

2912 "jo", "jp", "jpe", "js" 

2913 } 

2914 return mnemo in branch_mnemos 

2915 

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

2917 mnemo = insn.mnemonic 

2918 # all kudos to fG! (https://github.com/gdbinit/Gdbinit/blob/master/gdbinit#L1654) 

2919 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

2920 val = gef.arch.register(self.flag_register) 

2921 

2922 taken, reason = False, "" 

2923 

2924 if mnemo in ("ja", "jnbe"): 

2925 taken, reason = not val&(1<<flags["carry"]) and not bool(val&(1<<flags["zero"])), "!C && !Z" 

2926 elif mnemo in ("jae", "jnb", "jnc"): 

2927 taken, reason = not val&(1<<flags["carry"]), "!C" 

2928 elif mnemo in ("jb", "jc", "jnae"): 

2929 taken, reason = bool(val&(1<<flags["carry"])) != 0, "C" 

2930 elif mnemo in ("jbe", "jna"): 

2931 taken, reason = bool(val&(1<<flags["carry"])) or bool(val&(1<<flags["zero"])), "C || Z" 

2932 elif mnemo in ("jcxz", "jecxz", "jrcxz"): 

2933 cx = gef.arch.register("$rcx") if is_x86_64() else gef.arch.register("$ecx") 

2934 taken, reason = cx == 0, "!$CX" 

2935 elif mnemo in ("je", "jz"): 

2936 taken, reason = bool(val&(1<<flags["zero"])), "Z" 

2937 elif mnemo in ("jne", "jnz"): 

2938 taken, reason = not bool(val&(1<<flags["zero"])), "!Z" 

2939 elif mnemo in ("jg", "jnle"): 

2940 taken, reason = not bool(val&(1<<flags["zero"])) and bool(val&(1<<flags["overflow"])) == bool(val&(1<<flags["sign"])), "!Z && S==O" 

2941 elif mnemo in ("jge", "jnl"): 

2942 taken, reason = bool(val&(1<<flags["sign"])) == bool(val&(1<<flags["overflow"])), "S==O" 

2943 elif mnemo in ("jl", "jnge"): 

2944 taken, reason = bool(val&(1<<flags["overflow"]) != val&(1<<flags["sign"])), "S!=O" 

2945 elif mnemo in ("jle", "jng"): 

2946 taken, reason = bool(val&(1<<flags["zero"])) or bool(val&(1<<flags["overflow"])) != bool(val&(1<<flags["sign"])), "Z || S!=O" 

2947 elif mnemo in ("jo",): 

2948 taken, reason = bool(val&(1<<flags["overflow"])), "O" 

2949 elif mnemo in ("jno",): 

2950 taken, reason = not val&(1<<flags["overflow"]), "!O" 

2951 elif mnemo in ("jpe", "jp"): 

2952 taken, reason = bool(val&(1<<flags["parity"])), "P" 

2953 elif mnemo in ("jnp", "jpo"): 

2954 taken, reason = not val&(1<<flags["parity"]), "!P" 

2955 elif mnemo in ("js",): 

2956 taken, reason = bool(val&(1<<flags["sign"])) != 0, "S" 

2957 elif mnemo in ("jns",): 

2958 taken, reason = not val&(1<<flags["sign"]), "!S" 

2959 return taken, reason 

2960 

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

2962 ra = None 

2963 if self.is_ret(insn): 2963 ↛ 2966line 2963 didn't jump to line 2966, because the condition on line 2963 was always true

2964 ra = dereference(gef.arch.sp) 

2965 else: 

2966 older = frame.older() 

2967 if older: 

2968 ra = older.pc() 

2969 if ra is None: 2969 ↛ 2970line 2969 didn't jump to line 2970, because the condition on line 2969 was never true

2970 return None 

2971 return to_unsigned_long(ra) 

2972 

2973 @classmethod 

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

2975 _NR_mprotect = 125 

2976 insns = [ 

2977 "pushad", 

2978 "pushfd", 

2979 f"mov eax, {_NR_mprotect:d}", 

2980 f"mov ebx, {addr:d}", 

2981 f"mov ecx, {size:d}", 

2982 f"mov edx, {perm.value:d}", 

2983 "int 0x80", 

2984 "popfd", 

2985 "popad", 

2986 ] 

2987 return "; ".join(insns) 

2988 

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

2990 if in_func: 

2991 i += 1 # Account for RA being at the top of the stack 

2992 sp = gef.arch.sp 

2993 sz = gef.arch.ptrsize 

2994 loc = sp + (i * sz) 

2995 val = gef.memory.read_integer(loc) 

2996 key = f"[sp + {i * sz:#x}]" 

2997 return key, val 

2998 

2999 

3000class X86_64(X86): 

3001 aliases = ("X86_64", Elf.Abi.X86_64, "i386:x86-64") 

3002 arch = "X86" 

3003 mode = "64" 

3004 

3005 gpr_registers = ( 

3006 "$rax", "$rbx", "$rcx", "$rdx", "$rsp", "$rbp", "$rsi", "$rdi", "$rip", 

3007 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", ) 

3008 all_registers = gpr_registers + ( X86.flag_register, ) + X86.special_registers 

3009 return_register = "$rax" 

3010 function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] 

3011 syscall_register = "$rax" 

3012 syscall_instructions = ["syscall"] 

3013 # We don't want to inherit x86's stack based param getter 

3014 get_ith_parameter = Architecture.get_ith_parameter 

3015 _ptrsize = 8 

3016 

3017 @classmethod 

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

3019 _NR_mprotect = 10 

3020 insns = [ 

3021 "pushfq", 

3022 "push rax", 

3023 "push rdi", 

3024 "push rsi", 

3025 "push rdx", 

3026 "push rcx", 

3027 "push r11", 

3028 f"mov rax, {_NR_mprotect:d}", 

3029 f"mov rdi, {addr:d}", 

3030 f"mov rsi, {size:d}", 

3031 f"mov rdx, {perm.value:d}", 

3032 "syscall", 

3033 "pop r11", 

3034 "pop rcx", 

3035 "pop rdx", 

3036 "pop rsi", 

3037 "pop rdi", 

3038 "pop rax", 

3039 "popfq", 

3040 ] 

3041 return "; ".join(insns) 

3042 

3043 def canary_address(self) -> int: 

3044 return self.register("fs_base") + 0x28 

3045 

3046class PowerPC(Architecture): 

3047 aliases = ("PowerPC", Elf.Abi.POWERPC, "PPC") 

3048 arch = "PPC" 

3049 mode = "PPC32" 

3050 

3051 all_registers = ( 

3052 "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", 

3053 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", 

3054 "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", 

3055 "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", 

3056 "$pc", "$msr", "$cr", "$lr", "$ctr", "$xer", "$trap",) 

3057 instruction_length = 4 

3058 nop_insn = b"\x60\x00\x00\x00" # https://developer.ibm.com/articles/l-ppc/ 

3059 return_register = "$r0" 

3060 flag_register: str = "$cr" 

3061 flags_table = { 

3062 3: "negative[0]", 

3063 2: "positive[0]", 

3064 1: "equal[0]", 

3065 0: "overflow[0]", 

3066 # cr7 

3067 31: "less[7]", 

3068 30: "greater[7]", 

3069 29: "equal[7]", 

3070 28: "overflow[7]", 

3071 } 

3072 function_parameters = ("$i0", "$i1", "$i2", "$i3", "$i4", "$i5") 

3073 syscall_register = "$r0" 

3074 syscall_instructions = ("sc",) 

3075 _ptrsize = 4 

3076 

3077 

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

3079 # https://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3) 

3080 if val is None: 

3081 reg = self.flag_register 

3082 val = gef.arch.register(reg) 

3083 return flags_to_human(val, self.flags_table) 

3084 

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

3086 return False 

3087 

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

3089 return insn.mnemonic == "blr" 

3090 

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

3092 mnemo = insn.mnemonic 

3093 branch_mnemos = {"beq", "bne", "ble", "blt", "bgt", "bge"} 

3094 return mnemo in branch_mnemos 

3095 

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

3097 mnemo = insn.mnemonic 

3098 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

3099 val = gef.arch.register(self.flag_register) 

3100 taken, reason = False, "" 

3101 if mnemo == "beq": taken, reason = bool(val&(1<<flags["equal[7]"])), "E" 

3102 elif mnemo == "bne": taken, reason = val&(1<<flags["equal[7]"]) == 0, "!E" 

3103 elif mnemo == "ble": taken, reason = bool(val&(1<<flags["equal[7]"])) or bool(val&(1<<flags["less[7]"])), "E || L" 

3104 elif mnemo == "blt": taken, reason = bool(val&(1<<flags["less[7]"])), "L" 

3105 elif mnemo == "bge": taken, reason = bool(val&(1<<flags["equal[7]"])) or bool(val&(1<<flags["greater[7]"])), "E || G" 

3106 elif mnemo == "bgt": taken, reason = bool(val&(1<<flags["greater[7]"])), "G" 

3107 return taken, reason 

3108 

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

3110 ra = None 

3111 if self.is_ret(insn): 

3112 ra = gef.arch.register("$lr") 

3113 else: 

3114 older = frame.older() 

3115 if older: 

3116 ra = to_unsigned_long(older.pc()) 

3117 return ra 

3118 

3119 @classmethod 

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

3121 # Ref: https://developer.ibm.com/articles/l-ppc/ 

3122 _NR_mprotect = 125 

3123 insns = [ 

3124 "addi 1, 1, -16", # 1 = r1 = sp 

3125 "stw 0, 0(1)", 

3126 "stw 3, 4(1)", # r0 = syscall_code | r3, r4, r5 = args 

3127 "stw 4, 8(1)", 

3128 "stw 5, 12(1)", 

3129 f"li 0, {_NR_mprotect:d}", 

3130 f"lis 3, {addr:#x}@h", 

3131 f"ori 3, 3, {addr:#x}@l", 

3132 f"lis 4, {size:#x}@h", 

3133 f"ori 4, 4, {size:#x}@l", 

3134 f"li 5, {perm.value:d}", 

3135 "sc", 

3136 "lwz 0, 0(1)", 

3137 "lwz 3, 4(1)", 

3138 "lwz 4, 8(1)", 

3139 "lwz 5, 12(1)", 

3140 "addi 1, 1, 16", 

3141 ] 

3142 return ";".join(insns) 

3143 

3144 

3145class PowerPC64(PowerPC): 

3146 aliases = ("PowerPC64", Elf.Abi.POWERPC64, "PPC64") 

3147 arch = "PPC" 

3148 mode = "PPC64" 

3149 _ptrsize = 8 

3150 

3151 

3152class SPARC(Architecture): 

3153 """ Refs: 

3154 - https://www.cse.scu.edu/~atkinson/teaching/sp05/259/sparc.pdf 

3155 """ 

3156 aliases = ("SPARC", Elf.Abi.SPARC) 

3157 arch = "SPARC" 

3158 mode = "" 

3159 

3160 all_registers = ( 

3161 "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", 

3162 "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7", 

3163 "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", 

3164 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i7", 

3165 "$pc", "$npc", "$sp ", "$fp ", "$psr",) 

3166 instruction_length = 4 

3167 nop_insn = b"\x00\x00\x00\x00" # sethi 0, %g0 

3168 return_register = "$i0" 

3169 flag_register: str = "$psr" 

3170 flags_table = { 

3171 23: "negative", 

3172 22: "zero", 

3173 21: "overflow", 

3174 20: "carry", 

3175 7: "supervisor", 

3176 5: "trap", 

3177 } 

3178 function_parameters = ("$o0 ", "$o1 ", "$o2 ", "$o3 ", "$o4 ", "$o5 ", "$o7 ",) 

3179 syscall_register = "%g1" 

3180 syscall_instructions = ("t 0x10",) 

3181 

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

3183 # https://www.gaisler.com/doc/sparcv8.pdf 

3184 reg = self.flag_register 

3185 if val is None: 

3186 val = gef.arch.register(reg) 

3187 return flags_to_human(val, self.flags_table) 

3188 

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

3190 return False 

3191 

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

3193 return insn.mnemonic == "ret" 

3194 

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

3196 mnemo = insn.mnemonic 

3197 # http://moss.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/condbranch.html 

3198 branch_mnemos = { 

3199 "be", "bne", "bg", "bge", "bgeu", "bgu", "bl", "ble", "blu", "bleu", 

3200 "bneg", "bpos", "bvs", "bvc", "bcs", "bcc" 

3201 } 

3202 return mnemo in branch_mnemos 

3203 

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

3205 mnemo = insn.mnemonic 

3206 flags = dict((self.flags_table[k], k) for k in self.flags_table) 

3207 val = gef.arch.register(self.flag_register) 

3208 taken, reason = False, "" 

3209 

3210 if mnemo == "be": taken, reason = bool(val&(1<<flags["zero"])), "Z" 

3211 elif mnemo == "bne": taken, reason = bool(val&(1<<flags["zero"])) == 0, "!Z" 

3212 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)" 

3213 elif mnemo == "bge": taken, reason = val&(1<<flags["negative"]) == 0 or val&(1<<flags["overflow"]) == 0, "!N || !O" 

3214 elif mnemo == "bgu": taken, reason = val&(1<<flags["carry"]) == 0 and val&(1<<flags["zero"]) == 0, "!C && !Z" 

3215 elif mnemo == "bgeu": taken, reason = val&(1<<flags["carry"]) == 0, "!C" 

3216 elif mnemo == "bl": taken, reason = bool(val&(1<<flags["negative"])) and bool(val&(1<<flags["overflow"])), "N && O" 

3217 elif mnemo == "blu": taken, reason = bool(val&(1<<flags["carry"])), "C" 

3218 elif mnemo == "ble": taken, reason = bool(val&(1<<flags["zero"])) or bool(val&(1<<flags["negative"]) or val&(1<<flags["overflow"])), "Z || (N || O)" 

3219 elif mnemo == "bleu": taken, reason = bool(val&(1<<flags["carry"])) or bool(val&(1<<flags["zero"])), "C || Z" 

3220 elif mnemo == "bneg": taken, reason = bool(val&(1<<flags["negative"])), "N" 

3221 elif mnemo == "bpos": taken, reason = val&(1<<flags["negative"]) == 0, "!N" 

3222 elif mnemo == "bvs": taken, reason = bool(val&(1<<flags["overflow"])), "O" 

3223 elif mnemo == "bvc": taken, reason = val&(1<<flags["overflow"]) == 0, "!O" 

3224 elif mnemo == "bcs": taken, reason = bool(val&(1<<flags["carry"])), "C" 

3225 elif mnemo == "bcc": taken, reason = val&(1<<flags["carry"]) == 0, "!C" 

3226 return taken, reason 

3227 

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

3229 ra = None 

3230 if self.is_ret(insn): 

3231 ra = gef.arch.register("$o7") 

3232 else: 

3233 older = frame.older() 

3234 if older: 

3235 ra = to_unsigned_long(older.pc()) 

3236 return ra 

3237 

3238 @classmethod 

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

3240 hi = (addr & 0xffff0000) >> 16 

3241 lo = (addr & 0x0000ffff) 

3242 _NR_mprotect = 125 

3243 insns = ["add %sp, -16, %sp", 

3244 "st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]", 

3245 "st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]", 

3246 f"sethi %hi({hi}), %o0", 

3247 f"or %o0, {lo}, %o0", 

3248 "clr %o1", 

3249 "clr %o2", 

3250 f"mov {_NR_mprotect}, %g1", 

3251 "t 0x10", 

3252 "ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0", 

3253 "ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2", 

3254 "add %sp, 16, %sp",] 

3255 return "; ".join(insns) 

3256 

3257 

3258class SPARC64(SPARC): 

3259 """Refs: 

3260 - http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_sparc.pdf 

3261 - https://cr.yp.to/2005-590/sparcv9.pdf 

3262 """ 

3263 aliases = ("SPARC64", Elf.Abi.SPARC64) 

3264 arch = "SPARC" 

3265 mode = "V9" 

3266 

3267 all_registers = [ 

3268 "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", 

3269 "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7", 

3270 "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", 

3271 "$i0", "$i1", "$i2", "$i3", "$i4"