Repository: hugsy/gef
Branch: main
Commit: 0c95800c3ad3
Files: 205
Total size: 3.4 MB
Directory structure:
gitextract_tg8j9h13/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yaml
│ │ └── feature_request.yaml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── stale.yml
│ ├── tests/
│ │ ├── entrypoint.sh
│ │ ├── run-tests.sh
│ │ └── setup-dockerfile.sh
│ └── workflows/
│ ├── coverage.yml
│ ├── extended-tests.yml
│ ├── generate-docs.yml
│ ├── notify.yml
│ ├── tests.yml
│ └── validate.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .pylintrc
├── .python-version
├── .readthedocs.yml
├── LICENSE
├── README.md
├── docs/
│ ├── .markdownlint.yaml
│ ├── api/
│ │ └── gef.md
│ ├── api.md
│ ├── commands/
│ │ ├── aliases.md
│ │ ├── arch.md
│ │ ├── aslr.md
│ │ ├── canary.md
│ │ ├── checksec.md
│ │ ├── config.md
│ │ ├── context.md
│ │ ├── dereference.md
│ │ ├── edit-flags.md
│ │ ├── elf-info.md
│ │ ├── entry-break.md
│ │ ├── eval.md
│ │ ├── format-string-helper.md
│ │ ├── functions.md
│ │ ├── gef-remote.md
│ │ ├── gef.md
│ │ ├── got.md
│ │ ├── heap-analysis-helper.md
│ │ ├── heap.md
│ │ ├── help.md
│ │ ├── hexdump.md
│ │ ├── highlight.md
│ │ ├── hijack-fd.md
│ │ ├── memory.md
│ │ ├── name-break.md
│ │ ├── nop.md
│ │ ├── patch.md
│ │ ├── pattern.md
│ │ ├── pcustom.md
│ │ ├── pie.md
│ │ ├── print-format.md
│ │ ├── process-search.md
│ │ ├── process-status.md
│ │ ├── registers.md
│ │ ├── reset-cache.md
│ │ ├── scan.md
│ │ ├── search-pattern.md
│ │ ├── shellcode.md
│ │ ├── skipi.md
│ │ ├── stepover.md
│ │ ├── stub.md
│ │ ├── theme.md
│ │ ├── tmux-setup.md
│ │ ├── trace-run.md
│ │ ├── version.md
│ │ ├── vmmap.md
│ │ ├── xfiles.md
│ │ ├── xinfo.md
│ │ └── xor-memory.md
│ ├── compat.md
│ ├── config.md
│ ├── debugging.md
│ ├── deprecated.md
│ ├── faq.md
│ ├── functions/
│ │ ├── base.md
│ │ ├── bss.md
│ │ ├── got.md
│ │ ├── heap.md
│ │ └── stack.md
│ ├── index.md
│ ├── install.md
│ ├── obsolete/
│ │ ├── docs/
│ │ │ └── index.md
│ │ ├── mkdocs.yml
│ │ └── requirements.txt
│ ├── requirements.txt
│ ├── screenshots.md
│ └── testing.md
├── gef.py
├── get-pip.py
├── mkdocs.yml
├── ruff.toml
├── scripts/
│ ├── gef-extras.sh
│ ├── gef.sh
│ ├── generate-api-docs.sh
│ ├── generate-coverage-docs.sh
│ ├── generate-settings-docs.sh
│ ├── new-release.py
│ ├── remote_debug.py
│ └── vscode_debug.py
└── tests/
├── __init__.py
├── api/
│ ├── __init__.py
│ ├── deprecated.py
│ ├── gef_arch.py
│ ├── gef_disassemble.py
│ ├── gef_heap.py
│ ├── gef_memory.py
│ ├── gef_session.py
│ └── misc.py
├── base.py
├── binaries/
│ ├── Makefile
│ ├── bss.c
│ ├── canary.c
│ ├── checksec-no-canary.c
│ ├── checksec-no-nx.c
│ ├── checksec-no-pie.c
│ ├── class.cpp
│ ├── collision.c
│ ├── default.c
│ ├── format-string-helper.c
│ ├── heap-analysis.c
│ ├── heap-bins.c
│ ├── heap-fastbins.c
│ ├── heap-multiple-heaps.c
│ ├── heap-non-main.c
│ ├── heap-tcache.c
│ ├── heap.c
│ ├── memwatch.c
│ ├── mmap-known-address.c
│ ├── nested.c
│ ├── nested2.c
│ ├── pattern.c
│ ├── pcustom.c
│ └── utils.h
├── commands/
│ ├── __init__.py
│ ├── aliases.py
│ ├── arch.py
│ ├── aslr.py
│ ├── canary.py
│ ├── checksec.py
│ ├── context.py
│ ├── dereference.py
│ ├── edit_flags.py
│ ├── elf_info.py
│ ├── entry_break.py
│ ├── format_string_helper.py
│ ├── functions.py
│ ├── gef.py
│ ├── gef_remote.py
│ ├── got.py
│ ├── heap.py
│ ├── heap_analysis.py
│ ├── hexdump.py
│ ├── highlight.py
│ ├── hijack_fd.py
│ ├── memory.py
│ ├── name_break.py
│ ├── nop.py
│ ├── patch.py
│ ├── pattern.py
│ ├── pcustom.py
│ ├── pie.py
│ ├── print_format.py
│ ├── process_search.py
│ ├── process_status.py
│ ├── registers.py
│ ├── reset_cache.py
│ ├── scan.py
│ ├── search_pattern.py
│ ├── shellcode.py
│ ├── skipi.py
│ ├── smart_eval.py
│ ├── stepover.py
│ ├── stub.py
│ ├── theme.py
│ ├── trace_run.py
│ ├── version.py
│ ├── vmmap.py
│ ├── xfiles.py
│ ├── xinfo.py
│ └── xor_memory.py
├── config/
│ └── __init__.py
├── extended/
│ ├── archlinux.sh
│ ├── debian.sh
│ ├── fedora.sh
│ └── run_pytest.sh
├── functions/
│ ├── __init__.py
│ └── elf_sections.py
├── perf/
│ ├── __init__.py
│ └── benchmark.py
├── pytest.ini
├── regressions/
│ ├── __init__.py
│ ├── filename_collision_lookup.py
│ ├── gdbserver_connection.py
│ └── registers_register_order.py
├── requirements.txt
├── scripts/
│ ├── __init__.py
│ └── test_gef.py
└── utils.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
[Makefile]
indent_style = tab
[*.yml]
indent_style = space
indent_size = 2
[*.md]
max_line_length = 100
================================================
FILE: .gitattributes
================================================
# https://help.github.com/articles/dealing-with-line-endings/
* text eol=lf
*.png diff=none binary
*.jpg diff=none binary
*.jpeg diff=none binary
*.gif diff=none binary
*.svg diff=none binary
*.webp diff=none binary
================================================
FILE: .github/CONTRIBUTING.md
================================================
## Contributing to GEF
## License
`gef` is placed under [MIT license](https://github.com/hugsy/gef/blob/main/LICENSE) which provides
Open-Source access to the code and its use.
By contributing to `gef` code through the _Pull Requests_ mechanism, you accept to release the code
written by you under the said license.
## Submitting a Patch
1. Fork `gef` repository (requires GitHub account). Sending a patch from the
`patch` or `git diff --patch` commands is not accepted.
1. All the packages required for testing and documenting are listed in `tests/requirements.txt`
1. Adjust your development environment to GEF's: this is achieved using
[`pre-commit`](https://pre-commit.com/), and getting setup is simply done by
1. Installing `pre-commit` PIP package (part of the `requirements.txt` file)
1. Setup `pre-commit` : `pre-commit install`
1. Write the changes in your local repo making sure to respect the coding style (same indentation
format, explicit names as possible), comment your code sufficiently so it becomes maintainable by
someone other than you. Finally if you add a new feature/GDB command, also write the adequate
documentation (in [`docs/`](docs/))
1. Submit a pull request
1. The contributors will review your patch. If it is approved, the change will
be merged via the GitHub, and you will be seen as contributors. If it needs
additional work, the repo owner will respond with useful comments.
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [hugsy,]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: Bug Report
description: File a bug report.
title: "[Bug] "
labels: ["bug", "triage"]
body:
- type: markdown
id: md_welcome
attributes:
value: |
Complete this form is for reporting bugs in GEF. Incomplete/invalid report will simply being ignored or closed.
Also note that this is not the place to ask installation or usage problem. Use the Discord channel for that.
- type: textarea
attributes:
label: GEF+GDB version
render: shell
description: |
The exact version of GEF and GDB. Copy/paste the output of the `version` command.
validations:
required: true
- type: input
id: os_info
attributes:
label: Operating System
description: What OS are you using? Copy/paste the output of `lsb_release -a` or equivalent/
placeholder: "e.g., Arch Linux, FreeBSD"
validations:
required: false
- type: textarea
attributes:
label: Describe the issue you encountered
description: |
Describe the issue in detail and what you were doing beforehand.
validations:
required: true
- type: dropdown
attributes:
label: Do you read the docs and look at previously closed issues/PRs for similar cases?
multiple: false
options:
- "No"
- "Yes"
validations:
required: true
- type: checkboxes
attributes:
label: Architecture impacted
description: |
On which architecture can you confirm this bug exist?
options:
- label: X86
- label: X64
- label: ARM
- label: ARM64
- label: MIPS
- label: MIPS64
- label: PPC
- label: PPC64
- label: RISCV
- type: textarea
attributes:
label: Describe your issue. Without a proper reproduction step-by-step, your issue will be ignored.
value: |
Provide a step-by-step to reproduce your issue.
validations:
required: true
- type: textarea
attributes:
label: Minimalist test case
value: |
Use this field for a minimal code to compile and spot the issue:
```c
// compile with gcc -fPIE -pic -o my_issue.out my_issue.c
int main(){ return 0; }
```
You can also provide a Dockerfile if you prefer
validations:
required: false
- type: textarea
attributes:
label: Additional context?
description: |
If a bug/crash is found, use `gef config gef.debug 1` to enable the debug mode and collect more information.
You can copy/paste those information in the field.
value: |
- Screenshots
- Callstack
- Coredumps
- If possible and useful, please upload the binary
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yaml
================================================
name: Feature Request
description: Use this form to suggest a feature you would like to see in GEF
body:
- type: markdown
id: md_welcome
attributes:
value: |
Had an idea of a new useful feature for GEF, but can't implement it? Here's your chance
- type: dropdown
attributes:
label: Type of feature request
description: Describe the category your feature request falls under
options:
- Additional API
- New architecture support/Existing architecture improvement
- Misc
- type: input
attributes:
label: Misc
description: "If \"Misc\" was selected above, describe the type"
validations:
required: false
- type: textarea
attributes:
label: Summary Description
description: |
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Implementation idea/suggestion
description: |
A clear and concise description of how you would like this problem be solved.
validations:
required: true
- type: textarea
attributes:
label: Existing alternatives?
description: |
Describe alternatives already considered.
validations:
required: false
- type: textarea
attributes:
label: Additional information
description: |
Add any other context or screenshots about the feature request here.
validations:
required: false
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Description
- [ ] My code follows the code style of this project.
- [ ] My change includes a change to the documentation, if required.
- [ ] If my change adds new code, [adequate tests](docs/testing.md) have been added.
- [ ] I have read and agree to the **CONTRIBUTING** document.
================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 30
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. You can
reopen it by adding a comment to this issue.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed because it has not had recent activity.
If you are the owner of this issue, you can either re-open it and provide a more
complete description; or create a new issue.
Thank you for your contributions.
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true
================================================
FILE: .github/tests/entrypoint.sh
================================================
#!/bin/sh
set -x
PY_VER=$(gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit 2>/dev/null || echo "3")
GEF_CI_NB_CPU=$(grep -c ^processor /proc/cpuinfo)
# Setup GEF
echo "source /gef/gef.py" > /root/.gdbinit
# Verify GEF setup
gdb -q -ex "gef missing" -ex "gef help" -ex "gef config" -ex start -ex continue -ex quit /bin/pwd
# Build test binaries
make -C tests/binaries -j ${GEF_CI_NB_CPU}
# Run pytest
python${PY_VER} -m pytest --forked -n ${GEF_CI_NB_CPU} -v -m "not benchmark" tests/
================================================
FILE: .github/tests/run-tests.sh
================================================
#!/bin/sh
[ -z "${1}" ] && echo "Provide a container tag as an argument to this script" && exit 1
docker run --privileged --rm -e GITHUB_ACTIONS -v "$PWD:/gef" gef-test:${1}
================================================
FILE: .github/tests/setup-dockerfile.sh
================================================
#!/bin/sh
[ -z "${1}" ] && echo "Provide a container image as an argument to this script" && exit 1
echo "FROM ${1}" > Dockerfile
cat >> Dockerfile << 'EOF'
# Install dependencies for Ubuntu-based images
RUN if [ -f /etc/debian_version ]; then \
export DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=n && \
apt-get update && \
apt-get install -y gdb-multiarch python3-dev python3-pip python3-wheel python3-setuptools \
git cmake gcc g++ pkg-config libglib2.0-dev gdbserver qemu-user file; \
fi
# Install python3-full for Ubuntu 24.04
RUN if grep -q "24.04" /etc/os-release 2>/dev/null; then \
apt-get install -y python3-full; \
fi
# Install dependencies for Fedora-based images
RUN if [ -f /etc/fedora-release ]; then \
dnf install -y gdb gdb-gdbserver python3-devel python3-pip python3-wheel python3-setuptools python3-rpm \
git cmake gcc gcc-c++ pkg-config glib2-devel qemu-user qemu-user-static file procps-ng && \
dnf --enablerepo='*debug*' install -y glibc-debuginfo && \
dnf clean all; \
fi
# Copy only requirements.txt for caching
COPY tests/requirements.txt /tmp/requirements.txt
# Install Python requirements
RUN PY_VER=$(gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit 2>/dev/null || echo "3") && \
if grep -q "24.04" /etc/os-release 2>/dev/null; then \
python${PY_VER} -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt; \
else \
python${PY_VER} -m pip install --upgrade -r /tmp/requirements.txt; \
fi
RUN git config --global --add safe.directory /gef
WORKDIR /gef
# Copy entrypoint script
COPY .github/tests/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
EOF
================================================
FILE: .github/workflows/coverage.yml
================================================
name: Coverage
on:
pull_request_target:
types:
- opened
- synchronize
pull_request:
types:
- opened
- synchronize
jobs:
coverage:
env:
PY_VER: ''
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Setup
run: |
export NEEDRESTART_MODE=n
sudo apt-get update -qq
sudo apt-get install -qq -y gdb-multiarch python3-dev python3-pip python3-wheel python3-setuptools git cmake gcc g++ pkg-config libglib2.0-dev gdbserver qemu-user curl
sudo apt-get install -y python3-full
- name: Run coverage
id: get_coverage
env:
ALLOWED_MARGIN: 0.01
MIN_COVERAGE: 70
run: |
echo PY_VER=`gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit` >> $GITHUB_ENV
echo GEF_CI_NB_CPU=`grep -c ^processor /proc/cpuinfo` >> $GITHUB_ENV
echo GEF_CI_ARCH=`uname --processor` >> $GITHUB_ENV
python${{ env.PY_VER }} -m pip install --user --upgrade -r tests/requirements.txt --quiet
current_score=$(curl --silent https://hugsy.github.io/gef/coverage/gef_py.html | grep pc_cov | sed 's?.*\([^%]*\)%?\1?g')
bash scripts/generate-coverage-docs.sh
new_score=$(cat docs/coverage/gef_py.html | grep pc_cov | sed 's?.*\([^%]*\)%?\1?g')
score_diff=$(python -c "print(f'{${new_score} - ${current_score}:.04f}')")
echo "new_score=${new_score}" >> $GITHUB_OUTPUT
echo "current_score=${current_score}" >> $GITHUB_OUTPUT
echo "score_diff=${score_diff}" >> $GITHUB_OUTPUT
- name: Post results
uses: actions/github-script@v8.0.0
with:
script: |
const old_score = ${{ steps.get_coverage.outputs.current_score }};
const new_score = ${{ steps.get_coverage.outputs.new_score }};
const score_diff = ${{ steps.get_coverage.outputs.score_diff }};
const comment = `## 🤖 Coverage update for ${{ github.event.pull_request.head.sha }} ${(score_diff >= 0) ? "🟢" : "🔴"}
| | Old | New |
|--------|-----|-----|
| Commit | ${{ github.event.pull_request.base.sha }} | ${{ github.event.pull_request.head.sha }} |
| Score | ${old_score}% | ${new_score}% (${score_diff}) |
`;
try {
const { owner, repo, number } = context.issue;
await github.rest.issues.createComment({ owner, repo, issue_number: number, body: comment });
} catch (err) { console.log(err); }
================================================
FILE: .github/workflows/extended-tests.yml
================================================
name: Extended Tests
on:
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
variants:
- { os: fedora, version: 41 }
- { os: debian, version: bookworm }
- { os: archlinux, version: base }
name: "Tests on ${{ matrix.variants }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
- run: |
docker run -v ${PWD}:/gef ${{ matrix.variants.os }}:${{ matrix.variants.version }} "bash /gef/tests/extended/${{ matrix.variants.os }}.sh"
================================================
FILE: .github/workflows/generate-docs.yml
================================================
name: Generate GithubPages
on:
workflow_dispatch:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
if: github.event.repository.fork == false
steps:
- uses: actions/checkout@v6.0.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install pre-requisite
run: |
sudo apt update
sudo apt install gdb-multiarch python3 python3-dev python3-wheel python3-setuptools git cmake gcc g++ pkg-config libglib2.0-dev gdbserver qemu-user -y
version=$(gdb -q -nx -ex 'pi print(f"{sys.version_info.major}.{sys.version_info.minor}", end="")' -ex quit)
python${version} -m pip install --requirement docs/requirements.txt --upgrade
python${version} -m pip install --requirement tests/requirements.txt --upgrade
- name: Regenerate GEF API file
run: |
echo "source $(pwd)/gef.py" > ~/.gdbinit
bash scripts/generate-api-docs.sh
bash scripts/generate-settings-docs.sh
bash scripts/generate-coverage-docs.sh
- name: Build and publish the docs
run: |
git config --global user.name "hugsy"
git config --global user.email "hugsy@users.noreply.github.com"
mkdocs gh-deploy --force
================================================
FILE: .github/workflows/notify.yml
================================================
name: "Notifications"
on:
issues:
types:
- opened
- reopened
push:
branches:
- main
pull_request:
types:
- opened
- closed
branches:
- main
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
jobs:
discord:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Notify/Push
if: github.event_name == 'push' && github.repository_owner == 'hugsy'
uses: sarisia/actions-status-discord@v1.12
with:
nodetail: true
title: "[${{ github.repository }}] ${{ github.actor }} pushed to `${{ github.ref_name }}`"
description: |
**Commits**:
● ${{ join(github.event.commits.*.message, '
● ') }}
---
[Open Diff View](${{ github.event.compare }})
color: 0x00ff00
username: ${{ github.actor }} via GithubBot
avatar_url: ${{ github.actor.avatar_url }}
- name: Notify/Pull Request
if: github.event_name == 'pull_request'
uses: sarisia/actions-status-discord@v1.12
with:
nodetail: true
title: "[${{ github.repository }}] ${{ github.actor }} ${{ github.event.action }} PR #${{ github.event.pull_request.number }}"
description: |
**Title**: ${{ github.event.pull_request.title }}
---
[Goto PR](${{ github.event.pull_request.html_url }})
color: 0x0000ff
username: ${{ github.actor }} via GithubBot
avatar_url: ${{ github.actor.avatar_url }}
- name: Notify/Issue
if: github.event_name == 'issues' && github.repository_owner == 'hugsy'
uses: sarisia/actions-status-discord@v1.12
with:
nodetail: true
title: "[${{ github.repository }}] ${{ github.actor }} ${{ github.event.action}} issue #${{ github.event.issue.number }}"
description: |
**Title**: ${{ github.event.issue.title }}
---
[Goto issue](${{ github.event.issue.html_url }})
color: 0xff0000
username: ${{ github.actor }} via GithubBot
avatar_url: ${{ github.actor.avatar_url }}
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
env:
GEF_CI_NB_CPU: 1
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
strategy:
fail-fast: false
matrix:
arch: [amd64, arm64]
os: [ubuntu-24.04, ubuntu-22.04, fedora-42, fedora-43, fedora-rawhide]
include:
- os: ubuntu-24.04
base_image: ubuntu:24.04
- os: ubuntu-22.04
base_image: ubuntu:22.04
- os: fedora-42
base_image: fedora:42
- os: fedora-43
base_image: fedora:43
- os: fedora-rawhide
base_image: fedora:rawhide
name: "Run Unit tests on ${{ matrix.os }} (${{ matrix.arch }})"
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create Dockerfile
run: .github/tests/setup-dockerfile.sh ${{ matrix.base_image }}
- name: Build container image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: gef-test:${{ matrix.os }}-${{ matrix.arch }}
cache-from: type=gha,scope=${{ matrix.os }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.os }}-${{ matrix.arch }}
- name: Run tests in container
run: .github/tests/run-tests.sh ${{ matrix.os }}-${{ matrix.arch }}
================================================
FILE: .github/workflows/validate.yml
================================================
name: Validation
on:
pull_request:
branches:
- main
jobs:
pre_commit:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/setup-python@v6.2.0
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.1
docs_link_check:
name: Check URLs in docs
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: checkout
uses: actions/checkout@v6.0.1
- name: Restore lychee cache
id: restore-cache
uses: actions/cache/restore@v4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Check links
uses: lycheeverse/lychee-action@v2.7.0
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
with:
args: --exclude-mail --accept=401 --no-progress --exclude 'https://cs.github.com/hugsy/gef\?q=.*' '**/*.md'
fail: false
workingDirectory: docs/
- name: Save lychee cache
uses: actions/cache/save@v4
if: always()
with:
path: .lycheecache
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
================================================
FILE: .gitignore
================================================
*.out
*.pyc
TAGS
__pycache__
tests/*.pyc
tests/pylint.html
tests/pylint.txt
tests/pylint3.html
.vscode
_build
debug.log
.pytest_cache
.coverage
htmlcov
.benchmarks
site/
untracked/
================================================
FILE: .pre-commit-config.yaml
================================================
default_stages: [pre-commit, pre-push]
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v3.0.0a6
hooks:
- id: pylint
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.35.0
hooks:
- id: markdownlint-docker
args:
- --config=docs/.markdownlint.yaml
- --ignore=docs/api/gef.md
- --ignore=docs/obsolete/docs/index.md
- --ignore=docs/index.md
- --ignore=README.md
- "docs/**/*.md"
================================================
FILE: .pylintrc
================================================
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-allow-list=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
# Specify a score threshold to be exceeded before program exits with error.
fail-under=10.0
# Files or directories to be skipped. They should be base names, not paths.
ignore=CVS
# Add files or directories matching the regex patterns to the ignore-list. The
# regex matches against paths and can be in Posix or Windows format.
ignore-paths=
# Files or directories matching the regex patterns are skipped. The regex
# matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=0
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.10
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
; disable=invalid-name,
; disallowed-name,
; empty-docstring,
; missing-module-docstring,
; missing-class-docstring,
; missing-function-docstring,
; unidiomatic-typecheck,
; non-ascii-name,
; consider-using-enumerate,
; consider-iterating-dictionary,
; bad-classmethod-argument,
; bad-mcs-method-argument,
; bad-mcs-classmethod-argument,
; single-string-used-for-slots,
; consider-using-dict-items,
; use-maxsplit-arg,
; use-sequence-for-iteration,
; too-many-lines,
; missing-final-newline,
; trailing-newlines,
; superfluous-parens,
; mixed-line-endings,
; unexpected-line-ending-format,
; wrong-spelling-in-comment,
; wrong-spelling-in-docstring,
; invalid-characters-in-docstring,
; multiple-imports,
; wrong-import-order,
; ungrouped-imports,
; wrong-import-position,
; useless-import-alias,
; import-outside-toplevel,
; use-implicit-booleaness-not-len,
; use-implicit-booleaness-not-comparison,
; raw-checker-failed,
; bad-inline-option,
; locally-disabled,
; file-ignored,
; suppressed-message,
; useless-suppression,
; deprecated-pragma,
; use-symbolic-message-instead,
; c-extension-no-member,
; literal-comparison,
; comparison-with-itself,
; no-self-use,
; no-classmethod-decorator,
; no-staticmethod-decorator,
; useless-object-inheritance,
; property-with-parameters,
; cyclic-import,
; consider-using-from-import,
; duplicate-code,
; too-many-ancestors,
; too-many-instance-attributes,
; too-few-public-methods,
; too-many-public-methods,
; too-many-return-statements,
; too-many-branches,
; too-many-arguments,
; too-many-locals,
; too-many-statements,
; too-many-boolean-expressions,
; consider-merging-isinstance,
; too-many-nested-blocks,
; simplifiable-if-statement,
; redefined-argument-from-local,
; no-else-return,
; consider-using-ternary,
; trailing-comma-tuple,
; stop-iteration-return,
; simplify-boolean-expression,
; inconsistent-return-statements,
; useless-return,
; consider-swap-variables,
; consider-using-join,
; consider-using-in,
; consider-using-get,
; chained-comparison,
; consider-using-dict-comprehension,
; consider-using-set-comprehension,
; simplifiable-if-expression,
; no-else-raise,
; unnecessary-comprehension,
; consider-using-sys-exit,
; no-else-break,
; no-else-continue,
; super-with-arguments,
; simplifiable-condition,
; condition-evals-to-constant,
; consider-using-generator,
; use-a-generator,
; consider-using-min-builtin,
; consider-using-max-builtin,
; consider-using-with,
; unnecessary-dict-index-lookup,
; use-list-literal,
; use-dict-literal,
; pointless-statement,
; pointless-string-statement,
; expression-not-assigned,
; unnecessary-pass,
; unnecessary-lambda,
; assign-to-new-keyword,
; useless-else-on-loop,
; exec-used,
; eval-used,
; confusing-with-statement,
; using-constant-test,
; missing-parentheses-for-call-in-test,
; self-assigning-variable,
; redeclared-assigned-name,
; assert-on-string-literal,
; comparison-with-callable,
; lost-exception,
; nan-comparison,
; assert-on-tuple,
; attribute-defined-outside-init,
; bad-staticmethod-argument,
; protected-access,
; arguments-differ,
; signature-differs,
; abstract-method,
; super-init-not-called,
; no-init,
; non-parent-init-called,
; useless-super-delegation,
; invalid-overridden-method,
; arguments-renamed,
; unused-private-member,
; overridden-final-method,
; subclassed-final-class,
; bad-indentation,
; wildcard-import,
; deprecated-module,
; reimported,
; import-self,
; preferred-module,
; misplaced-future,
; fixme,
; global-variable-undefined,
; global-statement,
; global-at-module-level,
; unused-argument,
; unused-wildcard-import,
; redefined-outer-name,
; redefined-builtin,
; undefined-loop-variable,
; unbalanced-tuple-unpacking,
; cell-var-from-loop,
; possibly-unused-variable,
; self-cls-assignment,
; bare-except,
; broad-except,
; duplicate-except,
; try-except-raise,
; raise-missing-from,
; raising-format-tuple,
; wrong-exception-operation,
; keyword-arg-before-vararg,
; arguments-out-of-order,
; non-str-assignment-to-dunder-name,
; isinstance-second-argument-not-valid-type,
; logging-not-lazy,
; logging-format-interpolation,
; logging-fstring-interpolation,
; bad-format-string-key,
; unused-format-string-key,
; missing-format-argument-key,
; unused-format-string-argument,
; format-combined-specification,
; missing-format-attribute,
; invalid-format-index,
; duplicate-string-formatting-argument,
; f-string-without-interpolation,
; useless-with-lock
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
; enable=unneeded-not,
; format-string-without-interpolation,
; anomalous-unicode-escape-in-string,
; implicit-str-concat,
; inconsistent-quotes,
; redundant-u-string-prefix,
; boolean-datetime,
; redundant-unittest-assert,
; deprecated-method,
; bad-thread-instantiation,
; shallow-copy-environ,
; invalid-envvar-default,
; subprocess-popen-preexec-fn,
; subprocess-run-check,
; deprecated-argument,
; deprecated-class,
; deprecated-decorator,
; unspecified-encoding,
; forgotten-debug-statement,
; using-f-string-in-unsupported-version,
; using-final-decorator-in-unsupported-version,
; singleton-comparison,
; consider-using-f-string,
; line-too-long,
; trailing-whitespace,
; multiple-statements,
; syntax-error,
; unrecognized-inline-option,
; bad-option-value,
; bad-plugin-value,
; bad-configuration-section,
; init-is-generator,
; return-in-init,
; function-redefined,
; not-in-loop,
; return-outside-function,
; yield-outside-function,
; return-arg-in-generator,
; nonexistent-operator,
; duplicate-argument-name,
; abstract-class-instantiated,
; bad-reversed-sequence,
; too-many-star-expressions,
; invalid-star-assignment-target,
; star-needs-assignment-target,
; nonlocal-and-global,
; continue-in-finally,
; nonlocal-without-binding,
; used-prior-global-declaration,
; misplaced-format-function,
; method-hidden,
; access-member-before-definition,
; no-method-argument,
; no-self-argument,
; invalid-slots-object,
; assigning-non-slot,
; invalid-slots,
; inherit-non-class,
; inconsistent-mro,
; duplicate-bases,
; class-variable-slots-conflict,
; invalid-class-object,
; non-iterator-returned,
; unexpected-special-method-signature,
; invalid-length-returned,
; invalid-bool-returned,
; invalid-index-returned,
; invalid-repr-returned,
; invalid-str-returned,
; invalid-bytes-returned,
; invalid-hash-returned,
; invalid-length-hint-returned,
; invalid-format-returned,
; invalid-getnewargs-returned,
; invalid-getnewargs-ex-returned,
; import-error,
; relative-beyond-top-level,
; used-before-assignment,
; undefined-variable,
; undefined-all-variable,
; invalid-all-object,
; invalid-all-format,
; no-name-in-module,
; unpacking-non-sequence,
; bad-except-order,
; raising-bad-type,
; bad-exception-context,
; misplaced-bare-raise,
; raising-non-exception,
; notimplemented-raised,
; catching-non-exception,
; bad-super-call,
; no-member,
; not-callable,
; assignment-from-no-return,
; no-value-for-parameter,
; too-many-function-args,
; unexpected-keyword-arg,
; redundant-keyword-arg,
; missing-kwoa,
; invalid-sequence-index,
; invalid-slice-index,
; assignment-from-none,
; not-context-manager,
; invalid-unary-operand-type,
; unsupported-binary-operation,
; repeated-keyword,
; not-an-iterable,
; not-a-mapping,
; unsupported-membership-test,
; unsubscriptable-object,
; unsupported-assignment-operation,
; unsupported-delete-operation,
; invalid-metaclass,
; unhashable-dict-key,
; dict-iter-missing-items,
; await-outside-async,
; logging-unsupported-format,
; logging-format-truncated,
; logging-too-many-args,
; logging-too-few-args,
; bad-format-character,
; truncated-format-string,
; mixed-format-string,
; format-needs-mapping,
; missing-format-string-key,
; too-many-format-args,
; too-few-format-args,
; bad-string-format-type,
; bad-str-strip-call,
; invalid-envvar-value,
; yield-inside-async-function,
; not-async-context-manager,
; fatal,
; astroid-error,
; parse-error,
; config-parse-error,
; method-check-failed,
; unreachable,
; dangerous-default-value,
; duplicate-key,
; unnecessary-semicolon,
; global-variable-not-assigned,
; unused-import,
; unused-variable,
; binary-op-exception,
; bad-format-string,
; anomalous-backslash-in-string,
; bad-open-mode
enable =
F,
E,
anomalous-backslash-in-string,
bad-format-string,
bad-open-mode,
consider-using-f-string,
dangerous-default-value,
duplicate-key,
global-variable-not-assigned
line-too-long,
singleton-comparison,
trailing-whitespace,
unnecessary-semicolon,
unneeded-not,
unreachable,
unused-import,
unused-variable,
binary-op-exception
disable = all
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
# which contain the number of messages in each category, as well as 'statement'
# which is the total number of statements analyzed. This score is used by the
# global evaluation report (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=LF
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )??$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=200
# Maximum number of lines in a module.
max-module-lines=15000
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the 'python-enchant' package.
spelling-dict=
# List of comma separated words that should be considered directives if they
# appear and the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[SIMILARITIES]
# Comments are removed from the similarity computation
ignore-comments=yes
# Docstrings are removed from the similarity computation
ignore-docstrings=yes
# Imports are removed from the similarity computation
ignore-imports=no
# Signatures are removed from the similarity computation
ignore-signatures=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
#notes-rgx=
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style.
#variable-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# class is considered mixin if its name matches the mixin-class-rgx option.
ignore-mixin-members=yes
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# Regex pattern to define which classes are considered mixins ignore-mixin-
# members is set to 'yes'
mixin-class-rgx=.*[Mm]ixin
# List of decorators that change the signature of a decorated function.
signature-mutators=
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=
# Output a graph (.gv or any supported image format) of external dependencies
# to the given file (report RP0402 must not be disabled).
ext-import-graph=
# Output a graph (.gv or any supported image format) of all (i.e. internal and
# external) dependencies to the given file (report RP0402 must not be
# disabled).
import-graph=
# Output a graph (.gv or any supported image format) of internal dependencies
# to the given file (report RP0402 must not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[DESIGN]
# List of regular expressions of class ancestor names to ignore when counting
# public methods (see R0903)
exclude-too-few-public-methods=
# List of qualified class names to ignore when counting class parents (see
# R0901)
ignored-parents=
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception
================================================
FILE: .python-version
================================================
3.10.14
================================================
FILE: .readthedocs.yml
================================================
version: 2
mkdocs:
configuration: docs/obsolete/mkdocs.yml
fail_on_warning: false
formats: all
build:
image: latest
python:
version: 3.7
install:
- requirements: docs/obsolete/requirements.txt
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013-2025 crazy rabbidz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
`GEF` (pronounced ʤɛf - "Jeff") is a set of commands for x86/64, ARM, MIPS, PowerPC and SPARC to
assist exploit developers and reverse-engineers when using old school GDB. It provides additional
features to GDB using the Python API to assist during the process of dynamic analysis and exploit
development. Application developers will also benefit from it, as GEF lifts a great part of regular
GDB obscurity, avoiding repeating traditional commands, or bringing out the relevant information
from the debugging runtime.
## Instant Setup
Simply make sure you have [GDB 10.0 or higher](https://www.gnu.org/s/gdb) compiled with Python3.10+
bindings, then:
```bash
# via the install script
## using curl
$ bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
## using wget
$ bash -c "$(wget https://gef.blah.cat/sh -O -)"
# or manually
$ wget -O ~/.gdbinit-gef.py -q https://gef.blah.cat/py
$ echo source ~/.gdbinit-gef.py >> ~/.gdbinit
# or alternatively from inside gdb directly
$ gdb -q
(gdb) pi import urllib.request as u, tempfile as t; g=t.NamedTemporaryFile(suffix='-gef.py'); open(g.name, 'wb+').write(u.urlopen('https://tinyurl.com/gef-main').read()); gdb.execute('source %s' % g.name)
```
You can immediately see that GEF is correctly installed by launching GDB:

A few of `GEF` features include:
* **One** single GDB script
* Entirely **architecture agnostic**, **NO** dependencies: `GEF` is battery-included and [is
installable instantly](https://hugsy.github.io/gef/#setup)
* **Fast** limiting the number of dependencies and optimizing code to make the commands as fast as
possible
* Provides a great variety of commands to drastically change your experience in GDB.
* [**Easily** extensible](https://hugsy.github.io/gef/api/) to create other commands by providing
d more comprehensible layout to GDB Python API.
* Full Python3 support ([Python2 support was
dropped](https://github.com/hugsy/gef/releases/tag/2020.03) - see
[`gef-legacy`](https://github.com/hugsy/gef-legacy)).
* Built around an architecture abstraction layer, so all commands work in any GDB-supported
architecture such as x86-32/64, ARMv5/6/7, AARCH64, SPARC, MIPS, PowerPC, etc.
* Suited for real-life apps debugging, exploit development, just as much as CTF
* And a lot more commands contributed by the community available on
[GEF-Extras](https://github.com/hugsy/gef-extras) !!
Check out the [Screenshot page](docs/screenshots.md) for more or [try it
online](https://demo.gef.blah.cat) (user:`gef`/password:`gef-demo`)
## Documentation
Unlike other GDB plugins, GEF has an extensive and up-to-date
[documentation](https://hugsy.github.io/gef/). Users are recommended to refer to it as it may help
them in their attempts to use GEF. In particular, new users should navigate through it (see the
[FAQ](https://hugsy.github.io/gef/faq/) for common installation problems), and the problem persists,
try to reach out for help on the Discord channel or submit an issue.
## Current status
| Documentation | License | Compatibility | CI Tests (`main`) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| [](https://github.com/hugsy/gef/actions/workflows/generate-docs.yml) | [](https://github.com/hugsy/gef/blob/main/LICENSE) | [](https://github.com/hugsy/gef/) | [](https://github.com/hugsy/gef/actions/workflows/tests.yml) |
## Contribute
To get involved, refer to the [Contribution
documentation](https://hugsy.github.io/gef/#contribution) and the
[guidelines](https://github.com/hugsy/gef/blob/main/.github/CONTRIBUTING.md) to start.
## Sponsors
Another way to contribute to keeping the project alive is by sponsoring it! Check out [the
sponsoring documentation](https://hugsy.github.io/gef/#sponsors) for details so you can be part of
the list of those [awesome sponsors](https://github.com/sponsors/hugsy).
## Happy Hacking 🍻
================================================
FILE: docs/.markdownlint.yaml
================================================
#
# Rules: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
#
# Default state for all rules
default: true
# Path to configuration file to extend
extends: null
# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time
MD001: true
# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading
MD002:
# Heading level
level: 2
# MD003/heading-style/header-style - Heading style
MD003:
# Heading style
style: "consistent"
# MD004/ul-style - Unordered list style
MD004:
# List style
style: "consistent"
# MD005/list-indent - Inconsistent indentation for list items at the same level
MD005: true
# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line
MD006: true
# MD007/ul-indent - Unordered list indentation
MD007:
# Spaces for indent
indent: 2
# Whether to indent the first level of the list
start_indented: false
# Spaces for first level indent (when start_indented is set)
start_indent: 2
# MD009/no-trailing-spaces - Trailing spaces
MD009:
# Spaces for line break
br_spaces: 2
# Allow spaces for empty lines in list items
list_item_empty_lines: false
# Include unnecessary breaks
strict: false
# MD010/no-hard-tabs - Hard tabs
MD010:
# Include code blocks
code_blocks: false
# Fenced code languages to ignore
ignore_code_languages: []
# Number of spaces for each hard tab
spaces_per_tab: 4
# MD011/no-reversed-links - Reversed link syntax
MD011: true
# MD012/no-multiple-blanks - Multiple consecutive blank lines
MD012:
# Consecutive blank lines
maximum: 2
# MD013/line-length - Line length
MD013:
# Number of characters
line_length: 100
# Number of characters for headings
heading_line_length: 100
# Number of characters for code blocks
code_block_line_length: 100
# Include code blocks
code_blocks: false
# Include tables
tables: false
# Include headings
headings: true
# Include headings
headers: true
# Strict length checking
strict: false
# Stern length checking
stern: false
# MD014/commands-show-output - Dollar signs used before commands without showing output
MD014: true
# MD018/no-missing-space-atx - No space after hash on atx style heading
MD018: true
# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading
MD019: true
# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading
MD020: true
# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading
MD021: true
# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
MD022:
# Blank lines above heading
lines_above: 1
# Blank lines below heading
lines_below: 1
# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line
MD023: true
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
MD024:
# Only check sibling headings
allow_different_nesting: false
# Only check sibling headings
siblings_only: false
# MD025/single-title/single-h1 - Multiple top-level headings in the same document
MD025:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD026/no-trailing-punctuation - Trailing punctuation in heading
MD026:
# Punctuation characters not allowed at end of headings
punctuation: ".,;:!。,;:!"
# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol
MD027: true
# MD028/no-blanks-blockquote - Blank line inside blockquote
MD028: true
# MD029/ol-prefix - Ordered list item prefix
MD029:
# List style
style: "one_or_ordered"
# MD030/list-marker-space - Spaces after list markers
MD030:
# Spaces for single-line unordered list items
ul_single: 2
# Spaces for single-line ordered list items
ol_single: 2
# Spaces for multi-line unordered list items
ul_multi: 2
# Spaces for multi-line ordered list items
ol_multi: 2
# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
MD031:
# Include list items
list_items: true
# MD032/blanks-around-lists - Lists should be surrounded by blank lines
MD032: true
# MD033/no-inline-html - Inline HTML
MD033:
# Allowed elements
allowed_elements: ["img"]
# MD034/no-bare-urls - Bare URL used
MD034: true
# MD035/hr-style - Horizontal rule style
MD035:
# Horizontal rule style
style: "consistent"
# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
MD036:
# Punctuation characters
punctuation: ".,;:!?。,;:!?"
# MD037/no-space-in-emphasis - Spaces inside emphasis markers
MD037: true
# MD038/no-space-in-code - Spaces inside code span elements
MD038: true
# MD039/no-space-in-links - Spaces inside link text
MD039: true
# MD040/fenced-code-language - Fenced code blocks should have a language specified
MD040:
# List of languages
allowed_languages: []
# Require language only
language_only: false
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
MD041:
# Heading level
level: 2
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD042/no-empty-links - No empty links
MD042: true
# MD043/required-headings/required-headers - Required heading structure
MD043: false
# MD044/proper-names - Proper names should have the correct capitalization
MD044:
# List of proper names
names: []
# Include code blocks
code_blocks: false
# Include HTML elements
html_elements: false
# MD045/no-alt-text - Images should have alternate text (alt text)
MD045: true
# MD046/code-block-style - Code block style
MD046:
# Block style
style: "consistent"
# MD047/single-trailing-newline - Files should end with a single newline character
MD047: true
# MD048/code-fence-style - Code fence style
MD048:
# Code fence style
style: "consistent"
# MD049/emphasis-style - Emphasis style should be consistent
MD049:
# Emphasis style should be consistent
style: "consistent"
# MD050/strong-style - Strong style should be consistent
MD050:
# Strong style should be consistent
style: "consistent"
# MD051/link-fragments - Link fragments should be valid
MD051: true
# MD052/reference-links-images - Reference links and images should use a label that is defined
MD052: true
# MD053/link-image-reference-definitions - Link and image reference definitions should be needed
MD053:
# Ignored definitions
ignored_definitions:
- "//"
================================================
FILE: docs/api/gef.md
================================================
# module `GEF`
**Global Variables**
---------------
- **GEF_DEFAULT_BRANCH**
- **GEF_EXTRAS_DEFAULT_BRANCH**
- **GDB_MIN_VERSION**
- **GDB_VERSION**
- **PYTHON_MIN_VERSION**
- **PYTHON_VERSION**
- **DEFAULT_PAGE_ALIGN_SHIFT**
- **DEFAULT_PAGE_SIZE**
- **GEF_TEMP_DIR**
- **GEF_MAX_STRING_LENGTH**
- **LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME**
- **ANSI_SPLIT_RE**
- **LEFT_ARROW**
- **RIGHT_ARROW**
- **DOWN_ARROW**
- **HORIZONTAL_LINE**
- **VERTICAL_LINE**
- **CROSS**
- **TICK**
- **BP_GLYPH**
- **GEF_PROMPT**
- **GEF_PROMPT_ON**
- **GEF_PROMPT_OFF**
- **PREFIX**
- **gdb_initial_settings**
- **cmd**
- **gef**
- **errmsg**
---
## function `http_get`
```python
http_get(url: str) → Optional[bytes]
```
Basic HTTP wrapper for GET request. Return the body of the page if HTTP code is OK, otherwise return None.
---
## function `update_gef`
```python
update_gef(argv: List[str]) → int
```
Try to update `gef` to the latest version pushed on GitHub main branch. Return 0 on success, 1 on failure.
---
## function `reset_all_caches`
```python
reset_all_caches() → None
```
Free all caches. If an object is cached, it will have a callable attribute `cache_clear` which will be invoked to purge the function cache.
---
## function `reset`
```python
reset() → None
```
---
## function `highlight_text`
```python
highlight_text(text: str) → str
```
Highlight text using `gef.ui.highlight_table` { match -> color } settings.
If RegEx is enabled it will create a match group around all items in the `gef.ui.highlight_table` and wrap the specified color in the `gef.ui.highlight_table` around those matches.
If RegEx is disabled, split by ANSI codes and 'colorify' each match found within the specified string.
---
## function `gef_print`
```python
gef_print(*args: str, end='\n', sep=' ', **kwargs: Any) → None
```
Wrapper around print(), using string buffering feature.
---
## function `bufferize`
```python
bufferize(f: Callable) → Callable
```
Store the content to be printed for a function in memory, and flush it on function exit.
---
## function `p8`
```python
p8(
x: int,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → bytes
```
Pack one byte respecting the current architecture endianness.
---
## function `p16`
```python
p16(
x: int,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → bytes
```
Pack one word respecting the current architecture endianness.
---
## function `p32`
```python
p32(
x: int,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → bytes
```
Pack one dword respecting the current architecture endianness.
---
## function `p64`
```python
p64(
x: int,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → bytes
```
Pack one qword respecting the current architecture endianness.
---
## function `u8`
```python
u8(
x: bytes,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → int
```
Unpack one byte respecting the current architecture endianness.
---
## function `u16`
```python
u16(
x: bytes,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → int
```
Unpack one word respecting the current architecture endianness.
---
## function `u32`
```python
u32(
x: bytes,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → int
```
Unpack one dword respecting the current architecture endianness.
---
## function `u64`
```python
u64(
x: bytes,
s: bool = False,
e: Optional[ForwardRef('Endianness')] = None
) → int
```
Unpack one qword respecting the current architecture endianness.
---
## function `is_ascii_string`
```python
is_ascii_string(address: int) → bool
```
Helper function to determine if the buffer pointed by `address` is an ASCII string (in GDB)
---
## function `is_alive`
```python
is_alive() → bool
```
Check if GDB is running.
---
## function `calling_function`
```python
calling_function() → Optional[str]
```
Return the name of the calling function
---
## function `only_if_gdb_running`
```python
only_if_gdb_running(f: Callable) → Callable
```
Decorator wrapper to check if GDB is running.
---
## function `only_if_gdb_target_local`
```python
only_if_gdb_target_local(f: Callable) → Callable
```
Decorator wrapper to check if GDB is running locally (target not remote).
---
## function `deprecated`
```python
deprecated(solution: str = '') → Callable
```
Decorator to add a warning when a command is obsolete and will be removed.
---
## function `experimental_feature`
```python
experimental_feature(f: Callable) → Callable
```
Decorator to add a warning when a feature is experimental.
---
## function `only_if_events_supported`
```python
only_if_events_supported(event_type: str) → Callable
```
Checks if GDB supports events without crashing.
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `wrapped_f`
```python
wrapped_f(*args: Any, **kwargs: Any) → Any
```
---
## function `FakeExit`
```python
FakeExit(*args: Any, **kwargs: Any) → NoReturn
```
---
## function `parse_arguments`
```python
parse_arguments(
required_arguments: Dict[Union[str, Tuple[str, str]], Any],
optional_arguments: Dict[Union[str, Tuple[str, str]], Any]
) → Callable
```
Argument parsing decorator.
---
## function `search_for_main_arena`
```python
search_for_main_arena() → int
```
`search_for_main_arena` is **DEPRECATED** and will be removed in the future.
Use GefHeapManager.find_main_arena_addr()
---
## function `get_libc_version`
```python
get_libc_version() → Tuple[int, ...]
```
`get_libc_version` is **DEPRECATED** and will be removed in the future.
Use GefLibcManager.find_libc_version()
---
## function `titlify`
```python
titlify(
text: str,
color: Optional[str] = None,
msg_color: Optional[str] = None
) → str
```
Print a centered title.
---
## function `dbg`
```python
dbg(msg: str) → None
```
---
## function `err`
```python
err(msg: str) → None
```
---
## function `warn`
```python
warn(msg: str) → None
```
---
## function `ok`
```python
ok(msg: str) → None
```
---
## function `info`
```python
info(msg: str) → None
```
---
## function `push_context_message`
```python
push_context_message(level: str, message: str) → None
```
Push the message to be displayed the next time the context is invoked.
---
## function `show_last_exception`
```python
show_last_exception() → None
```
Display the last Python exception.
---
## function `gef_pystring`
```python
gef_pystring(x: bytes) → str
```
Returns a sanitized version as string of the bytes list given in input.
---
## function `gef_pybytes`
```python
gef_pybytes(x: str) → bytes
```
Returns an immutable bytes list from the string given as input.
---
## function `style_byte`
```python
style_byte(b: int, color: bool = True) → str
```
---
## function `hexdump`
```python
hexdump(
source: ByteString,
length: int = 16,
separator: str = '.',
show_raw: bool = False,
show_symbol: bool = True,
base: int = 0
) → str
```
Return the hexdump of `src` argument. @param source *MUST* be of type bytes or bytearray @param length is the length of items per line @param separator is the default character to use if one byte is not printable @param show_raw if True, do not add the line nor the text translation @param base is the start address of the block being hexdump @return a string with the hexdump
---
## function `is_debug`
```python
is_debug() → bool
```
Check if debug mode is enabled.
---
## function `buffer_output`
```python
buffer_output() → bool
```
Check if output should be buffered until command completion.
---
## function `hide_context`
```python
hide_context() → bool
```
Helper function to hide the context pane.
---
## function `unhide_context`
```python
unhide_context() → bool
```
Helper function to unhide the context pane.
---
## function `enable_redirect_output`
```python
enable_redirect_output(to_file: str = '/dev/null') → None
```
Redirect all GDB output to `to_file` parameter. By default, `to_file` redirects to `/dev/null`.
---
## function `disable_redirect_output`
```python
disable_redirect_output() → None
```
Disable the output redirection, if any.
---
## function `gef_makedirs`
```python
gef_makedirs(path: str, mode: int = 493) → Path
```
Recursive mkdir() creation. If successful, return the absolute path of the directory created.
---
## function `gdb_disassemble`
```python
gdb_disassemble(
start_pc: int,
**kwargs: int
) → Generator[__main__.Instruction, NoneType, NoneType]
```
Disassemble instructions from `start_pc` (Integer). Accepts the following named
**parameters:**
- `end_pc` (Integer) only instructions whose start address fall in the interval from start_pc to end_pc are returned.
- `count` (Integer) list at most this many disassembled instructions If `end_pc` and `count` are not provided, the function will behave as if `count=1`. Return an iterator of Instruction objects
---
## function `gdb_get_nth_previous_instruction_address`
```python
gdb_get_nth_previous_instruction_address(addr: int, n: int) → Optional[int]
```
Return the address (Integer) of the `n`-th instruction before `addr`.
---
## function `gdb_get_nth_next_instruction_address`
```python
gdb_get_nth_next_instruction_address(addr: int, n: int) → int
```
Return the address (Integer) of the `n`-th instruction after `addr`.
---
## function `gef_instruction_n`
```python
gef_instruction_n(addr: int, n: int) → Instruction
```
Return the `n`-th instruction after `addr` as an Instruction object.
---
## function `gef_get_instruction_at`
```python
gef_get_instruction_at(addr: int) → Instruction
```
Return the full Instruction found at the specified address.
---
## function `gef_current_instruction`
```python
gef_current_instruction(addr: int) → Instruction
```
Return the current instruction as an Instruction object.
---
## function `gef_next_instruction`
```python
gef_next_instruction(addr: int) → Instruction
```
Return the next instruction as an Instruction object.
---
## function `gef_disassemble`
```python
gef_disassemble(
addr: int,
nb_insn: int,
nb_prev: int = 0
) → Generator[__main__.Instruction, NoneType, NoneType]
```
Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr`. Return an iterator of Instruction objects.
---
## function `gef_execute_external`
```python
gef_execute_external(
command: Sequence[str],
as_list: bool = False,
**kwargs: Any
) → Union[str, List[str]]
```
Execute an external command and return the result.
---
## function `gef_execute_gdb_script`
```python
gef_execute_gdb_script(commands: str) → None
```
Execute the parameter `source` as GDB command. This is done by writing `commands` to a temporary file, which is then executed via GDB `source` command. The tempfile is then deleted.
---
## function `checksec`
```python
checksec(filename: str) → Dict[str, bool]
```
`checksec` is **DEPRECATED** and will be removed in the future.
Use Elf(fname).checksec()
---
## function `get_entry_point`
```python
get_entry_point() → Optional[int]
```
Return the binary entry point.
`get_entry_point` is **DEPRECATED** and will be removed in the future.
Use `gef.binary.entry_point` instead
---
## function `is_pie`
```python
is_pie(fpath: str) → bool
```
---
## function `is_big_endian`
```python
is_big_endian() → bool
```
`is_big_endian` is **DEPRECATED** and will be removed in the future.
Prefer `gef.arch.endianness == Endianness.BIG_ENDIAN`
---
## function `is_little_endian`
```python
is_little_endian() → bool
```
`is_little_endian` is **DEPRECATED** and will be removed in the future.
gef.arch.endianness == Endianness.LITTLE_ENDIAN
---
## function `flags_to_human`
```python
flags_to_human(reg_value: int, value_table: Dict[int, str]) → str
```
Return a human readable string showing the flag states.
---
## function `register_architecture`
```python
register_architecture(
cls: Type[ForwardRef('Architecture')]
) → Type[ForwardRef('Architecture')]
```
`register_architecture` is **DEPRECATED** and will be removed in the future.
Using the decorator `register_architecture` is unnecessary
---
## function `copy_to_clipboard`
```python
copy_to_clipboard(data: bytes) → None
```
Helper function to submit data to the clipboard
---
## function `use_stdtype`
```python
use_stdtype() → str
```
---
## function `use_default_type`
```python
use_default_type() → str
```
---
## function `use_golang_type`
```python
use_golang_type() → str
```
---
## function `use_rust_type`
```python
use_rust_type() → str
```
---
## function `to_unsigned_long`
```python
to_unsigned_long(v: gdb.Value) → int
```
Cast a gdb.Value to unsigned long.
---
## function `get_path_from_info_proc`
```python
get_path_from_info_proc() → Optional[str]
```
---
## function `get_os`
```python
get_os() → str
```
`get_os` is **DEPRECATED** and will be removed in the future.
Use `gef.session.os`
---
## function `get_filepath`
```python
get_filepath() → Optional[str]
```
Return the local absolute path of the file currently debugged.
---
## function `get_function_length`
```python
get_function_length(sym: str) → int
```
Attempt to get the length of the raw bytes of a function.
---
## function `process_lookup_address`
```python
process_lookup_address(address: int) → Optional[__main__.Section]
```
Look up for an address in memory. Return an Address object if found, None otherwise.
---
## function `xor`
```python
xor(data: ByteString, key: str) → bytearray
```
Return `data` xor-ed with `key`.
---
## function `is_hex`
```python
is_hex(pattern: str) → bool
```
Return whether provided string is a hexadecimal value.
---
## function `continue_handler`
```python
continue_handler(_: 'gdb.Event') → None
```
GDB event handler for new object continue cases.
---
## function `hook_stop_handler`
```python
hook_stop_handler(_: 'gdb.StopEvent') → None
```
GDB event handler for stop cases.
---
## function `new_objfile_handler`
```python
new_objfile_handler(evt: Optional[ForwardRef('gdb.NewObjFileEvent')]) → None
```
GDB event handler for new object file cases.
---
## function `exit_handler`
```python
exit_handler(_: 'gdb.ExitedEvent') → None
```
GDB event handler for exit cases.
---
## function `memchanged_handler`
```python
memchanged_handler(_: 'gdb.MemoryChangedEvent') → None
```
GDB event handler for mem changes cases.
---
## function `regchanged_handler`
```python
regchanged_handler(_: 'gdb.RegisterChangedEvent') → None
```
GDB event handler for reg changes cases.
---
## function `get_terminal_size`
```python
get_terminal_size() → Tuple[int, int]
```
Return the current terminal size.
---
## function `reset_architecture`
```python
reset_architecture(arch: Optional[str] = None) → None
```
Sets the current architecture. If an architecture is explicitly specified by parameter, try to use that one. If this fails, an `OSError` exception will occur. If no architecture is specified, then GEF will attempt to determine automatically based on the current ELF target. If this fails, an `OSError` exception will occur.
---
## function `get_memory_alignment`
```python
get_memory_alignment(in_bits: bool = False) → int
```
Try to determine the size of a pointer on this system. First, try to parse it out of the ELF header. Next, use the size of `size_t`. Finally, try the size of $pc. If `in_bits` is set to True, the result is returned in bits, otherwise in bytes.
`get_memory_alignment` is **DEPRECATED** and will be removed in the future.
Use `gef.arch.ptrsize` instead
---
## function `clear_screen`
```python
clear_screen(tty: str = '') → None
```
Clear the screen.
---
## function `format_address`
```python
format_address(addr: int) → str
```
Format the address according to its size.
---
## function `format_address_spaces`
```python
format_address_spaces(addr: int, left: bool = True) → str
```
Format the address according to its size, but with spaces instead of zeroes.
---
## function `align_address`
```python
align_address(address: int) → int
```
Align the provided address to the process's native length.
---
## function `align_address_to_size`
```python
align_address_to_size(address: int, align: int) → int
```
Align the address to the given size.
---
## function `align_address_to_page`
```python
align_address_to_page(address: int) → int
```
Align the address to a page.
---
## function `parse_address`
```python
parse_address(address: str) → int
```
Parse an address and return it as an Integer.
---
## function `is_in_x86_kernel`
```python
is_in_x86_kernel(address: int) → bool
```
---
## function `is_remote_debug`
```python
is_remote_debug() → bool
```
"Return True is the current debugging session is running through GDB remote session.
---
## function `de_bruijn`
```python
de_bruijn(alphabet: bytes, n: int) → Generator[str, NoneType, NoneType]
```
De Bruijn sequence for alphabet and subsequences of length n (for compat. w/ pwnlib).
---
## function `generate_cyclic_pattern`
```python
generate_cyclic_pattern(length: int, cycle: int = 4) → bytearray
```
Create a `length` byte bytearray of a de Bruijn cyclic pattern.
---
## function `safe_parse_and_eval`
```python
safe_parse_and_eval(value: str) → Optional[ForwardRef('gdb.Value')]
```
GEF wrapper for gdb.parse_and_eval(): this function returns None instead of raising gdb.error if the eval failed.
---
## function `gef_convenience`
```python
gef_convenience(value: Union[str, bytes]) → str
```
Defines a new convenience value.
---
## function `parse_string_range`
```python
parse_string_range(s: str) → Iterator[int]
```
Parses an address range (e.g. 0x400000-0x401000)
---
## function `gef_get_pie_breakpoint`
```python
gef_get_pie_breakpoint(num: int) → PieVirtualBreakpoint
```
`gef_get_pie_breakpoint` is **DEPRECATED** and will be removed in the future.
Use `gef.session.pie_breakpoints[num]`
---
## function `endian_str`
```python
endian_str() → str
```
`endian_str` is **DEPRECATED** and will be removed in the future.
Use `str(gef.arch.endianness)` instead
---
## function `get_gef_setting`
```python
get_gef_setting(name: str) → Any
```
`get_gef_setting` is **DEPRECATED** and will be removed in the future.
Use `gef.config[key]`
---
## function `set_gef_setting`
```python
set_gef_setting(name: str, value: Any) → None
```
`set_gef_setting` is **DEPRECATED** and will be removed in the future.
Use `gef.config[key] = value`
---
## function `gef_getpagesize`
```python
gef_getpagesize() → int
```
`gef_getpagesize` is **DEPRECATED** and will be removed in the future.
Use `gef.session.pagesize`
---
## function `gef_read_canary`
```python
gef_read_canary() → Optional[Tuple[int, int]]
```
`gef_read_canary` is **DEPRECATED** and will be removed in the future.
Use `gef.session.canary`
---
## function `get_pid`
```python
get_pid() → int
```
`get_pid` is **DEPRECATED** and will be removed in the future.
Use `gef.session.pid`
---
## function `get_filename`
```python
get_filename() → str
```
`get_filename` is **DEPRECATED** and will be removed in the future.
Use `gef.session.file.name`
---
## function `get_glibc_arena`
```python
get_glibc_arena() → Optional[__main__.GlibcArena]
```
`get_glibc_arena` is **DEPRECATED** and will be removed in the future.
Use `gef.heap.main_arena`
---
## function `get_register`
```python
get_register(regname) → Optional[int]
```
`get_register` is **DEPRECATED** and will be removed in the future.
Use `gef.arch.register(regname)`
---
## function `get_process_maps`
```python
get_process_maps() → List[__main__.Section]
```
`get_process_maps` is **DEPRECATED** and will be removed in the future.
Use `gef.memory.maps`
---
## function `set_arch`
```python
set_arch(arch: Optional[str] = None, _: Optional[str] = None) → None
```
`set_arch` is **DEPRECATED** and will be removed in the future.
Use `reset_architecture`
---
## function `register_external_context_pane`
```python
register_external_context_pane(
pane_name: str,
display_pane_function: Callable[[], NoneType],
pane_title_function: Callable[[], Optional[str]],
condition: Optional[Callable[[], bool]] = None
) → None
```
Registering function for new GEF Context View. pane_name: a string that has no spaces (used in settings) display_pane_function: a function that uses gef_print() to print strings pane_title_function: a function that returns a string or None, which will be displayed as the title. If None, no title line is displayed. condition: an optional callback: if not None, the callback will be executed first. If it returns true, then only the pane title and content will displayed. Otherwise, it's simply skipped.
Example usage for a simple text to show when we hit a syscall: def only_syscall(): return gef_current_instruction(gef.arch.pc).is_syscall() def display_pane(): gef_print("Wow, I am a context pane!") def pane_title(): return "example:pane" register_external_context_pane("example_pane", display_pane, pane_title, only_syscall)
---
## function `register_external_command`
```python
register_external_command(
cls: Type[ForwardRef('GenericCommand')]
) → Type[ForwardRef('GenericCommand')]
```
Registering function for new GEF (sub-)command to GDB.
`register_external_command` is **DEPRECATED** and will be removed in the future.
Use `register()`, and inherit from `GenericCommand` instead
---
## function `register_command`
```python
register_command(
cls: Type[ForwardRef('GenericCommand')]
) → Type[ForwardRef('GenericCommand')]
```
Decorator for registering new GEF (sub-)command to GDB.
`register_command` is **DEPRECATED** and will be removed in the future.
Use `register()`, and inherit from `GenericCommand` instead
---
## function `register_priority_command`
```python
register_priority_command(
cls: Type[ForwardRef('GenericCommand')]
) → Type[ForwardRef('GenericCommand')]
```
Decorator for registering new command with priority, meaning that it must loaded before the other generic commands.
`register_priority_command` is **DEPRECATED** and will be removed in the future.
---
## function `register`
```python
register(
cls: Union[Type[ForwardRef('GenericCommand')], Type[ForwardRef('GenericFunction')]]
) → Union[Type[ForwardRef('GenericCommand')], Type[ForwardRef('GenericFunction')]]
```
---
## function `register_function`
```python
register_function(
cls: Type[ForwardRef('GenericFunction')]
) → Type[ForwardRef('GenericFunction')]
```
Decorator for registering a new convenience function to GDB.
`register_function` is **DEPRECATED** and will be removed in the future.
---
## class `AARCH64`
---
#### property AARCH64.cpsr
---
#### property AARCH64.endianness
---
#### property AARCH64.fp
---
#### property AARCH64.instruction_length
---
#### property AARCH64.pc
---
#### property AARCH64.ptrsize
Determine the size of pointer from the current CPU mode
---
#### property AARCH64.registers
---
#### property AARCH64.sp
---
## function `AARCH64.canary_address`
```python
canary_address() → int
```
---
## function `AARCH64.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `AARCH64.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `AARCH64.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → int
```
---
## function `AARCH64.is_aarch32`
```python
is_aarch32() → bool
```
Determine if the CPU is currently in AARCH32 mode from runtime.
---
## function `AARCH64.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `AARCH64.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `AARCH64.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `AARCH64.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `AARCH64.is_thumb`
```python
is_thumb() → bool
```
Determine if the machine is currently in THUMB mode.
---
## function `AARCH64.is_thumb32`
```python
is_thumb32() → bool
```
Determine if the CPU is currently in THUMB32 mode from runtime.
---
## function `AARCH64.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `AARCH64.register`
```python
register(name: str) → int
```
---
## function `AARCH64.reset_caches`
```python
reset_caches() → None
```
---
## function `AARCH64.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `ARM`
---
#### property ARM.cpsr
---
#### property ARM.endianness
---
#### property ARM.fp
---
#### property ARM.instruction_length
---
#### property ARM.mode
---
#### property ARM.pc
---
#### property ARM.ptrsize
---
#### property ARM.registers
---
#### property ARM.sp
---
## function `ARM.canary_address`
```python
canary_address() → int
```
---
## function `ARM.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `ARM.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `ARM.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → int
```
---
## function `ARM.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `ARM.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `ARM.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `ARM.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `ARM.is_thumb`
```python
is_thumb() → bool
```
Determine if the machine is currently in THUMB mode.
---
## function `ARM.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `ARM.register`
```python
register(name: str) → int
```
---
## function `ARM.reset_caches`
```python
reset_caches() → None
```
---
## function `ARM.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `ASLRCommand`
View/modify the ASLR setting of GDB. By default, GDB will disable ASLR when it starts the process. (i.e. not attached). This command allows to change that setting.
## function `ASLRCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ASLRCommand.settings
Return the list of settings for this command.
---
## function `ASLRCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ASLRCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ASLRCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ASLRCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ASLRCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ASLRCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ASLRCommand.post_load`
```python
post_load() → None
```
---
## function `ASLRCommand.pre_load`
```python
pre_load() → None
```
---
## function `ASLRCommand.usage`
```python
usage() → None
```
---
## class `Address`
GEF representation of memory addresses.
## function `Address.__init__`
```python
__init__(**kwargs: Any) → None
```
---
#### property Address.valid
---
## function `Address.dereference`
```python
dereference() → Optional[int]
```
---
## function `Address.is_in_heap_segment`
```python
is_in_heap_segment() → bool
```
---
## function `Address.is_in_stack_segment`
```python
is_in_stack_segment() → bool
```
---
## function `Address.is_in_text_segment`
```python
is_in_text_segment() → bool
```
---
## class `AliasesAddCommand`
Command to add aliases.
## function `AliasesAddCommand.__init__`
```python
__init__() → None
```
---
#### property AliasesAddCommand.settings
Return the list of settings for this command.
---
## function `AliasesAddCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `AliasesAddCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `AliasesAddCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `AliasesAddCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `AliasesAddCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `AliasesAddCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `AliasesAddCommand.post_load`
```python
post_load() → None
```
---
## function `AliasesAddCommand.pre_load`
```python
pre_load() → None
```
---
## function `AliasesAddCommand.usage`
```python
usage() → None
```
---
## class `AliasesCommand`
Base command to add, remove, or list aliases.
## function `AliasesCommand.__init__`
```python
__init__() → None
```
---
#### property AliasesCommand.settings
Return the list of settings for this command.
---
## function `AliasesCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `AliasesCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `AliasesCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `AliasesCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `AliasesCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `AliasesCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `AliasesCommand.post_load`
```python
post_load() → None
```
---
## function `AliasesCommand.pre_load`
```python
pre_load() → None
```
---
## function `AliasesCommand.usage`
```python
usage() → None
```
---
## class `AliasesListCommand`
Command to list aliases.
## function `AliasesListCommand.__init__`
```python
__init__() → None
```
---
#### property AliasesListCommand.settings
Return the list of settings for this command.
---
## function `AliasesListCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `AliasesListCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `AliasesListCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `AliasesListCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `AliasesListCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `AliasesListCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `AliasesListCommand.post_load`
```python
post_load() → None
```
---
## function `AliasesListCommand.pre_load`
```python
pre_load() → None
```
---
## function `AliasesListCommand.usage`
```python
usage() → None
```
---
## class `AliasesRmCommand`
Command to remove aliases.
## function `AliasesRmCommand.__init__`
```python
__init__() → None
```
---
#### property AliasesRmCommand.settings
Return the list of settings for this command.
---
## function `AliasesRmCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `AliasesRmCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `AliasesRmCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `AliasesRmCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `AliasesRmCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `AliasesRmCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `AliasesRmCommand.post_load`
```python
post_load() → None
```
---
## function `AliasesRmCommand.pre_load`
```python
pre_load() → None
```
---
## function `AliasesRmCommand.usage`
```python
usage() → None
```
---
## class `Architecture`
Generic metaclass for the architecture supported by GEF.
---
#### property Architecture.endianness
---
#### property Architecture.fp
---
#### property Architecture.pc
---
#### property Architecture.ptrsize
---
#### property Architecture.registers
---
#### property Architecture.sp
---
## function `Architecture.canary_address`
```python
canary_address() → int
```
---
## function `Architecture.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `Architecture.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `Architecture.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `Architecture.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `Architecture.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `Architecture.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `Architecture.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `Architecture.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `Architecture.register`
```python
register(name: str) → int
```
---
## function `Architecture.reset_caches`
```python
reset_caches() → None
```
---
## function `Architecture.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `ArchitectureBase`
Class decorator for declaring an architecture to GEF.
---
## class `BssBaseFunction`
Return the current bss base address plus the given offset.
## function `BssBaseFunction.__init__`
```python
__init__() → None
```
---
## function `BssBaseFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `BssBaseFunction.do_invoke`
```python
do_invoke(args: List) → int
```
---
## function `BssBaseFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `CanaryCommand`
Shows the canary value of the current process.
## function `CanaryCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property CanaryCommand.settings
Return the list of settings for this command.
---
## function `CanaryCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `CanaryCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `CanaryCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `CanaryCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `CanaryCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `CanaryCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `CanaryCommand.post_load`
```python
post_load() → None
```
---
## function `CanaryCommand.pre_load`
```python
pre_load() → None
```
---
## function `CanaryCommand.usage`
```python
usage() → None
```
---
## class `ChangeFdCommand`
ChangeFdCommand: redirect file descriptor during runtime.
## function `ChangeFdCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ChangeFdCommand.settings
Return the list of settings for this command.
---
## function `ChangeFdCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ChangeFdCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ChangeFdCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ChangeFdCommand.get_fd_from_result`
```python
get_fd_from_result(res: str) → int
```
---
## function `ChangeFdCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ChangeFdCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ChangeFdCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ChangeFdCommand.post_load`
```python
post_load() → None
```
---
## function `ChangeFdCommand.pre_load`
```python
pre_load() → None
```
---
## function `ChangeFdCommand.usage`
```python
usage() → None
```
---
## class `ChangePermissionBreakpoint`
When hit, this temporary breakpoint will restore the original code, and position $pc correctly.
## function `ChangePermissionBreakpoint.__init__`
```python
__init__(loc: str, code: ByteString, pc: int) → None
```
---
## function `ChangePermissionBreakpoint.stop`
```python
stop() → bool
```
---
## class `ChecksecCommand`
Checksec the security properties of the current executable or passed as argument. The command checks for the following protections:
- PIE
- NX
- RelRO
- Glibc Stack Canaries
- Fortify Source
## function `ChecksecCommand.__init__`
```python
__init__() → None
```
---
#### property ChecksecCommand.settings
Return the list of settings for this command.
---
## function `ChecksecCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ChecksecCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ChecksecCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ChecksecCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ChecksecCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ChecksecCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ChecksecCommand.post_load`
```python
post_load() → None
```
---
## function `ChecksecCommand.pre_load`
```python
pre_load() → None
```
---
## function `ChecksecCommand.print_security_properties`
```python
print_security_properties(filename: str) → None
```
---
## function `ChecksecCommand.usage`
```python
usage() → None
```
---
## class `Color`
Used to colorify terminal output.
---
## function `Color.blinkify`
```python
blinkify(msg: str) → str
```
---
## function `Color.blueify`
```python
blueify(msg: str) → str
```
---
## function `Color.boldify`
```python
boldify(msg: str) → str
```
---
## function `Color.colorify`
```python
colorify(text: str, attrs: str) → str
```
Color text according to the given attributes.
---
## function `Color.cyanify`
```python
cyanify(msg: str) → str
```
---
## function `Color.grayify`
```python
grayify(msg: str) → str
```
---
## function `Color.greenify`
```python
greenify(msg: str) → str
```
---
## function `Color.highlightify`
```python
highlightify(msg: str) → str
```
---
## function `Color.light_grayify`
```python
light_grayify(msg: str) → str
```
---
## function `Color.pinkify`
```python
pinkify(msg: str) → str
```
---
## function `Color.redify`
```python
redify(msg: str) → str
```
---
## function `Color.underlinify`
```python
underlinify(msg: str) → str
```
---
## function `Color.yellowify`
```python
yellowify(msg: str) → str
```
---
## class `ContextCommand`
Displays a comprehensive and modular summary of runtime context. Unless setting `enable` is set to False, this command will be spawned automatically every time GDB hits a breakpoint, a watchpoint, or any kind of interrupt. By default, it will show panes that contain the register states, the stack, and the disassembly code around $pc.
## function `ContextCommand.__init__`
```python
__init__() → None
```
---
#### property ContextCommand.settings
Return the list of settings for this command.
---
## function `ContextCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ContextCommand.addr_has_breakpoint`
```python
addr_has_breakpoint(address: int, bp_locations: List[str]) → bool
```
---
## function `ContextCommand.context_additional_information`
```python
context_additional_information() → None
```
---
## function `ContextCommand.context_args`
```python
context_args() → None
```
---
## function `ContextCommand.context_code`
```python
context_code() → None
```
---
## function `ContextCommand.context_memory`
```python
context_memory() → None
```
---
## function `ContextCommand.context_regs`
```python
context_regs() → None
```
---
## function `ContextCommand.context_source`
```python
context_source() → None
```
---
## function `ContextCommand.context_stack`
```python
context_stack() → None
```
---
## function `ContextCommand.context_threads`
```python
context_threads() → None
```
---
## function `ContextCommand.context_title`
```python
context_title(m: Optional[str]) → None
```
---
## function `ContextCommand.context_trace`
```python
context_trace() → None
```
---
## function `ContextCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ContextCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ContextCommand.empty_extra_messages`
```python
empty_extra_messages(_) → None
```
---
## function `ContextCommand.get_pc_context_info`
```python
get_pc_context_info(pc: int, line: str) → str
```
---
## function `ContextCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ContextCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ContextCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ContextCommand.line_has_breakpoint`
```python
line_has_breakpoint(
file_name: str,
line_number: int,
bp_locations: List[str]
) → bool
```
---
## function `ContextCommand.post_load`
```python
post_load() → None
```
---
## function `ContextCommand.pre_load`
```python
pre_load() → None
```
---
## function `ContextCommand.print_arguments_from_symbol`
```python
print_arguments_from_symbol(function_name: str, symbol: 'gdb.Symbol') → None
```
If symbols were found, parse them and print the argument adequately.
---
## function `ContextCommand.print_guessed_arguments`
```python
print_guessed_arguments(function_name: str) → None
```
When no symbol, read the current basic block and look for "interesting" instructions.
---
## function `ContextCommand.show_legend`
```python
show_legend() → None
```
---
## function `ContextCommand.update_registers`
```python
update_registers(_) → None
```
---
## function `ContextCommand.usage`
```python
usage() → None
```
---
## class `DereferenceCommand`
Dereference recursively from an address and display information. This acts like WinDBG `dps` command.
## function `DereferenceCommand.__init__`
```python
__init__() → None
```
---
#### property DereferenceCommand.settings
Return the list of settings for this command.
---
## function `DereferenceCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `DereferenceCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `DereferenceCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `DereferenceCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `DereferenceCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `DereferenceCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `DereferenceCommand.post_load`
```python
post_load() → None
```
---
## function `DereferenceCommand.pprint_dereferenced`
```python
pprint_dereferenced(addr: int, idx: int, base_offset: int = 0) → str
```
---
## function `DereferenceCommand.pre_load`
```python
pre_load() → None
```
---
## function `DereferenceCommand.usage`
```python
usage() → None
```
---
## class `DetailRegistersCommand`
Display full details on one, many or all registers value from current architecture.
## function `DetailRegistersCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property DetailRegistersCommand.settings
Return the list of settings for this command.
---
## function `DetailRegistersCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `DetailRegistersCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `DetailRegistersCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `DetailRegistersCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `DetailRegistersCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `DetailRegistersCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `DetailRegistersCommand.post_load`
```python
post_load() → None
```
---
## function `DetailRegistersCommand.pre_load`
```python
pre_load() → None
```
---
## function `DetailRegistersCommand.usage`
```python
usage() → None
```
---
## class `DisableContextOutputContext`
---
## class `Elf`
Basic ELF parsing. Ref:
- https://refspecs.linuxfoundation.org/elf/TIS1.1.pdf
- https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf
- https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
## function `Elf.__init__`
```python
__init__(path: Union[str, pathlib.Path]) → None
```
Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown.
---
#### property Elf.checksec
Check the security property of the ELF binary. The following properties are:
- Canary
- NX
- PIE
- Fortify
- Partial/Full RelRO. Return a dict() with the different keys mentioned above, and the boolean associated whether the protection was found.
---
#### property Elf.entry_point
---
## function `Elf.is_valid`
```python
is_valid(path: pathlib.Path) → bool
```
---
## function `Elf.read`
```python
read(size: int) → bytes
```
---
## function `Elf.read_and_unpack`
```python
read_and_unpack(fmt: str) → Tuple[Any, ...]
```
---
## function `Elf.seek`
```python
seek(off: int) → None
```
---
## class `ElfInfoCommand`
Display a limited subset of ELF header information. If no argument is provided, the command will show information about the current ELF being debugged.
## function `ElfInfoCommand.__init__`
```python
__init__() → None
```
---
#### property ElfInfoCommand.settings
Return the list of settings for this command.
---
## function `ElfInfoCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ElfInfoCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ElfInfoCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `ElfInfoCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ElfInfoCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ElfInfoCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ElfInfoCommand.post_load`
```python
post_load() → None
```
---
## function `ElfInfoCommand.pre_load`
```python
pre_load() → None
```
---
## function `ElfInfoCommand.usage`
```python
usage() → None
```
---
## class `Endianness`
An enumeration.
---
## class `EntryBreakBreakpoint`
Breakpoint used internally to stop execution at the most convenient entry point.
## function `EntryBreakBreakpoint.__init__`
```python
__init__(location: str) → None
```
---
## function `EntryBreakBreakpoint.stop`
```python
stop() → bool
```
---
## class `EntryPointBreakCommand`
Tries to find best entry point and sets a temporary breakpoint on it. The command will test for well-known symbols for entry points, such as `main`, `_main`, `__libc_start_main`, etc. defined by the setting `entrypoint_symbols`.
## function `EntryPointBreakCommand.__init__`
```python
__init__() → None
```
---
#### property EntryPointBreakCommand.settings
Return the list of settings for this command.
---
## function `EntryPointBreakCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `EntryPointBreakCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `EntryPointBreakCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `EntryPointBreakCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `EntryPointBreakCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `EntryPointBreakCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `EntryPointBreakCommand.post_load`
```python
post_load() → None
```
---
## function `EntryPointBreakCommand.pre_load`
```python
pre_load() → None
```
---
## function `EntryPointBreakCommand.set_init_tbreak`
```python
set_init_tbreak(addr: int) → EntryBreakBreakpoint
```
---
## function `EntryPointBreakCommand.set_init_tbreak_pie`
```python
set_init_tbreak_pie(addr: int, argv: List[str]) → EntryBreakBreakpoint
```
---
## function `EntryPointBreakCommand.usage`
```python
usage() → None
```
---
## class `ExternalStructureManager`
## function `ExternalStructureManager.__init__`
```python
__init__() → None
```
---
#### property ExternalStructureManager.modules
---
#### property ExternalStructureManager.path
---
#### property ExternalStructureManager.structures
---
#### handler ExternalStructureManager.find
---
## function `ExternalStructureManager.clear_caches`
```python
clear_caches() → None
```
---
## class `FileFormat`
## function `FileFormat.__init__`
```python
__init__(path: Union[str, pathlib.Path]) → None
```
---
## function `FileFormat.is_valid`
```python
is_valid(path: pathlib.Path) → bool
```
---
## class `FileFormatSection`
---
## class `FlagsCommand`
Edit flags in a human friendly way.
## function `FlagsCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property FlagsCommand.settings
Return the list of settings for this command.
---
## function `FlagsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `FlagsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `FlagsCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `FlagsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `FlagsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `FlagsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `FlagsCommand.post_load`
```python
post_load() → None
```
---
## function `FlagsCommand.pre_load`
```python
pre_load() → None
```
---
## function `FlagsCommand.usage`
```python
usage() → None
```
---
## class `FormatStringBreakpoint`
Inspect stack for format string.
## function `FormatStringBreakpoint.__init__`
```python
__init__(spec: str, num_args: int) → None
```
---
## function `FormatStringBreakpoint.stop`
```python
stop() → bool
```
---
## class `FormatStringSearchCommand`
Exploitable format-string helper: this command will set up specific breakpoints at well-known dangerous functions (printf, snprintf, etc.), and check if the pointer holding the format string is writable, and therefore susceptible to format string attacks if an attacker can control its content.
## function `FormatStringSearchCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property FormatStringSearchCommand.settings
Return the list of settings for this command.
---
## function `FormatStringSearchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `FormatStringSearchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `FormatStringSearchCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `FormatStringSearchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `FormatStringSearchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `FormatStringSearchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `FormatStringSearchCommand.post_load`
```python
post_load() → None
```
---
## function `FormatStringSearchCommand.pre_load`
```python
pre_load() → None
```
---
## function `FormatStringSearchCommand.usage`
```python
usage() → None
```
---
## class `GdbRemoveReadlineFinder`
---
## function `GdbRemoveReadlineFinder.find_module`
```python
find_module(fullname, path=None)
```
---
## function `GdbRemoveReadlineFinder.load_module`
```python
load_module(fullname)
```
---
## class `Gef`
The GEF root class, which serves as a entrypoint for all the debugging session attributes (architecture, memory, settings, etc.).
## function `Gef.__init__`
```python
__init__() → None
```
---
## function `Gef.reinitialize_managers`
```python
reinitialize_managers() → None
```
Reinitialize the managers. Avoid calling this function directly, using `pi reset()` is preferred
---
## function `Gef.reset_caches`
```python
reset_caches() → None
```
Recursively clean the cache of all the managers. Avoid calling this function directly, using `reset-cache` is preferred
---
## function `Gef.setup`
```python
setup() → None
```
Setup initialize the runtime setup, which may require for the `gef` to be not None.
---
## class `GefAlias`
Simple aliasing wrapper because GDB doesn't do what it should.
## function `GefAlias.__init__`
```python
__init__(
alias: str,
command: str,
completer_class: int = 0,
command_class: int = -1
) → None
```
---
## function `GefAlias.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## function `GefAlias.lookup_command`
```python
lookup_command(cmd: str) → Optional[Tuple[str, __main__.GenericCommand]]
```
---
## class `GefCommand`
GEF main command: view all new commands by typing `gef`.
## function `GefCommand.__init__`
```python
__init__() → None
```
---
#### property GefCommand.loaded_command_names
---
#### property GefCommand.loaded_commands
---
#### property GefCommand.loaded_functions
---
#### property GefCommand.missing_commands
---
## function `GefCommand.add_context_pane`
```python
add_context_pane(
pane_name: str,
display_pane_function: Callable,
pane_title_function: Callable,
condition: Optional[Callable]
) → None
```
Add a new context pane to ContextCommand.
---
## function `GefCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## function `GefCommand.load`
```python
load() → None
```
Load all the commands and functions defined by GEF into GDB.
---
## function `GefCommand.load_extra_plugins`
```python
load_extra_plugins() → int
```
---
## function `GefCommand.setup`
```python
setup() → None
```
---
## function `GefCommand.show_banner`
```python
show_banner() → None
```
---
## class `GefConfigCommand`
GEF configuration sub-command This command will help set/view GEF settings for the current debugging session. It is possible to make those changes permanent by running `gef save` (refer to this command help), and/or restore previously saved settings by running `gef restore` (refer help).
## function `GefConfigCommand.__init__`
```python
__init__() → None
```
---
## function `GefConfigCommand.complete`
```python
complete(text: str, word: str) → List[str]
```
---
## function `GefConfigCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GefConfigCommand.print_setting`
```python
print_setting(plugin_name: str, verbose: bool = False) → None
```
---
## function `GefConfigCommand.print_settings`
```python
print_settings() → None
```
---
## function `GefConfigCommand.set_setting`
```python
set_setting(argv: Tuple[str, Any]) → None
```
---
## class `GefFunctionsCommand`
List the convenience functions provided by GEF.
## function `GefFunctionsCommand.__init__`
```python
__init__() → None
```
---
#### property GefFunctionsCommand.settings
Return the list of settings for this command.
---
## function `GefFunctionsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GefFunctionsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GefFunctionsCommand.do_invoke`
```python
do_invoke(argv) → None
```
---
## function `GefFunctionsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GefFunctionsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GefFunctionsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GefFunctionsCommand.post_load`
```python
post_load() → None
```
---
## function `GefFunctionsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GefFunctionsCommand.usage`
```python
usage() → None
```
---
## class `GefHeapManager`
Class managing session heap.
## function `GefHeapManager.__init__`
```python
__init__() → None
```
---
#### property GefHeapManager.arenas
---
#### property GefHeapManager.base_address
---
#### property GefHeapManager.chunks
---
#### property GefHeapManager.main_arena
---
#### property GefHeapManager.malloc_alignment
---
#### property GefHeapManager.min_chunk_size
---
#### property GefHeapManager.selected_arena
---
#### handler GefHeapManager.find_main_arena_addr
---
## function `GefHeapManager.csize2tidx`
```python
csize2tidx(size: int) → int
```
---
## function `GefHeapManager.malloc_align_address`
```python
malloc_align_address(address: int) → int
```
Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github
---
## function `GefHeapManager.reset_caches`
```python
reset_caches() → None
```
---
## function `GefHeapManager.tidx2size`
```python
tidx2size(idx: int) → int
```
---
## class `GefHelpCommand`
GEF help sub-command.
## function `GefHelpCommand.__init__`
```python
__init__() → None
```
---
## function `GefHelpCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## class `GefInstallExtraScriptCommand`
`gef install` command: installs one or more scripts from the `gef-extras` script repo. Note that the command doesn't check for external dependencies the script(s) might require.
## function `GefInstallExtraScriptCommand.__init__`
```python
__init__() → None
```
---
## function `GefInstallExtraScriptCommand.invoke`
```python
invoke(argv: str, from_tty: bool) → None
```
---
## class `GefLibcManager`
Class managing everything libc-related (except heap).
## function `GefLibcManager.__init__`
```python
__init__() → None
```
---
#### property GefLibcManager.version
---
#### handler GefLibcManager.find_libc_version
---
## function `GefLibcManager.reset_caches`
```python
reset_caches() → None
```
Reset the LRU-cached attributes
---
## class `GefManager`
---
## function `GefManager.reset_caches`
```python
reset_caches() → None
```
Reset the LRU-cached attributes
---
## class `GefMemoryManager`
Class that manages memory access for gef.
## function `GefMemoryManager.__init__`
```python
__init__() → None
```
---
#### property GefMemoryManager.maps
---
## function `GefMemoryManager.read`
```python
read(addr: int, length: int = 16) → bytes
```
Return a `length` long byte array with the copy of the process memory at `addr`.
---
## function `GefMemoryManager.read_ascii_string`
```python
read_ascii_string(address: int) → Optional[str]
```
Read an ASCII string from memory
---
## function `GefMemoryManager.read_cstring`
```python
read_cstring(
address: int,
max_length: int = 50,
encoding: Optional[str] = None
) → str
```
Return a C-string read from memory.
---
## function `GefMemoryManager.read_integer`
```python
read_integer(addr: int) → int
```
Return an integer read from memory.
---
## function `GefMemoryManager.reset_caches`
```python
reset_caches() → None
```
---
## function `GefMemoryManager.write`
```python
write(address: int, buffer: ByteString, length: int = 16) → None
```
Write `buffer` at address `address`.
---
## class `GefMissingCommand`
GEF missing sub-command Display the GEF commands that could not be loaded, along with the reason of why they could not be loaded.
## function `GefMissingCommand.__init__`
```python
__init__() → None
```
---
## function `GefMissingCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## class `GefRemoteSessionManager`
Class for managing remote sessions with GEF. It will create a temporary environment designed to clone the remote one.
## function `GefRemoteSessionManager.__init__`
```python
__init__(
host: str,
port: int,
pid: int = -1,
qemu: Optional[pathlib.Path] = None
) → None
```
---
#### property GefRemoteSessionManager.auxiliary_vector
---
#### property GefRemoteSessionManager.canary
Return a tuple of the canary address and value, read from the canonical location if supported by the architecture. Otherwise, read from the auxiliary vector.
---
#### property GefRemoteSessionManager.cwd
---
#### property GefRemoteSessionManager.file
Path to the file being debugged as seen by the remote endpoint.
---
#### property GefRemoteSessionManager.lfile
Local path to the file being debugged.
---
#### property GefRemoteSessionManager.maps
---
#### property GefRemoteSessionManager.original_canary
Return a tuple of the initial canary address and value, read from the auxiliary vector.
---
#### property GefRemoteSessionManager.os
Return the current OS.
---
#### property GefRemoteSessionManager.pagesize
Get the system page size
---
#### property GefRemoteSessionManager.pid
Return the PID of the target process.
---
#### property GefRemoteSessionManager.root
---
#### property GefRemoteSessionManager.target
---
## function `GefRemoteSessionManager.close`
```python
close() → None
```
---
## function `GefRemoteSessionManager.connect`
```python
connect(pid: int) → bool
```
Connect to remote target. If in extended mode, also attach to the given PID.
---
## function `GefRemoteSessionManager.in_qemu_user`
```python
in_qemu_user() → bool
```
---
## function `GefRemoteSessionManager.remote_objfile_event_handler`
```python
remote_objfile_event_handler(evt: 'gdb.NewObjFileEvent') → None
```
---
## function `GefRemoteSessionManager.reset_caches`
```python
reset_caches() → None
```
---
## function `GefRemoteSessionManager.setup`
```python
setup() → bool
```
---
## function `GefRemoteSessionManager.sync`
```python
sync(src: str, dst: Optional[str] = None) → bool
```
Copy the `src` into the temporary chroot. If `dst` is provided, that path will be used instead of `src`.
---
## class `GefRestoreCommand`
GEF restore sub-command. Loads settings from file '~/.gef.rc' and apply them to the configuration of GEF.
## function `GefRestoreCommand.__init__`
```python
__init__() → None
```
---
## function `GefRestoreCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GefRestoreCommand.reload`
```python
reload(quiet: bool)
```
---
## class `GefRunCommand`
Override GDB run commands with the context from GEF. Simple wrapper for GDB run command to use arguments set from `gef set args`.
## function `GefRunCommand.__init__`
```python
__init__() → None
```
---
## function `GefRunCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## class `GefSaveCommand`
GEF save sub-command. Saves the current configuration of GEF to disk (by default in file '~/.gef.rc').
## function `GefSaveCommand.__init__`
```python
__init__() → None
```
---
## function `GefSaveCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## class `GefSessionManager`
Class managing the runtime properties of GEF.
## function `GefSessionManager.__init__`
```python
__init__() → None
```
---
#### property GefSessionManager.auxiliary_vector
---
#### property GefSessionManager.canary
Return a tuple of the canary address and value, read from the canonical location if supported by the architecture. Otherwise, read from the auxiliary vector.
---
#### property GefSessionManager.cwd
---
#### property GefSessionManager.file
Return a Path object of the target process.
---
#### property GefSessionManager.maps
Returns the Path to the procfs entry for the memory mapping.
---
#### property GefSessionManager.original_canary
Return a tuple of the initial canary address and value, read from the auxiliary vector.
---
#### property GefSessionManager.os
Return the current OS.
---
#### property GefSessionManager.pagesize
Get the system page size
---
#### property GefSessionManager.pid
Return the PID of the target process.
---
#### property GefSessionManager.root
Returns the path to the process's root directory.
---
## function `GefSessionManager.reset_caches`
```python
reset_caches() → None
```
---
## class `GefSetCommand`
Override GDB set commands with the context from GEF.
## function `GefSetCommand.__init__`
```python
__init__() → None
```
---
## function `GefSetCommand.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## class `GefSetting`
Basic class for storing gef settings as objects
## function `GefSetting.__init__`
```python
__init__(
value: Any,
cls: Optional[type] = None,
description: Optional[str] = None,
hooks: Optional[Dict[str, Callable]] = None
) → None
```
---
## class `GefSettingsManager`
GefSettings acts as a dict where the global settings are stored and can be read, written or deleted as any other dict. For instance, to read a specific command setting: `gef.config[mycommand.mysetting]`
---
## function `GefSettingsManager.raw_entry`
```python
raw_entry(name: str) → GefSetting
```
---
## class `GefThemeCommand`
Customize GEF appearance.
## function `GefThemeCommand.__init__`
```python
__init__() → None
```
---
#### property GefThemeCommand.settings
Return the list of settings for this command.
---
## function `GefThemeCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GefThemeCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
comfortable
## function `GefThemeCommand.do_invoke`
```python
do_invoke(args: List[str]) → None
```
---
## function `GefThemeCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GefThemeCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GefThemeCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GefThemeCommand.post_load`
```python
post_load() → None
```
---
## function `GefThemeCommand.pre_load`
```python
pre_load() → None
```
---
## function `GefThemeCommand.usage`
```python
usage() → None
```
---
## class `GefTmuxSetup`
Setup a comfortable tmux debugging environment.
## function `GefTmuxSetup.__init__`
```python
__init__() → None
```
---
## function `GefTmuxSetup.invoke`
```python
invoke(args: Any, from_tty: bool) → None
```
---
## function `GefTmuxSetup.screen_setup`
```python
screen_setup() → None
```
Hackish equivalent of the tmux_setup() function for screen.
---
## function `GefTmuxSetup.tmux_setup`
```python
tmux_setup() → None
```
Prepare the tmux environment by vertically splitting the current pane, and forcing the context to be redirected there.
---
## class `GefUiManager`
Class managing UI settings.
## function `GefUiManager.__init__`
```python
__init__() → None
```
---
## function `GefUiManager.reset_caches`
```python
reset_caches() → None
```
Reset the LRU-cached attributes
---
## class `GenericArchitecture`
---
#### property GenericArchitecture.endianness
---
#### property GenericArchitecture.fp
---
#### property GenericArchitecture.pc
---
#### property GenericArchitecture.ptrsize
---
#### property GenericArchitecture.registers
---
#### property GenericArchitecture.sp
---
## function `GenericArchitecture.canary_address`
```python
canary_address() → int
```
---
## function `GenericArchitecture.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `GenericArchitecture.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `GenericArchitecture.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `GenericArchitecture.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `GenericArchitecture.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `GenericArchitecture.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `GenericArchitecture.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `GenericArchitecture.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `GenericArchitecture.register`
```python
register(name: str) → int
```
---
## function `GenericArchitecture.reset_caches`
```python
reset_caches() → None
```
---
## function `GenericArchitecture.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `GenericCommand`
This is an abstract class for invoking commands, should not be instantiated.
## function `GenericCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property GenericCommand.settings
Return the list of settings for this command.
---
## function `GenericCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GenericCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GenericCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `GenericCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GenericCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GenericCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GenericCommand.post_load`
```python
post_load() → None
```
---
## function `GenericCommand.pre_load`
```python
pre_load() → None
```
---
## function `GenericCommand.usage`
```python
usage() → None
```
---
## class `GenericFunction`
This is an abstract class for invoking convenience functions, should not be instantiated.
## function `GenericFunction.__init__`
```python
__init__() → None
```
---
## function `GenericFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `GenericFunction.do_invoke`
```python
do_invoke(args: Any) → int
```
---
## function `GenericFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `GlibcArena`
Glibc arena class
## function `GlibcArena.__init__`
```python
__init__(addr: str) → None
```
---
#### property GlibcArena.addr
---
#### property GlibcArena.address
---
#### property GlibcArena.attached_threads
---
#### property GlibcArena.binmap
---
#### property GlibcArena.bins
---
#### property GlibcArena.fastbinsY
---
#### property GlibcArena.last_remainder
---
#### property GlibcArena.max_system_mem
---
#### property GlibcArena.next
---
#### property GlibcArena.next_free
---
#### property GlibcArena.sizeof
---
#### property GlibcArena.system_mem
---
#### property GlibcArena.top
---
## function `GlibcArena.bin`
```python
bin(i: int) → Tuple[int, int]
```
---
## function `GlibcArena.bin_at`
```python
bin_at(i) → int
```
---
## function `GlibcArena.fastbin`
```python
fastbin(i: int) → Optional[ForwardRef('GlibcFastChunk')]
```
Return head chunk in fastbinsY[i].
---
## function `GlibcArena.get_heap_for_ptr`
```python
get_heap_for_ptr(ptr: int) → int
```
Find the corresponding heap for a given pointer (int). See https://github.com/bminor/glibc/blob/glibc-2.34/malloc/arena.c#L129
---
## function `GlibcArena.get_heap_info_list`
```python
get_heap_info_list() → Optional[List[__main__.GlibcHeapInfo]]
```
---
## function `GlibcArena.heap_addr`
```python
heap_addr(allow_unaligned: bool = False) → Optional[int]
```
---
## function `GlibcArena.is_main_arena`
```python
is_main_arena() → bool
```
---
## function `GlibcArena.malloc_state_t`
```python
malloc_state_t() → Type[_ctypes.Structure]
```
---
## function `GlibcArena.reset`
```python
reset()
```
---
## function `GlibcArena.verify`
```python
verify(addr: int) → bool
```
Verify that the address matches a possible valid GlibcArena
---
## class `GlibcChunk`
Glibc chunk class. The default behavior (from_base=False) is to interpret the data starting at the memory address pointed to as the chunk data. Setting from_base to True instead treats that data as the chunk header. Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/.
## function `GlibcChunk.__init__`
```python
__init__(
addr: int,
from_base: bool = False,
allow_unaligned: bool = True
) → None
```
---
#### property GlibcChunk.bk
---
#### property GlibcChunk.bk_nextsize
---
#### property GlibcChunk.fd
---
#### property GlibcChunk.fd_nextsize
---
#### property GlibcChunk.flags
---
#### property GlibcChunk.prev_size
---
#### property GlibcChunk.size
---
#### property GlibcChunk.usable_size
---
## function `GlibcChunk.get_next_chunk`
```python
get_next_chunk(allow_unaligned: bool = False) → GlibcChunk
```
---
## function `GlibcChunk.get_next_chunk_addr`
```python
get_next_chunk_addr() → int
```
---
## function `GlibcChunk.get_prev_chunk_size`
```python
get_prev_chunk_size() → int
```
---
## function `GlibcChunk.get_usable_size`
```python
get_usable_size() → int
```
---
## function `GlibcChunk.has_m_bit`
```python
has_m_bit() → bool
```
---
## function `GlibcChunk.has_n_bit`
```python
has_n_bit() → bool
```
---
## function `GlibcChunk.has_p_bit`
```python
has_p_bit() → bool
```
---
## function `GlibcChunk.is_used`
```python
is_used() → bool
```
Check if the current block is used by:
- checking the M bit is true
- or checking that next chunk PREV_INUSE flag is true
---
## function `GlibcChunk.malloc_chunk_t`
```python
malloc_chunk_t() → Type[_ctypes.Structure]
```
---
## function `GlibcChunk.psprint`
```python
psprint() → str
```
---
## function `GlibcChunk.reset`
```python
reset()
```
---
## class `GlibcFastChunk`
## function `GlibcFastChunk.__init__`
```python
__init__(
addr: int,
from_base: bool = False,
allow_unaligned: bool = True
) → None
```
---
#### property GlibcFastChunk.bk
---
#### property GlibcFastChunk.bk_nextsize
---
#### property GlibcFastChunk.fd
---
#### property GlibcFastChunk.fd_nextsize
---
#### property GlibcFastChunk.flags
---
#### property GlibcFastChunk.prev_size
---
#### property GlibcFastChunk.size
---
#### property GlibcFastChunk.usable_size
---
## function `GlibcFastChunk.get_next_chunk`
```python
get_next_chunk(allow_unaligned: bool = False) → GlibcChunk
```
---
## function `GlibcFastChunk.get_next_chunk_addr`
```python
get_next_chunk_addr() → int
```
---
## function `GlibcFastChunk.get_prev_chunk_size`
```python
get_prev_chunk_size() → int
```
---
## function `GlibcFastChunk.get_usable_size`
```python
get_usable_size() → int
```
---
## function `GlibcFastChunk.has_m_bit`
```python
has_m_bit() → bool
```
---
## function `GlibcFastChunk.has_n_bit`
```python
has_n_bit() → bool
```
---
## function `GlibcFastChunk.has_p_bit`
```python
has_p_bit() → bool
```
---
## function `GlibcFastChunk.is_used`
```python
is_used() → bool
```
Check if the current block is used by:
- checking the M bit is true
- or checking that next chunk PREV_INUSE flag is true
---
## function `GlibcFastChunk.malloc_chunk_t`
```python
malloc_chunk_t() → Type[_ctypes.Structure]
```
---
## function `GlibcFastChunk.protect_ptr`
```python
protect_ptr(pos: int, pointer: int) → int
```
https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L339
---
## function `GlibcFastChunk.psprint`
```python
psprint() → str
```
---
## function `GlibcFastChunk.reset`
```python
reset()
```
---
## function `GlibcFastChunk.reveal_ptr`
```python
reveal_ptr(pointer: int) → int
```
https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L341
---
## class `GlibcHeapArenaCommand`
Display information on a heap chunk.
## function `GlibcHeapArenaCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property GlibcHeapArenaCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapArenaCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapArenaCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapArenaCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `GlibcHeapArenaCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapArenaCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapArenaCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapArenaCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapArenaCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapArenaCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapBinsCommand`
Display information on the bins on an arena (default: main_arena). See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.
## function `GlibcHeapBinsCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapBinsCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapBinsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapBinsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapBinsCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `GlibcHeapBinsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapBinsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapBinsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapBinsCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapBinsCommand.pprint_bin`
```python
pprint_bin(arena_addr: str, index: int, _type: str = '') → int
```
---
## function `GlibcHeapBinsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapBinsCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapChunkCommand`
Display information on a heap chunk. See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.
## function `GlibcHeapChunkCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapChunkCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapChunkCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapChunkCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapChunkCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapChunkCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapChunkCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapChunkCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapChunkCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapChunkCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapChunkCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapChunksCommand`
Display all heap chunks for the current arena. As an optional argument the base address of a different arena can be passed
## function `GlibcHeapChunksCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapChunksCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapChunksCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapChunksCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapChunksCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapChunksCommand.dump_chunks_arena`
```python
dump_chunks_arena(
arena: __main__.GlibcArena,
print_arena: bool = False,
allow_unaligned: bool = False
) → None
```
---
## function `GlibcHeapChunksCommand.dump_chunks_heap`
```python
dump_chunks_heap(
start: int,
end: int,
arena: __main__.GlibcArena,
allow_unaligned: bool = False
) → bool
```
---
## function `GlibcHeapChunksCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapChunksCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapChunksCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapChunksCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapChunksCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapChunksCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapCommand`
Base command to get information about the Glibc heap structure.
## function `GlibcHeapCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `GlibcHeapCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapFastbinsYCommand`
Display information on the fastbinsY on an arena (default: main_arena). See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.
## function `GlibcHeapFastbinsYCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapFastbinsYCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapFastbinsYCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapFastbinsYCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapFastbinsYCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapFastbinsYCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapFastbinsYCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapFastbinsYCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapFastbinsYCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapFastbinsYCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapFastbinsYCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapInfo`
Glibc heap_info struct
## function `GlibcHeapInfo.__init__`
```python
__init__(addr: Union[str, int]) → None
```
---
#### property GlibcHeapInfo.addr
---
#### property GlibcHeapInfo.address
---
#### property GlibcHeapInfo.heap_end
---
#### property GlibcHeapInfo.heap_start
---
#### property GlibcHeapInfo.sizeof
---
## function `GlibcHeapInfo.heap_info_t`
```python
heap_info_t() → Type[_ctypes.Structure]
```
---
## function `GlibcHeapInfo.reset`
```python
reset()
```
---
## class `GlibcHeapLargeBinsCommand`
Convenience command for viewing large bins.
## function `GlibcHeapLargeBinsCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapLargeBinsCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapLargeBinsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapLargeBinsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapLargeBinsCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapLargeBinsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapLargeBinsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapLargeBinsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapLargeBinsCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapLargeBinsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapLargeBinsCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapSetArenaCommand`
Set the address of the main_arena or the currently selected arena.
## function `GlibcHeapSetArenaCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapSetArenaCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapSetArenaCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapSetArenaCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapSetArenaCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapSetArenaCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapSetArenaCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapSetArenaCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapSetArenaCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapSetArenaCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapSetArenaCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapSmallBinsCommand`
Convenience command for viewing small bins.
## function `GlibcHeapSmallBinsCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapSmallBinsCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapSmallBinsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapSmallBinsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapSmallBinsCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapSmallBinsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapSmallBinsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapSmallBinsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapSmallBinsCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapSmallBinsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapSmallBinsCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapTcachebinsCommand`
Display information on the Tcachebins on an arena (default: main_arena). See https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc.
## function `GlibcHeapTcachebinsCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapTcachebinsCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapTcachebinsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapTcachebinsCommand.check_thread_ids`
```python
check_thread_ids(tids: List[int]) → List[int]
```
Check the validity, dedup, and return all valid tids.
---
## function `GlibcHeapTcachebinsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapTcachebinsCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `GlibcHeapTcachebinsCommand.find_tcache`
```python
find_tcache() → int
```
Return the location of the current thread's tcache.
---
## function `GlibcHeapTcachebinsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapTcachebinsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapTcachebinsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapTcachebinsCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapTcachebinsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapTcachebinsCommand.tcachebin`
```python
tcachebin(
tcache_base: int,
i: int
) → Tuple[Optional[__main__.GlibcTcacheChunk], int]
```
Return the head chunk in tcache[i] and the number of chunks in the bin.
---
## function `GlibcHeapTcachebinsCommand.usage`
```python
usage() → None
```
---
## class `GlibcHeapUnsortedBinsCommand`
Display information on the Unsorted Bins of an arena (default: main_arena). See: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1689.
## function `GlibcHeapUnsortedBinsCommand.__init__`
```python
__init__() → None
```
---
#### property GlibcHeapUnsortedBinsCommand.settings
Return the list of settings for this command.
---
## function `GlibcHeapUnsortedBinsCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GlibcHeapUnsortedBinsCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GlibcHeapUnsortedBinsCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `GlibcHeapUnsortedBinsCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GlibcHeapUnsortedBinsCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GlibcHeapUnsortedBinsCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GlibcHeapUnsortedBinsCommand.post_load`
```python
post_load() → None
```
---
## function `GlibcHeapUnsortedBinsCommand.pre_load`
```python
pre_load() → None
```
---
## function `GlibcHeapUnsortedBinsCommand.usage`
```python
usage() → None
```
---
## class `GlibcTcacheChunk`
## function `GlibcTcacheChunk.__init__`
```python
__init__(
addr: int,
from_base: bool = False,
allow_unaligned: bool = True
) → None
```
---
#### property GlibcTcacheChunk.bk
---
#### property GlibcTcacheChunk.bk_nextsize
---
#### property GlibcTcacheChunk.fd
---
#### property GlibcTcacheChunk.fd_nextsize
---
#### property GlibcTcacheChunk.flags
---
#### property GlibcTcacheChunk.prev_size
---
#### property GlibcTcacheChunk.size
---
#### property GlibcTcacheChunk.usable_size
---
## function `GlibcTcacheChunk.get_next_chunk`
```python
get_next_chunk(allow_unaligned: bool = False) → GlibcChunk
```
---
## function `GlibcTcacheChunk.get_next_chunk_addr`
```python
get_next_chunk_addr() → int
```
---
## function `GlibcTcacheChunk.get_prev_chunk_size`
```python
get_prev_chunk_size() → int
```
---
## function `GlibcTcacheChunk.get_usable_size`
```python
get_usable_size() → int
```
---
## function `GlibcTcacheChunk.has_m_bit`
```python
has_m_bit() → bool
```
---
## function `GlibcTcacheChunk.has_n_bit`
```python
has_n_bit() → bool
```
---
## function `GlibcTcacheChunk.has_p_bit`
```python
has_p_bit() → bool
```
---
## function `GlibcTcacheChunk.is_used`
```python
is_used() → bool
```
Check if the current block is used by:
- checking the M bit is true
- or checking that next chunk PREV_INUSE flag is true
---
## function `GlibcTcacheChunk.malloc_chunk_t`
```python
malloc_chunk_t() → Type[_ctypes.Structure]
```
---
## function `GlibcTcacheChunk.protect_ptr`
```python
protect_ptr(pos: int, pointer: int) → int
```
https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L339
---
## function `GlibcTcacheChunk.psprint`
```python
psprint() → str
```
---
## function `GlibcTcacheChunk.reset`
```python
reset()
```
---
## function `GlibcTcacheChunk.reveal_ptr`
```python
reveal_ptr(pointer: int) → int
```
https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L341
---
## class `GotBaseFunction`
Return the current GOT base address plus the given offset.
## function `GotBaseFunction.__init__`
```python
__init__() → None
```
---
## function `GotBaseFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `GotBaseFunction.do_invoke`
```python
do_invoke(args: List) → int
```
---
## function `GotBaseFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `GotCommand`
Display current status of the got inside the process.
## function `GotCommand.__init__`
```python
__init__()
```
---
#### property GotCommand.settings
Return the list of settings for this command.
---
## function `GotCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `GotCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `GotCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `GotCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `GotCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `GotCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `GotCommand.post_load`
```python
post_load() → None
```
---
## function `GotCommand.pre_load`
```python
pre_load() → None
```
---
## function `GotCommand.usage`
```python
usage() → None
```
---
## class `HeapAnalysisCommand`
Heap vulnerability analysis helper: this command aims to track dynamic heap allocation done through malloc()/free() to provide some insights on possible heap vulnerabilities. The following vulnerabilities are checked:
- NULL free
- Use-after-Free
- Double Free
- Heap overlap
## function `HeapAnalysisCommand.__init__`
```python
__init__() → None
```
---
#### property HeapAnalysisCommand.settings
Return the list of settings for this command.
---
## function `HeapAnalysisCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HeapAnalysisCommand.clean`
```python
clean(_: 'gdb.Event') → None
```
---
## function `HeapAnalysisCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HeapAnalysisCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `HeapAnalysisCommand.dump_tracked_allocations`
```python
dump_tracked_allocations() → None
```
---
## function `HeapAnalysisCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HeapAnalysisCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HeapAnalysisCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HeapAnalysisCommand.post_load`
```python
post_load() → None
```
---
## function `HeapAnalysisCommand.pre_load`
```python
pre_load() → None
```
---
## function `HeapAnalysisCommand.setup`
```python
setup() → None
```
---
## function `HeapAnalysisCommand.usage`
```python
usage() → None
```
---
## class `HeapBaseFunction`
Return the current heap base address plus an optional offset.
## function `HeapBaseFunction.__init__`
```python
__init__() → None
```
---
## function `HeapBaseFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `HeapBaseFunction.do_invoke`
```python
do_invoke(args: List) → int
```
---
## function `HeapBaseFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `HexdumpByteCommand`
Display SIZE lines of hexdump as BYTE from the memory location pointed by ADDRESS.
## function `HexdumpByteCommand.__init__`
```python
__init__() → None
```
---
#### property HexdumpByteCommand.settings
Return the list of settings for this command.
---
## function `HexdumpByteCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HexdumpByteCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HexdumpByteCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `HexdumpByteCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HexdumpByteCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HexdumpByteCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HexdumpByteCommand.post_load`
```python
post_load() → None
```
---
## function `HexdumpByteCommand.pre_load`
```python
pre_load() → None
```
---
## function `HexdumpByteCommand.usage`
```python
usage() → None
```
---
## class `HexdumpCommand`
Display SIZE lines of hexdump from the memory location pointed by LOCATION.
## function `HexdumpCommand.__init__`
```python
__init__() → None
```
---
#### property HexdumpCommand.settings
Return the list of settings for this command.
---
## function `HexdumpCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HexdumpCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HexdumpCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `HexdumpCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HexdumpCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HexdumpCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HexdumpCommand.post_load`
```python
post_load() → None
```
---
## function `HexdumpCommand.pre_load`
```python
pre_load() → None
```
---
## function `HexdumpCommand.usage`
```python
usage() → None
```
---
## class `HexdumpDwordCommand`
Display SIZE lines of hexdump as DWORD from the memory location pointed by ADDRESS.
## function `HexdumpDwordCommand.__init__`
```python
__init__() → None
```
---
#### property HexdumpDwordCommand.settings
Return the list of settings for this command.
---
## function `HexdumpDwordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HexdumpDwordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HexdumpDwordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `HexdumpDwordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HexdumpDwordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HexdumpDwordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HexdumpDwordCommand.post_load`
```python
post_load() → None
```
---
## function `HexdumpDwordCommand.pre_load`
```python
pre_load() → None
```
---
## function `HexdumpDwordCommand.usage`
```python
usage() → None
```
---
## class `HexdumpQwordCommand`
Display SIZE lines of hexdump as QWORD from the memory location pointed by ADDRESS.
## function `HexdumpQwordCommand.__init__`
```python
__init__() → None
```
---
#### property HexdumpQwordCommand.settings
Return the list of settings for this command.
---
## function `HexdumpQwordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HexdumpQwordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HexdumpQwordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `HexdumpQwordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HexdumpQwordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HexdumpQwordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HexdumpQwordCommand.post_load`
```python
post_load() → None
```
---
## function `HexdumpQwordCommand.pre_load`
```python
pre_load() → None
```
---
## function `HexdumpQwordCommand.usage`
```python
usage() → None
```
---
## class `HexdumpWordCommand`
Display SIZE lines of hexdump as WORD from the memory location pointed by ADDRESS.
## function `HexdumpWordCommand.__init__`
```python
__init__() → None
```
---
#### property HexdumpWordCommand.settings
Return the list of settings for this command.
---
## function `HexdumpWordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HexdumpWordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HexdumpWordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `HexdumpWordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HexdumpWordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HexdumpWordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HexdumpWordCommand.post_load`
```python
post_load() → None
```
---
## function `HexdumpWordCommand.pre_load`
```python
pre_load() → None
```
---
## function `HexdumpWordCommand.usage`
```python
usage() → None
```
---
## class `HighlightAddCommand`
Add a match to the highlight table.
## function `HighlightAddCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property HighlightAddCommand.settings
Return the list of settings for this command.
---
## function `HighlightAddCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HighlightAddCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HighlightAddCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `HighlightAddCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HighlightAddCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HighlightAddCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HighlightAddCommand.post_load`
```python
post_load() → None
```
---
## function `HighlightAddCommand.pre_load`
```python
pre_load() → None
```
---
## function `HighlightAddCommand.usage`
```python
usage() → None
```
---
## class `HighlightClearCommand`
Clear the highlight table, remove all matches.
## function `HighlightClearCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property HighlightClearCommand.settings
Return the list of settings for this command.
---
## function `HighlightClearCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HighlightClearCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HighlightClearCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `HighlightClearCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HighlightClearCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HighlightClearCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HighlightClearCommand.post_load`
```python
post_load() → None
```
---
## function `HighlightClearCommand.pre_load`
```python
pre_load() → None
```
---
## function `HighlightClearCommand.usage`
```python
usage() → None
```
---
## class `HighlightCommand`
Highlight user-defined text matches in GEF output universally.
## function `HighlightCommand.__init__`
```python
__init__() → None
```
---
#### property HighlightCommand.settings
Return the list of settings for this command.
---
## function `HighlightCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HighlightCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HighlightCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `HighlightCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HighlightCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HighlightCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HighlightCommand.post_load`
```python
post_load() → None
```
---
## function `HighlightCommand.pre_load`
```python
pre_load() → None
```
---
## function `HighlightCommand.usage`
```python
usage() → None
```
---
## class `HighlightListCommand`
Show the current highlight table with matches to colors.
## function `HighlightListCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property HighlightListCommand.settings
Return the list of settings for this command.
---
## function `HighlightListCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HighlightListCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HighlightListCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `HighlightListCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HighlightListCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HighlightListCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HighlightListCommand.post_load`
```python
post_load() → None
```
---
## function `HighlightListCommand.pre_load`
```python
pre_load() → None
```
---
## function `HighlightListCommand.print_highlight_table`
```python
print_highlight_table() → None
```
---
## function `HighlightListCommand.usage`
```python
usage() → None
```
---
## class `HighlightRemoveCommand`
Remove a match in the highlight table.
## function `HighlightRemoveCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property HighlightRemoveCommand.settings
Return the list of settings for this command.
---
## function `HighlightRemoveCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `HighlightRemoveCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `HighlightRemoveCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `HighlightRemoveCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `HighlightRemoveCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `HighlightRemoveCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `HighlightRemoveCommand.post_load`
```python
post_load() → None
```
---
## function `HighlightRemoveCommand.pre_load`
```python
pre_load() → None
```
---
## function `HighlightRemoveCommand.usage`
```python
usage() → None
```
---
## class `Instruction`
GEF representation of a CPU instruction.
## function `Instruction.__init__`
```python
__init__(
address: int,
location: str,
mnemo: str,
operands: List[str],
opcodes: bytes
) → None
```
---
## function `Instruction.is_valid`
```python
is_valid() → bool
```
---
## function `Instruction.size`
```python
size() → int
```
---
## class `MIPS`
---
#### property MIPS.endianness
---
#### property MIPS.fp
---
#### property MIPS.pc
---
#### property MIPS.ptrsize
---
#### property MIPS.registers
---
#### property MIPS.sp
---
## function `MIPS.canary_address`
```python
canary_address() → int
```
---
## function `MIPS.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `MIPS.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `MIPS.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `MIPS.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `MIPS.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `MIPS.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `MIPS.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `MIPS.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `MIPS.register`
```python
register(name: str) → int
```
---
## function `MIPS.reset_caches`
```python
reset_caches() → None
```
---
## function `MIPS.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `MIPS64`
---
#### property MIPS64.endianness
---
#### property MIPS64.fp
---
#### property MIPS64.pc
---
#### property MIPS64.ptrsize
---
#### property MIPS64.registers
---
#### property MIPS64.sp
---
## function `MIPS64.canary_address`
```python
canary_address() → int
```
---
## function `MIPS64.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `MIPS64.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `MIPS64.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `MIPS64.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `MIPS64.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `MIPS64.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `MIPS64.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `MIPS64.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `MIPS64.register`
```python
register(name: str) → int
```
---
## function `MIPS64.reset_caches`
```python
reset_caches() → None
```
---
## function `MIPS64.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
---
## class `MemoryCommand`
Add or remove address ranges to the memory view.
## function `MemoryCommand.__init__`
```python
__init__() → None
```
---
#### property MemoryCommand.settings
Return the list of settings for this command.
---
## function `MemoryCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `MemoryCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `MemoryCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `MemoryCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `MemoryCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `MemoryCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `MemoryCommand.post_load`
```python
post_load() → None
```
---
## function `MemoryCommand.pre_load`
```python
pre_load() → None
```
---
## function `MemoryCommand.usage`
```python
usage() → None
```
---
## class `MemoryUnwatchCommand`
Removes address ranges to the memory view.
## function `MemoryUnwatchCommand.__init__`
```python
__init__() → None
```
---
#### property MemoryUnwatchCommand.settings
Return the list of settings for this command.
---
## function `MemoryUnwatchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `MemoryUnwatchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `MemoryUnwatchCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `MemoryUnwatchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `MemoryUnwatchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `MemoryUnwatchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `MemoryUnwatchCommand.post_load`
```python
post_load() → None
```
---
## function `MemoryUnwatchCommand.pre_load`
```python
pre_load() → None
```
---
## function `MemoryUnwatchCommand.usage`
```python
usage() → None
```
---
## class `MemoryWatchCommand`
Adds address ranges to the memory view.
## function `MemoryWatchCommand.__init__`
```python
__init__() → None
```
---
#### property MemoryWatchCommand.settings
Return the list of settings for this command.
---
## function `MemoryWatchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `MemoryWatchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `MemoryWatchCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `MemoryWatchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `MemoryWatchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `MemoryWatchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `MemoryWatchCommand.post_load`
```python
post_load() → None
```
---
## function `MemoryWatchCommand.pre_load`
```python
pre_load() → None
```
---
## function `MemoryWatchCommand.usage`
```python
usage() → None
```
---
## class `MemoryWatchListCommand`
Lists all watchpoints to display in context layout.
## function `MemoryWatchListCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property MemoryWatchListCommand.settings
Return the list of settings for this command.
---
## function `MemoryWatchListCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `MemoryWatchListCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `MemoryWatchListCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `MemoryWatchListCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `MemoryWatchListCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `MemoryWatchListCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `MemoryWatchListCommand.post_load`
```python
post_load() → None
```
---
## function `MemoryWatchListCommand.pre_load`
```python
pre_load() → None
```
---
## function `MemoryWatchListCommand.usage`
```python
usage() → None
```
---
## class `MemoryWatchResetCommand`
Removes all watchpoints.
## function `MemoryWatchResetCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property MemoryWatchResetCommand.settings
Return the list of settings for this command.
---
## function `MemoryWatchResetCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `MemoryWatchResetCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `MemoryWatchResetCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `MemoryWatchResetCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `MemoryWatchResetCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `MemoryWatchResetCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `MemoryWatchResetCommand.post_load`
```python
post_load() → None
```
---
## function `MemoryWatchResetCommand.pre_load`
```python
pre_load() → None
```
---
## function `MemoryWatchResetCommand.usage`
```python
usage() → None
```
---
## class `NamedBreakpoint`
Breakpoint which shows a specified name, when hit.
## function `NamedBreakpoint.__init__`
```python
__init__(location: str, name: str) → None
```
---
## function `NamedBreakpoint.stop`
```python
stop() → bool
```
---
## class `NamedBreakpointCommand`
Sets a breakpoint and assigns a name to it, which will be shown, when it's hit.
## function `NamedBreakpointCommand.__init__`
```python
__init__() → None
```
---
#### property NamedBreakpointCommand.settings
Return the list of settings for this command.
---
## function `NamedBreakpointCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `NamedBreakpointCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `NamedBreakpointCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `NamedBreakpointCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `NamedBreakpointCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `NamedBreakpointCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `NamedBreakpointCommand.post_load`
```python
post_load() → None
```
---
## function `NamedBreakpointCommand.pre_load`
```python
pre_load() → None
```
---
## function `NamedBreakpointCommand.usage`
```python
usage() → None
```
---
## class `NopCommand`
Patch the instruction(s) pointed by parameters with NOP. Note: this command is architecture aware.
## function `NopCommand.__init__`
```python
__init__() → None
```
---
#### property NopCommand.settings
Return the list of settings for this command.
---
## function `NopCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `NopCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `NopCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `NopCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `NopCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `NopCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `NopCommand.post_load`
```python
post_load() → None
```
---
## function `NopCommand.pre_load`
```python
pre_load() → None
```
---
## function `NopCommand.usage`
```python
usage() → None
```
---
## class `PCustomCommand`
Dump user defined structure. This command attempts to reproduce WinDBG awesome `dt` command for GDB and allows to apply structures (from symbols or custom) directly to an address. Custom structures can be defined in pure Python using ctypes, and should be stored in a specific directory, whose path must be stored in the `pcustom.struct_path` configuration setting.
## function `PCustomCommand.__init__`
```python
__init__() → None
```
---
#### property PCustomCommand.settings
Return the list of settings for this command.
---
## function `PCustomCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PCustomCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PCustomCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PCustomCommand.explode_type`
```python
explode_type(arg: str) → Tuple[str, str]
```
---
## function `PCustomCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PCustomCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PCustomCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PCustomCommand.post_load`
```python
post_load() → None
```
---
## function `PCustomCommand.pre_load`
```python
pre_load() → None
```
---
## function `PCustomCommand.usage`
```python
usage() → None
```
---
## class `PCustomEditCommand`
PCustom: edit the content of a given structure
## function `PCustomEditCommand.__init__`
```python
__init__() → None
```
---
#### property PCustomEditCommand.settings
Return the list of settings for this command.
---
## function `PCustomEditCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PCustomEditCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PCustomEditCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PCustomEditCommand.explode_type`
```python
explode_type(arg: str) → Tuple[str, str]
```
---
## function `PCustomEditCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PCustomEditCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PCustomEditCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PCustomEditCommand.post_load`
```python
post_load() → None
```
---
## function `PCustomEditCommand.pre_load`
```python
pre_load() → None
```
---
## function `PCustomEditCommand.usage`
```python
usage() → None
```
---
## class `PCustomListCommand`
PCustom: list available structures
## function `PCustomListCommand.__init__`
```python
__init__() → None
```
---
#### property PCustomListCommand.settings
Return the list of settings for this command.
---
## function `PCustomListCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PCustomListCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PCustomListCommand.do_invoke`
```python
do_invoke(_: List) → None
```
Dump the list of all the structures and their respective.
---
## function `PCustomListCommand.explode_type`
```python
explode_type(arg: str) → Tuple[str, str]
```
---
## function `PCustomListCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PCustomListCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PCustomListCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PCustomListCommand.post_load`
```python
post_load() → None
```
---
## function `PCustomListCommand.pre_load`
```python
pre_load() → None
```
---
## function `PCustomListCommand.usage`
```python
usage() → None
```
---
## class `PCustomShowCommand`
PCustom: show the content of a given structure
## function `PCustomShowCommand.__init__`
```python
__init__() → None
```
---
#### property PCustomShowCommand.settings
Return the list of settings for this command.
---
## function `PCustomShowCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PCustomShowCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PCustomShowCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PCustomShowCommand.explode_type`
```python
explode_type(arg: str) → Tuple[str, str]
```
---
## function `PCustomShowCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PCustomShowCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PCustomShowCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PCustomShowCommand.post_load`
```python
post_load() → None
```
---
## function `PCustomShowCommand.pre_load`
```python
pre_load() → None
```
---
## function `PCustomShowCommand.usage`
```python
usage() → None
```
---
## class `PatchByteCommand`
Write specified BYTE to the specified address.
## function `PatchByteCommand.__init__`
```python
__init__() → None
```
---
#### property PatchByteCommand.settings
Return the list of settings for this command.
---
## function `PatchByteCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchByteCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchByteCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatchByteCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchByteCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchByteCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchByteCommand.post_load`
```python
post_load() → None
```
---
## function `PatchByteCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchByteCommand.usage`
```python
usage() → None
```
---
## class `PatchCommand`
Write specified values to the specified address.
## function `PatchCommand.__init__`
```python
__init__() → None
```
---
#### property PatchCommand.settings
Return the list of settings for this command.
---
## function `PatchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchCommand.post_load`
```python
post_load() → None
```
---
## function `PatchCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchCommand.usage`
```python
usage() → None
```
---
## class `PatchDwordCommand`
Write specified DWORD to the specified address.
## function `PatchDwordCommand.__init__`
```python
__init__() → None
```
---
#### property PatchDwordCommand.settings
Return the list of settings for this command.
---
## function `PatchDwordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchDwordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchDwordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatchDwordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchDwordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchDwordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchDwordCommand.post_load`
```python
post_load() → None
```
---
## function `PatchDwordCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchDwordCommand.usage`
```python
usage() → None
```
---
## class `PatchQwordCommand`
Write specified QWORD to the specified address.
## function `PatchQwordCommand.__init__`
```python
__init__() → None
```
---
#### property PatchQwordCommand.settings
Return the list of settings for this command.
---
## function `PatchQwordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchQwordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchQwordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatchQwordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchQwordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchQwordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchQwordCommand.post_load`
```python
post_load() → None
```
---
## function `PatchQwordCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchQwordCommand.usage`
```python
usage() → None
```
---
## class `PatchStringCommand`
Write specified string to the specified memory location pointed by ADDRESS.
## function `PatchStringCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PatchStringCommand.settings
Return the list of settings for this command.
---
## function `PatchStringCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchStringCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchStringCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PatchStringCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchStringCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchStringCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchStringCommand.post_load`
```python
post_load() → None
```
---
## function `PatchStringCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchStringCommand.usage`
```python
usage() → None
```
---
## class `PatchWordCommand`
Write specified WORD to the specified address.
## function `PatchWordCommand.__init__`
```python
__init__() → None
```
---
#### property PatchWordCommand.settings
Return the list of settings for this command.
---
## function `PatchWordCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatchWordCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatchWordCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatchWordCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatchWordCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatchWordCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatchWordCommand.post_load`
```python
post_load() → None
```
---
## function `PatchWordCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatchWordCommand.usage`
```python
usage() → None
```
---
## class `PatternCommand`
Generate or Search a De Bruijn Sequence of unique substrings of length N and a total length of LENGTH. The default value of N is set to match the currently loaded architecture.
## function `PatternCommand.__init__`
```python
__init__() → None
```
---
#### property PatternCommand.settings
Return the list of settings for this command.
---
## function `PatternCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatternCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatternCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `PatternCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatternCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatternCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatternCommand.post_load`
```python
post_load() → None
```
---
## function `PatternCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatternCommand.usage`
```python
usage() → None
```
---
## class `PatternCreateCommand`
Generate a De Bruijn Sequence of unique substrings of length N and a total length of LENGTH. The default value of N is set to match the currently loaded architecture.
## function `PatternCreateCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PatternCreateCommand.settings
Return the list of settings for this command.
---
## function `PatternCreateCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatternCreateCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatternCreateCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatternCreateCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatternCreateCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatternCreateCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatternCreateCommand.post_load`
```python
post_load() → None
```
---
## function `PatternCreateCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatternCreateCommand.usage`
```python
usage() → None
```
---
## class `PatternSearchCommand`
Search a De Bruijn Sequence of unique substrings of length N and a maximum total length of MAX_LENGTH. The default value of N is set to match the currently loaded architecture. The PATTERN argument can be a GDB symbol (such as a register name), a string or a hexadecimal value
## function `PatternSearchCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PatternSearchCommand.settings
Return the list of settings for this command.
---
## function `PatternSearchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PatternSearchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PatternSearchCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PatternSearchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PatternSearchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PatternSearchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PatternSearchCommand.post_load`
```python
post_load() → None
```
---
## function `PatternSearchCommand.pre_load`
```python
pre_load() → None
```
---
## function `PatternSearchCommand.search`
```python
search(pattern: str, size: int, period: int) → None
```
---
## function `PatternSearchCommand.usage`
```python
usage() → None
```
---
## class `Permission`
GEF representation of Linux permission.
---
## class `Phdr`
## function `Phdr.__init__`
```python
__init__(elf: __main__.Elf, off: int) → None
```
---
## class `PieAttachCommand`
Do attach with PIE breakpoint support.
## function `PieAttachCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieAttachCommand.settings
Return the list of settings for this command.
---
## function `PieAttachCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieAttachCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieAttachCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PieAttachCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieAttachCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieAttachCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieAttachCommand.post_load`
```python
post_load() → None
```
---
## function `PieAttachCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieAttachCommand.usage`
```python
usage() → None
```
---
## class `PieBreakpointCommand`
Set a PIE breakpoint at an offset from the target binaries base address.
## function `PieBreakpointCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieBreakpointCommand.settings
Return the list of settings for this command.
---
## function `PieBreakpointCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieBreakpointCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieBreakpointCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PieBreakpointCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieBreakpointCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieBreakpointCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieBreakpointCommand.post_load`
```python
post_load() → None
```
---
## function `PieBreakpointCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieBreakpointCommand.set_pie_breakpoint`
```python
set_pie_breakpoint(set_func: Callable[[int], str], addr: int) → None
```
---
## function `PieBreakpointCommand.usage`
```python
usage() → None
```
---
## class `PieCommand`
PIE breakpoint support.
## function `PieCommand.__init__`
```python
__init__() → None
```
---
#### property PieCommand.settings
Return the list of settings for this command.
---
## function `PieCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PieCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieCommand.post_load`
```python
post_load() → None
```
---
## function `PieCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieCommand.usage`
```python
usage() → None
```
---
## class `PieDeleteCommand`
Delete a PIE breakpoint.
## function `PieDeleteCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieDeleteCommand.settings
Return the list of settings for this command.
---
## function `PieDeleteCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieDeleteCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieDeleteCommand.delete_bp`
```python
delete_bp(breakpoints: List[__main__.PieVirtualBreakpoint]) → None
```
---
## function `PieDeleteCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PieDeleteCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieDeleteCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieDeleteCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieDeleteCommand.post_load`
```python
post_load() → None
```
---
## function `PieDeleteCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieDeleteCommand.usage`
```python
usage() → None
```
---
## class `PieInfoCommand`
Display breakpoint info.
## function `PieInfoCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieInfoCommand.settings
Return the list of settings for this command.
---
## function `PieInfoCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieInfoCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieInfoCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PieInfoCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieInfoCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieInfoCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieInfoCommand.post_load`
```python
post_load() → None
```
---
## function `PieInfoCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieInfoCommand.usage`
```python
usage() → None
```
---
## class `PieRemoteCommand`
Attach to a remote connection with PIE breakpoint support.
## function `PieRemoteCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieRemoteCommand.settings
Return the list of settings for this command.
---
## function `PieRemoteCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieRemoteCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieRemoteCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PieRemoteCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieRemoteCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieRemoteCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieRemoteCommand.post_load`
```python
post_load() → None
```
---
## function `PieRemoteCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieRemoteCommand.usage`
```python
usage() → None
```
---
## class `PieRunCommand`
Run process with PIE breakpoint support.
## function `PieRunCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property PieRunCommand.settings
Return the list of settings for this command.
---
## function `PieRunCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PieRunCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PieRunCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `PieRunCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PieRunCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PieRunCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PieRunCommand.post_load`
```python
post_load() → None
```
---
## function `PieRunCommand.pre_load`
```python
pre_load() → None
```
---
## function `PieRunCommand.usage`
```python
usage() → None
```
---
## class `PieVirtualBreakpoint`
PIE virtual breakpoint (not real breakpoint).
## function `PieVirtualBreakpoint.__init__`
```python
__init__(set_func: Callable[[int], str], vbp_num: int, addr: int) → None
```
---
## function `PieVirtualBreakpoint.destroy`
```python
destroy() → None
```
---
## function `PieVirtualBreakpoint.instantiate`
```python
instantiate(base: int) → None
```
---
## class `PowerPC`
---
#### property PowerPC.endianness
---
#### property PowerPC.fp
---
#### property PowerPC.pc
---
#### property PowerPC.registers
---
#### property PowerPC.sp
---
## function `PowerPC.canary_address`
```python
canary_address() → int
```
---
## function `PowerPC.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `PowerPC.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `PowerPC.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `PowerPC.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `PowerPC.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `PowerPC.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `PowerPC.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `PowerPC.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `PowerPC.register`
```python
register(name: str) → int
```
---
## function `PowerPC.reset_caches`
```python
reset_caches() → None
```
---
## function `PowerPC.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `PowerPC64`
---
#### property PowerPC64.endianness
---
#### property PowerPC64.fp
---
#### property PowerPC64.pc
---
#### property PowerPC64.registers
---
#### property PowerPC64.sp
---
## function `PowerPC64.canary_address`
```python
canary_address() → int
```
---
## function `PowerPC64.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `PowerPC64.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `PowerPC64.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `PowerPC64.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `PowerPC64.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `PowerPC64.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `PowerPC64.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `PowerPC64.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `PowerPC64.register`
```python
register(name: str) → int
```
---
## function `PowerPC64.reset_caches`
```python
reset_caches() → None
```
---
## function `PowerPC64.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `PrintFormatCommand`
Print bytes format in commonly used formats, such as literals in high level languages.
## function `PrintFormatCommand.__init__`
```python
__init__() → None
```
---
#### property PrintFormatCommand.format_matrix
---
#### property PrintFormatCommand.settings
Return the list of settings for this command.
---
## function `PrintFormatCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `PrintFormatCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `PrintFormatCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `PrintFormatCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `PrintFormatCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `PrintFormatCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `PrintFormatCommand.post_load`
```python
post_load() → None
```
---
## function `PrintFormatCommand.pre_load`
```python
pre_load() → None
```
---
## function `PrintFormatCommand.usage`
```python
usage() → None
```
---
## class `ProcessListingCommand`
List and filter process. If a PATTERN is given as argument, results shown will be grepped by this pattern.
## function `ProcessListingCommand.__init__`
```python
__init__() → None
```
---
#### property ProcessListingCommand.settings
Return the list of settings for this command.
---
## function `ProcessListingCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ProcessListingCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ProcessListingCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `ProcessListingCommand.get_processes`
```python
get_processes() → Generator[Dict[str, str], NoneType, NoneType]
```
---
## function `ProcessListingCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ProcessListingCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ProcessListingCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ProcessListingCommand.post_load`
```python
post_load() → None
```
---
## function `ProcessListingCommand.pre_load`
```python
pre_load() → None
```
---
## function `ProcessListingCommand.usage`
```python
usage() → None
```
---
## class `ProcessStatusCommand`
Extends the info given by GDB `info proc`, by giving an exhaustive description of the process status (file descriptors, ancestor, descendants, etc.).
## function `ProcessStatusCommand.__init__`
```python
__init__() → None
```
---
#### property ProcessStatusCommand.settings
Return the list of settings for this command.
---
## function `ProcessStatusCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ProcessStatusCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ProcessStatusCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ProcessStatusCommand.get_children_pids`
```python
get_children_pids(pid: int) → List[int]
```
---
## function `ProcessStatusCommand.get_cmdline_of`
```python
get_cmdline_of(pid: int) → str
```
---
## function `ProcessStatusCommand.get_process_path_of`
```python
get_process_path_of(pid: int) → str
```
---
## function `ProcessStatusCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ProcessStatusCommand.get_state_of`
```python
get_state_of(pid: int) → Dict[str, str]
```
---
## function `ProcessStatusCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ProcessStatusCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ProcessStatusCommand.list_sockets`
```python
list_sockets(pid: int) → List[int]
```
---
## function `ProcessStatusCommand.parse_ip_port`
```python
parse_ip_port(addr: str) → Tuple[str, int]
```
---
## function `ProcessStatusCommand.post_load`
```python
post_load() → None
```
---
## function `ProcessStatusCommand.pre_load`
```python
pre_load() → None
```
---
## function `ProcessStatusCommand.show_ancestor`
```python
show_ancestor() → None
```
---
## function `ProcessStatusCommand.show_connections`
```python
show_connections() → None
```
---
## function `ProcessStatusCommand.show_descendants`
```python
show_descendants() → None
```
---
## function `ProcessStatusCommand.show_fds`
```python
show_fds() → None
```
---
## function `ProcessStatusCommand.show_info_proc`
```python
show_info_proc() → None
```
---
## function `ProcessStatusCommand.usage`
```python
usage() → None
```
---
## class `RISCV`
---
#### property RISCV.endianness
---
#### property RISCV.fp
---
#### property RISCV.instruction_length
---
#### property RISCV.pc
---
#### property RISCV.ptrsize
---
#### property RISCV.registers
---
#### property RISCV.sp
---
## function `RISCV.canary_address`
```python
canary_address() → int
```
---
## function `RISCV.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `RISCV.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `RISCV.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `RISCV.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `RISCV.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `RISCV.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `RISCV.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `RISCV.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `RISCV.register`
```python
register(name: str) → int
```
---
## function `RISCV.reset_caches`
```python
reset_caches() → None
```
---
## function `RISCV.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `RedirectOutputContext`
## function `RedirectOutputContext.__init__`
```python
__init__(to: str = '/dev/null') → None
```
---
## class `RemoteCommand`
GDB `target remote` command on steroids. This command will use the remote procfs to create a local copy of the execution environment, including the target binary and its libraries in the local temporary directory (the value by default is in `gef.config.tempdir`). Additionally, it will fetch all the /proc/PID/maps and loads all its information. If procfs is not available remotely, the command will likely fail. You can however still use the limited command provided by GDB `target remote`.
## function `RemoteCommand.__init__`
```python
__init__() → None
```
---
#### property RemoteCommand.settings
Return the list of settings for this command.
---
## function `RemoteCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `RemoteCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `RemoteCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `RemoteCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `RemoteCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `RemoteCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `RemoteCommand.post_load`
```python
post_load() → None
```
---
## function `RemoteCommand.pre_load`
```python
pre_load() → None
```
---
## function `RemoteCommand.usage`
```python
usage() → None
```
---
## class `ResetCacheCommand`
Reset cache of all stored data. This command is here for debugging and test purposes, GEF handles properly the cache reset under "normal" scenario.
## function `ResetCacheCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ResetCacheCommand.settings
Return the list of settings for this command.
---
## function `ResetCacheCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ResetCacheCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ResetCacheCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `ResetCacheCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ResetCacheCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ResetCacheCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ResetCacheCommand.post_load`
```python
post_load() → None
```
---
## function `ResetCacheCommand.pre_load`
```python
pre_load() → None
```
---
## function `ResetCacheCommand.usage`
```python
usage() → None
```
---
## class `SPARC`
Refs:
- https://math-atlas.sourceforge.net/devel/assembly/SunUS_isa.pdf
---
#### property SPARC.endianness
---
#### property SPARC.fp
---
#### property SPARC.pc
---
#### property SPARC.ptrsize
---
#### property SPARC.registers
---
#### property SPARC.sp
---
## function `SPARC.canary_address`
```python
canary_address() → int
```
---
## function `SPARC.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `SPARC.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `SPARC.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `SPARC.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `SPARC.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `SPARC.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `SPARC.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `SPARC.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `SPARC.register`
```python
register(name: str) → int
```
---
## function `SPARC.reset_caches`
```python
reset_caches() → None
```
---
## function `SPARC.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `SPARC64`
Refs:
- http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_sparc.pdf
- https://cr.yp.to/2005-590/sparcv9.pdf
---
#### property SPARC64.endianness
---
#### property SPARC64.fp
---
#### property SPARC64.pc
---
#### property SPARC64.ptrsize
---
#### property SPARC64.registers
---
#### property SPARC64.sp
---
## function `SPARC64.canary_address`
```python
canary_address() → int
```
---
## function `SPARC64.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `SPARC64.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `SPARC64.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `SPARC64.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `SPARC64.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `SPARC64.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `SPARC64.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `SPARC64.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `SPARC64.register`
```python
register(name: str) → int
```
---
## function `SPARC64.reset_caches`
```python
reset_caches() → None
```
---
## function `SPARC64.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `ScanSectionCommand`
Search for addresses that are located in a memory mapping (haystack) that belonging to another (needle).
## function `ScanSectionCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ScanSectionCommand.settings
Return the list of settings for this command.
---
## function `ScanSectionCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ScanSectionCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ScanSectionCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ScanSectionCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ScanSectionCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ScanSectionCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ScanSectionCommand.post_load`
```python
post_load() → None
```
---
## function `ScanSectionCommand.pre_load`
```python
pre_load() → None
```
---
## function `ScanSectionCommand.usage`
```python
usage() → None
```
---
## class `SearchPatternCommand`
SearchPatternCommand: search a pattern in memory. If given an hex value (starting with 0x) the command will also try to look for upwards cross-references to this address.
## function `SearchPatternCommand.__init__`
```python
__init__() → None
```
---
#### property SearchPatternCommand.settings
Return the list of settings for this command.
---
## function `SearchPatternCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `SearchPatternCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `SearchPatternCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `SearchPatternCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `SearchPatternCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `SearchPatternCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `SearchPatternCommand.post_load`
```python
post_load() → None
```
---
## function `SearchPatternCommand.pre_load`
```python
pre_load() → None
```
---
## function `SearchPatternCommand.print_loc`
```python
print_loc(loc: Tuple[int, int, str]) → None
```
---
## function `SearchPatternCommand.print_section`
```python
print_section(section: __main__.Section) → None
```
---
## function `SearchPatternCommand.search_binpattern_by_address`
```python
search_binpattern_by_address(
binpattern: bytes,
start_address: int,
end_address: int
) → List[Tuple[int, int, Optional[str]]]
```
Search a binary pattern within a range defined by arguments.
---
## function `SearchPatternCommand.search_pattern`
```python
search_pattern(pattern: str, section_name: str) → None
```
Search a pattern within the whole userland memory.
---
## function `SearchPatternCommand.search_pattern_by_address`
```python
search_pattern_by_address(
pattern: str,
start_address: int,
end_address: int
) → List[Tuple[int, int, Optional[str]]]
```
Search a pattern within a range defined by arguments.
---
## function `SearchPatternCommand.usage`
```python
usage() → None
```
---
## class `Section`
GEF representation of process memory sections.
## function `Section.__init__`
```python
__init__(**kwargs: Any) → None
```
---
#### property Section.realpath
---
#### property Section.size
---
## function `Section.is_executable`
```python
is_executable() → bool
```
---
## function `Section.is_readable`
```python
is_readable() → bool
```
---
## function `Section.is_writable`
```python
is_writable() → bool
```
---
## class `SectionBaseFunction`
Return the matching file's base address plus an optional offset. Defaults to current file. Note that quotes need to be escaped
## function `SectionBaseFunction.__init__`
```python
__init__() → None
```
---
## function `SectionBaseFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `SectionBaseFunction.do_invoke`
```python
do_invoke(args: List) → int
```
---
## function `SectionBaseFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `Shdr`
## function `Shdr.__init__`
```python
__init__(elf: Optional[__main__.Elf], off: int) → None
```
---
## class `ShellcodeCommand`
ShellcodeCommand uses @JonathanSalwan simple-yet-awesome shellcode API to download shellcodes.
## function `ShellcodeCommand.__init__`
```python
__init__() → None
```
---
#### property ShellcodeCommand.settings
Return the list of settings for this command.
---
## function `ShellcodeCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ShellcodeCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ShellcodeCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `ShellcodeCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ShellcodeCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ShellcodeCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ShellcodeCommand.post_load`
```python
post_load() → None
```
---
## function `ShellcodeCommand.pre_load`
```python
pre_load() → None
```
---
## function `ShellcodeCommand.usage`
```python
usage() → None
```
---
## class `ShellcodeGetCommand`
Download shellcode from shell-storm's shellcode database.
## function `ShellcodeGetCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ShellcodeGetCommand.settings
Return the list of settings for this command.
---
## function `ShellcodeGetCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ShellcodeGetCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ShellcodeGetCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ShellcodeGetCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ShellcodeGetCommand.get_shellcode`
```python
get_shellcode(sid: int) → None
```
---
## function `ShellcodeGetCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ShellcodeGetCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ShellcodeGetCommand.post_load`
```python
post_load() → None
```
---
## function `ShellcodeGetCommand.pre_load`
```python
pre_load() → None
```
---
## function `ShellcodeGetCommand.usage`
```python
usage() → None
```
---
## class `ShellcodeSearchCommand`
Search pattern in shell-storm's shellcode database.
## function `ShellcodeSearchCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property ShellcodeSearchCommand.settings
Return the list of settings for this command.
---
## function `ShellcodeSearchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `ShellcodeSearchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `ShellcodeSearchCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `ShellcodeSearchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `ShellcodeSearchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `ShellcodeSearchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `ShellcodeSearchCommand.post_load`
```python
post_load() → None
```
---
## function `ShellcodeSearchCommand.pre_load`
```python
pre_load() → None
```
---
## function `ShellcodeSearchCommand.search_shellcode`
```python
search_shellcode(search_options: List) → None
```
---
## function `ShellcodeSearchCommand.usage`
```python
usage() → None
```
---
## class `SmartEvalCommand`
SmartEval: Smart eval (vague approach to mimic WinDBG `?`).
## function `SmartEvalCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property SmartEvalCommand.settings
Return the list of settings for this command.
---
## function `SmartEvalCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `SmartEvalCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `SmartEvalCommand.distance`
```python
distance(args: Tuple[str, str]) → None
```
---
## function `SmartEvalCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `SmartEvalCommand.evaluate`
```python
evaluate(expr: List[str]) → None
```
---
## function `SmartEvalCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `SmartEvalCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `SmartEvalCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `SmartEvalCommand.post_load`
```python
post_load() → None
```
---
## function `SmartEvalCommand.pre_load`
```python
pre_load() → None
```
---
## function `SmartEvalCommand.usage`
```python
usage() → None
```
---
## class `SolveKernelSymbolCommand`
Solve kernel symbols from kallsyms table.
## function `SolveKernelSymbolCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property SolveKernelSymbolCommand.settings
Return the list of settings for this command.
---
## function `SolveKernelSymbolCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `SolveKernelSymbolCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `SolveKernelSymbolCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `SolveKernelSymbolCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `SolveKernelSymbolCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `SolveKernelSymbolCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `SolveKernelSymbolCommand.post_load`
```python
post_load() → None
```
---
## function `SolveKernelSymbolCommand.pre_load`
```python
pre_load() → None
```
---
## function `SolveKernelSymbolCommand.usage`
```python
usage() → None
```
---
## class `StackOffsetFunction`
Return the current stack base address plus an optional offset.
## function `StackOffsetFunction.__init__`
```python
__init__() → None
```
---
## function `StackOffsetFunction.arg_to_long`
```python
arg_to_long(args: List, index: int, default: int = 0) → int
```
---
## function `StackOffsetFunction.do_invoke`
```python
do_invoke(args: List) → int
```
---
## function `StackOffsetFunction.invoke`
```python
invoke(*args: Any) → int
```
---
## class `StubBreakpoint`
Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.).
## function `StubBreakpoint.__init__`
```python
__init__(func: str, retval: Optional[int]) → None
```
---
## function `StubBreakpoint.stop`
```python
stop() → bool
```
---
## class `StubCommand`
Stub out the specified function. This function is useful when needing to skip one function to be called and disrupt your runtime flow (ex. fork).
## function `StubCommand.__init__`
```python
__init__() → None
```
---
#### property StubCommand.settings
Return the list of settings for this command.
---
## function `StubCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `StubCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `StubCommand.wrapper`
```python
wrapper(*args: Any, **kwargs: Any) → Callable
```
---
## function `StubCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `StubCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `StubCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `StubCommand.post_load`
```python
post_load() → None
```
---
## function `StubCommand.pre_load`
```python
pre_load() → None
```
---
## function `StubCommand.usage`
```python
usage() → None
```
---
## class `TraceFreeBreakpoint`
Track calls to free() and attempts to detect inconsistencies.
## function `TraceFreeBreakpoint.__init__`
```python
__init__() → None
```
---
## function `TraceFreeBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceFreeRetBreakpoint`
Internal temporary breakpoint to track free()d values.
## function `TraceFreeRetBreakpoint.__init__`
```python
__init__(addr: int) → None
```
---
## function `TraceFreeRetBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceMallocBreakpoint`
Track allocations done with malloc() or calloc().
## function `TraceMallocBreakpoint.__init__`
```python
__init__(name: str) → None
```
---
## function `TraceMallocBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceMallocRetBreakpoint`
Internal temporary breakpoint to retrieve the return value of malloc().
## function `TraceMallocRetBreakpoint.__init__`
```python
__init__(size: int, name: str) → None
```
---
## function `TraceMallocRetBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceReallocBreakpoint`
Track re-allocations done with realloc().
## function `TraceReallocBreakpoint.__init__`
```python
__init__() → None
```
---
## function `TraceReallocBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceReallocRetBreakpoint`
Internal temporary breakpoint to retrieve the return value of realloc().
## function `TraceReallocRetBreakpoint.__init__`
```python
__init__(ptr: int, size: int) → None
```
---
## function `TraceReallocRetBreakpoint.stop`
```python
stop() → bool
```
---
## class `TraceRunCommand`
Create a runtime trace of all instructions executed from $pc to LOCATION specified. The trace is stored in a text file that can be next imported in IDA Pro to visualize the runtime path.
## function `TraceRunCommand.__init__`
```python
__init__() → None
```
---
#### property TraceRunCommand.settings
Return the list of settings for this command.
---
## function `TraceRunCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `TraceRunCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `TraceRunCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `TraceRunCommand.get_frames_size`
```python
get_frames_size() → int
```
---
## function `TraceRunCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `TraceRunCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `TraceRunCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `TraceRunCommand.post_load`
```python
post_load() → None
```
---
## function `TraceRunCommand.pre_load`
```python
pre_load() → None
```
---
## function `TraceRunCommand.start_tracing`
```python
start_tracing(loc_start: int, loc_end: int, depth: int) → None
```
---
## function `TraceRunCommand.trace`
```python
trace(loc_start: int, loc_end: int, depth: int) → None
```
---
## function `TraceRunCommand.usage`
```python
usage() → None
```
---
## class `UafWatchpoint`
Custom watchpoints set TraceFreeBreakpoint() to monitor free()d pointers being used.
## function `UafWatchpoint.__init__`
```python
__init__(addr: int) → None
```
---
## function `UafWatchpoint.stop`
```python
stop() → bool
```
If this method is triggered, we likely have a UaF. Break the execution and report it.
---
## class `VMMapCommand`
Display a comprehensive layout of the virtual memory mapping. If a filter argument, GEF will filter out the mapping whose pathname do not match that filter.
## function `VMMapCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property VMMapCommand.settings
Return the list of settings for this command.
---
## function `VMMapCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `VMMapCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `VMMapCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `VMMapCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `VMMapCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `VMMapCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `VMMapCommand.is_integer`
```python
is_integer(n: str) → bool
```
---
## function `VMMapCommand.post_load`
```python
post_load() → None
```
---
## function `VMMapCommand.pre_load`
```python
pre_load() → None
```
---
## function `VMMapCommand.print_entry`
```python
print_entry(entry: __main__.Section) → None
```
---
## function `VMMapCommand.show_legend`
```python
show_legend() → None
```
---
## function `VMMapCommand.usage`
```python
usage() → None
```
---
## class `VersionCommand`
Display GEF version info.
## function `VersionCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property VersionCommand.settings
Return the list of settings for this command.
---
## function `VersionCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `VersionCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `VersionCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `VersionCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `VersionCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `VersionCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `VersionCommand.post_load`
```python
post_load() → None
```
---
## function `VersionCommand.pre_load`
```python
pre_load() → None
```
---
## function `VersionCommand.usage`
```python
usage() → None
```
---
## class `X86`
---
#### property X86.endianness
---
#### property X86.fp
---
#### property X86.pc
---
#### property X86.ptrsize
---
#### property X86.registers
---
#### property X86.sp
---
## function `X86.canary_address`
```python
canary_address() → int
```
---
## function `X86.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `X86.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
---
## function `X86.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `X86.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `X86.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `X86.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `X86.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `X86.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `X86.register`
```python
register(name: str) → int
```
---
## function `X86.reset_caches`
```python
reset_caches() → None
```
---
## function `X86.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `X86_64`
---
#### property X86_64.endianness
---
#### property X86_64.fp
---
#### property X86_64.pc
---
#### property X86_64.ptrsize
---
#### property X86_64.registers
---
#### property X86_64.sp
---
## function `X86_64.canary_address`
```python
canary_address() → int
```
---
## function `X86_64.flag_register_to_human`
```python
flag_register_to_human(val: Optional[int] = None) → str
```
---
## function `X86_64.get_ith_parameter`
```python
get_ith_parameter(i: int, in_func: bool = True) → Tuple[str, Optional[int]]
```
Retrieves the correct parameter used for the current function call.
---
## function `X86_64.get_ra`
```python
get_ra(insn: __main__.Instruction, frame: 'gdb.Frame') → Optional[int]
```
---
## function `X86_64.is_branch_taken`
```python
is_branch_taken(insn: __main__.Instruction) → Tuple[bool, str]
```
---
## function `X86_64.is_call`
```python
is_call(insn: __main__.Instruction) → bool
```
---
## function `X86_64.is_conditional_branch`
```python
is_conditional_branch(insn: __main__.Instruction) → bool
```
---
## function `X86_64.is_ret`
```python
is_ret(insn: __main__.Instruction) → bool
```
---
## function `X86_64.mprotect_asm`
```python
mprotect_asm(addr: int, size: int, perm: __main__.Permission) → str
```
---
## function `X86_64.register`
```python
register(name: str) → int
```
---
## function `X86_64.reset_caches`
```python
reset_caches() → None
```
---
## function `X86_64.supports_gdb_arch`
```python
supports_gdb_arch(gdb_arch: str) → Optional[bool]
```
If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.
---
## class `XAddressInfoCommand`
Retrieve and display runtime information for the location(s) given as parameter.
## function `XAddressInfoCommand.__init__`
```python
__init__() → None
```
---
#### property XAddressInfoCommand.settings
Return the list of settings for this command.
---
## function `XAddressInfoCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `XAddressInfoCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `XAddressInfoCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `XAddressInfoCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `XAddressInfoCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `XAddressInfoCommand.infos`
```python
infos(address: int) → None
```
---
## function `XAddressInfoCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `XAddressInfoCommand.post_load`
```python
post_load() → None
```
---
## function `XAddressInfoCommand.pre_load`
```python
pre_load() → None
```
---
## function `XAddressInfoCommand.usage`
```python
usage() → None
```
---
## class `XFilesCommand`
Shows all libraries (and sections) loaded by binary. This command extends the GDB command `info files`, by retrieving more information from extra sources, and providing a better display. If an argument FILE is given, the output will grep information related to only that file. If an argument name is also given, the output will grep to the name within FILE.
## function `XFilesCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property XFilesCommand.settings
Return the list of settings for this command.
---
## function `XFilesCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `XFilesCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `XFilesCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `XFilesCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `XFilesCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `XFilesCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `XFilesCommand.post_load`
```python
post_load() → None
```
---
## function `XFilesCommand.pre_load`
```python
pre_load() → None
```
---
## function `XFilesCommand.usage`
```python
usage() → None
```
---
## class `XorMemoryCommand`
XOR a block of memory. The command allows to simply display the result, or patch it runtime at runtime.
## function `XorMemoryCommand.__init__`
```python
__init__() → None
```
---
#### property XorMemoryCommand.settings
Return the list of settings for this command.
---
## function `XorMemoryCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `XorMemoryCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `XorMemoryCommand.do_invoke`
```python
do_invoke(_: List[str]) → None
```
---
## function `XorMemoryCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `XorMemoryCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `XorMemoryCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `XorMemoryCommand.post_load`
```python
post_load() → None
```
---
## function `XorMemoryCommand.pre_load`
```python
pre_load() → None
```
---
## function `XorMemoryCommand.usage`
```python
usage() → None
```
---
## class `XorMemoryDisplayCommand`
Display a block of memory pointed by ADDRESS by xor-ing each byte with KEY. The key must be provided in hexadecimal format.
## function `XorMemoryDisplayCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property XorMemoryDisplayCommand.settings
Return the list of settings for this command.
---
## function `XorMemoryDisplayCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `XorMemoryDisplayCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `XorMemoryDisplayCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `XorMemoryDisplayCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `XorMemoryDisplayCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `XorMemoryDisplayCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `XorMemoryDisplayCommand.post_load`
```python
post_load() → None
```
---
## function `XorMemoryDisplayCommand.pre_load`
```python
pre_load() → None
```
---
## function `XorMemoryDisplayCommand.usage`
```python
usage() → None
```
---
## class `XorMemoryPatchCommand`
Patch a block of memory pointed by ADDRESS by xor-ing each byte with KEY. The key must be provided in hexadecimal format.
## function `XorMemoryPatchCommand.__init__`
```python
__init__(*args: Any, **kwargs: Any) → None
```
---
#### property XorMemoryPatchCommand.settings
Return the list of settings for this command.
---
## function `XorMemoryPatchCommand.add_setting`
```python
add_setting(
name: str,
value: Tuple[Any, type, str],
description: str = ''
) → None
```
`add_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name] = value` instead
---
## function `XorMemoryPatchCommand.del_setting`
```python
del_setting(name: str) → None
```
`del_setting` is **DEPRECATED** and will be removed in the future.
Use `del self[setting_name]` instead
---
## function `XorMemoryPatchCommand.do_invoke`
```python
do_invoke(argv: List[str]) → None
```
---
## function `XorMemoryPatchCommand.get_setting`
```python
get_setting(name: str) → Any
```
`get_setting` is **DEPRECATED** and will be removed in the future.
Use `self[setting_name]` instead
---
## function `XorMemoryPatchCommand.has_setting`
```python
has_setting(name: str) → bool
```
`has_setting` is **DEPRECATED** and will be removed in the future.
Use `setting_name in self` instead
---
## function `XorMemoryPatchCommand.invoke`
```python
invoke(args: str, from_tty: bool) → None
```
---
## function `XorMemoryPatchCommand.post_load`
```python
post_load() → None
```
---
## function `XorMemoryPatchCommand.pre_load`
```python
pre_load() → None
```
---
## function `XorMemoryPatchCommand.usage`
```python
usage() → None
```
---
## class `Zone`
Zone(name, zone_start, zone_end, filename)
---
## class `classproperty`
Make the attribute a `classproperty`.
---
_This file was automatically generated via [lazydocs](https://github.com/ml-tooling/lazydocs)._
================================================
FILE: docs/api.md
================================================
## Extending GEF
`GEF` intends to provide a battery-included, quickly installable and crazy fast debugging
environment sitting on top of GDB.
But it most importantly provides all the primitives required to allow hackers to quickly create
their own commands. This page intends to summarize how to create advanced GDB commands in moments
using `GEF` as a library.
A [dedicated repository](https://github.com/hugsy/gef-extras) was born to host [external
scripts](https://github.com/hugsy/gef-extras/tree/main/scripts). This repo is open to all for
contributions, no restrictions and the most valuable ones will be integrated into `gef.py`.
## Quick start
Here is the most basic skeleton for creating a new `GEF` command named `newcmd`:
```python
@register
class NewCommand(GenericCommand):
"""Dummy new command."""
_cmdline_ = "newcmd"
_syntax_ = f"{_cmdline_}"
# optionally
# _examples_ = [f"{_cmdline_} arg1 ...", ]
# _aliases_ = ["alias_to_cmdline", ]
@only_if_gdb_running # not required, ensures that the debug session is started
def do_invoke(self, argv):
# let's say we want to print some info about the architecture of the current binary
print(f"gef.arch={gef.arch}")
# or showing the current $pc
print(f"gef.arch.pc={gef.arch.pc:#x}")
```
Loading it in `GEF` is as easy as
```text
gef➤ source /path/to/newcmd.py
[+] Loading 'NewCommand'
```
The new command is now loaded and part of GEF and can be invoked as such:
```text
gef➤ newcmd
gef.arch=<__main__.X86_64 object at 0x7fd5583571c0>
gef.arch.pc=0x55555555a7d0
```
Yes, that's it! Check out [the complete API](api/gef.md) to see what else GEF offers.
## Detailed explanation
Our new command must be a class that inherits from GEF's `GenericCommand`. The *only* requirements are:
* a `_cmdline_` attribute (the command to type on the GDB prompt).
* a `_syntax_` attribute, which GEF will use to auto-generate the help menu.
* a method `do_invoke(self, args)` which will be executed when the command is invoked. `args` is a
list of the command line args provided when invoked.
We make GEF aware of this new command by registering it in the `__main__` section of the script, by
invoking the global function `register_external_command()`.
Now you have a new GEF command which you can load, either from cli:
```text
gef➤ source /path/to/newcmd.py
```
or add to your `~/.gdbinit`:
```bash
echo source /path/to/newcmd.py >> ~/.gdbinit
```
## Customizing context panes
Sometimes you want something similar to a command to run on each break-like event and display itself
as a part of the GEF context. This can be achieved using the following
function `register_external_context_pane()`.
Here is a simple example of how to make a custom context pane:
```python
__start_time__ = int(time.time())
def wasted_time_debugging():
gef_print("You have wasted {} seconds!".format(int(time.time()) - __start_time__))
def wasted_time_debugging_title():
return "wasted:time:debugging:{}".format(int(time.time()) - __start_time__)
register_external_context_pane("wasted_time_debugging", wasted_time_debugging, wasted_time_debugging_title)
```
Loading it in `GEF` is as easy as loading a command
```text
gef➤ source /path/to/custom_context_pane.py
```
It can even be included in the same file as a Command. Now on each break you will notice a new pane
near the bottom of the context. The order can be modified in the `GEF` context config.
### Context Pane API
The API demonstrated above requires very specific argument types:
```python
def register_external_context_pane(
name: str,
context_callback: Callable[None,[]],
context_callback_title: Callable[str, []],
condition_callback: Optional[Callable[bool, []]] = None
) -> None:
```
* `name`: a string that will be used as the panes setting name
* `context_callback`: a function that uses `gef_print()` to print content in the pane
* `context_callback_title`: a function that returns the title string or None to hide the title
* `condition_callback` (optional): a function that returns a boolean deciding whether this context
pane should be shown
### Context Layout Mapping API
This API is designed for registering a new layout mapping for a GEF Context View. It specifies
the interface for the function register_external_context_layout_mapping which operates similarly
to the previously discussed register_external_context_pane. Pane must have been previously
established in the layout configuration.
```python
def register_external_context_layout_mapping(
current_pane_name: str,
display_pane_function: Callable[[], None],
pane_title_function: Callable[[], Optional[str]],
condition: Optional[Callable[[], bool]] = None
) -> None:
```
Registers a new mapping for an existing pane within the GEF Context View.
* `current_pane_name`: the name of an already registered pane in the layout
* `display_pane_function`: a function that prints content in the pane using `gef_print()`
* `pane_title_function`: a function that returns a string to be used as the pane title or
None if no title should be displayed
* `condition`: (optional) a predicate function that must return True for the pane content
and title to be displayed; if it returns False, the pane is skipped
## API
Some of the most important parts of the API for creating new commands are mentioned (but not limited
to) below. To see the full help of a function, open GDB and GEF, and use the embedded Python
interpreter's `help` command.
For example:
```bash
gef➤ pi help(Architecture)
```
or even from outside GDB:
```bash
gdb -q -ex 'pi help(hexdump)' -ex quit
```
The GEF API aims to provide a simpler and more Pythonic approach to GDB's.
Some basic examples:
* read the memory
```text
gef ➤ pi print(hexdump( gef.memory.read(parse_address("$pc"), length=0x20 )))
0x0000000000000000 f3 0f 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 ....1.I..^H..H..
0x0000000000000010 f0 50 54 4c 8d 05 66 0d 01 00 48 8d 0d ef 0c 01 .PTL..f...H.....
```
* get access to the memory layout
```text
gef ➤ pi print('\n'.join([ f"{x.page_start:#x} -> {x.page_end:#x}" for x in gef.memory.maps]))
0x555555554000 -> 0x555555558000
0x555555558000 -> 0x55555556c000
0x55555556c000 -> 0x555555575000
0x555555576000 -> 0x555555577000
0x555555577000 -> 0x555555578000
0x555555578000 -> 0x55555559a000
0x7ffff7cd8000 -> 0x7ffff7cda000
0x7ffff7cda000 -> 0x7ffff7ce1000
0x7ffff7ce1000 -> 0x7ffff7cf2000
0x7ffff7cf2000 -> 0x7ffff7cf7000
[...]
```
The API also offers a number of decorators to simplify the creation of new/existing commands, such as:
* `@only_if_gdb_running` to execute only if a GDB session is running.
* `@only_if_gdb_target_local` to check if the target is local i.e. not debugging using GDB `remote`.
* and many more...
### Reference
For a complete reference of the API offered by GEF, visit [`docs/api/gef.md`](api/gef.md).
### Parsing command arguments
```python
@parse_arguments( {"required_argument_1": DefaultValue1, ...}, {"--optional-argument-1": DefaultValue1, ...} )
```
This decorator aims to facilitate the argument passing to a command. If added, it will use the
`argparse` module to parse arguments, and will store them in the `kwargs["arguments"]` of the
calling function (therefore the function **must** have `*args, **kwargs` added to its signature).
Argument type is inferred directly from the default value **except** for boolean, where a value of
`True` corresponds to `argparse`'s `store_true` action. For more details on `argparse`, refer to its
Python documentation.
Values given for the parameters also allow list of arguments being past. This can be useful in the
case where the number of exact option values is known in advance. This can be achieved simply by
using a type of `tuple` or `list` for the default value. `parse_arguments` will determine the type
of what to expect based on the first default value of the iterable, so make sure it's not empty. For
instance:
```python
@parse_arguments( {"instructions": ["nop", "int3", "hlt"], }, {"--arch": "x64", } )
```
Argument flags are also supported, allowing to write simpler version of the flag such as
```python
@parse_arguments( {}, {("--long-argument", "-l"): value, } )
```
A basic example would be as follow:
```python
class MyCommand(GenericCommand):
[...]
@parse_arguments({"foo": [1,]}, {"--bleh": "", ("--blah", "-l): False})
def do_invoke(self, argv, *args, **kwargs):
args = kwargs["arguments"]
if args.foo == 1: ...
if args.blah == True: ...
```
When the user enters the following command:
```text
gef➤ mycommand --blah 3 14 159 2653
```
The function `MyCommand!do_invoke()` can use the command line argument value
```python
args.foo == [3, 14, 159, 2653] # a List(int) from user input
args.bleh == "" # the default value
args.blah == True # set to True because user input declared the option (would have been False otherwise)
```
### Adding new architectures
Support for new architectures can be added by inheriting from the `Architecture` class. Examples can
be found in [gef-extras](https://github.com/hugsy/gef-extras/tree/main/archs).
Sometimes architectures can more precisely determine whether they apply to the current target by
looking at the architecture determined by gdb. For these cases the custom architecture may implement
the `supports_gdb_arch()` static function to signal that they should be used instead of the default.
The function receives only one argument:
* `gdb_str` (of type `str`) which is the architecture name as reported by GDB.
The function **must** return:
* `True` if the current `Architecture` class supports the target binary; `False` otherwise.
* `None` to simply ignore this check and let GEF try to determine the architecture.
One example is the ARM Cortex-M architecture which in some cases should be used over the generic ARM
one:
```python
@staticmethod
def supports_gdb_arch(gdb_arch: str) -> Optional[bool]:
return bool(re.search("^armv.*-m$", gdb_arch))
```
================================================
FILE: docs/commands/aliases.md
================================================
## Command `aliases`
Base command to add, remove, and list `GEF` defined aliases.
```text
gef➤ aliases
aliases (add|rm|list)
```
### Adding/Removing Aliases
`GEF` defines its own aliasing mechanism which overrides the traditional alias that GDB provides
through the built-in command `alias`. To add a new alias, simply use the `aliases add` command. The
"command" parameter may contain spaces.
```text
aliases add [alias] [command]
```
To remove an alias, simply use the `aliases rm` command.
```text
aliases rm [alias]
```
### Listing Aliases
One can list aliases by using the `aliases ls` command. Some sample output of this command is seen
below.
```text
[+] Aliases defined:
fmtstr-helper → format-string-helper
telescope → dereference
dps → dereference
dq → hexdump qword
dd → hexdump dword
dw → hexdump word
dc → hexdump byte
cs-dis → capstone-disassemble
ctx → context
start-break → entry-break
ps → process-search
[...]
```
### Using the Configuration File
Users can also create/modify/delete aliases by editing the `GEF` configuration file, by default
located at `~/.gef.rc`. The aliases must be in the `aliases` section of the configuration file.
Creating a new alias is as simple as creating a new entry in this section:
```text
$ nano ~/.gef.rc
[...]
[aliases]
my-new-alias = gdb-or-gef-command
```
#### Bringing some PEDA and WinDBG flavours into GEF
For example, for those (like me) who use WinDBG and like its bindings, they can be integrated into
GDB via GEF aliases like this:
```text
$ nano ~/.gef.rc
[...]
[aliases]
# some windbg aliases
dps = dereference
dq = hexdump qword
dd = hexdump dword
dw = hexdump word
dc = hexdump byte
dt = pcustom
bl = info breakpoints
bp = break
be = enable breakpoints
bd = disable breakpoints
bc = delete breakpoints
tbp = tbreak
tba = thbreak
pa = advance
ptc = finish
t = stepi
p = nexti
g = gef run
uf = disassemble
```
Or here are some `PEDA` aliases for people used to using `PEDA` who made the smart move to `GEF`.
```text
# some peda aliases
telescope = dereference
start = entry-break
stack = dereference -l 10 $sp
argv = show args
kp = info stack
findmem = search-pattern
```
The aliases will be loaded next time you load GDB (and `GEF`). Or you can force `GEF` to reload the
settings with the command:
```text
gef➤ gef restore
```
================================================
FILE: docs/commands/arch.md
================================================
## Command `arch`
`arch` manages the loaded architecture.
There are 3 available sub-commands:
- `list`: List the installed architectures.
- `get`: Print the currently loaded architecture, and why it is selected.
- `set`: Manually set the loaded architecture by providing its name as an argument, or let
gef do magic to detect the architecture by not providing arguments.
> [!WARNING]
> Setting manually should be done as a last resort as GEF expects to find the architecture
> automatically. Force-setting the architecture can lead to unexpected behavior if not done correctly.

================================================
FILE: docs/commands/aslr.md
================================================
## Command `aslr`
Easily check, enable or disable ASLR on the debugged binary.
Check the status:
```text
gef➤ aslr
ASLR is currently disabled
```
Activate ASLR:
```text
gef➤ aslr on
[+] Enabling ASLR
gef➤ aslr
ASLR is currently enabled
```
De-activate ASLR:
```text
gef➤ aslr off
[+] Disabling ASLR
```
**Note**: This command cannot affect a process that has already been loaded, to which GDB attached
to later. The only way to disable this randomization is by setting the kernel setting
`/proc/sys/kernel/randomize_va_space` to 0..
================================================
FILE: docs/commands/canary.md
================================================
## Command `canary`
If the currently debugged process was compiled with the Smash Stack Protector (SSP) - i.e. the
`-fstack-protector` flag was passed to the compiler, this command will display the value of the
canary. This makes it convenient to avoid manually searching for this value in memory.
The command `canary` does not take any arguments.
```text
gef➤ canary
```

================================================
FILE: docs/commands/checksec.md
================================================
## Command `checksec`
The `checksec` command is inspired from [`checksec.sh`](https://www.trapkit.de/tools/checksec.html).
It provides a convenient way to determine which security protections are enabled in a binary.
You can use the command on the currently debugged process:
```text
gef➤ checksec
[+] checksec for '/vagrant/test-bin'
Canary: No
NX Support: Yes
PIE Support: No
No RPATH: Yes
No RUNPATH: Yes
Partial RelRO: Yes
Full RelRO: No
```
Or specify directly the binary to check, for example:
```bash
gdb -ex "checksec ./tests/test-x86"
```
================================================
FILE: docs/commands/config.md
================================================
## Command `gef config`
`gef` reads its config from a file which is by default located at `~/.gef.rc`, but which can also be
specified via the `GEF_RC` environment variable. In addition, `gef` can also be configured at
runtime with the `gef config` command.
To view all settings for all commands loaded:
```text
gef➤ gef config
```

Or to get one setting value:
```text
gef➤ gef config pcustom.struct_path
```
Of course you can edit the values. For example, if you want the screen to be cleared before
displaying the current context when reaching a breakpoint:
```text
gef➤ gef config context.clear_screen 1
```
To save the current settings for `GEF` to the file system to have those options persist across all
your future `GEF` sessions, simply run:
```text
gef➤ gef save
[+] Configuration saved to '/home/vagrant/.gef.rc'
```
Upon startup, if `$GEF_RC` points to an existing file, or otherwise if `${HOME}/.gef.rc` exists,
`gef` will automatically load its values.
To reload the settings during the session, just run:
```text
gef➤ gef restore
[+] Configuration from '/home/hugsy/.gef.rc' restored
```
You can tweak this configuration file outside your `gdb` session to suit your needs.
================================================
FILE: docs/commands/context.md
================================================
## Command `context`

`gef` (not unlike `PEDA` or `fG! famous gdbinit`) provides comprehensive context menu when hitting a
breakpoint.
* The register context box displays current register values. Values in red indicate that this
register has had its value changed since the last time execution stopped. It makes it convenient
to track values. Register values can be also accessed and/or dereferenced through the `reg`
command.
* The stack context box shows the 10 (by default but can be tweaked) entries in memory pointed by
the stack pointer register. If those values are pointers, they are successively dereferenced.
* The code context box shows the 10 (by default but can be tweaked) next instructions to be executed.
### Adding custom context panes
As well as using the built-in context panes, you can add your own custom pane that will be displayed
at each `break`-like event with all the other panes. Custom panes can be added using the API:
```python
register_external_context_pane(pane_name, display_pane_function, pane_title_function)
```
Check the [API](../api.md) documentation to see a full usage of the registration API.
### Editing context layout
`gef` allows you to configure your own setup for the display, by re-arranging the order with which
contexts will be displayed.
```text
gef➤ gef config context.layout
```
There are currently 6 sections that can be displayed:
* `legend` : a text explanation of the color code
* `regs` : the state of registers
* `stack` : the content of memory pointed by `$sp` register
* `code` : the code being executed
* `args` : if stopping at a function calls, print the call arguments
* `source` : if compiled with source, this will show the corresponding line of source code
* `threads` : all the threads
* `trace` : the execution call trace
* `extra` : if an automatic behavior is detected (vulnerable format string, heap vulnerability,
etc.) it will be displayed in this pane
* `memory` : peek into arbitrary memory locations
To hide a section, simply use the `context.layout` setting, and prepend the section name with `-` or
just omit it.
```text
gef➤ gef config context.layout "-legend regs stack code args -source -threads -trace extra memory"
```
This configuration will not display the `legend`, `source`, `threads`, and `trace` sections.
The `memory` pane will display the content of all locations specified by the
`memory` command. For instance,
```text
gef➤ memory watch $sp 0x40 byte
```
will print a hexdump version of 0x40 bytes of the stack. This command makes it convenient for
tracking the evolution of arbitrary locations in memory. Tracked locations can be removed one by one
using `memory unwatch`, or altogether with `memory reset`.
The size of most sections are also customizable:
* `nb_lines_stack` configures how many lines of the stack to show.
* `nb_lines_backtrack` configures how many lines of the backtrace to show.
* `nb_lines_code` and `nb_lines_code_prev` configure how many lines to show after and before the PC,
respectively.
* `context.nb_lines_threads` determines the number of lines to display inside the thread pane. This
is convenient when debugging heavily multi-threaded applications (apache2, firefox, etc.). It
receives an integer as value: if this value is `-1` then all threads state will be displayed.
Otherwise, if the value is set to `N`, then at most `N` thread states will be shown.
To have the stack displayed with the largest stack addresses on top (i.e., grow the stack downward),
enable the following setting:
```text
gef➤ gef config context.grow_stack_down True
```
If the saved instruction pointer is not within the portion of the stack being displayed, then a
section is created that includes the saved ip and depending on the architecture the frame pointer.
```text
0x00007fffffffc9e8│+0x00: 0x00007ffff7a2d830 → <__main+240> mov edi, eax ($current_frame_savedip)
0x00007fffffffc9e0│+0x00: 0x00000000004008c0 → <__init+0> push r15 ← $rbp
. . . (440 bytes skipped)
0x00007fffffffc7e8│+0x38: 0x0000000000000000
0x00007fffffffc7e0│+0x30: 0x0000000000000026 ("&"?)
0x00007fffffffc7d8│+0x28: 0x0000000001958ac0
0x00007fffffffc7d0│+0x20: 0x00007ffff7ffa2b0 → 0x5f6f7364765f5f00
0x00007fffffffc7c8│+0x18: 0x00007fff00000000
0x00007fffffffc7c0│+0x10: 0x00007fffffffc950 → 0x0000000000000000
0x00007fffffffc7b8│+0x08: 0x0000000000000000
0x00007fffffffc7b0│+0x00: 0x00007fffffffc7e4 → 0x0000000000000000 ← $rsp
```
### Redirecting context output to another tty/file
By default, the `gef` context will be displayed on the current TTY. This can be overridden by
setting `context.redirect` variable to have the context sent to another section.
To do so, select the TTY/file/socket/etc. you want the context redirected to with `gef config`.
Enter the command `tty` in the prompt:
```text
$ tty
/dev/pts/0
```
Then tell `gef` about it!
```text
gef➤ gef config context.redirect /dev/pts/0
```
Enjoy:

To go back to normal, remove the value:
```text
gef➤ gef config context.redirect ""
```
### Display individual sections
You can display a single section by specifying it as an argument:
```text
gef➤ context regs
```
Multiple sections can be provided, even if they are not part of the current layout:
```text
gef➤ context regs stack
```
### Examples
* Display the code section first, then register, and stack, hiding everything else:
```text
gef➤ gef config context.layout "code regs stack"
```
* Stop showing the context sections when breaking:
```text
gef➤ gef config context.enable 0
```
* Clear the screen before showing the context sections when breaking:
```text
gef➤ gef config context.clear_screen 1
```
* Don't dereference the registers in the `regs` section (more compact):
```text
gef➤ gef config context.show_registers_raw 1
```
* Number of bytes of opcodes to display next to the disassembly.
```text
gef➤ gef config context.show_opcodes_size 4
```
* Don't 'peek' into the start of functions that are called.
```text
gef➤ gef config context.peek_calls False
```
* Hide specific registers from the registers view.
```text
gef➤ gef config context.ignore_registers "$cs $ds $gs"
```
* Hide the extra pc context info from the source code view.
```text
gef➤ gef config context.show_source_code_variable_values 0
```
* Control how source file path is displayed.
When displaying the source file name, above the source code view, the following
settings can be changed:
```text
gef➤ gef config context.show_full_source_file_name_max_len 30
gef➤ gef config context.show_prefix_source_path_name_len 10
gef➤ gef config context.show_basename_source_file_name_max_len 20
```
In this example, if the file path length is less than or equal to 30 it will be
displayed in its entirety. If however, it's more than 30 characters in length,
it will be truncated.
Truncation first splits the path into the prefix part and file name part. The
`show_prefix_source_path_name_len` controls how many characters of the prefix
path to show, and the `show_basename_source_file_name_max_len` controls how
many characters from the file name to show.
* Show better definitions for call to libc functions.
```text
gef➤ gef config context.libc_args True
gef➤ gef config context.libc_args_path /path/to/gef-extras/libc_args
```
================================================
FILE: docs/commands/dereference.md
================================================
## Command `dereference`
The `dereference` command (also aliased `telescope` for PEDA former users) aims to simplify the
dereferencing of an address in GDB to determine the content it actually points to.
It is a useful convenience function to spare to process of manually tracking values with successive
`x/x` in GDB.
`dereference` takes three optional arguments, a start address (or symbol or register, etc) to
dereference (by default, `$sp`), the number of consecutive addresses to dereference (by default,
`10`) and the base location for offset calculation (by default the same as the start address):
```text
gef➤ dereference
0x00007fffffffdec0│+0x0000: 0x00007ffff7ffe190 → 0x0000555555554000 → jg 0x555555554047 ← $rsp, $r13
0x00007fffffffdec8│+0x0008: 0x00007ffff7ffe730 → 0x00007ffff7fd3000 → 0x00010102464c457f
0x00007fffffffded0│+0x0010: 0x00007ffff7faa000 → 0x00007ffff7de9000 → 0x03010102464c457f
0x00007fffffffded8│+0x0018: 0x00007ffff7ffd9f0 → 0x00007ffff7fd5000 → 0x00010102464c457f
0x00007fffffffdee0│+0x0020: 0x00007fffffffdee0 → [loop detected]
0x00007fffffffdee8│+0x0028: 0x00007fffffffdee0 → 0x00007fffffffdee0 → [loop detected]
0x00007fffffffdef0│+0x0030: 0x00000000f7fa57e3
0x00007fffffffdef8│+0x0038: 0x0000555555755d60 → 0x0000555555554a40 → cmp BYTE PTR [rip+0x201601], 0x0 # 0x555555756048
0x00007fffffffdf00│+0x0040: 0x0000000000000004
0x00007fffffffdf08│+0x0048: 0x0000000000000001
```
Here is an example with arguments:
```text
gef➤ telescope $rbp+0x10 -l 8
0x00007fffffffdf40│+0x0000: 0x00007ffff7fa5760 → 0x00000000fbad2887
0x00007fffffffdf48│+0x0008: 0x00000001f7e65b63
0x00007fffffffdf50│+0x0010: 0x0000000000000004
0x00007fffffffdf58│+0x0018: 0x0000000000000000
0x00007fffffffdf60│+0x0020: 0x00007fffffffdfa0 → 0x0000555555554fd0 → push r15
0x00007fffffffdf68│+0x0028: 0x0000555555554980 → xor ebp, ebp
0x00007fffffffdf70│+0x0030: 0x00007fffffffe080 → 0x0000000000000001
0x00007fffffffdf78│+0x0038: 0x0000000000000000
```
It also optionally accepts a second argument, the number of consecutive addresses to dereference (by
default, `10`).
For example, if you want to dereference all the stack entries inside a function context (on a 64bit
architecture):
```text
gef➤ p ($rbp - $rsp)/8
$3 = 4
gef➤ dereference -l 5
0x00007fffffffe170│+0x0000: 0x0000000000400690 → push r15 ← $rsp
0x00007fffffffe178│+0x0008: 0x0000000000400460 → xor ebp, ebp
0x00007fffffffe180│+0x0010: 0x00007fffffffe270 → 0x1
0x00007fffffffe188│+0x0018: 0x1
0x00007fffffffe190│+0x0020: 0x0000000000400690 → push r15 ← $rbp
```
It is possible to change the offset calculation to use a different address than the start address:
```text
gef➤ dereference $sp -l 7 -r $rbp
0x00007ffe6ddaa3e0│-0x0030: 0x0000000000000000 ← $rsp
0x00007ffe6ddaa3e8│-0x0028: 0x0000000000400970 → <__libc_csu_init+0> push r15
0x00007ffe6ddaa3f0│-0x0020: 0x0000000000000000
0x00007ffe6ddaa3f8│-0x0018: 0x00000000004006e0 → <_start+0> xor ebp, ebp
0x00007ffe6ddaa400│-0x0010: 0x00007ffe6ddaa500 → 0x0000000000000001
0x00007ffe6ddaa408│-0x0008: 0xa42456b3ee465800
0x00007ffe6ddaa410│+0x0000: 0x0000000000000000 ← $rbp
```
Just like with `x`, you can pass a negative number of addresses to dereference, to examine memory
backwards from the start address:
```text
gef➤ dereference $sp -l 3
0x00007fffffffcf90│+0x0010: 0x00007ffff7f5aaa0 → 0x0000000000000000
0x00007fffffffcf88│+0x0008: 0x00000000000204a0
0x00007fffffffcf80│+0x0000: 0x00005555555a6b60 → 0x0000000000000000 ← $rsp
gef➤ dereference $sp -l -3
0x00007fffffffcf80│+0x0000: 0x00005555555a6b60 → 0x0000000000000000 ← $rsp
0x00007fffffffcf78│-0x0008: 0x0000000000000020 (" "?)
0x00007fffffffcf70│-0x0010: 0x000000000000000a ("\n"?)
```
================================================
FILE: docs/commands/edit-flags.md
================================================
## Command `edit-flags`
The `edit-flags` command (alias: `flags`) provides a quick and comprehensible way to view and edit
the flag register for the architectures that support it. Without argument, the command will simply
return a human-friendly display of the register flags.
One or many arguments can be provided, following the syntax below:
```text
gef➤ flags [(+|-|~)FLAGNAME ...]
```
Where `FLAGNAME` is the name of the flag (case insensitive), and `+|-|~` indicates the action on
whether to set, unset, or toggle the flag.
For instance, on x86 architecture, if we don't want to take a conditional jump (e.g. a `jz`
instruction), but we want to have the Carry flag set, simply go with:
```text
gef➤ flags -ZERO +CARRY
```

================================================
FILE: docs/commands/elf-info.md
================================================
## Command `elf-info`
`elf-info` (alias `elf`) provides some basic information on the currently loaded ELF binary:
```text
gef➤ elf
Magic : 7f 45 4c 46
Class : 0x2 - 64-bit
Endianness : 0x1 - Little-Endian
Version : 0x1
OS ABI : 0x0 - System V
ABI Version : 0x0
Type : 0x2 - Executable
Machine : 0x3e - x86-64
Program Header Table : 0x0000000000000040
Section Header Table : 0x00000000000021a8
Header Table : 0x0000000000000040
ELF Version : 0x1
Header size : 64 (0x40)
Entry point : 0x0000000000400750
────────────────────────────────────────── Program Header ──────────────────────────────────────────
[ #] Type Offset Virtaddr Physaddr FileSiz MemSiz Flags Align
[ 0] PHDR 0x40 0x400040 0x400040 0x1f8 0x1f8 R-X 0x8
[ 1] INTERP 0x238 0x400238 0x400238 0x1c 0x1c R-- 0x1
[ 2] LOAD 0x0 0x400000 0x400000 0x1414 0x1414 R-X 0x200000
[ 3] LOAD 0x1e10 0x601e10 0x601e10 0x268 0x330 RW- 0x200000
[ 4] DYNAMIC 0x1e28 0x601e28 0x601e28 0x1d0 0x1d0 RW- 0x8
[ 5] NOTE 0x254 0x400254 0x400254 0x44 0x44 R-- 0x4
[ 6] GNU_EH_FLAME 0x11a0 0x4011a0 0x4011a0 0x74 0x74 R-- 0x4
[ 7] GNU_STACK 0x0 0x0 0x0 0x0 0x0 RW- 0x10
[ 8] GNU_RELRO 0x1e10 0x601e10 0x601e10 0x1f0 0x1f0 R-- 0x1
────────────────────────────────────────── Section Header ──────────────────────────────────────────
[ #] Name Type Address Offset Size EntSiz Flags Link Info Align
[ 0] NULL 0x0 0x0 0x0 0x0 0x0 0x0 0x0
[ 1] .interp PROGBITS 0x400238 0x238 0x1c 0x0 A 0x0 0x0 0x1
[ 2] .note.ABI-tag NOTE 0x400254 0x254 0x20 0x0 A 0x0 0x0 0x4
[ 3] .note.gnu.build-id NOTE 0x400274 0x274 0x24 0x0 A 0x0 0x0 0x4
[ 4] .gnu.hash GNU_HASH 0x400298 0x298 0x30 0x0 A 0x5 0x0 0x8
[ 5] .dynsym DYNSYM 0x4002c8 0x2c8 0x168 0x18 A 0x6 0x1 0x8
[ 6] .dynstr STRTAB 0x400430 0x430 0x96 0x0 A 0x0 0x0 0x1
[ 7] .gnu.version HIOS 0x4004c6 0x4c6 0x1e 0x2 A 0x5 0x0 0x2
[ 8] .gnu.version_r GNU_verneed 0x4004e8 0x4e8 0x30 0x0 A 0x6 0x1 0x8
[ 9] .rela.dyn RELA 0x400518 0x518 0x60 0x18 A 0x5 0x0 0x8
[10] .rela.plt RELA 0x400578 0x578 0xf0 0x18 AI 0x5 0x18 0x8
[11] .init PROGBITS 0x400668 0x668 0x1a 0x0 AX 0x0 0x0 0x4
[12] .plt PROGBITS 0x400690 0x690 0xb0 0x10 AX 0x0 0x0 0x10
[13] .plt.got PROGBITS 0x400740 0x740 0x8 0x0 AX 0x0 0x0 0x8
[14] .text PROGBITS 0x400750 0x750 0x842 0x0 AX 0x0 0x0 0x10
[15] .fini PROGBITS 0x400f94 0xf94 0x9 0x0 AX 0x0 0x0 0x4
[16] .rodata PROGBITS 0x400fa0 0xfa0 0x200 0x0 A 0x0 0x0 0x8
[17] .eh_frame_hdr PROGBITS 0x4011a0 0x11a0 0x74 0x0 A 0x0 0x0 0x4
[18] .eh_frame PROGBITS 0x401218 0x1218 0x1fc 0x0 A 0x0 0x0 0x8
[19] .init_array INIT_ARRAY 0x601e10 0x1e10 0x8 0x0 WA 0x0 0x0 0x8
[20] .fini_array FINI_ARRAY 0x601e18 0x1e18 0x8 0x0 WA 0x0 0x0 0x8
[21] .jcr PROGBITS 0x601e20 0x1e20 0x8 0x0 WA 0x0 0x0 0x8
[22] .dynamic DYNAMIC 0x601e28 0x1e28 0x1d0 0x10 WA 0x6 0x0 0x8
[23] .got PROGBITS 0x601ff8 0x1ff8 0x8 0x8 WA 0x0 0x0 0x8
[24] .got.plt PROGBITS 0x602000 0x2000 0x68 0x8 WA 0x0 0x0 0x8
[25] .data PROGBITS 0x602068 0x2068 0x10 0x0 WA 0x0 0x0 0x8
[26] .bss NOBITS 0x602080 0x2078 0xc0 0x0 WA 0x0 0x0 0x20
[27] .comment PROGBITS 0x0 0x2078 0x34 0x1 MS 0x0 0x0 0x1
[28] .shstrtab STRTAB 0x0 0x20ac 0xfc 0x0 0x0 0x0 0x1
```
Optionally a filepath to another ELF binary can be provided to view the basic information for that
binary instead.
```text
gef➤ elf-info --filename /path/to/elf/executable
```
================================================
FILE: docs/commands/entry-break.md
================================================
## Command `entry-break`
The `entry-break` (alias `start`) command's goal is to find and break at the most obvious entry
point available in the binary. Since the binary will start running, some of the `PLT` entries will
also be resolved, making further debugging easier.
It will perform the following actions:
1. Look up a `main` symbol. If found, set a temporary breakpoint and go.
2. Otherwise, it will look up for `__libc_start_main`. If found, set a temporary breakpoint and go.
3. Finally, if the previous two symbols are not found, it will get the entry point from the ELF
header, set a breakpoint and run. This case should never fail if the ELF binary has a valid
structure.

================================================
FILE: docs/commands/eval.md
================================================
## Command `$`
The `$` command attempts to mimic WinDBG `?` command.
When provided one argument, it will evaluate the expression, and try to display the result with
various formats:
```text
gef➤ $ $pc+1
93824992252977
0x555555559431
0b10101010101010101010101010101011001010000110001
b'UUUU\x941'
b'1\x94UUUU'
gef➤ $ -0x1000
-4096
0xfffffffffffff000
0b1111111111111111111111111111111111111111111111111111000000000000
b'\xff\xff\xff\xff\xff\xff\xf0\x00'
b'\x00\xf0\xff\xff\xff\xff\xff\xff'
```
With two arguments, it will simply compute the delta between them:
```text
gef➤ vmmap libc
Start End Offset Perm
0x00007ffff7812000 0x00007ffff79a7000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.24.so
0x00007ffff79a7000 0x00007ffff7ba7000 0x0000000000195000 --- /lib/x86_64-linux-gnu/libc-2.24.so
0x00007ffff7ba7000 0x00007ffff7bab000 0x0000000000195000 r-- /lib/x86_64-linux-gnu/libc-2.24.so
0x00007ffff7bab000 0x00007ffff7bad000 0x0000000000199000 rw- /lib/x86_64-linux-gnu/libc-2.24.so
gef➤ $ 0x00007ffff7812000 0x00007ffff79a7000
-1658880
1658880
gef➤ $ 1658880
1658880
0x195000
0b110010101000000000000
b'\x19P\x00'
b'\x00P\x19'
```
================================================
FILE: docs/commands/format-string-helper.md
================================================
## Command `format-string-helper`
The `format-string-helper` command will create a `GEF` specific type of breakpoints dedicated to
detecting potentially insecure format string when using the GlibC library.
It will use this new breakpoint against several targets, including:
* `printf()`
* `sprintf()`
* `fprintf()`
* `snprintf()`
* `vsnprintf()`
Just call the command to enable this functionality.
`fmtstr-helper` is a shorter alias.
```text
gef➤ fmtstr-helper
```
Then start the binary execution.
```text
gef➤ r
```
If a potentially insecure entry is found, the breakpoint will trigger, stop the process execution,
display the reason for trigger and the associated context.

================================================
FILE: docs/commands/functions.md
================================================
## Command `functions`
The `functions` command will list all of the [convenience
functions](https://sourceware.org/gdb/onlinedocs/gdb/Convenience-Funs.html) provided by GEF.
- `$_base([filepath])` -- Return the matching file's base address plus an optional offset.
Defaults to the current file. Note that quotes need to be escaped.
- `$_bss([offset])` -- Return the current bss base address plus the given offset.
- `$_got([offset])` -- Return the current bss base address plus the given offset.
- `$_heap([offset])` -- Return the current heap base address plus an optional offset.
- `$_stack([offset])` -- Return the current stack base address plus an optional offset.
These functions can be used as arguments to other commands to dynamically calculate values.
```text
gef➤ deref -l 4 $_heap()
0x0000000000602000│+0x00: 0x0000000000000000 ← $r8
0x0000000000602008│+0x08: 0x0000000000000021 ("!"?)
0x0000000000602010│+0x10: 0x0000000000000000 ← $rax, $rdx
0x0000000000602018│+0x18: 0x0000000000000000
gef➤ deref -l 4 $_heap(0x20)
0x0000000000602020│+0x00: 0x0000000000000000 ← $rsi
0x0000000000602028│+0x08: 0x0000000000020fe1
0x0000000000602030│+0x10: 0x0000000000000000
0x0000000000602038│+0x18: 0x0000000000000000
gef➤ deref -l 4 $_base(\"libc\")
0x00007ffff7da9000│+0x0000: 0x03010102464c457f
0x00007ffff7da9008│+0x0008: 0x0000000000000000
0x00007ffff7da9010│+0x0010: 0x00000001003e0003
0x00007ffff7da9018│+0x0018: 0x0000000000027c60
```
================================================
FILE: docs/commands/gef-remote.md
================================================
## Command `gef-remote`
[`target remote`](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Debugging.html#Remote-Debugging)
is the traditional GDB way of debugging process or system remotely. However this command by itself
does a limited job (80's bandwidth FTW) to collect more information about the target, making the
process of debugging more cumbersome. GEF greatly improves that state with the `gef-remote` command.
📝 **Note**: If using GEF, `gef-remote` **must** be your way or debugging remote processes, never
`target remote`. Maintainers will provide minimal support or help if you decide to use the
traditional `target remote` command. For many reasons, you **should not** use `target remote` alone
with GEF. It is still important to note that the default `target remote` command has been
overwritten by a minimal copy `gef-remote`, in order to make most tools relying on this command work.
`gef-remote` can function in 2 ways:
- `remote` which is meant to enrich use of GDB `target remote` command, when connecting to a "real"
gdbserver instance
- `qemu-mode` when connecting to GDB stab of either `qemu-user` or `qemu-system`.
The reason for this difference being that Qemu provides *a lot* less information that GEF can
extract to enrich debugging. Whereas GDBServer allows to download remote file (therefore allowing to
create a small identical environment), GDB stub in Qemu does not support file transfer. As a
consequence, in order to use GEF in qemu mode, it is required to provide the binary being debugged.
GEF will create a mock (limited) environment so that all its most useful features are available.
### Remote mode
#### `remote`
If you want to remotely debug a binary that you already have, you simply need to tell to `gdb` where
to find the debug information.
For example, if we want to debug `uname`, we do on the server:
```text
$ gdbserver :1234 /tmp/default.out
Process /tmp/default.out created; pid = 258932
Listening on port 1234
```

On the client, when the original `gdb` would use `target remote`, GEF's syntax is roughly similar
(shown running in debug mode for more verbose output, but you don't have to):
```text
$ gdb -ex 'gef config gef.debug 1'
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 10.2 using Python engine 3.8
gef➤ gef-remote localhost 1234
[=] [remote] initializing remote session with localhost:1234 under /tmp/tmp8qd0r7iw
[=] [remote] Installing new objfile handlers
[=] [remote] Enabling extended remote: False
[=] [remote] Executing 'target remote localhost:1234'
Reading /tmp/default.out from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /tmp/default.out from remote target...
Reading symbols from target:/tmp/default.out...
[=] [remote] in remote_objfile_handler(target:/tmp/default.out))
[=] [remote] downloading '/tmp/default.out' -> '/tmp/tmp8qd0r7iw/tmp/default.out'
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
[=] [remote] in remote_objfile_handler(/usr/lib/debug/.build-id/45/87364908de169dec62ffa538170118c1c3a078.debug))
[=] [remote] in remote_objfile_handler(target:/lib64/ld-linux-x86-64.so.2))
[=] [remote] downloading '/lib64/ld-linux-x86-64.so.2' -> '/tmp/tmp8qd0r7iw/lib64/ld-linux-x86-64.so.2'
[=] [remote] in remote_objfile_handler(system-supplied DSO at 0x7ffff7fcd000))
[*] [remote] skipping 'system-supplied DSO at 0x7ffff7fcd000'
0x00007ffff7fd0100 in _start () from target:/lib64/ld-linux-x86-64.so.2
[=] Setting up as remote session
[=] [remote] downloading '/proc/258932/maps' -> '/tmp/tmp8qd0r7iw/proc/258932/maps'
[=] [remote] downloading '/proc/258932/environ' -> '/tmp/tmp8qd0r7iw/proc/258932/environ'
[=] [remote] downloading '/proc/258932/cmdline' -> '/tmp/tmp8qd0r7iw/proc/258932/cmdline'
[...]
```
And finally breaking into the program, showing the current context:

You will also notice the prompt has changed to indicate the debugging mode is now "remote". Besides
that, all of GEF features are available:

#### `remote-extended`
Extended mode works the same as `remote`. Being an extended session, gdbserver has not spawned or
attached to any process. Therefore, all that's required is to add the `--pid` flag when calling
`gef-remote`, along with the process ID of the process to debug.
### Qemu mode
Qemu mode of `gef-remote` allows to connect to the [Qemu GDB
stub](https://qemu-project.gitlab.io/qemu/system/gdb.html) which allows to live debug into either a
binary (`qemu-user`) or even the kernel (`qemu-system`), of any architecture supported by GEF, which
makes now even more sense 😉 And using it is very straight forward.
#### `qemu-user`
1. Run `qemu-x86_64 :1234 /bin/ls`
2. Use `--qemu-user` and `--qemu-binary /bin/ls` when starting `gef-remote`

#### `qemu-system`
To test locally, you can [download the mini image linux x64
vm](https://mega.nz/file/ldQCDQiR#yJWJ8RXAHTxREKVmR7Hnfr70tIAQDFeWSYj96SvPO1k).
1. Run `./run.sh`
2. Use `--qemu-user` and `--qemu-binary vmlinuz` when starting `gef-remote`

### `rr` support
GEF can be used with the time-travel tool [`rr`](https://rr-project.org/) as it will act as a
remote session. Most of the commands will work as long as the debugged binary is present on the
target.
GEF can be loaded from `rr` as such in a very similar way it is loaded gdb. The `-x` command line
toggle can be passed load it as it would be for any gdbinit script
```text
$ cat ~/load-with-gef-extras
source ~/code/gef/gef.py
gef config gef.extra_plugins_dir ~/code/gef-extras/scripts
gef config pcustom.struct_path ~/code/gef-extras/structs
$ rr record /usr/bin/date
[...]
$ rr replay -x ~/load-with-gef-extras
[...]
(remote) gef➤ pi gef.binary
ELF('/usr/bin/date', ELF_64_BITS, X86_64)
(remote) gef➤ pi gef.session
Session(Remote, pid=3068, os='linux')
(remote) gef➤ pi gef.session.remote
RemoteSession(target=':0', local='/', pid=3068, mode=RR)
(remote) gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000000068000000 0x0000000068200000 0x0000000000200000 rwx
0x000000006fffd000 0x0000000070001000 0x0000000000004000 r-x /usr/lib/rr/librrpage.so
0x0000000070001000 0x0000000070002000 0x0000000000001000 rw- /tmp/rr-shared-preload_thread_locals-801763-0
0x00005580b30a3000 0x00005580b30a6000 0x0000000000003000 r-- /usr/bin/date
0x00005580b30a6000 0x00005580b30b6000 0x0000000000010000 r-x /usr/bin/date
0x00005580b30b6000 0x00005580b30bb000 0x0000000000005000 r-- /usr/bin/date
0x00005580b30bc000 0x00005580b30be000 0x0000000000002000 rw- /usr/bin/date
0x00007f21107c7000 0x00007f21107c9000 0x0000000000002000 r-- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x00007f21107c9000 0x00007f21107f3000 0x000000000002a000 r-x /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x00007f21107f3000 0x00007f21107fe000 0x000000000000b000 r-- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x00007f21107ff000 0x00007f2110803000 0x0000000000004000 rw- /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x00007ffcc951a000 0x00007ffcc953c000 0x0000000000022000 rw- [stack]
0x00007ffcc95ab000 0x00007ffcc95ad000 0x0000000000002000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x0000000000001000 --x [vsyscall]
(remote) gef➤ pi len(gef.memory.maps)
14
```
================================================
FILE: docs/commands/gef.md
================================================
## Command `gef`
### GEF Base Command
Displays a list of GEF commands and their descriptions.
```text
gef➤ gef
─────────────────────────────────── GEF - GDB Enhanced Features ───────────────────────────────────
$ -- SmartEval: Smart eval (vague approach to mimic WinDBG `?`).
aslr -- View/modify the ASLR setting of GDB. By default, GDB will disable ASLR when it starts the process. (i.e. not
attached). This command allows to change that setting.
assemble -- Inline code assemble. Architecture can be set in GEF runtime config (default x86-32). (alias: asm)
bincompare -- BincompareCommand: compare an binary file with the memory position looking for badchars.
bytearray -- BytearrayCommand: Generate a bytearray to be compared with possible badchars.
[...snip...]
```
### GEF Missing Command
GEF is fully battery-included. However in some rare cases, it is possible that not all commands be
loaded. If that's the case the command `gef missing` will detail which command failed to load, along
with a (likely) reason. Read the documentation for a solution, or reach out on the Discord.
```text
gef➤ gef missing
[*] Command `XXXX` is missing, reason → YYYYY.
```
### GEF Config Command
Allows the user to set/view settings for the current debugging session. For making the changes
persistent see the `gef save` entry.
Using `gef config` by itself just shows all of the available settings and their values.
```text
gef➤ gef config
──────────────────────────────────── GEF configuration settings ────────────────────────────────────
context.clear_screen (bool) = False
context.enable (bool) = True
context.grow_stack_down (bool) = False
context.ignore_registers (str) = ""
context.layout (str) = "-code -stack"
context.libc_args (bool) = False
[...snip...]
```
To filter the config settings you can use `gef config [setting]`.
```text
gef➤ gef config theme
─────────────────────────── GEF configuration settings matching 'theme' ───────────────────────────
theme.context_title_line (str) = "gray"
theme.context_title_message (str) = "cyan"
theme.default_title_line (str) = "gray"
theme.default_title_message (str) = "cyan"
[...snip...]
```
You can use `gef config [setting] [value]` to set a setting for the current session (see example
below).
```text
gef➤ gef config theme.address_stack blue
```
### GEF Save Command
The `gef save` command saves the current settings (set with `gef config`) to the user's `~/.gef.rc`
file (making the changes persistent).
```text
gef➤ gef save
[+] Configuration saved to '/home/michael/.gef.rc'
```
### GEF Restore Command
Using `gef restore` loads and applies settings from the `~/.gef.rc` file to the current session.
This is useful if you are modifying your GEF configuration file and want to see the changes without
completely reloading GEF.
```text
gef➤ gef restore
[+] Configuration from '/home/michael/.gef.rc' restored
```
### GEF Set Command
The GEF set command allows the user to use GEF context within GDB set commands. This is useful when
you want to make a convenient variable which can be set and referenced later.
```text
gef➤ gef set $a=1
```
### GEF Run Command
The GEF run command is a wrapper around GDB's run command, allowing the user to use GEF context
within the command.
```text
gef➤ gef run ./binary
```
### GEF Install Command
`gef install` allows to install one (or more) specific script(s) from `gef-extras`. The new scripts
will be downloaded and sourced to be used immediately after by GEF. The syntax is straight forward:
```text
gef➤ gef install SCRIPTNAME1 [SCRIPTNAME2...]
```
Where `SCRIPTNAME1` ... are the names of script from the [`gef-extras`
repository](https://github.com/hugsy/gef-extras/tree/main/scripts/).
```text
gef➤ gef install remote windbg stack
[+] Searching for 'remote.py' in `gef-extras@main`...
[+] Installed file '/tmp/gef/remote.py', new command(s) available: `rpyc-remote`
[+] Searching for 'windbg.py' in `gef-extras@main`...
[+] Installed file '/tmp/gef/windbg.py', new command(s) available: `pt`, `hh`, `tt`, `ptc`, `sxe`, `u`, `xs`, `tc`, `pc`, `g`, `r`
[+] Searching for 'stack.py' in `gef-extras@main`...
[+] Installed file '/tmp/gef/stack.py', new command(s) available: `current-stack-frame`
gef➤
```
This makes it easier to deploy new functionalities in limited environment. By default, the command
looks up for script names in the `main` branch of `gef-extras`. However you can change specify a
different branch through the `gef.default_branch` configuration setting:
```text
gef➤ gef config gef.default_branch my_other_branch
```
The files will be downloaded in the path configured in the `gef.extra_plugins_dir` setting, allowing
to reload it easily without having to re-download.
================================================
FILE: docs/commands/got.md
================================================
## Command `got`
Display the current state of GOT table of the running process.
The `got` command optionally takes function names and filters the output displaying only the
matching functions.
```text
gef➤ got [--all] [filters]
```
`--all` Print the GOT for all shared objects in addition to the executable file

The applied filter partially matches the name of the functions, so you can do something like this.
```text
gef➤ got str
gef➤ got print
gef➤ got read
```

Example of multiple partial filters:
```text
gef➤ got str get
```

```text
gef➤ got --all str get
```
Print relocatable symbols matching "str" or "get" in the executable and all shared object files.
**Note**: Because gdbserver does not canonicalize paths, the --all option does not work correctly
for remote debugging. See gdb bug [23764](https://sourceware.org/bugzilla/show_bug.cgi?id=23764)
================================================
FILE: docs/commands/heap-analysis-helper.md
================================================
## Command `heap-analysis-helper`
Please note: This feature is still under development, expect bugs and unstability.
`heap-analysis-helper` command aims to help the process of idenfitying Glibc heap inconsistencies by
tracking and analyzing allocations and deallocations of chunks of memory.
Currently, the following issues can be tracked:
- NULL free
- Use-after-Free
- Double Free
- Heap overlap
The helper can simply be activated by running the command `heap-analysis-helper`.
```text
gef➤ heap-analysis
[+] Tracking malloc()
[+] Tracking free()
[+] Disabling hardware watchpoints (this may increase the latency)
[+] Dynamic breakpoints correctly setup, GEF will break execution if a possible vulnerability is found.
[+] To disable, clear the malloc/free breakpoints (`delete breakpoints`) and restore hardware breakpoints (`set can-use-hw-watchpoints 1`)
```
The helper will create specifically crafted breakoints to keep tracks of allocation, which allows to
discover *potential* vulnerabilities. Once activated, one can disable the heap analysis breakpoints
simply by clearing the `__GI___libc_free()` et `__GI___libc_malloc()`. It is also possible to
enable/disable manually punctual checks via the `gef config` command.
The following settings are accepted:
- `check_null_free`: to break execution when a free(NULL) is encountered (disabled by default);
- `check_double_free`: to break execution when a double free is encountered;

- `check_weird_free`: to execution when `free()` is called against a non-tracked pointer;
- `check_uaf`: to break execution when a possible Use-after-Free condition is found.

Just like the format string vulnerability helper, the `heap-analysis-helper` can fail to detect
complex heap scenarios and/or provide some false positive alerts. Each finding must of course be
ascertained manually.
The `heap-analysis-helper` can also be used to simply track allocation and liberation of chunks of
memory. One can simply enable the tracking by setting all the configurations stated above to False:
```text
gef➤ gef config heap-analysis-helper.check_double_free False
gef➤ gef config heap-analysis-helper.check_free_null False
gef➤ gef config heap-analysis-helper.check_weird_free False
gef➤ gef config heap-analysis-helper.check_uaf False
```
Then `gef` will not notify you of any inconsistency detected, but simply display a clear message
when a chunk is allocated/freed.

To get information regarding the currently tracked chunks, use the `show` subcommand:
```text
gef➤ heap-analysis-helper show
```

================================================
FILE: docs/commands/heap.md
================================================
## Command `heap`
The `heap` command provides information on the heap chunk specified as argument. For the moment, it
only supports GlibC heap format (see [this
link](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#malloc_chunk) for `malloc`
structure information). Syntax to the subcommands is straight forward:
```text
gef➤ heap
```
## `main_arena` symbol
If the linked glibc of the target program does not have debugging symbols it might be tricky for GEF
to find the address of the `main_arena` which is needed for most of the `heap` subcommands. If you
know the offset of this symbol from the glibc base address you can use GEF's config to provide said
value:
```text
gef➤ gef config gef.main_arena_offset
```
If you do not know this offset and you want GEF to try and find it via bruteforce when executing a
`heap` command the next time, you can try this instead:
```text
gef➤ gef config gef.bruteforce_main_arena True
```
Note that this might take a few seconds to complete. If GEF does find the symbol you can then
calculate the offset to the libc base address and save it in the config.
Sometimes, the dump might not contain proper info to help GEF find the libc version, which results in
failure to parse the arena information. In this case, you can try to provide GEF a specific libc
version to use with the following command:
```text
gef➤ gef config gef.libc_version 2.31
```
### `heap chunks` command
Displays all the chunks from the `heap` section of the current arena.
```text
gef➤ heap chunks
```

To select from which arena to display chunks either use the `heap set-arena` command or provide the
base address of the other arena like this:
```text
gef➤ heap chunks [arena_address]
```

In order to display the chunks of all the available arenas at once use
```text
gef➤ heap chunks -a
```

Because usually the heap chunks are aligned to a certain number of bytes in memory GEF automatically
re-aligns the chunks data start addresses to match Glibc's behavior. To be able to view unaligned
chunks as well, you can disable this with the `--allow-unaligned` flag. Note that this might result
in incorrect output.
To get a higher level overview of the chunks you can use the `--summary` flag too.
```text
gef➤ heap chunks --summary
```

Sometimes, multiple types of objects could have the same size, hence it might not be enough only
knowing the chunk size when debugging issues like memory leaks. GEF supports using the vtable to
determine the type of the object stored in the chunk. To enable this feature, use `--resolve` along
with the `--summary` flag.
```text
gef➤ heap chunks --summary --resolve
```

Heap chunk command also supports filtering chunks by their size. To do so, simply provide the
`--min-size` or `--max-size` argument:
```text
gef➤ heap chunks --min-size 16 --max-size 32
```

The range is inclusive, so the above command will display all chunks with size >=16 and <=32.
If heap chunks command still gives too many chunks, we can use `--count` argument to limit the number
of the chunks in the output:
```text
gef➤ heap chunks --count 1
```

### `heap chunk` command
This command gives visual information of a Glibc malloc-ed chunked. Simply provide the address to
the user memory pointer of the chunk to show the information related to a specific chunk:
```text
gef➤ heap chunk [address]
```

Because usually the heap chunks are aligned to a certain number of bytes in memory GEF automatically
re-aligns the chunks data start addresses to match Glibc's behavior. To be able to view unaligned
chunks as well, you can disable this with the `--allow-unaligned` flag. Note that this might result
in incorrect output.
There is an optional `number` argument, to specify the number of chunks printed by this command. To
do so, simply provide the `--number` argument:
```text
gef➤ heap chunk --number 6 0x4e5400
Chunk(addr=0x4e5400, size=0xd0, flags=PREV_INUSE)
Chunk(addr=0x4e54d0, size=0x1a0, flags=PREV_INUSE)
Chunk(addr=0x4e5670, size=0x200, flags=PREV_INUSE)
Chunk(addr=0x4e5870, size=0xbc0, flags=PREV_INUSE)
Chunk(addr=0x4e6430, size=0x330, flags=PREV_INUSE)
Chunk(addr=0x4e6760, size=0x4c0, flags=PREV_INUSE)
```
### `heap arenas` command
Multi-threaded programs have different arenas, and the knowledge of the `main_arena` is not enough.
`gef` therefore provides the `arena` sub-commands to help you list all the arenas allocated in your
program **at the moment you call the command**.

### `heap set-arena` command
In cases where the debug symbol are not present (e.g. statically stripped binary), it is possible to
instruct GEF to find the `main_arena` at a different location with the command:
```text
gef➤ heap set-arena [address]
```
If the arena address is correct, all `heap` commands will be functional, and use the specified
address for `main_arena`.
### `heap bins` command
Glibc uses bins for keeping tracks of `free`d chunks. This is because making allocations through
`sbrk` (requiring a syscall) is costly. Glibc uses those bins to remember formerly allocated chunks.
Because bins are structured in single or doubly linked list, I found that quite painful to always
interrogate `gdb` to get a pointer address, dereference it, get the value chunk, etc... So I decided
to implement the `heap bins` sub-command, which allows to get info on:
- `fastbins`
- `bins`
- `unsorted`
- `small bins`
- `large bins`
- `tcachebins`
#### `heap bins fast` command
When exploiting heap corruption vulnerabilities, it is sometimes convenient to know the state of the
`fastbinsY` array.
The `fast` sub-command helps by displaying the list of fast chunks in this array. Without any other
argument, it will display the info of the `main_arena` arena. It accepts an optional argument, the
address of another arena (which you can easily find using `heap arenas`).
```text
gef➤ heap bins fast
──────────────────────── Fastbins for arena 0x7ffff7fb8b80 ────────────────────────
Fastbins[idx=0, size=0x20] ← Chunk(addr=0x555555559380, size=0x20, flags=PREV_INUSE)
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
```
#### Other `heap bins X` command
All the other subcommands (with the exception of `tcache`) for the `heap bins` work the same way as
`fast`. If no argument is provided, `gef` will fall back to `main_arena`. Otherwise, it will use the
address pointed as the base of the `malloc_state` structure and print out information accordingly.
#### `heap bins tcache` command
Modern versions of `glibc` use `tcache` bins to speed up multithreaded programs. Unlike other bins,
`tcache` bins are allocated on a per-thread basis, so there is one set of `tcache` bins for each
thread.
```text
gef➤ heap bins tcache [all] [thread_ids...]
```
Without any arguments, `heap bins tcache` will display the `tcache` for the current thread. `heap
bins tcache all` will show the `tcache`s for every thread, or you can specify any number of thread
ids to see the `tcache` for each of them. For example, use the following command to show the
`tcache` bins for threads 1 and 2.
```text
gef➤ heap bins tcache 1 2
```
================================================
FILE: docs/commands/help.md
================================================
## Command `gef help`
Displays the help menu for the loaded GEF commands.
```text
gef➤ gef help
```
================================================
FILE: docs/commands/hexdump.md
================================================
## Command `hexdump`
Imitation of the WinDBG command.
This command takes 4 optional arguments:
- The format for representing the data (by default, byte)
- A value/address/symbol used as the location to print the hexdump from (by default, $sp)
- The number of qword/dword/word/bytes to display (by default, 64 if the format is byte, 16
otherwise)
- The direction of output lines (by default, from low to high addresses)
`hexdump byte` will also try to display the ASCII character values if the byte is printable
(similarly to the `hexdump -C` command on Linux).
The syntax is as following:
```text
hexdump (qword|dword|word|byte) [-h] [--reverse] [--size SIZE] [address]
```
Examples:
- Display 4 QWORDs from `$pc`:
```text
gef➤ hexdump qword $pc --size 4
0x7ffff7a5c1c0+0000 │ 0x4855544155415641
0x7ffff7a5c1c0+0008 │ 0x0090ec814853cd89
0x7ffff7a5c1c0+0010 │ 0x377d6f058b480000
0x7ffff7a5c1c0+0018 │ 0x748918247c894800
```
- Display 32 bytes from a location in the stack:
```text
gef➤ hexdump byte 0x00007fffffffe5e5 --size 32
0x00007fffffffe5e5 2f 68 6f 6d 65 2f 68 75 67 73 79 2f 63 6f 64 65 /home/hugsy/code
0x00007fffffffe5f5 2f 67 65 66 2f 74 65 73 74 73 2f 77 69 6e 00 41 /gef/tests/win.A
```
- Display 8 WORDs from `$sp` in reverse order:
```text
gef➤ hexdump word 8 --reverse
0x00007fffffffe0ee│+0x000e 0x0000
0x00007fffffffe0ec│+0x000c 0x7fff
0x00007fffffffe0ea│+0x000a 0xffff
0x00007fffffffe0e8│+0x0008 0xe3f5
0x00007fffffffe0e6│+0x0006 0x0000
0x00007fffffffe0e4│+0x0004 0x0000
0x00007fffffffe0e2│+0x0002 0x0000
0x00007fffffffe0e0│+0x0000 0x0001
```
================================================
FILE: docs/commands/highlight.md
================================================
## Command `highlight`
This command sets up custom highlighting for user set strings.
Syntax:
```text
highlight (add|remove|list|clear)
```
Alias:
- `hl`
## Adding matches
The following will add `41414141`/`'AAAA'` as yellow, and `42424242`/`'BBBB'`
as blue:
```text
gef➤ hl add 41414141 yellow
gef➤ hl add 42424242 blue
gef➤ hl add AAAA yellow
gef➤ hl add BBBB blue
```
## Removing matches
To remove a match, target it by the original string used, ex.:
```text
gef➤ hl rm 41414141
```
## Listing matches
To list all matches with their colors:
```text
gef➤ hl list
41414141 | yellow
42424242 | blue
AAAA | yellow
BBBB | blue
```
## Clearing all matches
To clear all matches currently setup:
```text
gef➤ hl clear
```
## RegEx support
RegEx support is disabled by default, this is done for performance reasons.
To enable regular expressions on text matches:
```text
gef➤ gef config highlight.regex True
```
To check the current status:
```text
gef➤ gef config highlight.regex
highlight.regex (bool) = True
```
## Performance
_**NOTE:** Adding many matches may slow down debugging while using GEF. This includes enabling RegEx
support._
## Colors
To find a list of supported colors, check the [theme](./theme.md#changing-colors) documentation.
================================================
FILE: docs/commands/hijack-fd.md
================================================
## Command `hijack-fd`
`gef` can be used to modify file descriptors of the debugged process. The new
file descriptor can point to a file, a pipe, a socket, a device etc.
To use it, simply run
```text
gef➤ hijack-fd FDNUM NEWFILE
```
For instance,
```text
gef➤ hijack-fd 1 /dev/null
```
Will modify the current process file descriptors to redirect STDOUT to
`/dev/null`.
This command also supports connecting to an ip:port if it is provided as an
argument. For example
```text
gef➤ hijack-fd 0 localhost:8888
```
Will redirect STDIN to localhost:8888
Check out the tutorial on GEF's YouTube channel:
[](https://www.youtube.com/watch?v=Ss_QFeYkEvk)
================================================
FILE: docs/commands/memory.md
================================================
## Command `memory`
As long as the 'memory' section is enabled in your context layout (which it is by default), you can
register addresses, lengths, and grouping size.

_Note_: this command **should NOT** be mistaken with the [GDB `watch`
command](https://sourceware.org/gdb/current/onlinedocs/gdb/Set-Watchpoints.html) meant to set
breakpoints on memory access (read,write,exec).
### Adding a watch
Specify a location to watch and display with the context, along with their optional size and format:
Syntax:
```text
memory watch [SIZE] [(qword|dword|word|byte|pointers)]
```
If the format specified is `pointers`, then the output will be similar to executing the command
`dereference $address`. For all other format, the output will be an hexdump of the designated
location.
Note that the address format is a GDB therefore a symbol can be passed to it. It also supports [GEF
functions
format](https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html)
allowing to easily track commonly used addresses:
For example, to watch the first 5 entries of the [GOT](https://hugsy.github.io/gef/functions/got/)
as pointers:
```text
gef ➤ memory watch $_got()+0x18 5
[+] Adding memwatch to 0x555555773c50
```
Which, when the `context` is displayed, will show something like:

### Removing a watch
Remove a watched address. To list all the addresses being watched, use `memory list`.
Syntax:
```text
memory unwatch
```
### Listing watches
Enumerate all the addresses currently watched by the `memory` command.
Syntax:
```text
memory list
```
The command will output a list of all the addresses watched, along with the size and format to
display them as.
### Resetting watches
Empties the list of addresses to watch.
Syntax:
```text
memory reset
```
================================================
FILE: docs/commands/name-break.md
================================================
## Command `name-break`
The command `name-break` (alias `nb`) can be used to set a breakpoint on a location with a name
assigned to it.
Every time this breakpoint is hit, the specified name will also be shown in the `extra` section to
make it easier to keep an overview when using multiple breakpoints in a stripped binary.
`name-break name [address]`
`address` may be a linespec, address, or explicit location, same as specified for `break`. If
`address` isn't specified, it will create the breakpoint at the current instruction pointer address.
Examples:
- `nb first *0x400ec0`
- `nb "main func" main`
- `nb read_secret *main+149`
- `nb check_heap`
Example output:
```text
─────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x400e04 add eax, 0xfffbe6e8
0x400e09 dec ecx
0x400e0b ret
→ 0x400e0c push rbp
0x400e0d mov rbp, rsp
0x400e10 sub rsp, 0x50
0x400e14 mov QWORD PTR [rbp-0x48], rdi
0x400e18 mov QWORD PTR [rbp-0x50], rsi
0x400e1c mov rax, QWORD PTR fs:0x28
───────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe288│+0x0000: 0x0000000000401117 → movzx ecx, al ← $rsp
0x00007fffffffe290│+0x0008: 0x00007fffffffe4b8 → 0x00007fffffffe71d → "/ctf/t19/srv_copy"
0x00007fffffffe298│+0x0010: 0x0000000100000000
0x00007fffffffe2a0│+0x0018: 0x0000000000000000
0x00007fffffffe2a8│+0x0020: 0x0000000000000004
0x00007fffffffe2b0│+0x0028: 0x0000000000000000
───────────────────────────────────────────────────────────────────────────────── extra ────
[+] Hit breakpoint *0x400e0c (check_entry)
────────────────────────────────────────────────────────────────────────────────────────────
gef➤
```
================================================
FILE: docs/commands/nop.md
================================================
## Command `nop`
The `nop` command allows you to easily patch instructions with nops.
```text
nop [LOCATION] [--i ITEMS] [--f] [--n] [--b]
```
`LOCATION` address/symbol to patch (by default this command replaces whole instructions)
`--i ITEMS` number of items to insert (default 1)
`--f` Force patch even when the selected settings could overwrite partial instructions
`--n` Instead of replacing whole instructions, insert ITEMS nop instructions, no matter how many
instructions it overwrites
`--b` Instead of replacing whole instructions, fill ITEMS bytes with nops
nop the current instruction ($pc):
```text
gef➤ nop
```
nop an instruction at $pc+3 address:
```text
gef➤ nop $pc+3
```
nop two instructions at address $pc+3:
```text
gef➤ nop --i 2 $pc+3
```
Replace 1 byte with nop at current instruction ($pc):
```text
gef➤ nop --b
```
Replace 1 byte with nop at address $pc+3:
```text
gef➤ nop --b $pc+3
```
Replace 2 bytes with nop(s) (breaking the last instruction) at address $pc+3:
```text
gef➤ nop --f --b --i 2 $pc+3
```
Patch 2 nops at address $pc+3:
```text
gef➤ nop --n --i 2 $pc+3
```
================================================
FILE: docs/commands/patch.md
================================================
## Command `patch`
`patch` lets you easily patch the specified values to the specified address.
```bash
gef➤ patch byte $eip 0x90
gef➤ patch string $eip "cool!"
```
These commands copy the first 10 bytes of $rsp+8 to $rip:
```text
gef➤ print-format --lang bytearray -l 10 $rsp+8
Saved data b'\xcb\xe3\xff\xff\xff\x7f\x00\x00\x00\x00'... in '$_gef0'
gef➤ patch byte $rip $_gef0
```
Very handy to copy-paste-execute shellcodes/data from different memory regions.
================================================
FILE: docs/commands/pattern.md
================================================
## Command `pattern`
This command will create or search a [De Bruijn](https://en.wikipedia.org/wiki/De_Bruijn_sequence)
cyclic pattern to facilitate determining offsets in memory. The sequence consists of a number of
unique substrings of a chosen length.
It should be noted that for better compatibility, the algorithm implemented in `GEF` is the same as
the one in `pwntools`, and can therefore be used in conjunction.
### `pattern create`
```text
pattern create [-h] [-n N] [length]
```
The sub-command `create` allows one create a new De Bruijn sequence. The optional argument `n`
determines the length of unique subsequences. Its default value matches the currently loaded
architecture. The `length` argument sets the total length of the whole sequence.
```text
gef➤ pattern create -n 4 128
[+] Generating a pattern of 128 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaab
[+] Saved as '$_gef0'
```
The equivalent command with `pwntools` is
```python
from pwn import *
p = cyclic(128, n=8)
```
### `pattern search`
```text
pattern search [-h] [-n N] [--max-length MAX_LENGTH] [pattern]
```
The `search` sub-command seeks the `pattern` given as argument, trying to find its offset in the De
Bruijn sequence. The optional argument `n` determines the length of unique subsequences, and it
should usually match the length of `pattern`. Using `MAX_LENGTH` the maximum length of the sequence
to search in can be adjusted.
Note that the `pattern` can be passed as a GDB symbol (such as a register name), a string or a
hexadecimal value
```text
gef➤ pattern search 0x6161616161616167
[+] Searching '0x6161616161616167'
[+] Found at offset 48 (little-endian search) likely
[+] Found at offset 41 (big-endian search)
gef➤ pattern search $rbp
[+] Searching '$rbp'
[+] Found at offset 32 (little-endian search) likely
[+] Found at offset 25 (big-endian search)
gef➤ pattern search aaaaaaac
[+] Searching for 'aaaaaaac'
[+] Found at offset 16 (little-endian search) likely
[+] Found at offset 9 (big-endian search)
```
================================================
FILE: docs/commands/pcustom.md
================================================
## Command `pcustom`
`gef` provides a way to create and apply to the currently debugged environment, any new structure
(in the C-struct way). On top of simply displaying known and user-defined structures, it also allows
to apply those structures to the current context. It intends to mimic the very useful [WinDBG
`dt`](https://msdn.microsoft.com/en-us/library/windows/hardware/ff542772(v=vs.85).aspx) command.
This is achieved via the command `pcustom` (for `print custom`), or you can use its alias, `dt` (in
reference to the WinDBG command) as provided by the [`WinDbg compatibility
extension`](https://github.com/hugsy/gef-extras/blob/main/scripts/windbg.py)
### Configuration
New structures can be stored in the location given by the configuration setting:
```text
gef➤ gef config pcustom.struct_path
```
By default, this location is in `$TEMP/gef/structs` (e.g. `/tmp/user/1000/gef/structs`). The
structure can be created as a simple `ctypes` structure, in a file called `.py`.
You can naturally set this path to a new location
```text
gef➤ gef config pcustom.struct_path /my/new/location
```
And save this change so you can re-use it directly next time you use `gdb`
```text
gef➤ gef save
[+] Configuration saved to '~/.gef.rc'
```
### Using user-defined structures
You can list existing custom structures via
```text
gef➤ pcustom list
[+] Listing custom structures from '/tmp/structs'
→ /tmp/structs/A.py (A, B)
→ /tmp/structs/elf32_t.py (elf32_t)
→ /tmp/structs/elf64_t.py (elf64_t)
[...]
```
To create or edit a structure, use `pcustom edit ` to spawn your EDITOR with the
targeted structure. If the file does not exist, `gef` will nicely create the tree and file, and fill
it with a `ctypes` template that you can use straight away!
```text
gef➤ pcustom new mystruct_t
[+] Creating '/tmp/gef/structs/mystruct_t.py' from template
```
If the structure already exists, GEF will open the text editor to edit the known structure. This is
equivalent to:
```text
gef➤ pcustom edit elf32_t
[+] Editing '/home/hugsy/code/gef-extras/structs/elf32_t.py'
```
#### Static `ctypes.Structure`-like classes
The code can be defined just as any Python (using `ctypes`) code.
```python
from ctypes import *
'''
typedef struct {
int age;
char name[256];
int id;
} person_t;
'''
class person_t(Structure):
_fields_ = [
("age", c_int),
("name", c_char * 256),
("id", c_int),
]
_values_ = [
# You can define a function to substitute the value
("age", lambda age: "Old" if age > 40 else "Young"),
# Or alternatively a list of 2-tuples
("id", [
(0, "root"),
(1, "normal user"),
(None, "Invalid person")
])
]
```
`pcustom` requires at least one argument, which is the name of the structure. With only one
argument, `pcustom` will dump all the fields of this structure.
```text
gef➤ dt person_t
+0000 age c_int /* size=0x4 */
+0004 name c_char_Array_256 /* size=0x100 */
+0104 id c_int /* size=0x4 */
```
By providing an address or a GDB symbol, `gef` will apply this user-defined structure to the
specified address:

This means that we can now create very easily new user-defined structures
For a full demo, watch the following tutorial:
[](https://www.youtube.com/watch?v=pid2aW7Bt_w)
Additionally, if you have successfully configured your IDA settings, you can also directly import
the structure(s) that was(were) reverse-engineered in IDA directly in your GDB session:
 - (see `gef-extras/ida-rpyc`,
which is the new improved version of `ida-interact`)
#### Dynamic `ctypes.Structure`-like classes
`pcustom` also supports the use of class factories to create a `ctypes.Structure` class whose
structure will be adjusted based on the runtime information we provide (information about the
currently debugged binary, the architecture, the size of a pointer and more).
The syntax is relatively close to the way we use to create static classes (see above), but instead
we define a function that will generate the class. The requirements for this class factory are:
- Take a single [`Gef`](https://github.com/hugsy/gef/blob/main/docs/api/gef.md#class-gef)
positional argument
- End the function name with `_t`
To continue the `person_t` function we defined in the example above, we could modify the static
class as a dynamic one very easily:
```python
import ctypes
from typing import Optional
def person_t(gef: Optional["Gef"]=None):
fields = [
("age", ctypes.c_int),
("name", ctypes.c_char * 256),
("id", ctypes.c_int),
]
class person_cls(ctypes.Structure):
_fields_ = fields
return person_cls
```
Thanks to the `gef` parameter, the structure can be transparently adjusted so that GEF will parse it
differently with its runtime information. For example, we can add constraints to the example above:
```python
import ctypes
from typing import Optional
def person_t(gef: Optional["Gef"]==None):
fields = [
("age", ctypes.c_uint8),
("name", ctypes.c_char * 256),
("id", ctypes.c_uint8),
]
# constraint on the libc version
if gef.libc.version > (2, 27):
# or on the pointer size
pointer_type = ctypes.c_uint64 if gef.arch.ptrsize == 8 else ctypes.c_uint32
fields += [
("new_field", pointer_size)
]
class person_cls(ctypes.Structure):
_fields_ = fields
return person_cls
```
### Public repository of structures
A community contributed repository of structures can be found in
[`gef-extras`](https://github.com/hugsy/gef-extras). To deploy it:
In bash:
```text
git clone https://github.com/hugsy/gef-extras
```
In GEF:
```text
gef➤ gef config pcustom.struct_path /path/to/gef-extras/structs
gef➤ gef save
```
Then either close GDB or `gef reload`. You can confirm the structures were correctly loaded in GEF's
prompt:
```text
gef➤ pcustom list
```
Should return several entries.
And remember this is collaborative repository, so feel free to contribute too!
================================================
FILE: docs/commands/pie.md
================================================
## Command `pie`
The `pie` command is handy when working with position-independent executables. At runtime, it can
automatically resolve addresses for breakpoints that are not static.
Note that you need to use the **entire `pie` command series** to support PIE breakpoints, especially
the "`pie` run commands", like `pie attach`, `pie run`, etc.
### `pie breakpoint` command
This command sets a new PIE breakpoint. It can be used like the normal `breakpoint` command in gdb.
The argument for the command is the offset from the base address or a symbol. The breakpoints will
not be set immediately after this command. Instead, it will be set when you use `pie attach`, `pie
run` or `pie remote` to actually attach to a process, so it can resolve the right base address.
Usage:
```text
gef➤ pie breakpoint OFFSET
```
### `pie info` command
Since a PIE breakpoint is not a real breakpoint, this command provides a way to observe the state of
all PIE breakpoints.
This works just like `info breakpoint` in gdb.
```text
gef➤ pie info
VNum Num Addr
1 N/A 0xdeadbeef
```
VNum stands for virtual number and is used to enumerate the PIE breakpoints. Num is the number of
the associated real breakpoints at runtime in GDB.
You can omit the VNum argument to get info on all PIE breakpoints.
Usage:
```text
gef➤ pie info [VNum]
```
### `pie delete` command
This command deletes a PIE breakpoint given its VNum.
Usage:
```text
gef➤ pie delete [VNum]
```
### `pie attach` command
This command behaves like GDB's `attach` command. Always use this command instead of `attach` if you
have PIE breakpoints. This will convert the PIE breakpoints to real breakpoints at runtime.
The usage is just the same as `attach`.
### `pie remote` command
This command behaves like GDB's `remote` command. Always use this command instead of `remote` if you
have PIE breakpoints. Behind the scenes this will connect to the remote target using `gef remote`
and then convert the PIE breakpoints to real breakpoints at runtime.
The usage is just the same as `remote`.
### `pie run` command
This command behaves like GDB's `run` command. Always use this command instead of `run` if you have
PIE breakpoints. This will convert the PIE breakpoints to real breakpoints at runtime.
The usage is just the same as `run`.
================================================
FILE: docs/commands/print-format.md
================================================
## Command `print-format`
The command `print-format` (alias `pf`) will dump an arbitrary location as an array of bytes
following the format specified. Currently, the output formats supported are
- Python (`py` - default)
- C (`c`)
- Assembly (`asm`)
- Javascript (`js`)
- Hex string (`hex`)
- For patch byte command or GDB $_gef[N] byte access (`bytearray`)
```text
gef➤ print-format -h
[+] print-format [--lang LANG] [--bitlen SIZE] [(--length,-l) LENGTH] [--clip] LOCATION
--lang LANG specifies the output format for programming language (available: ['py', 'c', 'js', 'asm', 'hex'], default 'py').
--bitlen SIZE specifies size of bit (possible values: [8, 16, 32, 64], default is 8).
--length LENGTH specifies length of array (default is 256).
--clip The output data will be copied to clipboard
LOCATION specifies where the address of bytes is stored.
```
For example this command will dump 10 bytes from `$rsp` and copy the result to the clipboard.
```text
gef➤ print-format --lang py --bitlen 8 -l 10 --clip $rsp
[+] Copied to clipboard
buf = [0x87, 0xfa, 0xa3, 0xf7, 0xff, 0x7f, 0x0, 0x0, 0x30, 0xe6]
```
These commands copy the first 10 bytes of $rsp+8 to $rip:
```text
gef➤ print-format --lang bytearray -l 10 $rsp+8
Saved data b'\xcb\xe3\xff\xff\xff\x7f\x00\x00\x00\x00'... in '$_gef0'
gef➤ display/x $_gef0[5]
4: /x $_gef0[5] = 0x7f
gef➤ patch byte $rip $_gef0
```
Very handy to copy-paste-execute shellcodes/data from different memory regions.
================================================
FILE: docs/commands/process-search.md
================================================
## Command `process-search`
`process-search` (aka `ps`) is a convenience command to list and filter process on the host. It is
aimed at making the debugging process a little easier when targeting forking process (such as
tcp/listening daemon that would fork upon `accept()`).
Without argument, it will return all processes reachable by user:
```text
gef➤ ps
1 root 0.0 0.4 ? /sbin/init
2 root 0.0 0.0 ? [kthreadd]
3 root 0.0 0.0 ? [ksoftirqd/0]
4 root 0.0 0.0 ? [kworker/0:0]
5 root 0.0 0.0 ? [kworker/0:0H]
6 root 0.0 0.0 ? [kworker/u2:0]
7 root 0.0 0.0 ? [rcu_sched]
8 root 0.0 0.0 ? [rcuos/0]
9 root 0.0 0.0 ? [rcu_bh]
10 root 0.0 0.0 ? [rcuob/0]
11 root 0.0 0.0 ? [migration/0]
[...]
```
Or to filter with pattern:
```text
gef➤ ps bash
22590 vagrant 0.0 0.8 pts/0 -bash
```
Note: Use "\\" for escaping and "\\\\" for a literal backslash" in the pattern.
`ps` also accepts options:
* `--smart-scan` will filter out probably less relevant processes (belonging to different users,
pattern matched to arguments instead of the commands themselves, etc.)
* `--attach` will automatically attach to the first process found
So, for example, if your targeted process is called `/home/foobar/plop`, but the existing instance
is used through `socat`, like
```text
socat tcp-l:1234,fork,reuseaddr exec:/home/foobar/plop
```
Then every time a new connection is opened to tcp/1234, `plop` will be forked, and GEF can easily
attach to it with the command
```text
gef➤ ps --attach --smart-scan plop
```
================================================
FILE: docs/commands/process-status.md
================================================
## Command `process-status`
> This command replaces the old commands `pid` and `fd`.
`process-status` provides an exhaustive description of the current running process, by extending the
information provided by GDB `info proc` command, with all the information from the `procfs`
structure.
```text
gef➤ ps --smart-scan zsh
22879
gef➤ attach 22879
[...]
gef➤ status
[+] Process Information
PID → 22879
Executable → /bin/zsh
Command line → '-zsh'
[+] Parent Process Information
Parent PID → 4475
Command line → 'tmux new -s cool vibe
[+] Children Process Information
PID → 26190 (Name: '/bin/sleep', CmdLine: 'sleep 100000')
[+] File Descriptors:
/proc/22879/fd/0 → /dev/pts/4
/proc/22879/fd/1 → /dev/pts/4
/proc/22879/fd/2 → /dev/pts/4
/proc/22879/fd/10 → /dev/pts/4
[+] File Descriptors:
No TCP connections
```
================================================
FILE: docs/commands/registers.md
================================================
## Command `registers`
The `registers` command will print all the registers and dereference any pointers.
Example on a MIPS host:
```text
gef➤ reg
$zero : 0x00000000
$at : 0x00000001
$v0 : 0x7fff6cd8 -> 0x77e5e7f8 -> <__libc_start_main+200>: bnez v0,0x77e5e8a8
$v1 : 0x77ff4490
$a0 : 0x00000001
$a1 : 0x7fff6d94 -> 0x7fff6e85 -> "/root/demo-mips"
$a2 : 0x7fff6d9c -> 0x7fff6e91 -> "SHELL=/bin/bash"
$a3 : 0x00000000
$t0 : 0x77fc26a0 -> 0x0
$t1 : 0x77fc26a0 -> 0x0
$t2 : 0x77fe5000 -> "_dl_fini"
$t3 : 0x77fe5000 -> "_dl_fini"
$t4 : 0xf0000000
$t5 : 0x00000070
$t6 : 0x00000020
$t7 : 0x7fff6bc8 -> 0x0
$s0 : 0x00000000
$s1 : 0x00000000
$s2 : 0x00000000
$s3 : 0x00500000
$s4 : 0x00522f48
$s5 : 0x00522608
$s6 : 0x00000000
$s7 : 0x00000000
$t8 : 0x0000000b
$t9 : 0x004008b0 -> : addiu sp,sp,-32
$k0 : 0x00000000
$k1 : 0x00000000
$s8 : 0x00000000
$status : 0x0000a413
$badvaddr : 0x77e7a874 -> <__cxa_atexit>: lui gp,0x15
$cause : 0x10800024
$pc : 0x004008c4 -> : li v0,2
$sp : 0x7fff6ca0 -> 0x77e4a834 -> 0x29bd
$hi : 0x000001a5
$lo : 0x00005e17
$fir : 0x00739300
$fcsr : 0x00000000
$ra : 0x77e5e834 -> <__libc_start_main+260>: lw gp,16(sp)
$gp : 0x00418b20
```
### Filtering registers
If one or more register names are passed to the `registers` command as optional arguments, then only
those will be shown:
```text
gef➤ reg $rax $rip $rsp
$rax : 0x0000555555555169 → endbr64
$rsp : 0x00007fffffffe3e8 → 0x00007ffff7df40b3 → <__libc_start_main+243> mov edi, eax
$rip : 0x0000555555555169 → endbr64
```
================================================
FILE: docs/commands/reset-cache.md
================================================
## Command `reset-cache`
This command is only useful for debugging `GEF` itself.
================================================
FILE: docs/commands/scan.md
================================================
## Command `scan`
`scan` searches for addresses of one memory region (needle) inside another region (haystack) and
lists all results.
Usage:
```text
gef➤ scan NEEDLE HAYSTACK
```
`scan` requires two arguments, the first is the memory section that will be searched and the second
is what will be searched for. The arguments are grepped against the process's memory mappings (just
like [vmmap](./vmmap.md)) to determine the memory ranges to search.
```text
gef➤ scan stack libc
[+] Searching for addresses in 'stack' that point to 'libc'
[stack]: 0x00007fffffffd6a8│+0x1f6a8: 0x00007ffff77cf482 → "__tunable_get_val"
[stack]: 0x00007fffffffd6b0│+0x1f6b0: 0x00007ffff77bff78 → 0x0000001200001ab2
[stack]: 0x00007fffffffd758│+0x1f758: 0x00007ffff77cd9d0 → 0x6c5f755f72647800
[stack]: 0x00007fffffffd778│+0x1f778: 0x00007ffff77bda6c → 0x0000090900000907
[stack]: 0x00007fffffffd7d8│+0x1f7d8: 0x00007ffff77cd9d0 → 0x6c5f755f72647800
[...]
```
### Advanced Needle/Haystack syntax
To check mappings without a path associated, an address range (start-end) can be used. Note that
ranges don't include whitespaces.

================================================
FILE: docs/commands/search-pattern.md
================================================
## Command `search-pattern`
`gef` allows you to search for a specific pattern at runtime in all the segments of your process
memory layout. The command `search-pattern`, alias `grep`, aims to be straight-forward to use:
```text
gef➤ search-pattern MyPattern
```

It will provide an easily understandable to spot occurrences of the specified pattern, including the
section it/they was/were found, and the permission associated to that section.
`search-pattern` can also be used to search for addresses. To do so, simply ensure that your pattern
starts with `0x` and is a valid hex address. For example:
```text
gef➤ search-pattern 0x4005f6
```

The `search-pattern` command can also be used as a way to search for cross-references to an address.
For this reason, the alias `xref` also points to the command `search-pattern`. Therefore the
command above is equivalent to `xref 0x4005f6` which makes it more intuitive to use.
### Searching in a specific range
Sometimes, you may need to search for a very common pattern. To limit the search space, you can also
specify an address range or the section to be checked.
```text
gef➤ search-pattern 0x4005f6 little libc
gef➤ search-pattern 0x4005f6 little 0x603100-0x603200
```
### Searching in a specific range using regex
Sometimes, you may need an advanced search using regex. Just use --regex arg.
Example: how to find null-end-printable(from x20-x7e) C strings (min size >=2 bytes) with a regex:
```text
gef➤ search-pattern --regex 0x401000 0x401500 ([\\x20-\\x7E]{2,})(?=\\x00)
```
================================================
FILE: docs/commands/shellcode.md
================================================
## Command `shellcode`
`shellcode` is a command line client for @JonathanSalwan shellcodes database. It can be used to
search and download directly via `GEF` the shellcode you're looking for. Two primitive subcommands
are available, `search` and `get`
```text
gef➤ shellcode search arm
[+] Showing matching shellcodes
901 Linux/ARM Add map in /etc/hosts file - 79 bytes
853 Linux/ARM chmod("/etc/passwd", 0777) - 39 bytes
854 Linux/ARM creat("/root/pwned", 0777) - 39 bytes
855 Linux/ARM execve("/bin/sh", [], [0 vars]) - 35 bytes
729 Linux/ARM Bind Connect UDP Port 68
730 Linux/ARM Bindshell port 0x1337
[...]
gef➤ shellcode get 698
[+] Downloading shellcode id=698
[+] Shellcode written as '/tmp/sc-EfcWtM.txt'
gef➤ system cat /tmp/sc-EfcWtM.txt
/*
Title: Linux/ARM - execve("/bin/sh", [0], [0 vars]) - 27 bytes
Date: 2010-09-05
Tested on: ARM926EJ-S rev 5 (v5l)
Author: Jonathan Salwan - twitter: @jonathansalwan
shell-storm.org
Shellcode ARM without 0x20, 0x0a and 0x00
[...]
```
================================================
FILE: docs/commands/skipi.md
================================================
## Command `skipi`
The `skipi` command allows you to easily skip instructions execution.
```text
skipi [LOCATION] [--n NUM_INSTRUCTIONS]
```
`LOCATION` address/symbol from where to skip (default is `$pc`)
`--n NUM_INSTRUCTIONS` Skip the specified number of instructions instead of the default 1.
```bash
gef➤ skipi
gef➤ skipi --n 3
gef➤ skipi 0x69696969
gef➤ skipi 0x69696969 --n 6
```
================================================
FILE: docs/commands/stepover.md
================================================
## Command `stepover`
The stepover command simplifies the process of stepping over instructions by continuing to a
temporary breakpoint at the next instruction.
This feature is particularly useful for stepping over call/rep instructions.
Ex: Step over call instruction
```text
stepover
```
```bash
gef➤ stepover
```
================================================
FILE: docs/commands/stub.md
================================================
## Command `stub`
The `stub` command allows you stub out functions, optionally specifying the return value.
```text
gef➤ stub [-h] [--retval RETVAL] [address]
```
`address` indicates the address of the function to bypass. If not specified, `GEF` will consider the
instruction at the program counter to be the start of the function.
If `--retval RETVAL` is provided, `GEF` will set the return value to the provided value. Otherwise,
it will set the return value to 0.
For example, it is trivial to bypass `fork()` calls. Since the return value is set to 0, it will in
fact drop us into the "child" process. It must be noted that this is a different behaviour from the
classic `set follow-fork-mode child` since here we do not spawn a new process, we only trick the
parent process into thinking it has become the child.
### Example
Patching `fork()` calls:
* Without stub:

* With stub:

================================================
FILE: docs/commands/theme.md
================================================
## Command `theme`
Customize `GEF` by changing its color scheme.
```text
gef➤ theme
context_title_message : red bold
default_title_message : red bold
default_title_line : green bold
context_title_line : green bold
disable_color : 0
xinfo_title_message : blue bold
```
### Changing colors
You have the possibility to change the coloring properties of `GEF` display with the `theme`
command. The command accepts 2 arguments, the name of the property to update, and its new coloring
value.
Colors can be one of the following:
- red
- green
- blue
- yellow
- gray
- pink
Color also accepts the following attributes:
- bold
- underline
- highlight
- blink
Any other will value simply be ignored.
```text
gef➤ theme context_title_message blue bold foobar
gef➤ theme
context_title_message : blue bold
default_title_message : red bold
default_title_line : green bold
context_title_line : green bold
disable_color : 0
xinfo_title_message : blue bold
```
================================================
FILE: docs/commands/tmux-setup.md
================================================
## Command `tmux-setup`
In the purpose of always making debugging sessions easier while being more effective, `GEF`
integrates two commands:
* `tmux-setup`
* `screen-setup`
Those commands will check whether GDB is being spawn from inside a `tmux` (resp. `screen`) session,
and if so, will split the pane vertically, and configure the context to be redirected to the new
pane, looking something like:

To set it up, simply enter
```text
gef➤ tmux-setup
```
**Note**: Although `screen-setup` provides a similar setup, the structure of `screen` does not allow
a very clean way to do this. Therefore, if possible, it would be recommended to use the `tmux-setup`
command instead.
### Possible color issues with tmux
On Linux tmux only supports 8 colors with some terminal capabilities (`$TERM` environment variable).
This can mess up your color themes when using GEF with tmux. To remedy this if your terminal
supports more colors you can either set the variable to something like `TERM=screen-256color` or if
you don't want or can't change that variable you can start `tmux` with the `-2` flag to force tmux
to use 256 colors.
================================================
FILE: docs/commands/trace-run.md
================================================
## Command `trace-run`
The `trace-run` command is meant to be provide a visual appreciation directly in IDA disassembler of
the path taken by a specific execution. It should be used with the IDA script
[`ida_color_gdb_trace.py`](https://github.com/hugsy/stuff/blob/main/ida_scripts/ida_color_gdb_trace.py)
It will trace and store all values taken by `$pc` during the execution flow, from its current value,
until the value provided as argument.
```text
gef> trace-run
```

By using the script `ida_color_gdb_trace.py` on the text file generated, it will color the path
taken:

================================================
FILE: docs/commands/version.md
================================================
## Command `version`
Print out version information about your current gdb environment.
### Usage Examples
When GEF is located in a directory tracked with git:
```text
gef➤ version
GEF: rev:48a9fd74dd39db524fb395e7db528f85cc49d081 (Git - clean)
SHA1(/gef/rules/gef.py): 848cdc87ba7c3e99e8129ad820c9fcc0973b1e99
GDB: 9.2
GDB-Python: 3.8
```
Otherwise the command shows the `standalone` information:
```text
gef➤ version
GEF: (Standalone)
Blob Hash(/gef/rules/gef.py): f0aef0f481e8157006b26690bd121585d3befee0
SHA1(/gef/rules/gef.py): 4b26a1175abcd8314d4816f97fdf908b3837c779
GDB: 9.2
GDB-Python: 3.8
```
The `Blob Hash` can be used to easily find the git commit(s) matching this file revision.
```text
git log --oneline --find-object
```
If this command does not return anything then the file was most likely modified and cannot be
matched to a specific git commit.
================================================
FILE: docs/commands/vmmap.md
================================================
## Command `vmmap`
`vmmap` displays the target process's entire memory space mapping.

Interestingly, it helps finding secret gems: as an aware reader might have seen, memory mapping
differs from one architecture to another (this is one of the main reasons I started `GEF` in a first
place). For example, you can learn that ELF running on SPARC architectures always have their `.data`
and `heap` sections set as Read/Write/Execute.
`vmmap` can accept multiple arguments, either patterns to match again mapping names, or addresses
to determine which section it belongs to:
1. `-a` / `--addr`:
- filter by address -> parses the next argument as an integer or asks gdb to interpret the value
2. `-n` / `--name`:
- filter based on section name
3. If nothing is specified, it prints a warning and guesses the type


The address can be also be given in the form of a register or variable.

And you can do all of them in one command 🙂

================================================
FILE: docs/commands/xfiles.md
================================================
## Command `xfiles`
`xfiles` is a more convenient representation of the GDB native command, `info files` allowing you to
filter by pattern given in argument. For example, if you only want to show the code sections (i.e.
`.text`):

================================================
FILE: docs/commands/xinfo.md
================================================
## Command `xinfo`
`xinfo` displays all the information known to `gef` about the specific address given as argument:

**Important note** : For performance reasons, `gef` caches certain results. `gef` will try to
automatically refresh its own cache to avoid relying on obsolete information of the debugged
process. However, in some dodgy scenario, `gef` might fail detecting some new events making its
cache partially obsolete. If you notice an inconsistency on your memory mapping, you might want to
force `gef` flushing its cache and fetching brand new data, by running the command `reset-cache`.
================================================
FILE: docs/commands/xor-memory.md
================================================
## Command `xor-memory`
This command is used to XOR a block of memory.
Its syntax is:
```text
xor-memory
```
The first argument (`display` or `patch`) is the action to perform:
- `display` will only show an hexdump of the result of the XOR-ed memory block, without writing the
debuggee's memory.
```text
gef➤ xor display $rsp 16 1337
[+] Displaying XOR-ing 0x7fff589b67f8-0x7fff589b6808 with '1337'
────────────────────────────────[ Original block ]────────────────────────────────────
0x00007fff589b67f8 46 4e 40 00 00 00 00 00 00 00 00 00 00 00 00 00 FN@.............
────────────────────────────────[ XOR-ed block ]──────────────────────────────────────
0x00007fff589b67f8 55 79 53 37 13 37 13 37 13 37 13 37 13 37 13 37 UyS7.7.7.7.7.7.7
```
- `patch` will overwrite the memory with the xor-ed content.
```text
gef➤ xor patch $rsp 16 1337
[+] Patching XOR-ing 0x7fff589b67f8-0x7fff589b6808 with '1337'
gef➤ hexdump byte $rsp 16
0x00007fff589b67f8 55 79 53 37 13 37 13 37 13 37 UyS7.7.7.7
```
================================================
FILE: docs/compat.md
================================================
## GEF Compatibility
This matrix indicates the version of Python and/or GDB
| GEF version | GDB Python compatibility | Python compatibility |
| :--: | :--: | :--: |
| [2018.02](https://github.com/hugsy/gef/releases/tag/2018.02) | 7.2 | Python 2.7, Python 3.4+ |
| [2020.03](https://github.com/hugsy/gef/releases/tag/2020.03) | 7.4 | Python 2.7, Python 3.4+ |
| [2022.01](https://github.com/hugsy/gef/releases/tag/2022.01) | 8.0 | Python 3.6+ |
| [2025.01](https://github.com/hugsy/gef/releases/tag/2025.01) | 10.0 | Python 3.10+ |
================================================
FILE: docs/config.md
================================================
## Configuring GEF
GEF comes with its own configuration and customization system, allowing fine
tweaking. The configuration file is located under `~/.gef.rc` by default, and
is automatically loaded when GEF is loaded by GDB. If not configuration file is
found, GEF will simply use the default settings.
The configuration file is a Python
[`configparser`](https://docs.python.org/3/library/configparser.html). To
create a basic file with all settings and their default values, simply run
```bash
gdb -ex 'gef save' -ex quit
```
You can now explore the configuration file under `~/.gef.rc`.
Once in GEF, the configuration settings can be set/unset/modified by the
[command `gef config`](/docs/commands/config.md). Without argument the command
will simply dump all known settings:

To update, follow the syntax
```text
gef➤ gef config .
```
Any setting updated this way will be specific to the current GDB session. To
make permanent, use the following command
```text
gef➤ gef save
```
Refer to the `gef config` [command documentation](/docs/commands/config.md) for
complete explanation.
================================================
FILE: docs/debugging.md
================================================
## Debugging
Debugging GEF has a trick, let's see some examples
## Debugging with pdb
Open gef.py
Search for **class NopCommand(GenericCommand)**, go to do_invoke method and insert:
```python
import pdb; pdb.set_trace()
```
Open a gdb session -> start -> nop
Done!
```bash
gef➤ nop
> /home/dreg/.gef-7c170cf6be3d84b2672a22e43b9128a23fe53c3b.py(6075)do_invoke()
-> args : argparse.Namespace = kwargs["arguments"]
(Pdb) ll
6070 @only_if_gdb_running
6071 @parse_arguments({"address": "$pc"}, {"--i": 1, "--b": True, "--f": True, "--n": True})
6072 def do_invoke(self, _: List[str], **kwargs: Any) -> None:
6073 import pdb; pdb.set_trace()
6074
6075 -> args : argparse.Namespace = kwargs["arguments"]
6076 address = parse_address(args.address)
```
Learn more about [pdb](https://docs.python.org/3/library/pdb.html)
## Debugging with PyCharm
Install [pycharm](https://www.jetbrains.com/help/pycharm/installation-guide.html)
Create a new project:


Go to menu -> Run -> Edit configurations...:

Create a Python Debug Server:


Debug your new Unnamed:


Copy the info from output Window to gef.py:

First, add to gef.py:
```python
import pydevd_pycharm
```
Second, search for **class NopCommand(GenericCommand)**, go to do_invoke method and insert:
```python
pydevd_pycharm.settrace('localhost', port=35747, stdoutToServer=True, stderrToServer=True)
```
Open a gdb session -> start -> nop
Done!

## Debugging with VSCode
The approach to debug GEF with VSCode is relatively similar to that of PyCharm. Make sure to
install the [Python extension for
VSCode](https://marketplace.visualstudio.com/items?itemName=ms-python.python). This will install
`debugpy`, a remote debugger that you can connect to from VSCode and debug anything in GEF from
your session (breakpoints, watchpoints, etc.). Debugging a Python app from VSCode is [extensively
covered in the official docs](https://code.visualstudio.com/docs/python/debugging) to refer to
them if you're not sure how it works.
To start a debugging session in GEF, manually run the following Python commands
```python
gef> pi import debugpy; debugpy.listen(5678); pi debugpy.wait_for_client()
```
Alternatively a convenience script named `vscode_debug.py` can also be found in the `scripts`
folder, which you can invoke easily simply using the GDB `source` command:
```text
gef> source /path/to/gef/scripts/vscode_debug.py
```
GEF will be suspended, waiting for a client to connect to the debugger to resume the execution.
Then from your VSCode, edit or create `/path/to/gef/.vscode/launch.json`, and add a debug
configuration to attach to GEF, by specifying the IP address and port (on `localhost` in the
example below, but the remote server can be anywhere):
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Attach to GEF",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
}
}
]
}
```
Everything is ready to attach to GEF. By default, you can simply hit F5 on VSCode (Start Debugging)

================================================
FILE: docs/deprecated.md
================================================
## Deprecated commands
GEF is in itself a large file, but to avoid it to be out of control some commands once part of GEF
were either moved to [GEF-Extras](https://github.com/hugsy/gef-extras) or even simply removed. This
page aims to track those changes.
| Command | Status | Since | Link (if Applicable) | Notes |
| -- | -- | -- | -- | -- |
| `cs-disassemble` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/capstone.py) | Depends on `capstone` |
| `assemble` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/assemble.py) | Depends on `keystone` |
| `emulate` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/emulate/__init__.py) | Depends on `unicorn` and `capstone` |
| `set-permission` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/emulate/__init__.py) | Depends on `keystone` |
| `ropper` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/ropper.py) | Depends on `ropper` |
| `ida-interact` | Moved | 2022.06 | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/ida_interact.py) | Depends on `rpyc` |
| `exploit-template` | Moved | [c402900](https://github.com/hugsy/gef-extras/commit/c4029007994d5e508cb3df900b60821b0b61e0e5) | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/skel.py) | |
| `windbg` | Moved | [a933a5a](https://github.com/hugsy/gef-extras/commit/a933a5ac43933742d91f4e299eadf05e3e0670be) | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/windbg.py) | |
| `is-syscall` | Moved | [3f79fb38](https://github.com/hugsy/gef-extras/commit/3f79fb382aa9052d073698d40237f98982c5d2de) | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/syscall_args) | |
| `syscall-args` | Moved | [3f79fb38](https://github.com/hugsy/gef-extras/commit/3f79fb382aa9052d073698d40237f98982c5d2de) | [New location](https://github.com/hugsy/gef-extras/blob/main/scripts/syscall_args) | |
## Removed support for pyenv
Support for [pyenv](https://github.com/pyenv/pyenv) has been removed after `2024.05`, for more
information see [#1078](https://github.com/hugsy/gef/issues/1078) and [#1062](https://github.com/hugsy/gef/issues/1062).
================================================
FILE: docs/faq.md
================================================
## Frequently Asked Questions
## Why use GEF over PEDA?
[PEDA](https://github.com/longld/peda) is a fantastic tool that provides similar commands to make
the exploitation development process smoother.
However, PEDA suffers from a major drawbacks, which the code is too fundamentally linked to Intel
architectures (x86-32 and x86-64). On the other hand, GEF not only supports all the architecture
supported by GDB (currently x86, ARM, AARCH64, MIPS, PowerPC, SPARC) but is designed to integrate
new architectures very easily as well!
Also, PEDA development has been quite idle for a few years now, and many new interesting features a
debugger can provide simply do not exist.
## What if my GDB is < 10.0 ?
GDB was introduced with its Python support early 2011 with the release of GDB 7. A (very) long way
has gone since and the Python API has been massively improved, and GEF is taking advantage of them
to provide the coolest features with as little performance impact as possible.
Currently, GEF is optimized for running against GDB version 10.0+, and Python 3.10+. This allows for
the best performance and use of the GDB Python API. However, GEF can run on older versions too,
check out [the version compatibility matrix](compat.md). For really older versions of GDB, you can
use [`gef-legacy`](https://github.com/hugsy/gef-legacy) which supports a lot of older GDB, and a
Python 2/3 compatibility layer.
Therefore, it is highly recommended to run GEF with the latest version of GDB. However, all
functions should work on a GDB 10.0 and up. If not, send a [bug
report](https://github.com/hugsy/gef/issues) and provide as many details as possible.
If you are running an obsolete version, GEF will show a error and message and exit.
Some pre-compiled static binaries for both recent GDB and GDBServer can be downloaded from the
[`gdb-static`](https://github.com/hugsy/gdb-static) repository.
## I cannot get GEF setup
GEF will work on any GDB 10+ compiled with Python 3.10+ support. You can view that commands that
failed to load using `gef missing`, but this will not affect GEF generally.
If you experience problems setting it up on your host, first go to the [Discord
channel](https://discord.gg/HCS8Hg7) for that. You will find great people there willing to help.
Note that the GitHub issue section is to be used to **report bugs** and **GEF issues** (like
unexpected crash, improper error handling, weird edge case, etc.), not a place to ask for help.
All recent distributions ship packaged GDB that should be ready-to-go, with GDB >= 10.0 and Python
3.10+. Any version higher or equal will work just fine. So you might actually only need to run `apt
install gdb` to get the full-force of GEF.
## I get a SegFault when starting GDB with GEF
A long standing bug in the `readline` library can make `gef` crash GDB when displaying certain
characters (SOH/ETX). As a result, this would SIGSEGV GDB as `gef` is loading, a bit like this:
```text
root@debian-aarch64:~# gdb -q ./test-bin-aarch64
GEF ready, type `gef' to start, `gef config' to configure
53 commands loaded, using Python engine 3.4
[*] 5 commands could not be loaded, run `gef missing` to know why.
[+] Configuration from '/root/.gef.rc' restored
Reading symbols from ./bof-aarch64...(no debugging symbols found)...done.
Segmentation fault (core dumped)
```
If so, this can be fixed easily by setting the `gef.readline_compat` variable to `True` in the
`~/.gef.rc` file. Something like this:
```text
root@debian-aarch64:~# nano ~/.gef.rc
[...]
[gef]
readline_compat = True
```
You can now use all features of `gef` even on versions of GDB compiled against old `readline`
library.
## Does GEF prevent the use of other GDB plugins?
Definitely not! You can use any other GDB plugin on top of it for an even better debugging
experience.
Some interesting plugins highly recommended too:
- [!exploitable](https://github.com/jfoote/exploitable/)
- [Voltron](https://github.com/snare/voltron)

Src: [@rick2600: terminator + gdb + gef + voltron cc: @snare @_hugsy_](https://twitter.com/rick2600/status/775926070566490113)
## I want to contribute, where should I head first?
I would suggest thoroughly reading this documentation, just having a look to the
[CONTRIBUTE](https://github.com/hugsy/gef/blob/main/.github/CONTRIBUTING.md) file of the project to
give you pointers.
Also a good thing would be to join our [Discord channel](https://discord.gg/HCS8Hg7) to get in touch
with the people involved/using it.
## I think I've found a bug, how can I help fixing it?
`gef` is only getting better through people (like you!) using it, but most importantly reporting
unexpected behavior.
In most locations, Python exceptions will be properly intercepted. If not, `gef` wraps all commands
with a generic exception handler, to disturb as little as possible your debugging session. If it
happens, you'll only get to see a message like this:

By switching to debug mode, `gef` will give much more information:
```text
gef➤ gef config gef.debug 1
```

If you think fixing it is in your skills, then send a [Pull
Request](https://github.com/hugsy/gef/pulls) with your patched version, explaining your bug, and
what was your solution for it.
Otherwise, you can open an [issue](https://github.com/hugsy/gef/issues), give a thorough description
of your bug and copy/paste the content from above. This will greatly help for solving the issue.
## I get weird issues/characters using GDB + Python3, what's up?
Chances are you are not using UTF-8. Python3 is [highly relying on
UTF-8](https://www.diveintopython3.net/strings.html) to display correctly characters of any alphabet
and [also some cool emojis](https://unicode.org/emoji/charts/full-emoji-list.html). When GDB is
compiled with Python3, GEF will assume that your current charset is UTF-8 (for instance,
`en_US.UTF-8`). Use your `$LANG` environment variable to tweak this setting.
In addition, some unexpected results were observed when your local is not set to English. If you
aren't sure, simply run `gdb` like this:
```text
LC_ALL=en_US.UTF-8 gdb /path/to/your/binary
```
## GDB crashes on ARM memory corruption with `gdb_exception_RETURN_MASK_ERROR`
This issue is **NOT** GEF related, but GDB's, or more precisely some versions of GDB packaged with
Debian/Kali for ARM
>
> Original Issue and Mitigation
>
> gdb version 7.12, as distributed w/ Raspbian/Kali rolling (only distro's
> tested,) throws an exception while disassembling ARM binaries when using gef.
> This is not a gef problem, this is a gdb problem. gef is just the tool that
> revealed the gdb dain bramage! (The issue was not observed using vanilla
> gdb/peda/pwndbg) This issue was first noted when using si to step through a
> simple ARM assembly program (noted above) when instead of exiting cleanly,
> gdb's disassembly failed with a SIGABRT and threw an exception:
>
> `gdb_exception_RETURN_MASK_ERROR`
>
> This turns out to be a known problem (regression) with gdb, and affects
> gef users running the ARM platform (Raspberry Pi).
>
> The mitigation is for ARM users to compile gdb from source and run the latest
> version, 8.1 as of this writing.
>
**Do not file an issue**, again it is **NOT** a bug from GEF, or neither from GDB Python API.
Therefore, there is nothing GEF's developers can do about that. The correct solution as mentioned
above is to recompile your GDB with a newer (better) version.
The whole topic was already internally discussed, so please refer to the
[issue 206](https://github.com/hugsy/gef/issues/206) for the whole story.
## I still don't have my answer... Where can I go?
Discord is your answer: join and talk to us by clicking here
[](https://discord.gg/HCS8Hg7)
If you cannot find the answer to your problem here or on the Discord, then go to the project [Issues
page](https://github.com/hugsy/gef/issues) and fill up the forms with as much information as you
can!
## How can I use GEF to debug a process in a container?
GEF can attach to a process running in a container using `gdb --pid=$PID`, where `$PID` is the ID of
the running process _on the host_. To find this, you can use `docker top -o pid | awk
'!/PID/' | xargs -I'{}' pstree -psa {}` to view the process tree for the container.
`sudo` may be required to attach to the process, which will depend on your system's security
settings.
Please note that cross-container debugging may have unexpected issues. Installing gdb and GEF inside
the container, or using [the official GEF docker image](https://hub.docker.com/r/crazyhugsy/gef) may
improve results.
================================================
FILE: docs/functions/base.md
================================================
## Function `$_base()`
Return the matching file's base address plus an optional offset. Defaults to current file. Note that
quotes need to be escaped.
_Note_: a debugging session must be active
```text
$_base([filepath])
```
Example:
```text
gef➤ p $_base(\"/usr/lib/ld-2.33.so\")
```
================================================
FILE: docs/functions/bss.md
================================================
## Function `$_bss()`
Return the current BSS base address plus the given offset.
_Note_: a debugging session must be active
```text
$_bss([offset])
```
Example:
```text
gef➤ p $_bss(0x20)
```
================================================
FILE: docs/functions/got.md
================================================
## Function `$_got()`
Return the current GOT base address plus the given offset.
_Note_: a debugging session must be active
```text
$_got([offset])
```
Example:
```text
gef➤ p $_got(0x20)
```
================================================
FILE: docs/functions/heap.md
================================================
## Function `$_heap()`
Return the current heap base address plus the given offset.
_Note_: a debugging session must be active
```text
$_heap([offset])
```
Example:
```text
gef➤ p $_heap(0x20)
```
================================================
FILE: docs/functions/stack.md
================================================
## Function `$_stack()`
Return the current stack base address plus the given offset.
_Note_: a debugging session must be active
```text
$_stack([offset])
```
Example:
```text
gef➤ p $_stack(0x20)
```
================================================
FILE: docs/index.md
================================================
# GEF - GDB Enhanced Features
[](https://hugsy.github.io/gef/) [](https://hugsy.github.io/gef/coverage/) [](https://github.com/hugsy/gef/blob/main/LICENSE) [](https://github.com/hugsy/gef/) [](https://discord.gg/HCS8Hg7)
`GEF` (pronounced ʤɛf - "Jeff") is a kick-ass set of commands for X86, ARM, MIPS, PowerPC and SPARC
to make GDB cool again for exploit dev. It is aimed to be used mostly by exploit developers and
reverse-engineers, to provide additional features to GDB using the Python API to assist during the
process of dynamic analysis and exploit development.
It requires Python 3, but [`gef-legacy`](https://github.com/hugsy/gef-legacy) can be used if Python
2 support is needed.

## GDB Made Easy
* **One** single GDB script
* Entirely **architecture agnostic**, **NO** dependencies: `GEF` is battery-included and [is
installable instantly](https://hugsy.github.io/gef/#setup)
* **Fast** limiting the number of dependencies and optimizing code to make the commands as fast as
possible
* Provides a great variety of commands to drastically change your debugging experience in GDB.
* [**Easily** extensible](https://hugsy.github.io/gef/api/) to create other commands by providing
more comprehensible layout to GDB Python API.
* Full Python3 support ([Python2 support was dropped in
2020.03](https://github.com/hugsy/gef/releases/tag/2020.03)) - check out
[`gef-legacy`](https://github.com/hugsy/gef-legacy) for a Python2 compatible version, and [the
compatibility matrix](/docs/compat.md) for a complete rundown of version support.
* Built around an architecture abstraction layer, so all commands work in any GDB-supported
architecture such as x86-32/64, ARMv5/6/7, AARCH64, SPARC, MIPS, PowerPC, etc.
* Suited for real-life debugging, exploit development, just as much as for CTFs
* And a lot more commands contributed by the community available on
[GEF-Extras](https://github.com/hugsy/gef-extras) !!
Check out the [showroom page](https://hugsy.github.io/gef/screenshots/) for more | or [try it online
yourself!](https://demo.gef.blah.cat) (user:`gef`/password:`gef-demo`)
## Quick start
### Automated installation
GEF has no dependencies, is fully battery-included and works out of the box. You can get started
with GEF in a matter of seconds, by simply running:
```bash
bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
```
For more details and other ways to install GEF please see [installation
page](https://hugsy.github.io/gef/install/).
### Run
Then just start playing (for local files):
```bash
$ gdb -q /path/to/my/bin
gef➤ gef help
```
Or (for remote debugging):
```bash
remote:~ $ gdbserver 0.0.0.0:1234 /path/to/file
Running as PID: 666
```
And:
```bash
local:~ $ gdb -q
gef➤ gef-remote -t your.ip.address:1234 -p 666
```
## Bugs & Feedbacks
To discuss `gef`, `gdb`, exploitation or other topics, feel free to join our [Discord
channel](https://discord.gg/HCS8Hg7).
For bugs or feature requests, just go [here](https://github.com/hugsy/gef/issues) and provide a
thorough description if you want help.
_Side Note_: `GEF` fully relies on the GDB API and other Linux-specific sources of information (such
as `/proc/`). As a consequence, some of the features might not work on custom or hardened
systems such as GrSec.
## Contribution
`gef` was created and maintained by myself, [`@_hugsy_`](https://twitter.com/_hugsy_), but kept
fresh thanks to [all the contributors](https://github.com/hugsy/gef/graphs/contributors).
[](https://github.com/hugsy/gef/graphs/contributors)
Or if you just like the tool, feel free to drop a simple _"thanks"_ on Discord, Twitter or other, it
is **always** very appreciated.
## Sponsors
We would like to thank in particular the following people who've been sponsoring GEF allowing us to
dedicate more time and resources to the project:
[
](https://github.com/nkaretnikov)
[
](https://github.com/r3zk0n)
[
](https://github.com/merces)
[
](https://github.com/nbars)
[
](https://github.com/maycon)
[
](https://github.com/jespinhara)
[
](https://github.com/therealdreg)
[
](https://github.com/mikesart)
## Extra Credits
* The GEF logo was designed by [TheZakMan](https://twitter.com/thezakman)
## 🍺 Happy hacking
================================================
FILE: docs/install.md
================================================
## Installing GEF
## Prerequisites
Specific GEF commands rely on commonly used Unix commands to extract additional information.
Therefore it requires the following binaries to be present:
* `file`
* `readelf`
* `nm`
* `ps`
* `python3`
Those tools are included by default in many modern distributions. If they're missing, you can use
your OS package manager to install them.
### GDB
Only [GDB 10.0 and higher](https://www.sourceware.org/gdb/) is required. It must be compiled with
Python 3.10 or higher support. For most people, simply using your distribution package manager
should be enough.
As of January 2020, GEF officially doesn't support Python 2 any longer, due to Python 2 becoming
officially deprecated.
GEF will then only work for Python 3. If you absolutely require GDB + Python 2, please use
[GEF-Legacy](https://github.com/hugsy/gef-legacy) instead. Note that `gef-legacy` won't provide new
features, and only functional bugs will be handled.
You can verify it with the following command:
```bash
gdb -nx -ex 'pi print(sys.version)' -ex quit
```
This should display your version of Python compiled with `gdb`.
```bash
$ gdb -nx -ex 'pi print(sys.version)' -ex quit
3.12.3 (main, Jul 31 2024, 17:43:48) [GCC 13.2.0]
```
### Python dependencies
There are **none**: `GEF` works out of the box!
GEF itself provides most (if not all 🤯) features required for typical sessions. However, GEF can be
easily extended via
* community-built scripts, functions and architectures in the repo
`gef-extras` (see below)
* your own script which can leverage the GEF API for the heavy lifting
## Standalone
### Quick install
The quickest way to get started with GEF is through the installation script available. Simply make
sure you have [GDB 10.0 or higher](https://www.sourceware.org/gdb/), compiled with Python 3.10 or higher,
and run
```bash
bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
```
Or if you prefer `wget`
```bash
bash -c "$(wget https://gef.blah.cat/sh -O -)"
```
Alternatively from inside `gdb` directly:
```bash
$ gdb -q
(gdb) pi import urllib.request as u, tempfile as t; g=t.NamedTemporaryFile(suffix='-gef.py'); open(g.name, 'wb+').write(u.urlopen('https://tinyurl.com/gef-main').read()); gdb.execute('source %s' % g.name)
```
That's it! GEF is installed and correctly set up. You can confirm it by checking the `~/.gdbinit`
file and see a line that sources (i.e. loads) GEF.
```bash
$ cat ~/.gdbinit
source ~/.gdbinit-gef.py
```
### Update
If your host/VM is connected to the Internet, you can update `gef` easily to the latest version
(even without `git` installed). with `python /path/to/gef.py --update`
```bash
$ python ~/.gdbinit-gef.py --update
Updated
```
This will deploy the latest version of `gef`'s _main_ branch from Github. If no
updates are available, `gef` will respond `No update` instead.
## Using git
To contribute to GEF, you might prefer using git directly.
```bash
git clone https://github.com/hugsy/gef.git
echo source `pwd`/gef/gef.py >> ~/.gdbinit
```
## Community repository: GEF-Extras
GEF was built to also provide a solid base for external scripts. The repository
[`gef-extras`](https://github.com/hugsy/gef-extras) is an open repository where
anyone can freely submit their own commands to extend GDB via GEF's API.
To benefit from it:
```bash
# using the automated way
## via the install script
$ bash -c "$(wget https://github.com/hugsy/gef/raw/main/scripts/gef-extras.sh -O -)"
# or manually
## clone the repo
$ git clone --branch main https://github.com/hugsy/gef-extras.git
## then specify gef to load this directory
$ gdb -ex 'gef config gef.extra_plugins_dir "/path/to/gef-extras/scripts"' -ex 'gef save' -ex quit
[+] Configuration saved
```
You can also use the structures defined from this repository:
```bash
$ gdb -ex 'gef config pcustom.struct_path "/path/to/gef-extras/structs"' -ex 'gef save' -ex quit
[+] Configuration saved
```
There, you're now fully equipped epic pwnage with **all** GEF's goodness!!
# Uninstalling GEF
## Prevent script loading
GDB provides the `-nx` command line flag to disable the commands from the
`~/.gdbinit` to be executed.
```text
gdb -nx
```
## Disable GEF
To disable GEF without removing it, go to editing `~/.gdbinit`, spot the line
that sources GEF, and comment / delete that line:
So:
```text
$ cat ~/.gdbinit
source /my/path/to/gef.py
```
Will become:
```text
$ cat ~/.gdbinit
# source /my/path/to/gef.py
```
Restart GDB, GEF is gone. Note that you can also load GEF at any moment during
your GDB session as such:
```text
$ gdb
(gdb) source /my/path/to/gef.py
```
## Remove GEF
GEF is a one-file GDB script. Therefore, to remove GEF simply spot the location
it was installed (for example, by using `~/.gdbinit`) and delete the file. If a
configuration file was created, it will be located as `~/.gef.rc` and can also
be deleted:
```text
$ cat ~/.gdbinit
# source /my/path/to/gef.py
$ rm /my/path/to/gef.py ~/.gef.rc
```
GEF is totally removed from your system.
================================================
FILE: docs/obsolete/docs/index.md
================================================
---
## The latest version of the documentation is hosted on [hugsy.github.io/gef](https://hugsy.github.io/gef)
---

================================================
FILE: docs/obsolete/mkdocs.yml
================================================
site_name: GEF - GDB Enhanced Features documentation
repo_url: https://github.com/hugsy/gef/
docs_dir: docs/
theme: readthedocs
nav:
- Home: index.md
================================================
FILE: docs/obsolete/requirements.txt
================================================
mkdocs>=1.2.3
================================================
FILE: docs/requirements.txt
================================================
mkdocs-material
lazydocs
================================================
FILE: docs/screenshots.md
================================================
## Screenshots
This page illustrates a few of the possibilities available to you when using `GEF`.
## Multi-architecture support
`GEF` was designed to support any architecture supported by GDB via an easily
extensible architecture API.
Currently `GEF` supports the following architectures:
- Intel x86 (32b & 64b)
- ARM (v6/v7)
- AARCH64
- MIPS/MIPS64
- PowerPC
- SPARC/SPARCv9
## Features
### Embedded hexdump view
To this day, GDB doesn't come with a hexdump-like view. Well `GEF` fixes that for you via the
`hexdump` command:

### Dereferencing data or registers
No more endless manual pointer dereferencing `x/x` style. Just use `dereference` for that. Or for a
comprehensive view of the registers, `registers` might become your best friend:

### Heap analysis
#### Detailed view of Glibc Chunks

#### Automatic detection of UaF during runtime

### Display ELF information
#### ELF structure

#### Security settings

### Automatic vulnerable string detection

### Code emulation with Unicorn-Engine (x86-64)

### Comprehensive address space layout display

### Defining arbitrary custom structures

### Highlight custom strings

================================================
FILE: docs/testing.md
================================================
## Testing GEF
This page describes how GEF testing is done. Any new command/functionality must receive adequate
testing to be merged. Also PR failing CI (test + linting) won't be merged either.
### Prerequisites
All the prerequisite packages are in `requirements.txt` file at the root of the project. So running
```bash
python -m pip install -r tests/requirements.txt --user -U
```
is enough to get started.
### Running tests
#### Basic `pytest`
For testing GEF on the architecture on the host running the tests (most cases), simply run
```bash
cd /root/of/gef
python3 -m pytest -v -k "not benchmark" tests
```
Note that to ensure compatibility, tests must be executed with the same Python version GDB was
compiled against. To obtain this version, you can execute the following command:
```bash
gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit
```
At the end, a summary of explanation will be shown, clearly indicating the tests that have failed,
for instance:
```text
=================================== short test summary info ==================================
FAILED tests/commands/heap.py::HeapCommand::test_cmd_heap_bins_large - AssertionError: 'siz...
FAILED tests/commands/heap.py::HeapCommand::test_cmd_heap_bins_small - AssertionError: 'siz...
FAILED tests/commands/heap.py::HeapCommand::test_cmd_heap_bins_unsorted - AssertionError: '...
======================== 3 failed, 4 passed, 113 deselected in 385.77s (0:06:25)==============
```
You can then use `pytest` directly to help you fix each error specifically.
#### Using `pytest`
GEF entirely relies on [`pytest`](https://pytest.org) for its testing. Refer to the project
documentation for details.
Adding new code __requires__ extensive testing. Tests can be added in their own module in the
`tests/` folder. For example, if adding a new command to `gef`, a new test module should be created
and located in `/root/of/gef/tests/commands/my_new_command.py`. The test class __must__ inherit
`tests.base.RemoteGefUnitTestGeneric`. This class allows one to manipulate gdb and gef through rpyc
under their respective `self._gdb` and `self._gef` attributes.
A skeleton of a test module would look something like:
```python
"""
`my-command` command test module
"""
from tests.utils import RemoteGefUnitTestGeneric
class MyCommandCommand(RemoteGefUnitTestGeneric):
"""`my-command` command test module"""
def setUp(self) -> None:
# By default, tests will be executed against the default.out binary
# You can change this behavior in the `setUp` function
self._target = debug_target("my-custom-binary-for-tests")
return super().setUp()
def test_cmd_my_command(self):
# some convenience variables
root, gdb, gef = self._conn.root, self._gdb, self._gef
# You can then interact with any command from gdb or any class/function/variable from gef
# For instance:
# * tests that `my-command` is expected to fail if the session is not active
output = gdb.execute("my-command", to_string=True)
assert output == ERROR_INACTIVE_SESSION_MESSAGE
# * `my-command` must print "Hello World" message when executed in running context
gdb.execute("start")
output = gdb.execute("my-command", to_string=True)
assert "Hello World" == output
```
You might want to refer to the following documentations:
* [`pytest`](https://docs.pytest.org/en/)
* [`gdb Python API`](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html)
* (maybe) [`rpyc`](https://rpyc.readthedocs.io/en/latest/)
When running your test, you can summon `pytest` with the `--pdb` flag to enter the python testing
environment to help you get more information about the reason of failure.
One of the most convenient ways to test `gef` properly is using the `pytest` integration of modern
editors such as VisualStudio Code or PyCharm. Without proper tests, new code will not be integrated.
Also note that GEF can be remotely controlled using the script `scripts/remote_debug.py` as such:
```text
$ gdb -q -nx
(gdb) source /path/to/gef/gef.py
[...]
gef➤ source /path/to/gef/scripts/remote_debug.py
gef➤ pi start_rpyc_service(4444)
```
Here RPyC will be started on the local host, and bound to the TCP port 4444. We can now connect
using a regular Python REPL:
```text
>>> import rpyc
>>> c = rpyc.connect("localhost", 4444)
>>> gdb = c.root.gdb
>>> gef = c.root.gef
# We can now fully control the remote GDB
>>> gdb.execute("file /bin/ls")
>>> gdb.execute("start")
>>> print(hex(gef.arch.pc))
0x55555555aab0
>>> print(hex(gef.arch.sp))
0x7fffffffdcf0
```
### Linting GEF
You can use the Makefile at the root of the project to get the proper linting settings. For most
cases, the following command is enough:
```bash
cd /root/of/gef
python3 -m pylint --rcfile .pylintrc
```
Note that to ensure compatibility, tests must be executed with the same Python version GDB was
compiled against. To obtain this version, you can execute the following command:
```bash
gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit
```
### Code quality
To ensure a consistent code quality and make it easy for both contributors and developers, GEF and
GEF-Extras both rely on [`pre-commit`](https://pre-commit.com). The `pre-commit` tool is a
framework used to manage and maintain multi-language pre-commit hooks. These hooks are scripts that
run automatically before each commit to identify issues in code, such as missing semicolons,
trailing whitespace, and debug statements. This helps in ensuring code quality and consistency
before submission to code review, and therefore is triggered automatically when submitting a Pull
Request to GEF. This check is treated equally with the unit tests and therefore failing to pass
will result in your PR not being merged.
`pre-commit` is part of the [dev package requirements](https://github.com/hugsy/gef/blob/main/tests/requirements.txt)
```console
cd /root/to/gef/repo
python -m pip install --user -r tests/requirements.txt
```
But if you need to install separately it can be done using
```console
python -m pip install pre-commit
```
And to enable it
```console
pre-commit install
```
By default, `pre-commit` will use git hook to run the validation checks after each commit but you
can modify this behavior as desired or even run it manually
```console
pre-commit run --all-files
```
By default, `pre-commit` will report and attempt to fix the code to match what the coding style
defined with GEF.
### Benchmarking GEF
Benchmarking relies on `pytest-benchmark` and is experimental for now.
You can run all benchmark test cases as such:
```bash
cd /root/of/gef
pytest -k benchmark
```
which will return (after some time) an execution summary
```text
tests/perf/benchmark.py .. [100%]
---------------------------------------- benchmark: 3 tests -----------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
time_baseline 612.2325 (1.0) 630.3416 (1.01) 623.7984 (1.01) 7.2848 (1.64) 626.1485 (1.01) 9.9971 (1.81) 1;0 1.6031 (0.99) 5 1
time_cmd_context 613.8124 (1.00) 625.8964 (1.0) 620.1908 (1.0) 4.4532 (1.0) 619.8831 (1.0) 5.5109 (1.0) 2;0 1.6124 (1.0) 5 1
time_elf_parsing 616.5053 (1.01) 638.6965 (1.02) 628.1588 (1.01) 8.2465 (1.85) 629.0099 (1.01) 10.7885 (1.96) 2;0 1.5920 (0.99) 5 1
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Legend:
Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
OPS: Operations Per Second, computed as 1 / Mean
============================================== 3 passed, 117 deselected in 14.78s =============================================
```
================================================
FILE: gef.py
================================================
#######################################################################################
# GEF - Multi-Architecture GDB Enhanced Features for Exploiters & Reverse-Engineers
#
# by @_hugsy_
#######################################################################################
#
# GEF is a kick-ass set of commands for X86, ARM, MIPS, PowerPC and SPARC to
# make GDB cool again for exploit dev. It is aimed to be used mostly by exploit
# devs and reversers, to provides additional features to GDB using the Python
# API to assist during the process of dynamic analysis.
#
# GEF fully relies on GDB API and other Linux-specific sources of information
# (such as /proc/). As a consequence, some of the features might not work
# on custom or hardened systems such as GrSec.
#
# Since January 2020, GEF solely support GDB compiled with Python3 and was tested on
# * x86-32 & x86-64
# * arm v5,v6,v7
# * aarch64 (armv8)
# * mips & mips64
# * powerpc & powerpc64
# * sparc & sparc64(v9)
#
# For GEF with Python2 (only) support was moved to the GEF-Legacy
# (https://github.com/hugsy/gef-legacy)
#
# To start: in gdb, type `source /path/to/gef.py`
#
#######################################################################################
#
# gef is distributed under the MIT License (MIT)
# Copyright (c) 2013-2024 crazy rabbidz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import abc
import argparse
import ast
import atexit
import binascii
import codecs
import collections
import configparser
import ctypes
import enum
import functools
import hashlib
import importlib
import importlib.util
import inspect
import itertools
import os
import pathlib
import platform
import re
import shutil
import socket
import string
import struct
import subprocess
import sys
import tempfile
import time
import traceback
import warnings
from functools import lru_cache
from io import StringIO, TextIOWrapper
from types import ModuleType
from typing import (Any, ByteString, Callable, Generator, Iterable, Iterator,
NoReturn, Sequence, Type, TypeVar, cast)
from urllib.request import urlopen
GEF_DEFAULT_BRANCH = "main"
GEF_EXTRAS_DEFAULT_BRANCH = "main"
def http_get(url: str) -> bytes | None:
"""Basic HTTP wrapper for GET request. Return the body of the page if HTTP code is OK,
otherwise return None."""
try:
http = urlopen(url)
return http.read() if http.getcode() == 200 else None
except Exception:
return None
def update_gef(argv: list[str]) -> int:
"""Obsolete. Use `gef.sh`."""
return -1
try:
import gdb # type:ignore
except ImportError:
if len(sys.argv) >= 2 and sys.argv[1].lower() in ("--update", "--upgrade"):
print("[-] `update_gef` is obsolete. Use the `gef.sh` script to update gef from the command line.")
print("[-] gef cannot run as standalone")
sys.exit(1)
GDB_MIN_VERSION: tuple[int, int] = (10, 0)
PYTHON_MIN_VERSION: tuple[int, int] = (3, 10)
PYTHON_VERSION: tuple[int, int] = sys.version_info[0:2]
GDB_VERSION: tuple[int, int] = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) # type:ignore
DEFAULT_PAGE_ALIGN_SHIFT = 12
DEFAULT_PAGE_SIZE = 1 << DEFAULT_PAGE_ALIGN_SHIFT
GEF_RC = (pathlib.Path(os.getenv("GEF_RC", "")).absolute()
if os.getenv("GEF_RC")
else pathlib.Path().home() / ".gef.rc")
GEF_TEMP_DIR = pathlib.Path(tempfile.gettempdir())/ "gef"
GEF_MAX_STRING_LENGTH = 50
LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME = "main_arena"
ANSI_SPLIT_RE = r"(\033\[[\d;]*m)"
LEFT_ARROW = " ← "
RIGHT_ARROW = " → "
DOWN_ARROW = "↳"
HORIZONTAL_LINE = "─"
VERTICAL_LINE = "│"
CROSS = "✘ "
TICK = "✓ "
BP_GLYPH = "●"
GEF_PROMPT = "gef➤ "
GEF_PROMPT_ON = f"\001\033[1;32m\002{GEF_PROMPT}\001\033[0m\002"
GEF_PROMPT_OFF = f"\001\033[1;31m\002{GEF_PROMPT}\001\033[0m\002"
__registered_commands__ : set[Type["GenericCommand"]] = set()
__registered_functions__ : set[Type["GenericFunction"]] = set()
__registered_architectures__ : dict["Elf.Abi | str", Type["Architecture"]] = {}
__registered_file_formats__ : set[ Type["FileFormat"] ] = set()
GefMemoryMapProvider = Callable[[], Generator["Section", None, None]]
def reset_all_caches() -> None:
"""Free all caches. If an object is cached, it will have a callable attribute `cache_clear`
which will be invoked to purge the function cache."""
for mod in dir(sys.modules["__main__"]):
obj = getattr(sys.modules["__main__"], mod)
if hasattr(obj, "cache_clear"):
obj.cache_clear()
gef.reset_caches()
return
def reset() -> None:
global gef
arch = None
if "gef" in locals().keys():
reset_all_caches()
arch = gef.arch
del gef
gef = Gef()
gef.setup()
if arch:
gef.arch = arch
return
def highlight_text(text: str) -> str:
"""
Highlight text using `gef.ui.highlight_table` { match -> color } settings.
If RegEx is enabled it will create a match group around all items in the
`gef.ui.highlight_table` and wrap the specified color in the `gef.ui.highlight_table`
around those matches.
If RegEx is disabled, split by ANSI codes and 'colorify' each match found
within the specified string.
"""
global gef
if not gef.ui.highlight_table:
return text
if gef.config["highlight.regex"]:
for match, color in gef.ui.highlight_table.items():
text = re.sub("(" + match + ")", Color.colorify("\\1", color), text)
return text
ansiSplit = re.split(ANSI_SPLIT_RE, text)
for match, color in gef.ui.highlight_table.items():
for index, val in enumerate(ansiSplit):
found = val.find(match)
if found > -1:
ansiSplit[index] = val.replace(match, Color.colorify(match, color))
break
text = "".join(ansiSplit)
ansiSplit = re.split(ANSI_SPLIT_RE, text)
return "".join(ansiSplit)
def gef_print(*args: str, end="\n", sep=" ", **kwargs: Any) -> None:
"""Wrapper around print(), using string buffering feature."""
parts = [highlight_text(a) for a in args]
if buffer_output() and gef.ui.stream_buffer and not is_debug():
gef.ui.stream_buffer.write(sep.join(parts) + end)
return
print(*parts, sep=sep, end=end, **kwargs)
return
def bufferize(f: Callable) -> Callable:
"""Store the content to be printed for a function in memory, and flush it on function exit."""
@functools.wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Any:
global gef
if gef.ui.stream_buffer:
return f(*args, **kwargs)
gef.ui.stream_buffer = StringIO()
try:
rv = f(*args, **kwargs)
finally:
redirect = gef.config["context.redirect"]
if redirect.startswith("/dev/pts/"):
if not gef.ui.redirect_fd:
# if the FD has never been open, open it
fd = open(redirect, "wt")
gef.ui.redirect_fd = fd
elif redirect != gef.ui.redirect_fd.name:
# if the user has changed the redirect setting during runtime, update the state
gef.ui.redirect_fd.close()
fd = open(redirect, "wt")
gef.ui.redirect_fd = fd
else:
# otherwise, keep using it
fd = gef.ui.redirect_fd
else:
fd = sys.stdout
gef.ui.redirect_fd = None
if gef.ui.redirect_fd and fd.closed:
# if the tty was closed, revert back to stdout
fd = sys.stdout
gef.ui.redirect_fd = None
gef.config["context.redirect"] = ""
fd.write(gef.ui.stream_buffer.getvalue())
fd.flush()
gef.ui.stream_buffer = None
return rv
return wrapper
class ValidationError(Exception): pass
#
# Helpers
#
class ObsoleteException(Exception): pass
class AlreadyRegisteredException(Exception): pass
def p8(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes:
"""Pack one byte respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.pack(f"{endian}B", x) if not s else struct.pack(f"{endian:s}b", x)
def p16(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes:
"""Pack one word respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.pack(f"{endian}H", x) if not s else struct.pack(f"{endian:s}h", x)
def p32(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes:
"""Pack one dword respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.pack(f"{endian}I", x) if not s else struct.pack(f"{endian:s}i", x)
def p64(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes:
"""Pack one qword respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.pack(f"{endian}Q", x) if not s else struct.pack(f"{endian:s}q", x)
def u8(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int:
"""Unpack one byte respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.unpack(f"{endian}B", x)[0] if not s else struct.unpack(f"{endian:s}b", x)[0]
def u16(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int:
"""Unpack one word respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.unpack(f"{endian}H", x)[0] if not s else struct.unpack(f"{endian:s}h", x)[0]
def u32(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int:
"""Unpack one dword respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.unpack(f"{endian}I", x)[0] if not s else struct.unpack(f"{endian:s}i", x)[0]
def u64(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int:
"""Unpack one qword respecting the current architecture endianness."""
endian = e or gef.arch.endianness
return struct.unpack(f"{endian}Q", x)[0] if not s else struct.unpack(f"{endian:s}q", x)[0]
def is_ascii_string(address: int) -> bool:
"""Helper function to determine if the buffer pointed by `address` is an ASCII string (in GDB)"""
try:
return gef.memory.read_ascii_string(address) is not None
except Exception:
return False
def is_alive() -> bool:
"""Check if GDB is running."""
try:
return gdb.selected_inferior().pid > 0
except Exception:
return False
def calling_function() -> str | None:
"""Return the name of the calling function"""
try:
stack_info = traceback.extract_stack()[-3]
return stack_info.name
except Exception as e:
dbg(f"traceback failed with {str(e)}")
return None
#
# Decorators
#
def only_if_gdb_running(f: Callable) -> Callable:
"""Decorator wrapper to check if GDB is running."""
@functools.wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Any:
if is_alive():
return f(*args, **kwargs)
else:
warn("No debugging session active")
return wrapper
def only_if_gdb_target_local(f: Callable) -> Callable:
"""Decorator wrapper to check if GDB is running locally (target not remote)."""
@functools.wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Any:
if not is_remote_debug():
return f(*args, **kwargs)
else:
warn("This command cannot work for remote sessions.")
return wrapper
def deprecated(solution: str = "") -> Callable:
"""Decorator to add a warning when a command is obsolete and will be removed."""
def decorator(f: Callable) -> Callable:
@functools.wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Any:
caller = inspect.stack()[1]
caller_file = pathlib.Path(caller.filename)
caller_loc = caller.lineno
msg = f"{caller_file.name}:L{caller_loc} '{f.__name__}' is deprecated and will be removed in a feature release. "
if not gef:
print(msg)
elif gef.config["gef.show_deprecation_warnings"] is True:
if solution:
msg += solution
warn(msg)
return f(*args, **kwargs)
if not wrapper.__doc__:
wrapper.__doc__ = ""
wrapper.__doc__ += f"\r\n`{f.__name__}` is **DEPRECATED** and will be removed in the future.\r\n{solution}"
return wrapper
return decorator
def experimental_feature(f: Callable) -> Callable:
"""Decorator to add a warning when a feature is experimental."""
@functools.wraps(f)
def wrapper(*args: Any, **kwargs: Any) -> Any:
warn("This feature is under development, expect bugs and unstability...")
return f(*args, **kwargs)
return wrapper
def only_if_events_supported(event_type: str) -> Callable:
"""Checks if GDB supports events without crashing."""
def wrap(f: Callable) -> Callable:
def wrapped_f(*args: Any, **kwargs: Any) -> Any:
if getattr(gdb, "events") and getattr(gdb.events, event_type):
return f(*args, **kwargs)
warn("GDB events cannot be set")
return wrapped_f
return wrap
class classproperty(property):
"""Make the attribute a `classproperty`."""
def __get__(self, cls, owner):
assert self.fget
return classmethod(self.fget).__get__(None, owner)()
def FakeExit(*args: Any, **kwargs: Any) -> NoReturn:
raise RuntimeWarning
sys.exit = FakeExit
def parse_arguments(required_arguments: dict[str | tuple[str, str], Any],
optional_arguments: dict[str | tuple[str, str], Any]) -> Callable:
"""Argument parsing decorator."""
def int_wrapper(x: str) -> int: return int(x, 0)
def decorator(f: Callable) -> Callable | None:
def wrapper(*args: Any, **kwargs: Any) -> Callable:
parser = argparse.ArgumentParser(prog=args[0]._cmdline_, add_help=True)
for argname in required_arguments:
argvalue = required_arguments[argname]
argtype = type(argvalue)
if argtype is int:
argtype = int_wrapper
argname_is_list = not isinstance(argname, str)
assert not argname_is_list and isinstance(argname, str)
if not argname_is_list and argname.startswith("-"):
# optional args
if argtype is bool:
parser.add_argument(argname, action="store_true" if argvalue else "store_false")
else:
parser.add_argument(argname, type=argtype, required=True, default=argvalue)
else:
if argtype in (list, tuple):
nargs = "*"
argtype = type(argvalue[0])
else:
nargs = "?"
# positional args
parser.add_argument(argname, type=argtype, default=argvalue, nargs=nargs)
for argname in optional_arguments:
if isinstance(argname, str) and not argname.startswith("-"):
# refuse positional arguments
continue
argvalue = optional_arguments[argname]
argtype = type(argvalue)
if isinstance(argname, str):
argname = [argname,]
if argtype is int:
argtype = int_wrapper
elif argtype is bool:
parser.add_argument(*argname, action="store_false" if argvalue else "store_true")
continue
elif argtype in (list, tuple):
parser.add_argument(*argname, type=type(argvalue[0]),
default=[], action="append")
continue
parser.add_argument(*argname, type=argtype, default=argvalue)
parsed_args = parser.parse_args(*(args[1:]))
kwargs["arguments"] = parsed_args
return f(*args, **kwargs)
return wrapper
return decorator
class Color:
"""Used to colorify terminal output."""
### Special chars:
# \001 -> Tell the readline library that we start a special sequence
# which won't be displayed (takes no column in the output)
# \002 -> Tell the readline library that we end a special sequence
# started with \001
# \033 -> Start an ANSI escape code for displaying colors
colors = {
"normal" : "\001\033[0m\002",
"gray" : "\001\033[1;38;5;240m\002",
"light_gray" : "\001\033[0;37m\002",
"red" : "\001\033[31m\002",
"green" : "\001\033[32m\002",
"yellow" : "\001\033[33m\002",
"blue" : "\001\033[34m\002",
"pink" : "\001\033[35m\002",
"cyan" : "\001\033[36m\002",
"bold" : "\001\033[1m\002",
"underline" : "\001\033[4m\002",
"underline_off" : "\001\033[24m\002",
"highlight" : "\001\033[3m\002",
"highlight_off" : "\001\033[23m\002",
"blink" : "\001\033[5m\002",
"blink_off" : "\001\033[25m\002",
}
@staticmethod
def redify(msg: str) -> str: return Color.colorify(msg, "red")
@staticmethod
def greenify(msg: str) -> str: return Color.colorify(msg, "green")
@staticmethod
def blueify(msg: str) -> str: return Color.colorify(msg, "blue")
@staticmethod
def yellowify(msg: str) -> str: return Color.colorify(msg, "yellow")
@staticmethod
def grayify(msg: str) -> str: return Color.colorify(msg, "gray")
@staticmethod
def light_grayify(msg: str) -> str: return Color.colorify(msg, "light_gray")
@staticmethod
def pinkify(msg: str) -> str: return Color.colorify(msg, "pink")
@staticmethod
def cyanify(msg: str) -> str: return Color.colorify(msg, "cyan")
@staticmethod
def boldify(msg: str) -> str: return Color.colorify(msg, "bold")
@staticmethod
def underlinify(msg: str) -> str: return Color.colorify(msg, "underline")
@staticmethod
def highlightify(msg: str) -> str: return Color.colorify(msg, "highlight")
@staticmethod
def blinkify(msg: str) -> str: return Color.colorify(msg, "blink")
@staticmethod
def colorify(text: str, attrs: str) -> str:
"""Color text according to the given attributes."""
if gef.config["gef.disable_color"] is True: return text
colors = Color.colors
msg = [colors[attr] for attr in attrs.split() if attr in colors]
msg.append(str(text))
if colors["highlight"] in msg: msg.append(colors["highlight_off"])
if colors["underline"] in msg: msg.append(colors["underline_off"])
if colors["blink"] in msg: msg.append(colors["blink_off"])
msg.append(colors["normal"])
return "".join(msg)
class Address:
"""GEF representation of memory addresses."""
def __init__(self, **kwargs: Any) -> None:
self.value: int = kwargs.get("value", 0)
self.section: "Section" = kwargs.get("section", None)
self.info: "Zone" = kwargs.get("info", None)
return
def __str__(self) -> str:
value = format_address(self.value)
code_color = gef.config["theme.address_code"]
stack_color = gef.config["theme.address_stack"]
heap_color = gef.config["theme.address_heap"]
if self.is_in_text_segment():
return Color.colorify(value, code_color)
if self.is_in_heap_segment():
return Color.colorify(value, heap_color)
if self.is_in_stack_segment():
return Color.colorify(value, stack_color)
return value
def __int__(self) -> int:
return self.value
def is_in_text_segment(self) -> bool:
return (hasattr(self.info, "name") and ".text" in self.info.name) or \
(hasattr(self.section, "path") and get_filepath() == self.section.path and self.section.is_executable())
def is_in_stack_segment(self) -> bool:
return hasattr(self.section, "path") and "[stack]" == self.section.path
def is_in_heap_segment(self) -> bool:
return hasattr(self.section, "path") and "[heap]" == self.section.path
def dereference(self) -> int | None:
addr = align_address(int(self.value))
derefed = dereference(addr)
return None if derefed is None else int(derefed)
@property
def valid(self) -> bool:
return any(map(lambda x: x.page_start <= self.value < x.page_end, gef.memory.maps))
class Permission(enum.Flag):
"""GEF representation of Linux permission."""
NONE = 0
EXECUTE = 1
WRITE = 2
READ = 4
ALL = 7
def __str__(self) -> str:
perm_str = ""
perm_str += "r" if self & Permission.READ else "-"
perm_str += "w" if self & Permission.WRITE else "-"
perm_str += "x" if self & Permission.EXECUTE else "-"
return perm_str
@classmethod
def from_info_sections(cls, *args: str) -> "Permission":
perm = cls(0)
for arg in args:
if "READONLY" in arg: perm |= Permission.READ
if "DATA" in arg: perm |= Permission.WRITE
if "CODE" in arg: perm |= Permission.EXECUTE
return perm
@classmethod
def from_process_maps(cls, perm_str: str) -> "Permission":
perm = cls(0)
if perm_str[0] == "r": perm |= Permission.READ
if perm_str[1] == "w": perm |= Permission.WRITE
if perm_str[2] == "x": perm |= Permission.EXECUTE
return perm
@classmethod
def from_monitor_info_mem(cls, perm_str: str) -> "Permission":
perm = cls(0)
# perm_str[0] shows if this is a user page, which
# we don't track
if perm_str[1] == "r": perm |= Permission.READ
if perm_str[2] == "w": perm |= Permission.WRITE
return perm
@classmethod
def from_info_mem(cls, perm_str: str) -> "Permission":
perm = cls(0)
if "r" in perm_str: perm |= Permission.READ
if "w" in perm_str: perm |= Permission.WRITE
if "x" in perm_str: perm |= Permission.EXECUTE
return perm
class Section:
"""GEF representation of process memory sections."""
def __init__(self, **kwargs: Any) -> None:
self.page_start: int = kwargs.get("page_start", 0)
self.page_end: int = kwargs.get("page_end", 0)
self.offset: int = kwargs.get("offset", 0)
self.permission: Permission = kwargs.get("permission", Permission(0))
self.inode: int = kwargs.get("inode", 0)
self.path: str = kwargs.get("path", "")
return
def is_readable(self) -> bool:
return bool(self.permission & Permission.READ)
def is_writable(self) -> bool:
return bool(self.permission & Permission.WRITE)
def is_executable(self) -> bool:
return bool(self.permission & Permission.EXECUTE)
@property
def size(self) -> int:
if self.page_end is None or self.page_start is None:
raise AttributeError
return self.page_end - self.page_start
def _search_for_realpath_without_versions(self, path: pathlib.Path) -> str | None:
"""Given a path, search for a file that exists without numeric suffixes."""
# Match the path string against a regex that will remove a suffix
# consisting of a dot followed by numbers.
candidate = re.match(r"^(.*)\.(\d*)$", str(path))
while candidate:
candidate = candidate.group(1)
# If the prefix from the regex match is a file, return that path.
if pathlib.Path(candidate).is_file():
return candidate
# Otherwise, try to match again.
candidate = re.match(r"^(.*)\.(\d*)$", candidate)
return None
def _search_for_realpath(self) -> str | None:
"""This function is a workaround for gdb bug #23764
path might be wrong for remote sessions, so try a simple search for files
that aren't found at the path indicated, which should be canonical.
"""
assert gef.session.remote
remote_path = pathlib.Path(self.path)
# First, try the canonical path in the remote session root.
candidate1 = gef.session.remote.root / remote_path.relative_to(remote_path.anchor)
if candidate1.is_file():
return str(candidate1)
# Also try that path without version suffixes.
candidate = self._search_for_realpath_without_versions(candidate1)
if candidate:
return candidate
# On some systems, /lib(64) might be a symlink to /usr/lib(64), so try removing
# the /usr prefix.
if self.path.startswith("/usr"):
candidate = gef.session.remote.root / remote_path.relative_to("/usr")
if candidate.is_file():
return str(candidate)
# Also try that path without version suffixes.
candidate = self._search_for_realpath_without_versions(candidate)
if candidate:
return candidate
# Base case, return the original realpath
return str(candidate1)
@property
def realpath(self) -> str:
# when in a `gef-remote` session, realpath returns the path to the binary on the local disk, not remote
if gef.session.remote is None:
return self.path
default = self._search_for_realpath()
if default:
return default
raise FileNotFoundError
def __str__(self) -> str:
return (f"Section(start={self.page_start:#x}, end={self.page_end:#x}, "
f"perm={self.permission!s})")
def __repr__(self) -> str:
return str(self)
def __eq__(self, other: "Section") -> bool:
return other and \
self.page_start == other.page_start and \
self.size == other.size and \
self.permission == other.permission and \
self.path == other.path
def overlaps(self, other: "Section") -> bool:
return max(self.page_start, other.page_start) <= min(self.page_end, other.page_end)
def contains(self, addr: int) -> bool:
return addr in range(self.page_start, self.page_end)
Zone = collections.namedtuple("Zone", ["name", "zone_start", "zone_end", "filename"])
class Endianness(enum.Enum):
LITTLE_ENDIAN = 1
BIG_ENDIAN = 2
def __str__(self) -> str:
return "<" if self == Endianness.LITTLE_ENDIAN else ">"
def __repr__(self) -> str:
return self.name
def __int__(self) -> int:
return self.value
class FileFormatSection:
misc: Any
class FileFormat:
name: str
path: pathlib.Path
entry_point: int
checksec: dict[str, bool]
sections: list[FileFormatSection]
def __init__(self, path: str | pathlib.Path) -> None:
raise NotImplementedError
def __init_subclass__(cls: Type["FileFormat"], **kwargs):
global __registered_file_formats__
super().__init_subclass__(**kwargs)
required_attributes = ("name", "entry_point", "is_valid", "checksec",)
for attr in required_attributes:
if not hasattr(cls, attr):
raise NotImplementedError(f"File format '{cls.__name__}' is invalid: missing attribute '{attr}'")
__registered_file_formats__.add(cls)
return
@classmethod
def is_valid(cls, _: pathlib.Path) -> bool:
raise NotImplementedError
def __str__(self) -> str:
return f"{self.name}('{self.path.absolute()}', entry @ {self.entry_point:#x})"
class Elf(FileFormat):
"""Basic ELF parsing.
Ref:
- http://www.skyfree.org/linux/references/ELF_Format.pdf
- https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf
- https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
"""
class Class(enum.Enum):
ELF_32_BITS = 0x01
ELF_64_BITS = 0x02
ELF_MAGIC = 0x7f454c46
class Abi(enum.Enum):
X86_64 = 0x3e
X86_32 = 0x03
ARM = 0x28
MIPS = 0x08
POWERPC = 0x14
POWERPC64 = 0x15
SPARC = 0x02
SPARC64 = 0x2b
AARCH64 = 0xb7
RISCV = 0xf3
IA64 = 0x32
M68K = 0x04
class Type(enum.Enum):
ET_RELOC = 1
ET_EXEC = 2
ET_DYN = 3
ET_CORE = 4
class OsAbi(enum.Enum):
SYSTEMV = 0x00
HPUX = 0x01
NETBSD = 0x02
LINUX = 0x03
SOLARIS = 0x06
AIX = 0x07
IRIX = 0x08
FREEBSD = 0x09
OPENBSD = 0x0C
e_magic: int = ELF_MAGIC
e_class: "Elf.Class" = Class.ELF_32_BITS
e_endianness: Endianness = Endianness.LITTLE_ENDIAN
e_eiversion: int
e_osabi: "Elf.OsAbi"
e_abiversion: int
e_pad: bytes
e_type: "Elf.Type" = Type.ET_EXEC
e_machine: Abi = Abi.X86_32
e_version: int
e_entry: int
e_phoff: int
e_shoff: int
e_flags: int
e_ehsize: int
e_phentsize: int
e_phnum: int
e_shentsize: int
e_shnum: int
e_shstrndx: int
path: pathlib.Path
phdrs : list["Phdr"]
shdrs : list["Shdr"]
name: str = "ELF"
__checksec : dict[str, bool]
def __init__(self, path: str | pathlib.Path) -> None:
"""Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown."""
if isinstance(path, str):
self.path = pathlib.Path(path).expanduser()
elif isinstance(path, pathlib.Path):
self.path = path
else:
raise TypeError
if not self.path.exists():
raise FileNotFoundError(f"'{self.path}' not found/readable, most gef features will not work")
self.__checksec = {}
with self.path.open("rb") as self.fd:
# off 0x0
self.e_magic, e_class, e_endianness, self.e_eiversion = self.read_and_unpack(">IBBB")
if self.e_magic != Elf.ELF_MAGIC:
# The ELF is corrupted, GDB won't handle it, no point going further
raise RuntimeError("Not a valid ELF file (magic)")
self.e_class, self.e_endianness = Elf.Class(e_class), Endianness(e_endianness)
if self.e_endianness != gef.arch.endianness:
warn("Unexpected endianness for architecture")
endian = self.e_endianness
# off 0x7
e_osabi, self.e_abiversion = self.read_and_unpack(f"{endian}BB")
self.e_osabi = Elf.OsAbi(e_osabi)
# off 0x9
self.e_pad = self.read(7)
# off 0x10
e_type, e_machine, self.e_version = self.read_and_unpack(f"{endian}HHI")
self.e_type, self.e_machine = Elf.Type(e_type), Elf.Abi(e_machine)
# off 0x18
if self.e_class == Elf.Class.ELF_64_BITS:
self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}QQQ")
else:
self.e_entry, self.e_phoff, self.e_shoff = self.read_and_unpack(f"{endian}III")
self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum = self.read_and_unpack(f"{endian}IHHH")
self.e_shentsize, self.e_shnum, self.e_shstrndx = self.read_and_unpack(f"{endian}HHH")
self.phdrs = []
for i in range(self.e_phnum):
self.phdrs.append(Phdr(self, self.e_phoff + self.e_phentsize * i))
self.shdrs = []
for i in range(self.e_shnum):
self.shdrs.append(Shdr(self, self.e_shoff + self.e_shentsize * i))
return
def read(self, size: int) -> bytes:
return self.fd.read(size)
def read_and_unpack(self, fmt: str) -> tuple[Any, ...]:
size = struct.calcsize(fmt)
data = self.fd.read(size)
return struct.unpack(fmt, data)
def seek(self, off: int) -> None:
self.fd.seek(off, 0)
def __str__(self) -> str:
return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})"
def __repr__(self) -> str:
return f"ELF('{self.path.absolute()}', {self.e_class.name}, {self.e_machine.name})"
@property
def entry_point(self) -> int:
return self.e_entry
@classmethod
def is_valid(cls, path: pathlib.Path) -> bool:
return u32(path.open("rb").read(4), e = Endianness.BIG_ENDIAN) == Elf.ELF_MAGIC
@property
def checksec(self) -> dict[str, bool]:
"""Check the security property of the ELF binary. The following properties are:
- Canary
- NX
- PIE
- Fortify
- Partial/Full RelRO.
Return a dict() with the different keys mentioned above, and the boolean
associated whether the protection was found."""
if not self.__checksec:
def __check_security_property(opt: str, filename: str, pattern: str) -> bool:
cmd = [readelf,]
cmd += opt.split()
cmd += [filename,]
lines = gef_execute_external(cmd, as_list=True)
for line in lines:
if re.search(pattern, line):
return True
return False
abspath = str(self.path.absolute())
readelf = gef.session.constants["readelf"]
self.__checksec["Canary"] = __check_security_property("-rs", abspath, r"__stack_chk_fail") is True
has_gnu_stack = __check_security_property("-W -l", abspath, r"GNU_STACK") is True
if has_gnu_stack:
self.__checksec["NX"] = __check_security_property("-W -l", abspath, r"GNU_STACK.*RWE") is False
else:
self.__checksec["NX"] = False
self.__checksec["PIE"] = __check_security_property("-h", abspath, r":.*EXEC") is False
self.__checksec["Fortify"] = __check_security_property("-s", abspath, r"_chk@GLIBC") is True
self.__checksec["Partial RelRO"] = __check_security_property("-l", abspath, r"GNU_RELRO") is True
self.__checksec["Full RelRO"] = self.__checksec["Partial RelRO"] and __check_security_property("-d", abspath, r"BIND_NOW") is True
return self.__checksec
@classproperty
@deprecated("use `Elf.Abi.X86_64`")
def X86_64(cls) -> int: return Elf.Abi.X86_64.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.X86_32`")
def X86_32(cls) -> int : return Elf.Abi.X86_32.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.ARM`")
def ARM(cls) -> int : return Elf.Abi.ARM.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.MIPS`")
def MIPS(cls) -> int : return Elf.Abi.MIPS.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.POWERPC`")
def POWERPC(cls) -> int : return Elf.Abi.POWERPC.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.POWERPC64`")
def POWERPC64(cls) -> int : return Elf.Abi.POWERPC64.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.SPARC`")
def SPARC(cls) -> int : return Elf.Abi.SPARC.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.SPARC64`")
def SPARC64(cls) -> int : return Elf.Abi.SPARC64.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.AARCH64`")
def AARCH64(cls) -> int : return Elf.Abi.AARCH64.value # pylint: disable=no-self-argument
@classproperty
@deprecated("use `Elf.Abi.RISCV`")
def RISCV(cls) -> int : return Elf.Abi.RISCV.value # pylint: disable=no-self-argument
class Phdr:
class Type(enum.IntEnum):
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_TLS = 7
PT_LOOS = 0x60000000
PT_GNU_EH_FRAME = 0x6474e550
PT_GNU_STACK = 0x6474e551
PT_GNU_RELRO = 0x6474e552
PT_GNU_PROPERTY = 0x6474e553
PT_LOSUNW = 0x6ffffffa
PT_SUNWBSS = 0x6ffffffa
PT_SUNWSTACK = 0x6ffffffb
PT_HISUNW = PT_HIOS = 0x6fffffff
PT_LOPROC = 0x70000000
PT_ARM_EIDX = 0x70000001
PT_MIPS_ABIFLAGS= 0x70000003
PT_HIPROC = 0x7fffffff
UNKNOWN_PHDR = 0xffffffff
@classmethod
def _missing_(cls, _:int) -> "Phdr.Type":
return cls.UNKNOWN_PHDR
class Flags(enum.IntFlag):
PF_X = 1
PF_W = 2
PF_R = 4
p_type: "Phdr.Type"
p_flags: "Phdr.Flags"
p_offset: int
p_vaddr: int
p_paddr: int
p_filesz: int
p_memsz: int
p_align: int
def __init__(self, elf: Elf, off: int) -> None:
if not elf:
return
elf.seek(off)
self.offset = off
endian = elf.e_endianness
if elf.e_class == Elf.Class.ELF_64_BITS:
p_type, p_flags, self.p_offset = elf.read_and_unpack(f"{endian}IIQ")
self.p_vaddr, self.p_paddr = elf.read_and_unpack(f"{endian}QQ")
self.p_filesz, self.p_memsz, self.p_align = elf.read_and_unpack(f"{endian}QQQ")
else:
p_type, self.p_offset = elf.read_and_unpack(f"{endian}II")
self.p_vaddr, self.p_paddr = elf.read_and_unpack(f"{endian}II")
self.p_filesz, self.p_memsz, p_flags, self.p_align = elf.read_and_unpack(f"{endian}IIII")
self.p_type, self.p_flags = Phdr.Type(p_type), Phdr.Flags(p_flags)
return
def __str__(self) -> str:
return (f"Phdr(offset={self.offset}, type={self.p_type.name}, flags={self.p_flags.name}, "
f"vaddr={self.p_vaddr}, paddr={self.p_paddr}, filesz={self.p_filesz}, "
f"memsz={self.p_memsz}, align={self.p_align})")
class Shdr:
class Type(enum.IntEnum):
SHT_NULL = 0
SHT_PROGBITS = 1
SHT_SYMTAB = 2
SHT_STRTAB = 3
SHT_RELA = 4
SHT_HASH = 5
SHT_DYNAMIC = 6
SHT_NOTE = 7
SHT_NOBITS = 8
SHT_REL = 9
SHT_SHLIB = 10
SHT_DYNSYM = 11
SHT_NUM = 12
SHT_INIT_ARRAY = 14
SHT_FINI_ARRAY = 15
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
SHT_LOOS = 0x60000000
SHT_GNU_ATTRIBUTES = 0x6ffffff5
SHT_GNU_HASH = 0x6ffffff6
SHT_GNU_LIBLIST = 0x6ffffff7
SHT_CHECKSUM = 0x6ffffff8
SHT_LOSUNW = 0x6ffffffa
SHT_SUNW_move = 0x6ffffffa
SHT_SUNW_COMDAT = 0x6ffffffb
SHT_SUNW_syminfo = 0x6ffffffc
SHT_GNU_verdef = 0x6ffffffd
SHT_GNU_verneed = 0x6ffffffe
SHT_GNU_versym = 0x6fffffff
SHT_LOPROC = 0x70000000
SHT_ARM_EXIDX = 0x70000001
SHT_X86_64_UNWIND = 0x70000001
SHT_ARM_ATTRIBUTES = 0x70000003
SHT_MIPS_OPTIONS = 0x7000000d
DT_MIPS_INTERFACE = 0x7000002a
SHT_HIPROC = 0x7fffffff
SHT_LOUSER = 0x80000000
SHT_HIUSER = 0x8fffffff
UNKNOWN_SHDR = 0xffffffff
@classmethod
def _missing_(cls, _:int) -> "Shdr.Type":
return cls.UNKNOWN_SHDR
class Flags(enum.IntFlag):
WRITE = 1
ALLOC = 2
EXECINSTR = 4
MERGE = 0x10
STRINGS = 0x20
INFO_LINK = 0x40
LINK_ORDER = 0x80
OS_NONCONFORMING = 0x100
GROUP = 0x200
TLS = 0x400
COMPRESSED = 0x800
RELA_LIVEPATCH = 0x00100000
RO_AFTER_INIT = 0x00200000
ORDERED = 0x40000000
EXCLUDE = 0x80000000
UNKNOWN_FLAG = 0xffffffff
@classmethod
def _missing_(cls, _:int):
return cls.UNKNOWN_FLAG
sh_name: int
sh_type: "Shdr.Type"
sh_flags: "Shdr.Flags"
sh_addr: int
sh_offset: int
sh_size: int
sh_link: int
sh_info: int
sh_addralign: int
sh_entsize: int
name: str
def __init__(self, elf: Elf | None, off: int) -> None:
if elf is None:
return
elf.seek(off)
endian = elf.e_endianness
if elf.e_class == Elf.Class.ELF_64_BITS:
self.sh_name, sh_type, sh_flags = elf.read_and_unpack(f"{endian}IIQ")
self.sh_addr, self.sh_offset = elf.read_and_unpack(f"{endian}QQ")
self.sh_size, self.sh_link, self.sh_info = elf.read_and_unpack(f"{endian}QII")
self.sh_addralign, self.sh_entsize = elf.read_and_unpack(f"{endian}QQ")
else:
self.sh_name, sh_type, sh_flags = elf.read_and_unpack(f"{endian}III")
self.sh_addr, self.sh_offset = elf.read_and_unpack(f"{endian}II")
self.sh_size, self.sh_link, self.sh_info = elf.read_and_unpack(f"{endian}III")
self.sh_addralign, self.sh_entsize = elf.read_and_unpack(f"{endian}II")
self.sh_type = Shdr.Type(sh_type)
self.sh_flags = Shdr.Flags(sh_flags)
stroff = elf.e_shoff + elf.e_shentsize * elf.e_shstrndx
if elf.e_class == Elf.Class.ELF_64_BITS:
elf.seek(stroff + 16 + 8)
offset = u64(elf.read(8))
else:
elf.seek(stroff + 12 + 4)
offset = u32(elf.read(4))
elf.seek(offset + self.sh_name)
self.name = ""
while True:
c = u8(elf.read(1))
if c == 0:
break
self.name += chr(c)
return
def __str__(self) -> str:
return (f"Shdr(name={self.name}, type={self.sh_type.name}, flags={self.sh_flags.name}, "
f"addr={self.sh_addr:#x}, offset={self.sh_offset}, size={self.sh_size}, link={self.sh_link}, "
f"info={self.sh_info}, addralign={self.sh_addralign}, entsize={self.sh_entsize})")
class Instruction:
"""GEF representation of a CPU instruction."""
def __init__(self, address: int, location: str, mnemo: str, operands: list[str], opcodes: bytes) -> None:
self.address, self.location, self.mnemonic, self.operands, self.opcodes = \
address, location, mnemo, operands, opcodes
return
# Allow formatting an instruction with {:o} to show opcodes.
# The number of bytes to display can be configured, e.g. {:4o} to only show 4 bytes of the opcodes
def __format__(self, format_spec: str) -> str:
if len(format_spec) == 0 or format_spec[-1] != "o":
return str(self)
if format_spec == "o":
opcodes_len = len(self.opcodes)
else:
opcodes_len = int(format_spec[:-1])
opcodes_text = "".join(f"{b:02x}" for b in self.opcodes[:opcodes_len])
if opcodes_len < len(self.opcodes):
opcodes_text += "..."
return (f"{self.address:#10x} {opcodes_text:{opcodes_len * 2 + 3:d}s} {self.location:16} "
f"{self.mnemonic:6} {', '.join(self.operands)}")
def __str__(self) -> str:
return f"{self.address:#10x} {self.location:16} {self.mnemonic:6} {', '.join(self.operands)}"
def is_valid(self) -> bool:
return "(bad)" not in self.mnemonic
def size(self) -> int:
return len(self.opcodes)
def next(self) -> "Instruction":
address = self.address + self.size()
return gef_get_instruction_at(address)
@deprecated("Use GefHeapManager.find_main_arena_addr()")
def search_for_main_arena() -> int:
return GefHeapManager.find_main_arena_addr()
class GlibcHeapInfo:
"""Glibc heap_info struct"""
@staticmethod
def heap_info_t() -> Type[ctypes.Structure]:
assert gef.libc.version
class heap_info_cls(ctypes.Structure):
pass
pointer = ctypes.c_uint64 if gef.arch.ptrsize == 8 else ctypes.c_uint32
pad_size = -5 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1)
fields = [
("ar_ptr", ctypes.POINTER(GlibcArena.malloc_state_t())),
("prev", ctypes.POINTER(heap_info_cls)),
("size", pointer)
]
if gef.libc.version >= (2, 5):
fields += [
("mprotect_size", pointer)
]
pad_size = -6 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1)
if gef.libc.version >= (2, 34):
fields += [
("pagesize", pointer)
]
pad_size = -3 * gef.arch.ptrsize & (gef.heap.malloc_alignment - 1)
fields += [
("pad", ctypes.c_uint8*pad_size)
]
heap_info_cls._fields_ = fields
return heap_info_cls
def __init__(self, addr: str | int) -> None:
self.__address : int = parse_address(f"&{addr}") if isinstance(addr, str) else addr
self.reset()
return
def reset(self):
self._sizeof = ctypes.sizeof(GlibcHeapInfo.heap_info_t())
self._data = gef.memory.read(self.__address, ctypes.sizeof(GlibcHeapInfo.heap_info_t()))
self._heap_info = GlibcHeapInfo.heap_info_t().from_buffer_copy(self._data)
return
def __getattr__(self, item: Any) -> Any:
if item in dir(self._heap_info):
return ctypes.cast(getattr(self._heap_info, item), ctypes.c_void_p).value
return getattr(self, item)
def __abs__(self) -> int:
return self.__address
def __int__(self) -> int:
return self.__address
@property
def address(self) -> int:
return self.__address
@property
def sizeof(self) -> int:
return self._sizeof
@property
def addr(self) -> int:
return int(self)
@property
def heap_start(self) -> int:
# check special case: first heap of non-main-arena
if self.ar_ptr - self.address < 0x60:
# the first heap of a non-main-arena starts with a `heap_info`
# struct, which should fit easily into 0x60 bytes throughout
# all architectures and glibc versions. If this check succeeds
# then we are currently looking at such a "first heap"
arena = GlibcArena(f"*{self.ar_ptr:#x}")
heap_addr = arena.heap_addr()
if heap_addr:
return heap_addr
else:
err(f"Cannot find heap address for arena {self.ar_ptr:#x}")
return 0
return self.address + self.sizeof
@property
def heap_end(self) -> int:
return self.address + self.size
class GlibcArena:
"""Glibc arena class"""
NFASTBINS = 10
NBINS = 128
NSMALLBINS = 64
BINMAPSHIFT = 5
BITSPERMAP = 1 << BINMAPSHIFT
BINMAPSIZE = NBINS // BITSPERMAP
@staticmethod
def malloc_state_t() -> Type[ctypes.Structure]:
pointer = ctypes.c_uint64 if gef and gef.arch.ptrsize == 8 else ctypes.c_uint32
fields = [
("mutex", ctypes.c_uint32),
("flags", ctypes.c_uint32),
]
if gef and gef.libc.version and gef.libc.version >= (2, 27):
# https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c#L1684
fields += [
("have_fastchunks", ctypes.c_uint32),
("UNUSED_c", ctypes.c_uint32), # padding to align to 0x10
]
fields += [
("fastbinsY", GlibcArena.NFASTBINS * pointer),
("top", pointer),
("last_remainder", pointer),
("bins", (GlibcArena.NBINS * 2 - 2) * pointer),
("binmap", GlibcArena.BINMAPSIZE * ctypes.c_uint32),
("next", pointer),
("next_free", pointer)
]
if gef and gef.libc.version and gef.libc.version >= (2, 23):
# https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c#L1719
fields += [
("attached_threads", pointer)
]
fields += [
("system_mem", pointer),
("max_system_mem", pointer),
]
class malloc_state_cls(ctypes.Structure):
_fields_ = fields
return malloc_state_cls
def __init__(self, addr: str) -> None:
try:
self.__address : int = parse_address(f"&{addr}")
except gdb.error:
self.__address : int = GefHeapManager.find_main_arena_addr()
# if `find_main_arena_addr` throws `gdb.error` on symbol lookup:
# it means the session is not started, so just propagate the exception
self.reset()
return
def reset(self):
self._sizeof = ctypes.sizeof(GlibcArena.malloc_state_t())
self._data = gef.memory.read(self.__address, ctypes.sizeof(GlibcArena.malloc_state_t()))
self.__arena = GlibcArena.malloc_state_t().from_buffer_copy(self._data)
return
def __abs__(self) -> int:
return self.__address
def __int__(self) -> int:
return self.__address
def __iter__(self) -> Generator["GlibcArena", None, None]:
assert gef.heap.main_arena
main_arena = int(gef.heap.main_arena)
current_arena = self
yield current_arena
while True:
if current_arena.next == 0 or current_arena.next == main_arena:
break
current_arena = GlibcArena(f"*{current_arena.next:#x} ")
yield current_arena
return
def __eq__(self, other: "GlibcArena") -> bool:
return self.__address == int(other)
def __str__(self) -> str:
properties = f"base={self.__address:#x}, top={self.top:#x}, " \
f"last_remainder={self.last_remainder:#x}, next={self.next:#x}, " \
f"mem={self.system_mem}, mempeak={self.max_system_mem}"
return (f"{Color.colorify('Arena', 'blue bold underline')}({properties})")
def __repr__(self) -> str:
return f"GlibcArena(address={self.__address:#x}, size={self._sizeof})"
@property
def address(self) -> int:
return self.__address
@property
def sizeof(self) -> int:
return self._sizeof
@property
def addr(self) -> int:
return int(self)
@property
def top(self) -> int:
return self.__arena.top
@property
def last_remainder(self) -> int:
return self.__arena.last_remainder
@property
def fastbinsY(self) -> ctypes.Array:
return self.__arena.fastbinsY
@property
def bins(self) -> ctypes.Array:
return self.__arena.bins
@property
def binmap(self) -> ctypes.Array:
return self.__arena.binmap
@property
def next(self) -> int:
return self.__arena.next
@property
def next_free(self) -> int:
return self.__arena.next_free
@property
def attached_threads(self) -> int:
return self.__arena.attached_threads
@property
def system_mem(self) -> int:
return self.__arena.system_mem
@property
def max_system_mem(self) -> int:
return self.__arena.max_system_mem
def fastbin(self, i: int) -> "GlibcFastChunk | None":
"""Return head chunk in fastbinsY[i]."""
addr = int(self.fastbinsY[i])
if addr == 0:
return None
return GlibcFastChunk(addr + 2 * gef.arch.ptrsize)
def bin(self, i: int) -> tuple[int, int]:
idx = i * 2
fd = int(self.bins[idx])
bk = int(self.bins[idx + 1])
return fd, bk
def bin_at(self, i) -> int:
header_sz = 2 * gef.arch.ptrsize
offset = ctypes.addressof(self.__arena.bins) - ctypes.addressof(self.__arena)
return self.__address + offset + (i-1) * 2 * gef.arch.ptrsize + header_sz
def is_main_arena(self) -> bool:
return gef.heap.main_arena is not None and int(self) == int(gef.heap.main_arena)
def heap_addr(self, allow_unaligned: bool = False) -> int | None:
if self.is_main_arena():
heap_section = gef.heap.base_address
if not heap_section:
return None
return heap_section
_addr = int(self) + self.sizeof
if allow_unaligned:
return _addr
return gef.heap.malloc_align_address(_addr)
def get_heap_info_list(self) -> list[GlibcHeapInfo] | None:
if self.is_main_arena():
return None
heap_addr = self.get_heap_for_ptr(self.top)
heap_infos = [GlibcHeapInfo(heap_addr)]
while heap_infos[-1].prev is not None:
prev = int(heap_infos[-1].prev)
heap_info = GlibcHeapInfo(prev)
heap_infos.append(heap_info)
return heap_infos[::-1]
@staticmethod
def get_heap_for_ptr(ptr: int) -> int:
"""Find the corresponding heap for a given pointer (int).
See https://github.com/bminor/glibc/blob/glibc-2.34/malloc/arena.c#L129"""
if is_32bit():
default_mmap_threshold_max = 512 * 1024
else: # 64bit
val = cached_lookup_type("long")
sz = val.sizeof if val else gef.arch.ptrsize
default_mmap_threshold_max = 4 * 1024 * 1024 * sz
heap_max_size = 2 * default_mmap_threshold_max
return ptr & ~(heap_max_size - 1)
@staticmethod
def verify(addr: int) -> bool:
"""Verify that the address matches a possible valid GlibcArena"""
try:
test_arena = GlibcArena(f"*{addr:#x}")
cur_arena = GlibcArena(f"*{test_arena.next:#x}")
while cur_arena != test_arena:
if cur_arena == 0:
return False
cur_arena = GlibcArena(f"*{cur_arena.next:#x}")
except Exception as e:
dbg(f"GlibcArena.verify({addr:#x}) failed: {str(e)}")
return False
return True
class GlibcChunk:
"""Glibc chunk class. The default behavior (from_base=False) is to interpret the data starting at the memory
address pointed to as the chunk data. Setting from_base to True instead treats that data as the chunk header.
Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/."""
class ChunkFlags(enum.IntFlag):
PREV_INUSE = 1
IS_MMAPPED = 2
NON_MAIN_ARENA = 4
def __str__(self) -> str:
return " | ".join([
Color.greenify("PREV_INUSE") if self.value & self.PREV_INUSE else Color.redify("PREV_INUSE"),
Color.greenify("IS_MMAPPED") if self.value & self.IS_MMAPPED else Color.redify("IS_MMAPPED"),
Color.greenify("NON_MAIN_ARENA") if self.value & self.NON_MAIN_ARENA else Color.redify("NON_MAIN_ARENA")
])
@staticmethod
def malloc_chunk_t() -> Type[ctypes.Structure]:
pointer = ctypes.c_uint64 if gef and gef.arch.ptrsize == 8 else ctypes.c_uint32
class malloc_chunk_cls(ctypes.Structure):
pass
malloc_chunk_cls._fields_ = [
("prev_size", pointer),
("size", pointer),
("fd", pointer),
("bk", pointer),
("fd_nextsize", ctypes.POINTER(malloc_chunk_cls)),
("bk_nextsize", ctypes.POINTER(malloc_chunk_cls)),
]
return malloc_chunk_cls
def __init__(self, addr: int, from_base: bool = False, allow_unaligned: bool = True) -> None:
ptrsize = gef.arch.ptrsize
self.data_address = addr + 2 * ptrsize if from_base else addr
self.base_address = addr if from_base else addr - 2 * ptrsize
if not allow_unaligned:
self.data_address = gef.heap.malloc_align_address(self.data_address)
self.size_addr = int(self.data_address - ptrsize)
self.prev_size_addr = self.base_address
self.reset()
return
def reset(self):
self._sizeof = ctypes.sizeof(GlibcChunk.malloc_chunk_t())
self._data = gef.memory.read(
self.base_address, ctypes.sizeof(GlibcChunk.malloc_chunk_t()))
self._chunk = GlibcChunk.malloc_chunk_t().from_buffer_copy(self._data)
return
@property
def prev_size(self) -> int:
return self._chunk.prev_size
@property
def size(self) -> int:
return self._chunk.size & (~0x07)
@property
def flags(self) -> ChunkFlags:
return GlibcChunk.ChunkFlags(self._chunk.size & 0x07)
@property
def fd(self) -> int:
return self._chunk.fd
@property
def bk(self) -> int:
return self._chunk.bk
@property
def fd_nextsize(self) -> int:
return self._chunk.fd_nextsize
@property
def bk_nextsize(self) -> int:
return self._chunk.bk_nextsize
def get_usable_size(self) -> int:
# https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L4537
ptrsz = gef.arch.ptrsize
cursz = self.size
if cursz == 0: return cursz
if self.has_m_bit(): return cursz - 2 * ptrsz
return cursz - ptrsz
@property
def usable_size(self) -> int:
return self.get_usable_size()
def get_prev_chunk_size(self) -> int:
return gef.memory.read_integer(self.prev_size_addr)
def __iter__(self) -> Generator["GlibcChunk", None, None]:
assert gef.heap.main_arena
current_chunk = self
top = gef.heap.main_arena.top
while True:
yield current_chunk
if current_chunk.base_address == top:
break
if current_chunk.size == 0:
break
next_chunk_addr = current_chunk.get_next_chunk_addr()
if not Address(value=next_chunk_addr).valid:
break
next_chunk = current_chunk.get_next_chunk()
if next_chunk is None:
break
current_chunk = next_chunk
return
def get_next_chunk(self, allow_unaligned: bool = False) -> "GlibcChunk":
addr = self.get_next_chunk_addr()
return GlibcChunk(addr, allow_unaligned=allow_unaligned)
def get_next_chunk_addr(self) -> int:
return self.data_address + self.size
def has_p_bit(self) -> bool:
return bool(self.flags & GlibcChunk.ChunkFlags.PREV_INUSE)
def has_m_bit(self) -> bool:
return bool(self.flags & GlibcChunk.ChunkFlags.IS_MMAPPED)
def has_n_bit(self) -> bool:
return bool(self.flags & GlibcChunk.ChunkFlags.NON_MAIN_ARENA)
def is_used(self) -> bool:
"""Check if the current block is used by:
- checking the M bit is true
- or checking that next chunk PREV_INUSE flag is true"""
if self.has_m_bit():
return True
next_chunk = self.get_next_chunk()
return True if next_chunk.has_p_bit() else False
def __str_sizes(self) -> str:
msg = []
failed = False
try:
msg.append(f"Chunk size: {self.size:d} ({self.size:#x})")
msg.append(f"Usable size: {self.usable_size:d} ({self.usable_size:#x})")
failed = True
except gdb.MemoryError:
msg.append(f"Chunk size: Cannot read at {self.size_addr:#x} (corrupted?)")
try:
prev_chunk_sz = self.get_prev_chunk_size()
msg.append(f"Previous chunk size: {prev_chunk_sz:d} ({prev_chunk_sz:#x})")
failed = True
except gdb.MemoryError:
msg.append(f"Previous chunk size: Cannot read at {self.base_address:#x} (corrupted?)")
if failed:
msg.append(str(self.flags))
return "\n".join(msg)
def _str_pointers(self) -> str:
fwd = self.data_address
bkw = self.data_address + gef.arch.ptrsize
msg = []
try:
msg.append(f"Forward pointer: {self.fd:#x}")
except gdb.MemoryError:
msg.append(f"Forward pointer: {fwd:#x} (corrupted?)")
try:
msg.append(f"Backward pointer: {self.bk:#x}")
except gdb.MemoryError:
msg.append(f"Backward pointer: {bkw:#x} (corrupted?)")
return "\n".join(msg)
def __str__(self) -> str:
return (f"{Color.colorify('Chunk', 'yellow bold underline')}(addr={self.data_address:#x}, "
f"size={self.size:#x}, flags={self.flags!s})")
def psprint(self) -> str:
msg = [
str(self),
self.__str_sizes(),
]
if not self.is_used():
msg.append(f"\n\n{self._str_pointers()}")
return "\n".join(msg) + "\n"
def resolve_type(self) -> str:
ptr_data = gef.memory.read_integer(self.data_address)
if ptr_data != 0:
sym = gdb_get_location_from_symbol(ptr_data)
if sym is not None and "vtable for" in sym[0]:
return sym[0].replace("vtable for ", "")
return ""
class GlibcFastChunk(GlibcChunk):
@property
def fd(self) -> int:
assert(gef and gef.libc.version)
if gef.libc.version < (2, 32):
return self._chunk.fd
return self.reveal_ptr(self.data_address)
def protect_ptr(self, pos: int, pointer: int) -> int:
"""https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L339"""
assert(gef and gef.libc.version)
if gef.libc.version < (2, 32):
return pointer
return (pos >> 12) ^ pointer
def reveal_ptr(self, pointer: int) -> int:
"""https://elixir.bootlin.com/glibc/glibc-2.32/source/malloc/malloc.c#L341"""
assert(gef and gef.libc.version)
if gef.libc.version < (2, 32):
return pointer
return gef.memory.read_integer(pointer) ^ (pointer >> 12)
class GlibcTcacheChunk(GlibcFastChunk):
pass
@deprecated("Use GefLibcManager.find_libc_version()")
def get_libc_version() -> tuple[int, ...]:
return GefLibcManager.find_libc_version()
def titlify(text: str, color: str | None = None, msg_color: str | None = None) -> str:
"""Print a centered title."""
_, cols = get_terminal_size()
nb = (cols - len(text) - 2) // 2
line_color = color or gef.config["theme.default_title_line"]
text_color = msg_color or gef.config["theme.default_title_message"]
msg = [Color.colorify(f"{HORIZONTAL_LINE * nb} ", line_color),
Color.colorify(text, text_color),
Color.colorify(f" {HORIZONTAL_LINE * nb}", line_color)]
return "".join(msg)
def dbg(msg: str) -> None:
if gef.config["gef.debug"] is True:
gef_print(f"{Color.colorify('[=]', 'bold cyan')} {msg}")
return
def err(msg: str) -> None:
gef_print(f"{Color.colorify('[!]', 'bold red')} {msg}")
return
def warn(msg: str) -> None:
gef_print(f"{Color.colorify('[*]', 'bold yellow')} {msg}")
return
def ok(msg: str) -> None:
gef_print(f"{Color.colorify('[+]', 'bold green')} {msg}")
return
def info(msg: str) -> None:
gef_print(f"{Color.colorify('[+]', 'bold blue')} {msg}")
return
def push_context_message(level: str, message: str) -> None:
"""Push the message to be displayed the next time the context is invoked."""
if level not in ("error", "warn", "ok", "info"):
err(f"Invalid level '{level}', discarding message")
return
gef.ui.context_messages.append((level, message))
return
def show_last_exception() -> None:
"""Display the last Python exception."""
def _show_code_line(fname: str, idx: int) -> str:
fpath = pathlib.Path(os.path.expanduser(os.path.expandvars(fname)))
_data = fpath.read_text().splitlines()
return _data[idx - 1] if 0 < idx < len(_data) else ""
gef_print("")
exc_type, exc_value, exc_traceback = sys.exc_info()
exc_name = exc_type.__name__ if exc_type else "Unknown"
gef_print(" Exception raised ".center(80, HORIZONTAL_LINE))
gef_print(f"{Color.colorify(exc_name, 'bold underline red')}: {exc_value}")
gef_print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE))
for fs in traceback.extract_tb(exc_traceback)[::-1]:
filename, lineno, method, code = fs
if not code or not code.strip():
code = _show_code_line(filename, lineno)
gef_print(f"""{DOWN_ARROW} File "{Color.yellowify(filename)}", line {lineno:d}, in {Color.greenify(method)}()""")
gef_print(f" {RIGHT_ARROW} {code}")
gef_print(" Version ".center(80, HORIZONTAL_LINE))
gdb.execute("version full")
gef_print(" Last 10 GDB commands ".center(80, HORIZONTAL_LINE))
gdb.execute("show commands")
gef_print(" Runtime environment ".center(80, HORIZONTAL_LINE))
gef_print(f"* GDB: {gdb.VERSION}")
gef_print(f"* Python: {sys.version_info.major:d}.{sys.version_info.minor:d}.{sys.version_info.micro:d} - {sys.version_info.releaselevel}")
gef_print(f"* OS: {platform.system()} - {platform.release()} ({platform.machine()})")
try:
lsb_release = which("lsb_release")
gdb.execute(f"!'{lsb_release}' -a")
except FileNotFoundError:
pass
gef_print(HORIZONTAL_LINE*80)
gef_print("")
return
def gef_pystring(x: bytes) -> str:
"""Returns a sanitized version as string of the bytes list given in input."""
res = str(x, encoding="utf-8")
substs = [("\n", "\\n"), ("\r", "\\r"), ("\t", "\\t"), ("\v", "\\v"), ("\b", "\\b"), ]
for _x, _y in substs: res = res.replace(_x, _y)
return res
def gef_pybytes(x: str) -> bytes:
"""Returns an immutable bytes list from the string given as input."""
return bytes(str(x), encoding="utf-8")
@lru_cache()
def which(program: str) -> pathlib.Path:
"""Locate a command on the filesystem."""
res = shutil.which(program)
if not res:
raise FileNotFoundError(f"Missing file `{program}`")
return pathlib.Path(res)
def style_byte(b: int, color: bool = True) -> str:
style = {
"nonprintable": "yellow",
"printable": "white",
"00": "gray",
"0a": "blue",
"ff": "green",
}
sbyte = f"{b:02x}"
if not color or gef.config["highlight.regex"]:
return sbyte
if sbyte in style:
st = style[sbyte]
elif chr(b) in (string.ascii_letters + string.digits + string.punctuation + " "):
st = style.get("printable")
else:
st = style.get("nonprintable")
if st:
sbyte = Color.colorify(sbyte, st)
return sbyte
def hexdump(source: ByteString, length: int = 0x10, separator: str = ".", show_raw: bool = False, show_symbol: bool = True, base: int = 0x00) -> str:
"""Return the hexdump of `src` argument.
@param source *MUST* be of type bytes or bytearray
@param length is the length of items per line
@param separator is the default character to use if one byte is not printable
@param show_raw if True, do not add the line nor the text translation
@param base is the start address of the block being hexdump
@return a string with the hexdump"""
result = []
align = gef.arch.ptrsize * 2 + 2 if is_alive() else 18
for i in range(0, len(source), length):
chunk = bytearray(source[i : i + length])
hexa = " ".join([style_byte(b, color=not show_raw) for b in chunk])
if show_raw:
result.append(hexa)
continue
text = "".join([chr(b) if 0x20 <= b < 0x7F else separator for b in chunk])
if show_symbol:
sym = gdb_get_location_from_symbol(base + i)
sym = f"<{sym[0]:s}+{sym[1]:04x}>" if sym else ""
else:
sym = ""
result.append(f"{base + i:#0{align}x} {sym} {hexa:<{3 * length}} {text}")
return "\n".join(result)
def is_debug() -> bool:
"""Check if debug mode is enabled."""
return gef.config["gef.debug"] is True
def buffer_output() -> bool:
"""Check if output should be buffered until command completion."""
return gef.config["gef.buffer"] is True
def hide_context() -> bool:
"""Helper function to hide the context pane."""
gef.ui.context_hidden = True
return True
def unhide_context() -> bool:
"""Helper function to unhide the context pane."""
gef.ui.context_hidden = False
return True
class DisableContextOutputContext:
def __enter__(self) -> None:
hide_context()
return
def __exit__(self, *exc: Any) -> None:
unhide_context()
return
class RedirectOutputContext:
def __init__(self, to_file: str = "/dev/null") -> None:
if " " in to_file: raise ValueError("Target filepath cannot contain spaces")
self.redirection_target_file = to_file
return
def __enter__(self) -> None:
"""Redirect all GDB output to `to_file` parameter. By default, `to_file` redirects to `/dev/null`."""
gdb.execute("set logging overwrite")
gdb.execute(f"set logging file {self.redirection_target_file}")
gdb.execute("set logging redirect on")
if GDB_VERSION >= (12, 0):
gdb.execute("set logging enabled on")
else:
gdb.execute("set logging on")
return
def __exit__(self, *exc: Any) -> None:
"""Disable the output redirection, if any."""
if GDB_VERSION >= (12, 0):
gdb.execute("set logging enabled off")
else:
gdb.execute("set logging off")
gdb.execute("set logging redirect off")
return
def enable_redirect_output(to_file: str = "/dev/null") -> None:
"""Redirect all GDB output to `to_file` parameter. By default, `to_file` redirects to `/dev/null`."""
if " " in to_file: raise ValueError("Target filepath cannot contain spaces")
gdb.execute("set logging overwrite")
gdb.execute(f"set logging file {to_file}")
gdb.execute("set logging redirect on")
if GDB_VERSION >= (12, 0):
gdb.execute("set logging enabled on")
else:
gdb.execute("set logging on")
return
def disable_redirect_output() -> None:
"""Disable the output redirection, if any."""
if GDB_VERSION >= (12, 0):
gdb.execute("set logging enabled off")
else:
gdb.execute("set logging off")
gdb.execute("set logging redirect off")
return
@deprecated("use `pathlib.Path(...).mkdir()`")
def gef_makedirs(path: str, mode: int = 0o755) -> pathlib.Path:
"""Recursive mkdir() creation. If successful, return the absolute path of the directory created."""
fpath = pathlib.Path(path)
if not fpath.is_dir():
fpath.mkdir(mode=mode, exist_ok=True, parents=True)
return fpath.absolute()
@lru_cache()
def gdb_lookup_symbol(sym: str) -> tuple[gdb.Symtab_and_line, ...] | None:
"""Fetch the proper symbol or None if not defined."""
try:
res = gdb.decode_line(sym)[1] # pylint: disable=E1136
return res
except gdb.error:
return None
@lru_cache(maxsize=512)
def gdb_get_location_from_symbol(address: int) -> tuple[str, int] | None:
"""Retrieve the location of the `address` argument from the symbol table.
Return a tuple with the name and offset if found, None otherwise."""
# this is horrible, ugly hack and shitty perf...
# find a *clean* way to get gdb.Location from an address
sym = str(gdb.execute(f"info symbol {address:#x}", to_string=True))
if sym.startswith("No symbol matches"):
return None
# gdb outputs symbols with format: " + in section of ",
# here, we are only interested in symbol name and offset.
i = sym.find(" in section ")
sym = sym[:i].split(" + ")
name, offset = sym[0], 0
if len(sym) == 2 and sym[1].isdigit():
offset = int(sym[1])
return name, offset
def gdb_disassemble(start_pc: int, **kwargs: int) -> Generator[Instruction, None, None]:
"""Disassemble instructions from `start_pc` (Integer). Accepts the following named
parameters:
- `end_pc` (Integer) only instructions whose start address fall in the interval from
start_pc to end_pc are returned.
- `count` (Integer) list at most this many disassembled instructions
If `end_pc` and `count` are not provided, the function will behave as if `count=1`.
Return an iterator of Instruction objects
"""
frame = gdb.selected_frame()
arch = frame.architecture()
for insn in arch.disassemble(start_pc, **kwargs):
assert isinstance(insn["addr"], int)
assert isinstance(insn["length"], int)
assert isinstance(insn["asm"], str)
address = insn["addr"]
asm = insn["asm"].rstrip().split(None, 1)
if len(asm) > 1:
mnemo, operands = asm
operands = operands.split(",")
else:
mnemo, operands = asm[0], []
loc = gdb_get_location_from_symbol(address)
location = f"<{loc[0]}+{loc[1]:04x}>" if loc else ""
opcodes = gef.memory.read(insn["addr"], insn["length"])
yield Instruction(address, location, mnemo, operands, opcodes)
def gdb_get_nth_previous_instruction_address(addr: int, n: int) -> int | None:
"""Return the address (Integer) of the `n`-th instruction before `addr`."""
# fixed-length ABI
if gef.arch.instruction_length:
return max(0, addr - n * gef.arch.instruction_length)
# variable-length ABI
cur_insn_addr = gef_current_instruction(addr).address
# we try to find a good set of previous instructions by "guessing" disassembling backwards
# the 15 comes from the longest instruction valid size
for i in range(15 * n, 0, -1):
try:
insns = list(gdb_disassemble(addr - i, end_pc=cur_insn_addr))
except gdb.MemoryError:
# this is because we can hit an unmapped page trying to read backward
break
# 1. check that the disassembled instructions list size can satisfy
if len(insns) < n + 1: # we expect the current instruction plus the n before it
continue
# If the list of instructions is longer than what we need, then we
# could get lucky and already have more than what we need, so slice down
insns = insns[-n - 1 :]
# 2. check that the sequence ends with the current address
if insns[-1].address != cur_insn_addr:
continue
# 3. check all instructions are valid
if all(insn.is_valid() for insn in insns):
return insns[0].address
return None
@deprecated(solution="Use `gef_instruction_n().address`")
def gdb_get_nth_next_instruction_address(addr: int, n: int) -> int:
"""Return the address of the `n`-th instruction after `addr`. """
return gef_instruction_n(addr, n).address
def gef_instruction_n(addr: int, n: int) -> Instruction:
"""Return the `n`-th instruction after `addr` as an Instruction object. Note that `n` is treated as
an positive index, starting from 0 (current instruction address)"""
return list(gdb_disassemble(addr, count=n + 1))[n]
def gef_get_instruction_at(addr: int) -> Instruction:
"""Return the full Instruction found at the specified address."""
insn = next(gef_disassemble(addr, 1))
return insn
def gef_current_instruction(addr: int) -> Instruction:
"""Return the current instruction as an Instruction object."""
return gef_instruction_n(addr, 0)
def gef_next_instruction(addr: int) -> Instruction:
"""Return the next instruction as an Instruction object."""
return gef_instruction_n(addr, 1)
def gef_disassemble(addr: int, nb_insn: int, nb_prev: int = 0) -> Generator[Instruction, None, None]:
"""Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr`.
Return an iterator of Instruction objects."""
nb_insn = max(1, nb_insn)
if nb_prev:
try:
start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev)
if start_addr:
for insn in gdb_disassemble(start_addr, count=nb_prev):
if insn.address == addr: break
yield insn
except gdb.MemoryError:
# If the address pointing to the previous instruction(s) is not mapped, simply skip them
pass
for insn in gdb_disassemble(addr, count=nb_insn):
yield insn
def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> str | list[str]:
"""Execute an external command and return the result."""
res = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=kwargs.get("shell", False))
return [gef_pystring(_) for _ in res.splitlines()] if as_list else gef_pystring(res)
def gef_execute_gdb_script(commands: str) -> None:
"""Execute the parameter `source` as GDB command. This is done by writing `commands` to
a temporary file, which is then executed via GDB `source` command. The tempfile is then deleted."""
fd, fname = tempfile.mkstemp(suffix=".gdb", prefix="gef_")
with os.fdopen(fd, "w") as f:
f.write(commands)
f.flush()
fname = pathlib.Path(fname)
if fname.is_file() and os.access(fname, os.R_OK):
gdb.execute(f"source {fname}")
fname.unlink()
return
@deprecated("Use Elf(fname).checksec()")
def checksec(filename: str) -> dict[str, bool]:
return Elf(filename).checksec
@deprecated("Use `gef.arch` instead")
def get_arch() -> str:
"""Return the binary's architecture."""
if is_alive():
arch = gdb.selected_frame().architecture()
return arch.name()
arch_str = (gdb.execute("show architecture", to_string=True) or "").strip()
pat = "The target architecture is set automatically (currently "
if arch_str.startswith(pat):
arch_str = arch_str[len(pat):].rstrip(")")
return arch_str
pat = "The target architecture is assumed to be "
if arch_str.startswith(pat):
return arch_str[len(pat):]
pat = "The target architecture is set to "
if arch_str.startswith(pat):
# GDB version >= 10.1
if '"auto"' in arch_str:
return re.findall(r"currently \"(.+)\"", arch_str)[0]
return re.findall(r"\"(.+)\"", arch_str)[0]
# Unknown, we throw an exception to be safe
raise RuntimeError(f"Unknown architecture: {arch_str}")
@deprecated("Use `gef.binary.entry_point` instead")
def get_entry_point() -> int | None:
"""Return the binary entry point."""
return gef.binary.entry_point if gef.binary else None
def is_pie(fpath: str) -> bool:
return Elf(fpath).checksec["PIE"]
@deprecated("Prefer `gef.arch.endianness == Endianness.BIG_ENDIAN`")
def is_big_endian() -> bool:
return gef.arch.endianness == Endianness.BIG_ENDIAN
@deprecated("gef.arch.endianness == Endianness.LITTLE_ENDIAN")
def is_little_endian() -> bool:
return gef.arch.endianness == Endianness.LITTLE_ENDIAN
def flags_to_human(reg_value: int, value_table: dict[int, str]) -> str:
"""Return a human readable string showing the flag states."""
flags = []
for bit_index, name in value_table.items():
flags.append(Color.boldify(name.upper()) if reg_value & (1< int | None:
section = process_lookup_path(name)
return section.page_start if section else None
@lru_cache()
def get_zone_base_address(name: str) -> int | None:
zone = file_lookup_name_path(name, get_filepath())
return zone.zone_start if zone else None
#
# Architecture classes
#
@deprecated("Using the decorator `register_architecture` is unnecessary")
def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]:
return cls
class ArchitectureBase:
"""Class decorator for declaring an architecture to GEF."""
aliases: tuple[str | Elf.Abi, ...] = ()
def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs):
global __registered_architectures__
super().__init_subclass__(**kwargs)
for key in getattr(cls, "aliases"):
if issubclass(cls, Architecture):
if isinstance(key, str):
__registered_architectures__[key.lower()] = cls
else:
__registered_architectures__[key] = cls
return
class Architecture(ArchitectureBase):
"""Generic metaclass for the architecture supported by GEF."""
# Mandatory defined attributes by inheriting classes
arch: str
mode: str
all_registers: tuple[str, ...]
nop_insn: bytes
return_register: str
flag_register: str | None
instruction_length: int | None
flags_table: dict[int, str]
syscall_register: str | None
syscall_instructions: tuple[str, ...]
function_parameters: tuple[str, ...]
# Optionally defined attributes
_ptrsize: int | None = None
_endianness: Endianness | None = None
special_registers: tuple[()] | tuple[str, ...] = ()
maps: GefMemoryMapProvider | None = None
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
attributes = ("arch", "mode", "aliases", "all_registers", "nop_insn",
"return_register", "flag_register", "instruction_length", "flags_table",
"function_parameters",)
if not all(map(lambda x: hasattr(cls, x), attributes)):
raise NotImplementedError
def __str__(self) -> str:
return f"Architecture({self.arch}, {self.mode or 'None'}, {repr(self.endianness)})"
def __repr__(self) -> str:
return self.__str__()
@staticmethod
def supports_gdb_arch(gdb_arch: str) -> bool | None:
"""If implemented by a child `Architecture`, this function dictates if the current class
supports the loaded ELF file (which can be accessed via `gef.binary`). This callback
function will override any assumption made by GEF to determine the architecture."""
return None
def flag_register_to_human(self, val: int | None = None) -> str:
raise NotImplementedError
def is_call(self, insn: Instruction) -> bool:
raise NotImplementedError
def is_ret(self, insn: Instruction) -> bool:
raise NotImplementedError
def is_conditional_branch(self, insn: Instruction) -> bool:
raise NotImplementedError
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
raise NotImplementedError
def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None:
raise NotImplementedError
def canary_address(self) -> int:
raise NotImplementedError
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
raise NotImplementedError
def reset_caches(self) -> None:
self.__get_register_for_selected_frame.cache_clear()
return
def __get_register(self, regname: str) -> int:
"""Return a register's value."""
curframe = gdb.selected_frame()
key = curframe.pc() ^ int(curframe.read_register('sp')) # todo: check when/if gdb.Frame implements `level()`
return self.__get_register_for_selected_frame(regname, int(key))
@lru_cache()
def __get_register_for_selected_frame(self, regname: str, hash_key: int) -> int:
# 1st chance
try:
return parse_address(regname)
except gdb.error:
pass
# 2nd chance - if an exception, propagate it
regname = regname.lstrip("$")
value = gdb.selected_frame().read_register(regname)
return int(value)
def register(self, name: str) -> int:
if not is_alive():
raise gdb.error("No debugging session active")
return self.__get_register(name)
@property
def registers(self) -> Generator[str, None, None]:
yield from self.all_registers
@property
def pc(self) -> int:
return self.register("$pc")
@property
def sp(self) -> int:
return self.register("$sp")
@property
def fp(self) -> int:
return self.register("$fp")
@property
def ptrsize(self) -> int:
if not self._ptrsize:
res = cached_lookup_type("size_t")
if res is not None:
self._ptrsize = res.sizeof
else:
self._ptrsize = gdb.parse_and_eval("$pc").type.sizeof
return self._ptrsize
@property
def endianness(self) -> Endianness:
if not self._endianness:
output = (gdb.execute("show endian", to_string=True) or "").strip().lower()
if "little endian" in output:
self._endianness = Endianness.LITTLE_ENDIAN
elif "big endian" in output:
self._endianness = Endianness.BIG_ENDIAN
else:
raise OSError(f"No valid endianness found in '{output}'")
return self._endianness
def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, int | None]:
"""Retrieves the correct parameter used for the current function call."""
reg = self.function_parameters[i]
val = self.register(reg)
key = reg
return key, val
class GenericArchitecture(Architecture):
arch = "Generic"
mode = ""
aliases = ("GenericArchitecture",)
all_registers = ()
instruction_length = 0
return_register = ""
function_parameters = ()
syscall_register = ""
syscall_instructions = ()
nop_insn = b""
flag_register = None
flags_table = {}
class RISCV(Architecture):
arch = "RISCV"
mode = "RISCV"
aliases = ("RISCV", Elf.Abi.RISCV)
all_registers = ("$zero", "$ra", "$sp", "$gp", "$tp", "$t0", "$t1",
"$t2", "$fp", "$s1", "$a0", "$a1", "$a2", "$a3",
"$a4", "$a5", "$a6", "$a7", "$s2", "$s3", "$s4",
"$s5", "$s6", "$s7", "$s8", "$s9", "$s10", "$s11",
"$t3", "$t4", "$t5", "$t6",)
return_register = "$a0"
function_parameters = ("$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7")
syscall_register = "$a7"
syscall_instructions = ("ecall",)
nop_insn = b"\x00\x00\x00\x13"
# RISC-V has no flags registers
flag_register = None
flags_table = {}
@property
def instruction_length(self) -> int:
return 4
def is_call(self, insn: Instruction) -> bool:
return insn.mnemonic == "call"
def is_ret(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
if mnemo == "ret":
return True
elif (mnemo == "jalr" and insn.operands[0] == "zero" and
insn.operands[1] == "ra" and insn.operands[2] == 0):
return True
elif (mnemo == "c.jalr" and insn.operands[0] == "ra"):
return True
return False
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
raise OSError(f"Architecture {cls.arch} not supported yet")
@property
def ptrsize(self) -> int:
if self._ptrsize is not None:
return self._ptrsize
if is_alive():
self._ptrsize = gdb.parse_and_eval("$pc").type.sizeof
return self._ptrsize
return 4
def is_conditional_branch(self, insn: Instruction) -> bool:
return insn.mnemonic.startswith("b")
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
def long_to_twos_complement(v: int) -> int:
"""Convert a python long value to its two's complement."""
if is_32bit():
if v & 0x80000000:
return v - 0x100000000
elif is_64bit():
if v & 0x8000000000000000:
return v - 0x10000000000000000
else:
raise OSError("RISC-V: ELF file is not ELF32 or ELF64. This is not currently supported")
return v
mnemo = insn.mnemonic
condition = mnemo[1:]
if condition.endswith("z"):
# r2 is the zero register if we are comparing to 0
rs1 = gef.arch.register(insn.operands[0])
rs2 = gef.arch.register("$zero")
condition = condition[:-1]
elif len(insn.operands) > 2:
# r2 is populated with the second operand
rs1 = gef.arch.register(insn.operands[0])
rs2 = gef.arch.register(insn.operands[1])
else:
raise OSError(f"RISC-V: Failed to get rs1 and rs2 for instruction: `{insn}`")
# If the conditional operation is not unsigned, convert the python long into
# its two's complement
if not condition.endswith("u"):
rs2 = long_to_twos_complement(rs2)
rs1 = long_to_twos_complement(rs1)
else:
condition = condition[:-1]
if condition == "eq":
if rs1 == rs2: taken, reason = True, f"{rs1}={rs2}"
else: taken, reason = False, f"{rs1}!={rs2}"
elif condition == "ne":
if rs1 != rs2: taken, reason = True, f"{rs1}!={rs2}"
else: taken, reason = False, f"{rs1}={rs2}"
elif condition == "lt":
if rs1 < rs2: taken, reason = True, f"{rs1}<{rs2}"
else: taken, reason = False, f"{rs1}>={rs2}"
elif condition == "le":
if rs1 <= rs2: taken, reason = True, f"{rs1}<={rs2}"
else: taken, reason = False, f"{rs1}>{rs2}"
elif condition == "ge":
if rs1 >= rs2: taken, reason = True, f"{rs1}>={rs2}"
else: taken, reason = False, f"{rs1}<{rs2}"
else:
raise OSError(f"RISC-V: Conditional instruction `{insn}` not supported yet")
return taken, reason
def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None:
ra = None
if self.is_ret(insn):
ra = gef.arch.register("$ra")
else:
older = frame.older()
if older:
ra = to_unsigned_long(older.pc())
return ra
def flag_register_to_human(self, val: int | None = None) -> str:
# RISC-V has no flags registers, return an empty string to
# preserve the Architecture API
return ""
class ARM(Architecture):
aliases = ("ARM", Elf.Abi.ARM)
arch = "ARM"
all_registers = ("$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6",
"$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp",
"$lr", "$pc", "$cpsr",)
nop_insn = b"\x00\xf0\x20\xe3" # hint #0
return_register = "$r0"
flag_register: str = "$cpsr"
flags_table = {
31: "negative",
30: "zero",
29: "carry",
28: "overflow",
7: "interrupt",
6: "fast",
5: "thumb",
}
function_parameters = ("$r0", "$r1", "$r2", "$r3")
syscall_register = "$r7"
syscall_instructions = ("swi 0x0", "swi NR")
_endianness = Endianness.LITTLE_ENDIAN
def is_thumb(self) -> bool:
"""Determine if the machine is currently in THUMB mode."""
return is_alive() and (self.cpsr & (1 << 5) == 1)
@property
def pc(self) -> int | None:
pc = gef.arch.register("$pc")
if self.is_thumb():
pc += 1
return pc
@property
def cpsr(self) -> int:
if not is_alive():
raise RuntimeError("Cannot get CPSR, program not started?")
return gef.arch.register(self.flag_register)
@property
def mode(self) -> str:
return "THUMB" if self.is_thumb() else "ARM"
@property
def instruction_length(self) -> int | None:
# Thumb instructions have variable-length (2 or 4-byte)
return None if self.is_thumb() else 4
@property
def ptrsize(self) -> int:
return 4
def is_call(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
call_mnemos = {"bl", "blx"}
return mnemo in call_mnemos
def is_ret(self, insn: Instruction) -> bool:
pop_mnemos = {"pop"}
branch_mnemos = {"bl", "bx"}
write_mnemos = {"ldr", "add"}
if insn.mnemonic in pop_mnemos:
return insn.operands[-1] == " pc}"
if insn.mnemonic in branch_mnemos:
return insn.operands[-1] == "lr"
if insn.mnemonic in write_mnemos:
return insn.operands[0] == "pc"
return False
def flag_register_to_human(self, val: int | None = None) -> str:
# https://www.botskool.com/user-pages/tutorials/electronics/arm-7-tutorial-part-1
if val is None:
reg = self.flag_register
val = gef.arch.register(reg)
return flags_to_human(val, self.flags_table)
def is_conditional_branch(self, insn: Instruction) -> bool:
conditions = {"eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cc", "cs"}
return insn.mnemonic[-2:] in conditions
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo = insn.mnemonic
# ref: https://www.davespace.co.uk/arm/introduction-to-arm/conditional.html
flags = dict((self.flags_table[k], k) for k in self.flags_table)
val = gef.arch.register(self.flag_register)
taken, reason = False, ""
if mnemo.endswith("eq"): taken, reason = bool(val&(1< int | None:
if not self.is_ret(insn):
older = frame.older()
if not older:
return None
return int(older.pc())
# If it's a pop, we have to peek into the stack, otherwise use lr
if insn.mnemonic == "pop":
ra_addr = gef.arch.sp + (len(insn.operands)-1) * self.ptrsize
if not ra_addr:
return None
ra = dereference(ra_addr)
if ra is None:
return None
return to_unsigned_long(ra)
elif insn.mnemonic == "ldr":
ra = dereference(gef.arch.sp)
if ra is None:
return None
return to_unsigned_long(ra)
else: # 'bx lr' or 'add pc, lr, #0'
return gef.arch.register("$lr")
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
_NR_mprotect = 125
insns = [
"push {r0-r2, r7}",
f"mov r1, {addr & 0xffff:d}",
f"mov r0, {(addr & 0xffff0000) >> 16:d}",
"lsl r0, r0, 16",
"add r0, r0, r1",
f"mov r1, {size & 0xffff:d}",
f"mov r2, {perm.value & 0xff:d}",
f"mov r7, {_NR_mprotect:d}",
"svc 0",
"pop {r0-r2, r7}",
]
return "; ".join(insns)
class AARCH64(ARM):
aliases = ("ARM64", "AARCH64", Elf.Abi.AARCH64)
arch = "ARM64"
mode: str = ""
all_registers = (
"$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7",
"$x8", "$x9", "$x10", "$x11", "$x12", "$x13", "$x14","$x15",
"$x16", "$x17", "$x18", "$x19", "$x20", "$x21", "$x22", "$x23",
"$x24", "$x25", "$x26", "$x27", "$x28", "$x29", "$x30", "$sp",
"$pc", "$cpsr", "$fpsr", "$fpcr",)
return_register = "$x0"
flag_register = "$cpsr"
flags_table = {
31: "negative",
30: "zero",
29: "carry",
28: "overflow",
7: "interrupt",
9: "endian",
6: "fast",
5: "t32",
4: "m[4]",
}
nop_insn = b"\x1f\x20\x03\xd5" # hint #0
function_parameters = ("$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7",)
syscall_register = "$x8"
syscall_instructions = ("svc $x0",)
def is_call(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
call_mnemos = {"bl", "blr"}
return mnemo in call_mnemos
def flag_register_to_human(self, val: int | None = None) -> str:
# https://events.linuxfoundation.org/sites/events/files/slides/KoreaLinuxForum-2014.pdf
reg = self.flag_register
if not val:
val = gef.arch.register(reg)
return flags_to_human(val, self.flags_table)
def is_aarch32(self) -> bool:
"""Determine if the CPU is currently in AARCH32 mode from runtime."""
return (self.cpsr & (1 << 4) != 0) and (self.cpsr & (1 << 5) == 0)
def is_thumb32(self) -> bool:
"""Determine if the CPU is currently in THUMB32 mode from runtime."""
return (self.cpsr & (1 << 4) == 1) and (self.cpsr & (1 << 5) == 1)
@property
def ptrsize(self) -> int:
"""Determine the size of pointer from the current CPU mode"""
if not is_alive():
return 8
if self.is_aarch32():
return 4
if self.is_thumb32():
return 2
return 8
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
_NR_mprotect = 226
insns = [
"str x8, [sp, -16]!",
"str x0, [sp, -16]!",
"str x1, [sp, -16]!",
"str x2, [sp, -16]!",
f"mov x8, {_NR_mprotect:d}",
f"movz x0, {addr & 0xFFFF:#x}",
f"movk x0, {(addr >> 16) & 0xFFFF:#x}, lsl 16",
f"movk x0, {(addr >> 32) & 0xFFFF:#x}, lsl 32",
f"movk x0, {(addr >> 48) & 0xFFFF:#x}, lsl 48",
f"movz x1, {size & 0xFFFF:#x}",
f"movk x1, {(size >> 16) & 0xFFFF:#x}, lsl 16",
f"mov x2, {perm.value:d}",
"svc 0",
"ldr x2, [sp], 16",
"ldr x1, [sp], 16",
"ldr x0, [sp], 16",
"ldr x8, [sp], 16",
]
return "; ".join(insns)
def is_conditional_branch(self, insn: Instruction) -> bool:
# https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf
# sect. 5.1.1
mnemo = insn.mnemonic
branch_mnemos = {"cbnz", "cbz", "tbnz", "tbz"}
return mnemo.startswith("b.") or mnemo in branch_mnemos
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo, operands = insn.mnemonic, insn.operands
taken, reason = False, ""
if mnemo in {"cbnz", "cbz", "tbnz", "tbz"}:
reg = f"${operands[0]}"
op = gef.arch.register(reg)
if mnemo == "cbnz":
if op!=0: taken, reason = True, f"{reg}!=0"
else: taken, reason = False, f"{reg}==0"
elif mnemo == "cbz":
if op == 0: taken, reason = True, f"{reg}==0"
else: taken, reason = False, f"{reg}!=0"
elif mnemo == "tbnz":
# operands[1] has one or more white spaces in front, then a #, then the number
# so we need to eliminate them
i = int(operands[1].strip().lstrip("#"))
if (op & 1< str:
reg = self.flag_register
if val is None:
val = gef.arch.register(reg)
return flags_to_human(val, self.flags_table)
def is_call(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
call_mnemos = {"call", "callq"}
return mnemo in call_mnemos
def is_ret(self, insn: Instruction) -> bool:
return insn.mnemonic == "ret"
def is_conditional_branch(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
branch_mnemos = {
"ja", "jnbe", "jae", "jnb", "jnc", "jb", "jc", "jnae", "jbe", "jna",
"jcxz", "jecxz", "jrcxz", "je", "jz", "jg", "jnle", "jge", "jnl",
"jl", "jnge", "jle", "jng", "jne", "jnz", "jno", "jnp", "jpo", "jns",
"jo", "jp", "jpe", "js"
}
return mnemo in branch_mnemos
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo = insn.mnemonic
# all kudos to fG! (https://github.com/gdbinit/Gdbinit/blob/master/gdbinit#L1654)
flags = dict((self.flags_table[k], k) for k in self.flags_table)
val = gef.arch.register(self.flag_register)
taken, reason = False, ""
if mnemo in ("ja", "jnbe"):
taken, reason = not val&(1< int | None:
ra = None
if self.is_ret(insn):
ra = dereference(gef.arch.sp)
else:
older = frame.older()
if older:
ra = older.pc()
if ra is None:
return None
return to_unsigned_long(ra)
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
_NR_mprotect = 125
insns = [
"pushad",
"pushfd",
f"mov eax, {_NR_mprotect:d}",
f"mov ebx, {addr:d}",
f"mov ecx, {size:d}",
f"mov edx, {perm.value:d}",
"int 0x80",
"popfd",
"popad",
]
return "; ".join(insns)
def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, int | None]:
if in_func:
i += 1 # Account for RA being at the top of the stack
sp = gef.arch.sp
sz = gef.arch.ptrsize
loc = sp + (i * sz)
val = gef.memory.read_integer(loc)
key = f"[sp + {i * sz:#x}]"
return key, val
class X86_64(X86):
aliases = ("X86_64", Elf.Abi.X86_64, "i386:x86-64")
arch = "X86"
mode = "64"
gpr_registers = (
"$rax", "$rbx", "$rcx", "$rdx", "$rsp", "$rbp", "$rsi", "$rdi", "$rip",
"$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", )
all_registers = gpr_registers + ( X86.flag_register, ) + X86.special_registers
return_register = "$rax"
function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"]
syscall_register = "$rax"
syscall_instructions = ["syscall"]
# We don't want to inherit x86's stack based param getter
get_ith_parameter = Architecture.get_ith_parameter
_ptrsize = 8
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
_NR_mprotect = 10
insns = [
"pushfq",
"push rax",
"push rdi",
"push rsi",
"push rdx",
"push rcx",
"push r11",
f"mov rax, {_NR_mprotect:d}",
f"mov rdi, {addr:d}",
f"mov rsi, {size:d}",
f"mov rdx, {perm.value:d}",
"syscall",
"pop r11",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi",
"pop rax",
"popfq",
]
return "; ".join(insns)
def canary_address(self) -> int:
return self.register("fs_base") + 0x28
class PowerPC(Architecture):
aliases = ("PowerPC", Elf.Abi.POWERPC, "PPC")
arch = "PPC"
mode = "PPC32"
all_registers = (
"$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
"$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
"$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23",
"$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31",
"$pc", "$msr", "$cr", "$lr", "$ctr", "$xer", "$trap",)
instruction_length = 4
nop_insn = b"\x60\x00\x00\x00" # https://developer.ibm.com/articles/l-ppc/
return_register = "$r0"
flag_register: str = "$cr"
flags_table = {
3: "negative[0]",
2: "positive[0]",
1: "equal[0]",
0: "overflow[0]",
# cr7
31: "less[7]",
30: "greater[7]",
29: "equal[7]",
28: "overflow[7]",
}
function_parameters = ("$i0", "$i1", "$i2", "$i3", "$i4", "$i5")
syscall_register = "$r0"
syscall_instructions = ("sc",)
_ptrsize = 4
def flag_register_to_human(self, val: int | None = None) -> str:
# https://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3)
if val is None:
reg = self.flag_register
val = gef.arch.register(reg)
return flags_to_human(val, self.flags_table)
def is_call(self, insn: Instruction) -> bool:
return False
def is_ret(self, insn: Instruction) -> bool:
return insn.mnemonic == "blr"
def is_conditional_branch(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
branch_mnemos = {"beq", "bne", "ble", "blt", "bgt", "bge"}
return mnemo in branch_mnemos
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo = insn.mnemonic
flags = dict((self.flags_table[k], k) for k in self.flags_table)
val = gef.arch.register(self.flag_register)
taken, reason = False, ""
if mnemo == "beq": taken, reason = bool(val&(1< int | None:
ra = None
if self.is_ret(insn):
ra = gef.arch.register("$lr")
else:
older = frame.older()
if older:
ra = to_unsigned_long(older.pc())
return ra
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
# Ref: https://developer.ibm.com/articles/l-ppc/
_NR_mprotect = 125
insns = [
"addi 1, 1, -16", # 1 = r1 = sp
"stw 0, 0(1)",
"stw 3, 4(1)", # r0 = syscall_code | r3, r4, r5 = args
"stw 4, 8(1)",
"stw 5, 12(1)",
f"li 0, {_NR_mprotect:d}",
f"lis 3, {addr:#x}@h",
f"ori 3, 3, {addr:#x}@l",
f"lis 4, {size:#x}@h",
f"ori 4, 4, {size:#x}@l",
f"li 5, {perm.value:d}",
"sc",
"lwz 0, 0(1)",
"lwz 3, 4(1)",
"lwz 4, 8(1)",
"lwz 5, 12(1)",
"addi 1, 1, 16",
]
return ";".join(insns)
class PowerPC64(PowerPC):
aliases = ("PowerPC64", Elf.Abi.POWERPC64, "PPC64")
arch = "PPC"
mode = "PPC64"
_ptrsize = 8
class SPARC(Architecture):
""" Refs:
- https://www.cse.scu.edu/~atkinson/teaching/sp05/259/sparc.pdf
"""
aliases = ("SPARC", Elf.Abi.SPARC)
arch = "SPARC"
mode = ""
all_registers = (
"$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7",
"$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7",
"$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7",
"$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i7",
"$pc", "$npc", "$sp ", "$fp ", "$psr",)
instruction_length = 4
nop_insn = b"\x00\x00\x00\x00" # sethi 0, %g0
return_register = "$i0"
flag_register: str = "$psr"
flags_table = {
23: "negative",
22: "zero",
21: "overflow",
20: "carry",
7: "supervisor",
5: "trap",
}
function_parameters = ("$o0 ", "$o1 ", "$o2 ", "$o3 ", "$o4 ", "$o5 ", "$o7 ",)
syscall_register = "%g1"
syscall_instructions = ("t 0x10",)
def flag_register_to_human(self, val: int | None = None) -> str:
# https://www.gaisler.com/doc/sparcv8.pdf
reg = self.flag_register
if val is None:
val = gef.arch.register(reg)
return flags_to_human(val, self.flags_table)
def is_call(self, insn: Instruction) -> bool:
return False
def is_ret(self, insn: Instruction) -> bool:
return insn.mnemonic == "ret"
def is_conditional_branch(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
# http://moss.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/condbranch.html
branch_mnemos = {
"be", "bne", "bg", "bge", "bgeu", "bgu", "bl", "ble", "blu", "bleu",
"bneg", "bpos", "bvs", "bvc", "bcs", "bcc"
}
return mnemo in branch_mnemos
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo = insn.mnemonic
flags = dict((self.flags_table[k], k) for k in self.flags_table)
val = gef.arch.register(self.flag_register)
taken, reason = False, ""
if mnemo == "be": taken, reason = bool(val&(1< int | None:
ra = None
if self.is_ret(insn):
ra = gef.arch.register("$o7")
else:
older = frame.older()
if older:
ra = to_unsigned_long(older.pc())
return ra
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
hi = (addr & 0xffff0000) >> 16
lo = (addr & 0x0000ffff)
_NR_mprotect = 125
insns = ["add %sp, -16, %sp",
"st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]",
"st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]",
f"sethi %hi({hi}), %o0",
f"or %o0, {lo}, %o0",
"clr %o1",
"clr %o2",
f"mov {_NR_mprotect}, %g1",
"t 0x10",
"ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0",
"ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2",
"add %sp, 16, %sp",]
return "; ".join(insns)
class SPARC64(SPARC):
"""Refs:
- http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_sparc.pdf
- https://cr.yp.to/2005-590/sparcv9.pdf
"""
aliases = ("SPARC64", Elf.Abi.SPARC64)
arch = "SPARC"
mode = "V9"
all_registers = [
"$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7",
"$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$o7",
"$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7",
"$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i7",
"$pc", "$npc", "$sp", "$fp", "$state", ]
flag_register = "$state" # sparcv9.pdf, 5.1.5.1 (ccr)
flags_table = {
35: "negative",
34: "zero",
33: "overflow",
32: "carry",
}
syscall_instructions = ["t 0x6d"]
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
hi = (addr & 0xffff0000) >> 16
lo = (addr & 0x0000ffff)
_NR_mprotect = 125
insns = ["add %sp, -16, %sp",
"st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]",
"st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]",
f"sethi %hi({hi}), %o0",
f"or %o0, {lo}, %o0",
"clr %o1",
"clr %o2",
f"mov {_NR_mprotect}, %g1",
"t 0x6d",
"ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0",
"ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2",
"add %sp, 16, %sp",]
return "; ".join(insns)
class MIPS(Architecture):
aliases = ("MIPS", Elf.Abi.MIPS)
arch = "MIPS"
mode = "MIPS32"
# https://vhouten.home.xs4all.nl/mipsel/r3000-isa.html
all_registers = (
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
"$t8", "$t9", "$k0", "$k1", "$s8", "$pc", "$sp", "$hi",
"$lo", "$fir", "$ra", "$gp", )
instruction_length = 4
_ptrsize = 4
nop_insn = b"\x00\x00\x00\x00" # sll $0,$0,0
return_register = "$v0"
flag_register = "$fcsr"
flags_table = {}
function_parameters = ("$a0", "$a1", "$a2", "$a3")
syscall_register = "$v0"
syscall_instructions = ("syscall",)
def flag_register_to_human(self, val: int | None = None) -> str:
return Color.colorify("No flag register", "yellow underline")
def is_call(self, insn: Instruction) -> bool:
return False
def is_ret(self, insn: Instruction) -> bool:
return insn.mnemonic == "jr" and insn.operands[0] == "ra"
def is_conditional_branch(self, insn: Instruction) -> bool:
mnemo = insn.mnemonic
branch_mnemos = {"beq", "bne", "beqz", "bnez", "bgtz", "bgez", "bltz", "blez"}
return mnemo in branch_mnemos
def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]:
mnemo, ops = insn.mnemonic, insn.operands
taken, reason = False, ""
if mnemo == "beq":
taken, reason = gef.arch.register(ops[0]) == gef.arch.register(ops[1]), f"{ops[0]} == {ops[1]}"
elif mnemo == "bne":
taken, reason = gef.arch.register(ops[0]) != gef.arch.register(ops[1]), f"{ops[0]} != {ops[1]}"
elif mnemo == "beqz":
taken, reason = gef.arch.register(ops[0]) == 0, f"{ops[0]} == 0"
elif mnemo == "bnez":
taken, reason = gef.arch.register(ops[0]) != 0, f"{ops[0]} != 0"
elif mnemo == "bgtz":
taken, reason = gef.arch.register(ops[0]) > 0, f"{ops[0]} > 0"
elif mnemo == "bgez":
taken, reason = gef.arch.register(ops[0]) >= 0, f"{ops[0]} >= 0"
elif mnemo == "bltz":
taken, reason = gef.arch.register(ops[0]) < 0, f"{ops[0]} < 0"
elif mnemo == "blez":
taken, reason = gef.arch.register(ops[0]) <= 0, f"{ops[0]} <= 0"
return taken, reason
def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None:
ra = None
if self.is_ret(insn):
ra = gef.arch.register("$ra")
else:
older = frame.older()
if older:
ra = to_unsigned_long(older.pc())
return ra
@classmethod
def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str:
_NR_mprotect = 4125
insns = ["addi $sp, $sp, -16",
"sw $v0, 0($sp)", "sw $a0, 4($sp)",
"sw $a3, 8($sp)", "sw $a3, 12($sp)",
f"li $v0, {_NR_mprotect:d}",
f"li $a0, {addr:d}",
f"li $a1, {size:d}",
f"li $a2, {perm.value:d}",
"syscall",
"lw $v0, 0($sp)", "lw $a1, 4($sp)",
"lw $a3, 8($sp)", "lw $a3, 12($sp)",
"addi $sp, $sp, 16",]
return "; ".join(insns)
class MIPS64(MIPS):
aliases = ("MIPS64",)
arch = "MIPS"
mode = "MIPS64"
_ptrsize = 8
@staticmethod
def supports_gdb_arch(gdb_arch: str) -> bool | None:
if not gef.binary or not isinstance(gef.binary, Elf):
return False
return gdb_arch.startswith("mips") and gef.binary.e_class == Elf.Class.ELF_64_BITS
def copy_to_clipboard(data: bytes) -> None:
"""Helper function to submit data to the clipboard"""
if sys.platform == "linux":
xclip = which("xclip")
prog = [xclip, "-selection", "clipboard", "-i"]
elif sys.platform == "darwin":
pbcopy = which("pbcopy")
prog = [pbcopy]
else:
raise NotImplementedError("copy: Unsupported OS")
with subprocess.Popen(prog, stdin=subprocess.PIPE) as p:
assert p.stdin
p.stdin.write(data)
p.stdin.close()
p.wait()
return
def use_stdtype() -> str:
if is_32bit(): return "uint32_t"
elif is_64bit(): return "uint64_t"
return "uint16_t"
def use_default_type() -> str:
if is_32bit(): return "unsigned int"
elif is_64bit(): return "unsigned long"
return "unsigned short"
def use_golang_type() -> str:
if is_32bit(): return "uint32"
elif is_64bit(): return "uint64"
return "uint16"
def use_rust_type() -> str:
if is_32bit(): return "u32"
elif is_64bit(): return "u64"
return "u16"
def to_unsigned_long(v: gdb.Value) -> int:
"""Cast a gdb.Value to unsigned long."""
mask = (1 << (gef.arch.ptrsize*8)) - 1
return int(v.cast(gdb.Value(mask).type)) & mask
def get_path_from_info_proc() -> str | None:
for x in (gdb.execute("info proc", to_string=True) or "").splitlines():
if x.startswith("exe = "):
return x.split(" = ")[1].replace("'", "")
return None
@deprecated("Use `gef.session.os`")
def get_os() -> str:
return gef.session.os
@lru_cache()
def is_qemu() -> bool:
if not is_remote_debug():
return False
response = gdb.execute("maintenance packet Qqemu.sstepbits", to_string=True, from_tty=False) or ""
return "ENABLE=" in response
@lru_cache()
def is_qemu_usermode() -> bool:
if not is_qemu():
return False
response = gdb.execute("maintenance packet qOffsets", to_string=True, from_tty=False) or ""
return "Text=" in response
@lru_cache()
def is_qemu_system() -> bool:
if not is_qemu():
return False
response = gdb.execute("maintenance packet qOffsets", to_string=True, from_tty=False) or ""
return "received: \"\"" in response
def is_target_coredump() -> bool:
global gef
if gef.session.coredump_mode is not None:
return gef.session.coredump_mode
lines = (gdb.execute("maintenance info section", to_string=True) or "").splitlines()
is_coredump_mode = any(map(lambda line: line.startswith("Core file: "), lines))
gef.session.coredump_mode = is_coredump_mode
return is_coredump_mode
def get_filepath() -> str | None:
"""Return the local absolute path of the file currently debugged."""
if gef.session.remote:
return str(gef.session.remote.lfile.absolute())
if gef.session.file:
return str(gef.session.file.absolute())
return None
def get_function_length(sym: str) -> int:
"""Attempt to get the length of the raw bytes of a function."""
dis = (gdb.execute(f"disassemble '{sym}'", to_string=True) or "").splitlines()
start_addr = int(dis[1].split()[0], 16)
end_addr = int(dis[-2].split()[0], 16)
return end_addr - start_addr
@lru_cache()
def get_info_files() -> list[Zone]:
"""Retrieve all the files loaded by debuggee."""
lines = (gdb.execute("info files", to_string=True) or "").splitlines()
infos = []
for line in lines:
line = line.strip()
if not line:
break
if not line.startswith("0x"):
continue
blobs = [x.strip() for x in line.split(" ")]
addr_start = int(blobs[0], 16)
addr_end = int(blobs[2], 16)
section_name = blobs[4]
if len(blobs) == 7:
filename = blobs[6]
else:
filename = get_filepath()
infos.append(Zone(section_name, addr_start, addr_end, filename))
return infos
def process_lookup_address(address: int) -> Section | None:
"""Look up for an address in memory.
Return an Address object if found, None otherwise."""
if not is_alive():
err("Process is not running")
return None
if is_x86():
if is_in_x86_kernel(address):
return None
for sect in gef.memory.maps:
if sect.page_start <= address < sect.page_end:
return sect
return None
@lru_cache()
def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Section | None:
"""Look up for a path in the process memory mapping.
Return a Section object if found, None otherwise."""
if not is_alive():
err("Process is not running")
return None
matches: dict[str, Section] = dict()
for sect in gef.memory.maps:
filename = pathlib.Path(sect.path).name
if name in filename and sect.permission & perm:
if sect.path not in matches.keys():
matches[sect.path] = sect
matches_count = len(matches)
if matches_count == 0:
return None
if matches_count > 1:
warn(f"{matches_count} matches! You should probably refine your search!")
for x in matches.keys():
warn(f"- '{x}'")
warn("Returning the first match")
return list(matches.values())[0]
@lru_cache()
def file_lookup_name_path(name: str, path: str) -> Zone | None:
"""Look up a file by name and path.
Return a Zone object if found, None otherwise."""
for xfile in get_info_files():
if path == xfile.filename and name == xfile.name:
return xfile
return None
@lru_cache()
def file_lookup_address(address: int) -> Zone | None:
"""Look up for a file by its address.
Return a Zone object if found, None otherwise."""
for info in get_info_files():
if info.zone_start <= address < info.zone_end:
return info
return None
@lru_cache()
def lookup_address(address: int) -> Address:
"""Try to find the address in the process address space.
Return an Address object, with validity flag set based on success."""
sect = process_lookup_address(address)
info = file_lookup_address(address)
if sect is None and info is None:
# i.e. there is no info on this address
return Address(value=address, valid=False)
return Address(value=address, section=sect, info=info)
def xor(data: ByteString, key: str) -> bytearray:
"""Return `data` xor-ed with `key`."""
key_raw = binascii.unhexlify(key.lstrip("0x"))
return bytearray(x ^ y for x, y in zip(data, itertools.cycle(key_raw)))
def is_hex(pattern: str) -> bool:
"""Return whether provided string is a hexadecimal value."""
if not pattern.lower().startswith("0x"):
return False
return len(pattern) % 2 == 0 and all(c in string.hexdigits for c in pattern[2:])
def continue_handler(_: "gdb.ContinueEvent") -> None:
"""GDB event handler for new object continue cases."""
return
def hook_stop_handler(_: "gdb.StopEvent") -> None:
"""GDB event handler for stop cases."""
reset_all_caches()
gdb.execute("context")
return
def new_objfile_handler(evt: "gdb.NewObjFileEvent | None") -> None:
"""GDB event handler for new object file cases."""
reset_all_caches()
progspace = gdb.current_progspace()
if evt:
path = evt.new_objfile.filename or ""
elif progspace:
path = progspace.filename or ""
else:
raise RuntimeError("Cannot determine file path")
try:
if gef.session.root and path.startswith("target:"):
# If the process is in a container, replace the "target:" prefix
# with the actual root directory of the process.
path = path.replace("target:", str(gef.session.root), 1)
target = pathlib.Path(path)
FileFormatClasses = list(filter(lambda fmtcls: fmtcls.is_valid(target), __registered_file_formats__))
GuessedFileFormatClass : Type[FileFormat] = FileFormatClasses.pop() if len(FileFormatClasses) else Elf
binary = GuessedFileFormatClass(target)
if not gef.binary:
gef.binary = binary
reset_architecture()
else:
gef.session.modules.append(binary)
except FileNotFoundError as fne:
# Linux automatically maps the vDSO into our process, and GDB
# will give us the string 'system-supplied DSO' as a path.
# This is normal, so we shouldn't warn the user about it
if "system-supplied DSO" not in path:
warn(f"Failed to find objfile or not a valid file format: {str(fne)}")
except RuntimeError as re:
warn(f"Not a valid file format: {str(re)}")
return
def exit_handler(_: "gdb.ExitedEvent") -> None:
"""GDB event handler for exit cases."""
global gef
# flush the caches
reset_all_caches()
# disconnect properly the remote session
gef.session.qemu_mode = False
if gef.session.remote:
gef.session.remote.close()
del gef.session.remote
gef.session.remote = None
gef.session.remote_initializing = False
# if `autosave_breakpoints_file` setting is configured, save the breakpoints to disk
setting = (gef.config["gef.autosave_breakpoints_file"] or "").strip()
if not setting:
return
bkp_fpath = pathlib.Path(setting).expanduser().absolute()
if bkp_fpath.exists():
warn(f"{bkp_fpath} exists, content will be overwritten")
with bkp_fpath.open("w") as fd:
for bp in list(gdb.breakpoints()):
if not bp.enabled or not bp.is_valid:
continue
fd.write(f"{'t' if bp.temporary else ''}break {bp.location}\n")
return
def memchanged_handler(_: "gdb.MemoryChangedEvent") -> None:
"""GDB event handler for mem changes cases."""
reset_all_caches()
return
def regchanged_handler(_: "gdb.RegisterChangedEvent") -> None:
"""GDB event handler for reg changes cases."""
reset_all_caches()
return
def get_terminal_size() -> tuple[int, int]:
"""Return the current terminal size."""
if is_debug():
return 600, 100
if platform.system() == "Windows":
from ctypes import create_string_buffer, windll # type: ignore
hStdErr = -12
herr = windll.kernel32.GetStdHandle(hStdErr)
csbi = create_string_buffer(22)
res = windll.kernel32.GetConsoleScreenBufferInfo(herr, csbi)
if res:
_, _, _, _, _, left, top, right, bottom, _, _ = struct.unpack("hhhhHhhhhhh", csbi.raw)
tty_columns = right - left + 1
tty_rows = bottom - top + 1
return tty_rows, tty_columns
else:
return 600, 100
else:
import fcntl
import termios
try:
tty_rows, tty_columns, _, _ = struct.unpack("hhhh", fcntl.ioctl(1, termios.TIOCGWINSZ, "12345678")) # type: ignore
return tty_rows, tty_columns
except OSError:
return 600, 100
@lru_cache()
def is_64bit() -> bool:
"""Checks if current target is 64bit."""
return gef.arch.ptrsize == 8
@lru_cache()
def is_32bit() -> bool:
"""Checks if current target is 32bit."""
return gef.arch.ptrsize == 4
@lru_cache()
def is_x86_64() -> bool:
"""Checks if current target is x86-64"""
return Elf.Abi.X86_64 in gef.arch.aliases
@lru_cache()
def is_x86_32():
"""Checks if current target is an x86-32"""
return Elf.Abi.X86_32 in gef.arch.aliases
@lru_cache()
def is_x86() -> bool:
return is_x86_32() or is_x86_64()
@lru_cache()
def is_arch(arch: Elf.Abi) -> bool:
return arch in gef.arch.aliases
def reset_architecture(arch: str | None = None) -> None:
"""Sets the current architecture.
If an architecture is explicitly specified by parameter, try to use that one. If this fails, an `OSError`
exception will occur.
If no architecture is specified, then GEF will attempt to determine automatically based on the current
ELF target. If this fails, an `OSError` exception will occur.
"""
global gef
arches = __registered_architectures__
# check if the architecture is forced by parameter
if arch:
try:
gef.arch = arches[arch.lower()]()
gef.arch_reason = "The architecture has been set manually"
except KeyError:
raise OSError(f"Specified arch {arch.upper()} is not supported")
return
# check for bin running
if is_alive():
gdb_arch = gdb.selected_frame().architecture().name()
preciser_arch = next((a for a in arches.values() if a.supports_gdb_arch(gdb_arch)), None)
if preciser_arch:
gef.arch = preciser_arch()
gef.arch_reason = "The architecture has been detected by GDB"
return
# last resort, use the info from elf header to find it from the known architectures
if gef.binary and isinstance(gef.binary, Elf):
try:
gef.arch = arches[gef.binary.e_machine]()
gef.arch_reason = "The architecture has been detected via the ELF headers"
except KeyError:
raise OSError(f"CPU type is currently not supported: {gef.binary.e_machine}")
return
warn("Did not find any way to guess the correct architecture :(")
@lru_cache()
def cached_lookup_type(_type: str) -> gdb.Type | None:
try:
return gdb.lookup_type(_type).strip_typedefs()
except RuntimeError:
return None
@deprecated("Use `gef.arch.ptrsize` instead")
def get_memory_alignment(in_bits: bool = False) -> int:
"""Try to determine the size of a pointer on this system.
First, try to parse it out of the ELF header.
Next, use the size of `size_t`.
Finally, try the size of $pc.
If `in_bits` is set to True, the result is returned in bits, otherwise in
bytes."""
res = cached_lookup_type("size_t")
if res is not None:
return res.sizeof if not in_bits else res.sizeof * 8
try:
return gdb.parse_and_eval("$pc").type.sizeof
except Exception:
pass
raise OSError("GEF is running under an unsupported mode")
def clear_screen(tty: str = "") -> None:
"""Clear the screen."""
clean_sequence = "\x1b[H\x1b[J"
if tty:
pathlib.Path(tty).write_text(clean_sequence)
else:
sys.stdout.write(clean_sequence)
return
def format_address(addr: int) -> str:
"""Format the address according to its size."""
memalign_size = gef.arch.ptrsize
addr = align_address(addr)
return f"0x{addr:016x}" if memalign_size == 8 else f"0x{addr:08x}"
def format_address_spaces(addr: int, left: bool = True) -> str:
"""Format the address according to its size, but with spaces instead of zeroes."""
width = gef.arch.ptrsize * 2 + 2
addr = align_address(addr)
if not left:
return f"{addr:#x}".rjust(width)
return f"{addr:#x}".ljust(width)
def align_address(address: int) -> int:
"""Align the provided address to the process's native length."""
return address & 0xFFFFFFFFFFFFFFFF if gef.arch.ptrsize == 8 else address & 0xFFFFFFFF
def align_address_to_size(address: int, align: int) -> int:
"""Align the address to the given size."""
return address + ((align - (address % align)) % align)
def align_address_to_page(address: int) -> int:
"""Align the address to a page."""
a = align_address(address) >> DEFAULT_PAGE_ALIGN_SHIFT
return a << DEFAULT_PAGE_ALIGN_SHIFT
def parse_address(address: str) -> int:
"""Parse an address and return it as an Integer."""
if is_hex(address):
return int(address, 16)
return int(gdb.parse_and_eval(address))
def is_in_x86_kernel(address: int) -> bool:
address = align_address(address)
memalign = gef.arch.ptrsize*8 - 1
return (address >> memalign) == 0xF
def is_remote_debug() -> bool:
""""Return True is the current debugging session is running through GDB remote session."""
return gef.session.remote_initializing or gef.session.remote is not None
def de_bruijn(alphabet: bytes, n: int) -> Generator[int, None, None]:
"""De Bruijn sequence for alphabet and subsequences of length n (for compat. w/ pwnlib)."""
k = len(alphabet)
a = [0] * k * n
def db(t: int, p: int) -> Generator[int, None, None]:
if t > n:
if n % p == 0:
for j in range(1, p + 1):
yield alphabet[a[j]]
else:
a[t] = a[t - p]
yield from db(t + 1, p)
for j in range(a[t - p] + 1, k):
a[t] = j
yield from db(t + 1, t)
return db(1, 1)
def generate_cyclic_pattern(length: int, cycle: int = 4) -> bytearray:
"""Create a `length` byte bytearray of a de Bruijn cyclic pattern."""
charset = bytearray(b"abcdefghijklmnopqrstuvwxyz")
return bytearray(itertools.islice(de_bruijn(charset, cycle), length))
def safe_parse_and_eval(value: str) -> "gdb.Value | None":
"""GEF wrapper for gdb.parse_and_eval(): this function returns None instead of raising
gdb.error if the eval failed."""
try:
return gdb.parse_and_eval(value)
except gdb.error as e:
dbg(f"gdb.parse_and_eval() failed, reason: {str(e)}")
return None
@lru_cache()
def dereference(addr: int) -> "gdb.Value | None":
"""GEF wrapper for gdb dereference function."""
try:
ulong_t = cached_lookup_type(use_stdtype()) or \
cached_lookup_type(use_default_type()) or \
cached_lookup_type(use_golang_type()) or \
cached_lookup_type(use_rust_type())
if not ulong_t:
raise gdb.MemoryError("Failed to determine unsigned long type")
unsigned_long_type = ulong_t.pointer()
res = gdb.Value(addr).cast(unsigned_long_type).dereference()
# GDB does lazy fetch by default so we need to force access to the value
res.fetch_lazy()
return res
except gdb.MemoryError as e:
dbg(str(e))
return None
def gef_convenience(value: str | bytes) -> str:
"""Defines a new convenience value."""
global gef
var_name = f"$_gef{gef.session.convenience_vars_index:d}"
gef.session.convenience_vars_index += 1
if isinstance(value, str):
gdb.execute(f"""set {var_name} = "{value}" """)
elif isinstance(value, bytes):
value_as_array = "{" + ", ".join([f"0x{b:02x}" for b in value]) + "}"
gdb.execute(f"""set {var_name} = {value_as_array} """)
else:
raise TypeError
return var_name
def parse_string_range(s: str) -> Iterator[int]:
"""Parses an address range (e.g. 0x400000-0x401000)"""
addrs = s.split("-")
return map(lambda x: int(x, 16), addrs)
@lru_cache()
def is_syscall(instruction: Instruction | int) -> bool:
"""Checks whether an instruction or address points to a system call."""
if isinstance(instruction, int):
instruction = gef_current_instruction(instruction)
insn_str = instruction.mnemonic
if len(instruction.operands):
insn_str += f" {', '.join(instruction.operands)}"
return insn_str in gef.arch.syscall_instructions
#
# Deprecated API
#
@deprecated("Use `gef.session.pie_breakpoints[num]`")
def gef_get_pie_breakpoint(num: int) -> "PieVirtualBreakpoint":
return gef.session.pie_breakpoints[num]
@deprecated("Use `str(gef.arch.endianness)` instead")
def endian_str() -> str:
return str(gef.arch.endianness)
@deprecated("Use `gef.config[key]`")
def get_gef_setting(name: str) -> Any:
return gef.config[name]
@deprecated("Use `gef.config[key] = value`")
def set_gef_setting(name: str, value: Any) -> None:
gef.config[name] = value
return
@deprecated("Use `gef.session.pagesize`")
def gef_getpagesize() -> int:
return gef.session.pagesize
@deprecated("Use `gef.session.canary`")
def gef_read_canary() -> tuple[int, int] | None:
return gef.session.canary
@deprecated("Use `gef.session.pid`")
def get_pid() -> int:
return gef.session.pid
@deprecated("Use `gef.session.file.name`")
def get_filename() -> str:
assert gef.session.file
return gef.session.file.name
@deprecated("Use `gef.heap.main_arena`")
def get_glibc_arena() -> GlibcArena | None:
return gef.heap.main_arena
@deprecated("Use `gef.arch.register(regname)`")
def get_register(regname) -> int | None:
return gef.arch.register(regname)
@deprecated("Use `gef.memory.maps`")
def get_process_maps() -> list[Section]:
return gef.memory.maps
@deprecated("Use `reset_architecture`")
def set_arch(arch: str | None = None, _: str | None = None) -> None:
return reset_architecture(arch)
#
# GDB event hooking
#
@only_if_events_supported("cont")
def gef_on_continue_hook(func: Callable[["gdb.ContinueEvent"], None]) -> None:
gdb.events.cont.connect(func)
@only_if_events_supported("cont")
def gef_on_continue_unhook(func: Callable[["gdb.ThreadEvent"], None]) -> None:
gdb.events.cont.disconnect(func)
@only_if_events_supported("stop")
def gef_on_stop_hook(func: Callable[["gdb.StopEvent"], None]) -> None:
gdb.events.stop.connect(func)
@only_if_events_supported("stop")
def gef_on_stop_unhook(func: Callable[["gdb.StopEvent"], None]) -> None:
gdb.events.stop.disconnect(func)
@only_if_events_supported("exited")
def gef_on_exit_hook(func: Callable[["gdb.ExitedEvent"], None]) -> None:
gdb.events.exited.connect(func)
@only_if_events_supported("exited")
def gef_on_exit_unhook(func: Callable[["gdb.ExitedEvent"], None]) -> None:
gdb.events.exited.disconnect(func)
@only_if_events_supported("new_objfile")
def gef_on_new_hook(func: Callable[["gdb.NewObjFileEvent"], None]) -> None:
gdb.events.new_objfile.connect(func)
@only_if_events_supported("new_objfile")
def gef_on_new_unhook(func: Callable[["gdb.NewObjFileEvent"], None]) -> None:
gdb.events.new_objfile.disconnect(func)
@only_if_events_supported("clear_objfiles")
def gef_on_unload_objfile_hook(func: Callable[["gdb.ClearObjFilesEvent"], None]) -> None:
gdb.events.clear_objfiles.connect(func)
@only_if_events_supported("clear_objfiles")
def gef_on_unload_objfile_unhook(func: Callable[["gdb.ClearObjFilesEvent"], None]) -> None:
gdb.events.clear_objfiles.disconnect(func)
@only_if_events_supported("memory_changed")
def gef_on_memchanged_hook(func: Callable[["gdb.MemoryChangedEvent"], None]) -> None:
gdb.events.memory_changed.connect(func)
@only_if_events_supported("memory_changed")
def gef_on_memchanged_unhook(func: Callable[["gdb.MemoryChangedEvent"], None]) -> None:
gdb.events.memory_changed.disconnect(func)
@only_if_events_supported("register_changed")
def gef_on_regchanged_hook(func: Callable[["gdb.RegisterChangedEvent"], None]) -> None:
gdb.events.register_changed.connect(func)
@only_if_events_supported("register_changed")
def gef_on_regchanged_unhook(func: Callable[["gdb.RegisterChangedEvent"], None]) -> None:
gdb.events.register_changed.disconnect(func)
#
# Virtual breakpoints
#
class PieVirtualBreakpoint:
"""PIE virtual breakpoint (not real breakpoint)."""
def __init__(self, set_func: Callable[[int], str], vbp_num: int, addr: int) -> None:
# set_func(base): given a base address return a
# "set breakpoint" gdb command string
self.set_func = set_func
self.vbp_num = vbp_num
# breakpoint num, 0 represents not instantiated yet
self.bp_num = 0
self.bp_addr = 0
# this address might be a symbol, just to know where to break
if isinstance(addr, int):
self.addr: int | str = hex(addr)
else:
self.addr = addr
return
def instantiate(self, base: int) -> None:
if self.bp_num:
self.destroy()
try:
res = gdb.execute(self.set_func(base), to_string=True) or ""
if not res: return
except gdb.error as e:
err(str(e))
return
if "Breakpoint" not in res:
err(res)
return
res_list = res.split()
self.bp_num = res_list[1]
self.bp_addr = res_list[3]
return
def destroy(self) -> None:
if not self.bp_num:
err("Destroy PIE breakpoint not even set")
return
gdb.execute(f"delete {self.bp_num}")
self.bp_num = 0
return
#
# Breakpoints
#
class FormatStringBreakpoint(gdb.Breakpoint):
"""Inspect stack for format string."""
def __init__(self, spec: str, num_args: int) -> None:
super().__init__(spec, type=gdb.BP_BREAKPOINT, internal=False)
self.num_args = num_args
self.enabled = True
return
def stop(self) -> bool:
reset_all_caches()
msg = []
ptr, addr = gef.arch.get_ith_parameter(self.num_args)
addr = lookup_address(addr)
if not addr.valid:
return False
if addr.section.is_writable():
content = gef.memory.read_cstring(addr.value)
name = addr.info.name if addr.info else addr.section.path
msg.append(Color.colorify("Format string helper", "yellow bold"))
msg.append(f"Possible insecure format string: {self.location}('{ptr}' {RIGHT_ARROW} {addr.value:#x}: '{content}')")
msg.append(f"Reason: Call to '{self.location}()' with format string argument in position "
f"#{self.num_args:d} is in page {addr.section.page_start:#x} ({name}) that has write permission")
push_context_message("warn", "\n".join(msg))
return True
return False
class StubBreakpoint(gdb.Breakpoint):
"""Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.)."""
def __init__(self, func: str, retval: int | None) -> None:
super().__init__(func, gdb.BP_BREAKPOINT, internal=False)
self.func = func
self.retval = retval
m = f"All calls to '{self.func}' will be skipped"
if self.retval is not None:
m += f" (with return value set to {self.retval:#x})"
info(m)
return
def stop(self) -> bool:
size = "long" if gef.arch.ptrsize == 8 else "int"
gdb.execute(f"return (unsigned {size}){self.retval:#x}")
ok(f"Ignoring call to '{self.func}' "
f"(setting return value to {self.retval:#x})")
return False
class ChangePermissionBreakpoint(gdb.Breakpoint):
"""When hit, this temporary breakpoint will restore the original code, and position
$pc correctly."""
def __init__(self, loc: str, code: ByteString, pc: int) -> None:
super().__init__(loc, gdb.BP_BREAKPOINT, internal=False)
self.original_code = code
self.original_pc = pc
return
def stop(self) -> bool:
info("Restoring original context")
gef.memory.write(self.original_pc, self.original_code, len(self.original_code))
info("Restoring $pc")
gdb.execute(f"set $pc = {self.original_pc:#x}")
return True
class TraceMallocBreakpoint(gdb.Breakpoint):
"""Track allocations done with malloc() or calloc()."""
def __init__(self, name: str) -> None:
super().__init__(name, gdb.BP_BREAKPOINT, internal=True)
self.silent = True
self.name = name
return
def stop(self) -> bool:
reset_all_caches()
_, size = gef.arch.get_ith_parameter(0)
assert size
self.retbp = TraceMallocRetBreakpoint(size, self.name)
return False
class TraceMallocRetBreakpoint(gdb.FinishBreakpoint):
"""Internal temporary breakpoint to retrieve the return value of malloc()."""
def __init__(self, size: int, name: str) -> None:
super().__init__(gdb.newest_frame(), internal=True)
self.size = size
self.name = name
self.silent = True
return
def stop(self) -> bool:
if self.return_value:
loc = int(self.return_value)
else:
loc = parse_address(gef.arch.return_register)
size = self.size
ok(f"{Color.colorify('Heap-Analysis', 'yellow bold')} - {self.name}({size})={loc:#x}")
check_heap_overlap = gef.config["heap-analysis-helper.check_heap_overlap"]
# pop from free-ed list if it was in it
if gef.session.heap_freed_chunks:
idx = 0
for item in gef.session.heap_freed_chunks:
addr = item[0]
if addr == loc:
gef.session.heap_freed_chunks.remove(item)
continue
idx += 1
# pop from uaf watchlist
if gef.session.heap_uaf_watchpoints:
idx = 0
for wp in gef.session.heap_uaf_watchpoints:
wp_addr = wp.address
if loc <= wp_addr < loc + size:
gef.session.heap_uaf_watchpoints.remove(wp)
wp.enabled = False
continue
idx += 1
item = (loc, size)
if check_heap_overlap:
# seek all the currently allocated chunks, read their effective size and check for overlap
msg = []
align = gef.arch.ptrsize
for chunk_addr, _ in gef.session.heap_allocated_chunks:
current_chunk = GlibcChunk(chunk_addr)
current_chunk_size = current_chunk.size
if chunk_addr <= loc < chunk_addr + current_chunk_size:
offset = loc - chunk_addr - 2*align
if offset < 0: continue # false positive, discard
msg.append(Color.colorify("Heap-Analysis", "yellow bold"))
msg.append("Possible heap overlap detected")
msg.append(f"Reason {RIGHT_ARROW} new allocated chunk {loc:#x} (of size {size:d}) overlaps in-used chunk {chunk_addr:#x} (of size {current_chunk_size:#x})")
msg.append(f"Writing {offset:d} bytes from {chunk_addr:#x} will reach chunk {loc:#x}")
msg.append(f"Payload example for chunk {chunk_addr:#x} (to overwrite {loc:#x} headers):")
msg.append(f" data = 'A'*{offset:d} + 'B'*{align:d} + 'C'*{align:d}")
push_context_message("warn", "\n".join(msg))
return True
# add it to alloc-ed list
gef.session.heap_allocated_chunks.append(item)
return False
class TraceReallocBreakpoint(gdb.Breakpoint):
"""Track re-allocations done with realloc()."""
def __init__(self) -> None:
super().__init__("__libc_realloc", gdb.BP_BREAKPOINT, internal=True)
self.silent = True
return
def stop(self) -> bool:
_, ptr = gef.arch.get_ith_parameter(0)
_, size = gef.arch.get_ith_parameter(1)
assert ptr is not None and size is not None
self.retbp = TraceReallocRetBreakpoint(ptr, size)
return False
class TraceReallocRetBreakpoint(gdb.FinishBreakpoint):
"""Internal temporary breakpoint to retrieve the return value of realloc()."""
def __init__(self, ptr: int, size: int) -> None:
super().__init__(gdb.newest_frame(), internal=True)
self.ptr = ptr
self.size = size
self.silent = True
return
def stop(self) -> bool:
if self.return_value:
newloc = int(self.return_value)
else:
newloc = parse_address(gef.arch.return_register)
title = Color.colorify("Heap-Analysis", "yellow bold")
if newloc != self:
loc = Color.colorify(f"{newloc:#x}", "green")
ok(f"{title} - realloc({self.ptr:#x}, {self.size})={loc}")
else:
loc = Color.colorify(f"{newloc:#x}", "red")
ok(f"{title} - realloc({self.ptr:#x}, {self.size})={loc}")
item = (newloc, self.size)
try:
# check if item was in alloc-ed list
idx = [x for x, y in gef.session.heap_allocated_chunks].index(self.ptr)
# if so pop it out
item = gef.session.heap_allocated_chunks.pop(idx)
except ValueError:
if is_debug():
warn(f"Chunk {self.ptr:#x} was not in tracking list")
finally:
# add new item to alloc-ed list
gef.session.heap_allocated_chunks.append(item)
return False
class TraceFreeBreakpoint(gdb.Breakpoint):
"""Track calls to free() and attempts to detect inconsistencies."""
def __init__(self) -> None:
super().__init__("__libc_free", gdb.BP_BREAKPOINT, internal=True)
self.silent = True
return
def stop(self) -> bool:
reset_all_caches()
_, addr = gef.arch.get_ith_parameter(0)
msg = []
check_free_null = gef.config["heap-analysis-helper.check_free_null"]
check_double_free = gef.config["heap-analysis-helper.check_double_free"]
check_weird_free = gef.config["heap-analysis-helper.check_weird_free"]
check_uaf = gef.config["heap-analysis-helper.check_uaf"]
ok(f"{Color.colorify('Heap-Analysis', 'yellow bold')} - free({addr:#x})")
if not addr:
if check_free_null:
msg.append(Color.colorify("Heap-Analysis", "yellow bold"))
msg.append(f"Attempting to free(NULL) at {gef.arch.pc:#x}")
msg.append("Reason: if NULL page is allocatable, this can lead to code execution.")
push_context_message("warn", "\n".join(msg))
return True
return False
if addr in [x for (x, _) in gef.session.heap_freed_chunks]:
if check_double_free:
msg.append(Color.colorify("Heap-Analysis", "yellow bold"))
msg.append(f"Double-free detected {RIGHT_ARROW} free({addr:#x}) is called at {gef.arch.pc:#x} but is already in the free-ed list")
msg.append("Execution will likely crash...")
push_context_message("warn", "\n".join(msg))
return True
return False
# if here, no error
# 1. move alloc-ed item to free list
try:
# pop from alloc-ed list
idx = [x for x, y in gef.session.heap_allocated_chunks].index(addr)
item = gef.session.heap_allocated_chunks.pop(idx)
except ValueError:
if check_weird_free:
msg.append(Color.colorify("Heap-Analysis", "yellow bold"))
msg.append("Heap inconsistency detected:")
msg.append(f"Attempting to free an unknown value: {addr:#x}")
push_context_message("warn", "\n".join(msg))
return True
return False
# 2. add it to free-ed list
gef.session.heap_freed_chunks.append(item)
self.retbp = None
if check_uaf:
# 3. (opt.) add a watchpoint on pointer
self.retbp = TraceFreeRetBreakpoint(addr)
return False
class TraceFreeRetBreakpoint(gdb.FinishBreakpoint):
"""Internal temporary breakpoint to track free()d values."""
def __init__(self, addr: int) -> None:
super().__init__(gdb.newest_frame(), internal=True)
self.silent = True
self.addr = addr
return
def stop(self) -> bool:
reset_all_caches()
wp = UafWatchpoint(self.addr)
gef.session.heap_uaf_watchpoints.append(wp)
return False
class UafWatchpoint(gdb.Breakpoint):
"""Custom watchpoints set TraceFreeBreakpoint() to monitor free()d pointers being used."""
def __init__(self, addr: int) -> None:
super().__init__(f"*{addr:#x}", gdb.BP_WATCHPOINT, internal=True)
self.address = addr
self.silent = True
self.enabled = True
return
def stop(self) -> bool:
"""If this method is triggered, we likely have a UaF. Break the execution and report it."""
reset_all_caches()
frame = gdb.selected_frame()
if frame.name() in ("_int_malloc", "malloc_consolidate", "__libc_calloc", ):
return False
# software watchpoints stop after the next statement (see
# https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html)
pc = gdb_get_nth_previous_instruction_address(gef.arch.pc, 2)
assert pc
insn = gef_current_instruction(pc)
msg = []
msg.append(Color.colorify("Heap-Analysis", "yellow bold"))
msg.append(f"Possible Use-after-Free in '{get_filepath()}': "
f"pointer {self.address:#x} was freed, but is attempted to be used at {pc:#x}")
msg.append(f"{insn.address:#x} {insn.mnemonic} {Color.yellowify(', '.join(insn.operands))}")
push_context_message("warn", "\n".join(msg))
return True
class EntryBreakBreakpoint(gdb.Breakpoint):
"""Breakpoint used internally to stop execution at the most convenient entry point."""
def __init__(self, location: str) -> None:
super().__init__(location, gdb.BP_BREAKPOINT, internal=True, temporary=True)
self.silent = True
return
def stop(self) -> bool:
reset_all_caches()
return True
class NamedBreakpoint(gdb.Breakpoint):
"""Breakpoint which shows a specified name, when hit."""
def __init__(self, location: str, name: str) -> None:
super().__init__(spec=location, type=gdb.BP_BREAKPOINT, internal=False, temporary=False)
self.name = name
self.loc = location
return
def stop(self) -> bool:
reset_all_caches()
push_context_message("info", f"Hit breakpoint {self.loc} ({Color.colorify(self.name, 'red bold')})")
return True
class JustSilentStopBreakpoint(gdb.Breakpoint):
"""When hit, this temporary breakpoint stop the execution."""
def __init__(self, loc: str) -> None:
super().__init__(loc, gdb.BP_BREAKPOINT, temporary=True)
self.silent = True
return
#
# Context Panes
#
def register_external_context_pane(pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], str | None], condition : Callable[[], bool] | None = None) -> None:
"""
Registering function for new GEF Context View.
pane_name: a string that has no spaces (used in settings)
display_pane_function: a function that uses gef_print() to print strings
pane_title_function: a function that returns a string or None, which will be displayed as the title.
If None, no title line is displayed.
condition: an optional callback: if not None, the callback will be executed first. If it returns true,
then only the pane title and content will displayed. Otherwise, it's simply skipped.
Example usage for a simple text to show when we hit a syscall:
def only_syscall(): return gef_current_instruction(gef.arch.pc).is_syscall()
def display_pane():
gef_print("Wow, I am a context pane!")
def pane_title():
return "example:pane"
register_external_context_pane("example_pane", display_pane, pane_title, only_syscall)
"""
gef.gdb.add_context_pane(pane_name, display_pane_function, pane_title_function, condition)
return
def register_external_context_layout_mapping(current_pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], str | None], condition : Callable[[], bool] | None = None) -> None:
gef.gdb.add_context_layout_mapping(current_pane_name, display_pane_function, pane_title_function, condition)
return
#
# Commands
#
@deprecated("Use `register()`, and inherit from `GenericCommand` instead")
def register_external_command(cls: Type["GenericCommand"]) -> Type["GenericCommand"]:
"""Registering function for new GEF (sub-)command to GDB."""
return cls
@deprecated("Use `register()`, and inherit from `GenericCommand` instead")
def register_command(cls: Type["GenericCommand"]) -> Type["GenericCommand"]:
"""Decorator for registering new GEF (sub-)command to GDB."""
return cls
@deprecated("")
def register_priority_command(cls: Type["GenericCommand"]) -> Type["GenericCommand"]:
"""Decorator for registering new command with priority, meaning that it must
loaded before the other generic commands."""
return cls
ValidCommandType = TypeVar("ValidCommandType", bound="GenericCommand")
ValidFunctionType = TypeVar("ValidFunctionType", bound="GenericFunction")
def register(cls: Type["ValidCommandType"] | Type["ValidFunctionType"]) -> Type["ValidCommandType"] | Type["ValidFunctionType"]:
global __registered_commands__, __registered_functions__
if issubclass(cls, GenericCommand):
assert hasattr(cls, "_cmdline_")
assert hasattr(cls, "do_invoke")
if any(map(lambda x: x._cmdline_ == cls._cmdline_, __registered_commands__)):
raise AlreadyRegisteredException(cls._cmdline_)
__registered_commands__.add(cls)
return cls
if issubclass(cls, GenericFunction):
assert hasattr(cls, "_function_")
assert hasattr(cls, "invoke")
if any(map(lambda x: x._function_ == cls._function_, __registered_functions__)):
raise AlreadyRegisteredException(cls._function_)
__registered_functions__.add(cls)
return cls
raise TypeError(f"`{cls.__class__}` is an illegal class for `register`")
class GenericCommand(gdb.Command):
"""This is an abstract class for invoking commands, should not be instantiated."""
_cmdline_: str
_syntax_: str
_example_: str | list[str] = ""
_aliases_: list[str] = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
attributes = ("_cmdline_", "_syntax_", )
if not all(map(lambda x: hasattr(cls, x), attributes)):
raise NotImplementedError
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.pre_load()
syntax = Color.yellowify("\nSyntax: ") + self._syntax_
example = Color.yellowify("\nExamples: \n\t")
if isinstance(self._example_, list):
example += "\n\t".join(self._example_)
elif isinstance(self._example_, str):
example += self._example_
self.__doc__ = (self.__doc__ or "").replace(" "*4, "") + syntax + example
self.repeat = False
self.repeat_count = 0
self.__last_command = None
command_type = kwargs.setdefault("command", gdb.COMMAND_USER)
complete_type = kwargs.setdefault("complete", -1) # -1=allow user-defined `complete()`
prefix = kwargs.setdefault("prefix", False)
super().__init__(name=self._cmdline_, command_class=command_type,
completer_class=complete_type, prefix=prefix)
self.post_load()
return
def invoke(self, args: str, from_tty: bool) -> None:
try:
argv = gdb.string_to_argv(args)
self.__set_repeat_count(argv, from_tty)
bufferize(self.do_invoke)(argv)
except Exception as e:
# Note: since we are intercepting cleaning exceptions here, commands preferably should avoid
# catching generic Exception, but rather specific ones. This is allows a much cleaner use.
if is_debug():
show_last_exception()
if gef.config["gef.propagate_debug_exception"] is True:
raise
else:
err(f"Command '{self._cmdline_}' failed to execute properly, reason: {e}")
return
def usage(self) -> None:
err(f"Syntax\n{self._syntax_}")
return
def do_invoke(self, argv: list[str]) -> None:
raise NotImplementedError
def pre_load(self) -> None:
return
def post_load(self) -> None:
return
def __get_setting_name(self, name: str) -> str:
clsname = self.__class__._cmdline_.replace(" ", "-")
return f"{clsname}.{name}"
def __iter__(self) -> Generator[str, None, None]:
for key in gef.config.keys():
if key.startswith(self._cmdline_):
yield key.replace(f"{self._cmdline_}.", "", 1)
@property
def settings(self) -> list[str]:
"""Return the list of settings for this command."""
return list(iter(self))
@deprecated("Use `self[setting_name]` instead")
def get_setting(self, name: str) -> Any:
return self.__getitem__(name)
def __getitem__(self, name: str) -> Any:
key = self.__get_setting_name(name)
return gef.config[key]
@deprecated("Use `setting_name in self` instead")
def has_setting(self, name: str) -> bool:
return self.__contains__(name)
def __contains__(self, name: str) -> bool:
return self.__get_setting_name(name) in gef.config
@deprecated("Use `self[setting_name] = value` instead")
def add_setting(self, name: str, value: tuple[Any, type, str], description: str = "") -> None:
return self.__setitem__(name, (value, description))
def __setitem__(self, name: str, value: "GefSetting | tuple[Any, str]") -> None:
# make sure settings are always associated to the root command (which derives from GenericCommand)
if "GenericCommand" not in [x.__name__ for x in self.__class__.__bases__]:
return
key = self.__get_setting_name(name)
if key in gef.config:
# If the setting already exists, update the entry
setting = gef.config.raw_entry(key)
setting.value = value
return
# otherwise create it
if isinstance(value, GefSetting):
gef.config[key] = value
else:
if len(value) == 1:
gef.config[key] = GefSetting(value[0])
elif len(value) == 2:
gef.config[key] = GefSetting(value[0], description=value[1])
return
@deprecated("Use `del self[setting_name]` instead")
def del_setting(self, name: str) -> None:
return self.__delitem__(name)
def __delitem__(self, name: str) -> None:
del gef.config[self.__get_setting_name(name)]
return
def __set_repeat_count(self, argv: list[str], from_tty: bool) -> None:
if not from_tty:
self.repeat = False
self.repeat_count = 0
return
command = (gdb.execute("show commands", to_string=True) or "").strip().split("\n")[-1]
self.repeat = self.__last_command == command
self.repeat_count = self.repeat_count + 1 if self.repeat else 0
self.__last_command = command
return
@register
class ArchCommand(GenericCommand):
"""Manage the current loaded architecture."""
_cmdline_ = "arch"
_syntax_ = f"{_cmdline_} (list|get|set) ..."
_example_ = f"{_cmdline_} set X86"
def __init__(self) -> None:
super().__init__(prefix=True)
return
def do_invoke(self, argv: list[str]) -> None:
if not argv:
self.usage()
return
@register
class ArchGetCommand(GenericCommand):
"""Get the current loaded architecture."""
_cmdline_ = "arch get"
_syntax_ = f"{_cmdline_}"
_example_ = f"{_cmdline_}"
def do_invoke(self, args: list[str]) -> None:
gef_print(f"{Color.greenify('Arch')}: {gef.arch}")
gef_print(f"{Color.greenify('Reason')}: {gef.arch_reason}")
@register
class ArchSetCommand(GenericCommand):
"""Set the current loaded architecture."""
_cmdline_ = "arch set"
_syntax_ = f"{_cmdline_} "
_example_ = f"{_cmdline_} X86"
def do_invoke(self, args: list[str]) -> None:
reset_architecture(args[0] if args else None)
def complete(self, text: str, word: str) -> list[str]:
return sorted(x for x in __registered_architectures__.keys() if
isinstance(x, str) and x.lower().startswith(text.lower().strip()))
@register
class ArchListCommand(GenericCommand):
"""List the available architectures."""
_cmdline_ = "arch list"
_syntax_ = f"{_cmdline_}"
_example_ = f"{_cmdline_}"
def do_invoke(self, args: list[str]) -> None:
gef_print(Color.greenify("Available architectures:"))
for arch in sorted(set(__registered_architectures__.values()), key=lambda x: x.arch):
if arch is GenericArchitecture:
continue
gef_print(' ' + Color.yellowify(str(arch())))
for alias in arch.aliases:
if isinstance(alias, str):
gef_print(f" {alias}")
@register
class VersionCommand(GenericCommand):
"""Display GEF version info."""
_cmdline_ = "version"
_syntax_ = f"{_cmdline_}"
_example_ = f"{_cmdline_}"
def do_invoke(self, argv: list[str]) -> None:
gef_fpath = pathlib.Path(inspect.stack()[0][1]).expanduser().absolute()
gef_dir = gef_fpath.parent
gef_hash = hashlib.sha256(gef_fpath.read_bytes()).hexdigest()
try:
git = which("git")
except FileNotFoundError:
git = None
if git:
if (gef_dir / ".git").is_dir():
ver = subprocess.check_output("git log --format='%H' -n 1 HEAD", cwd=gef_dir, shell=True).decode("utf8").strip()
extra = "dirty" if len(subprocess.check_output("git ls-files -m", cwd=gef_dir, shell=True).decode("utf8").strip()) else "clean"
gef_print(f"GEF: rev:{ver} (Git - {extra})")
else:
gef_blob_hash = subprocess.check_output(f"git hash-object {gef_fpath}", shell=True).decode().strip()
gef_print("GEF: (Standalone)")
gef_print(f"Blob Hash({gef_fpath}): {gef_blob_hash}")
gef_print(f"SHA256({gef_fpath}): {gef_hash}")
gef_print(f"GDB: {gdb.VERSION}")
py_ver = f"{sys.version_info.major:d}.{sys.version_info.minor:d}"
gef_print(f"GDB-Python: {py_ver}")
if "full" in argv:
gef_print(f"Loaded commands: {', '.join(gef.gdb.loaded_command_names)}")
return
@register
class PrintFormatCommand(GenericCommand):
"""Print bytes format in commonly used formats, such as literals in high level languages."""
valid_formats = ("py", "c", "js", "asm", "hex", "bytearray")
valid_bitness = (8, 16, 32, 64)
_cmdline_ = "print-format"
_aliases_ = ["pf",]
_syntax_ = (f"{_cmdline_} [--lang LANG] [--bitlen SIZE] [(--length,-l) LENGTH] [--clip] LOCATION"
f"\t--lang LANG specifies the output format for programming language (available: {valid_formats!s}, default 'py')."
f"\t--bitlen SIZE specifies size of bit (possible values: {valid_bitness!s}, default is 8)."
"\t--length LENGTH specifies length of array (default is 256)."
"\t--clip The output data will be copied to clipboard"
"\tLOCATION specifies where the address of bytes is stored.")
_example_ = f"{_cmdline_} --lang py -l 16 $rsp"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
self["max_size_preview"] = (10, "max size preview of bytes")
return
@property
def format_matrix(self) -> dict[int, tuple[str, str, str]]:
# `gef.arch.endianness` is a runtime property, should not be defined as a class property
return {
8: (f"{gef.arch.endianness}B", "char", "db"),
16: (f"{gef.arch.endianness}H", "short", "dw"),
32: (f"{gef.arch.endianness}I", "int", "dd"),
64: (f"{gef.arch.endianness}Q", "long long", "dq"),
}
@only_if_gdb_running
@parse_arguments({"location": "$pc", }, {("--length", "-l"): 256, "--bitlen": 0, "--lang": "py", "--clip": False,})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
"""Default value for print-format command."""
args: argparse.Namespace = kwargs["arguments"]
args.bitlen = args.bitlen or gef.arch.ptrsize * 2
valid_bitlens = self.format_matrix.keys()
if args.bitlen not in valid_bitlens:
err(f"Size of bit must be in: {valid_bitlens!s}")
return
if args.lang not in self.valid_formats:
err(f"Language must be in: {self.valid_formats!s}")
return
start_addr = parse_address(args.location)
size = int(args.bitlen / 8)
end_addr = start_addr + args.length * size
fmt = self.format_matrix[args.bitlen][0]
data = []
if args.lang != "bytearray":
for addr in range(start_addr, end_addr, size):
value = struct.unpack(fmt, gef.memory.read(addr, size))[0]
data += [value]
sdata = ", ".join(map(hex, data))
else:
sdata = ""
if args.lang == "bytearray":
data = gef.memory.read(start_addr, args.length)
preview = str(data[0:self["max_size_preview"]])
out = f"Saved data {preview}... in '{gef_convenience(data)}'"
elif args.lang == "py":
out = f"buf = [{sdata}]"
elif args.lang == "c":
c_type = self.format_matrix[args.bitlen][1]
out = f"unsigned {c_type} buf[{args.length}] = {{{sdata}}};"
elif args.lang == "js":
out = f"var buf = [{sdata}]"
elif args.lang == "asm":
asm_type = self.format_matrix[args.bitlen][2]
out = f"buf {asm_type} {sdata}"
elif args.lang == "hex":
out = gef.memory.read(start_addr, end_addr-start_addr).hex()
else:
raise ValueError(f"Invalid format: {args.lang}")
if args.clip:
if copy_to_clipboard(gef_pybytes(out)):
info("Copied to clipboard")
else:
warn("There's a problem while copying")
gef_print(out)
return
@register
class PieCommand(GenericCommand):
"""PIE breakpoint support."""
_cmdline_ = "pie"
_syntax_ = f"{_cmdline_} (breakpoint|info|delete|run|attach|remote)"
def __init__(self) -> None:
super().__init__(prefix=True)
return
def do_invoke(self, argv: list[str]) -> None:
if not argv:
self.usage()
return
@register
class PieBreakpointCommand(GenericCommand):
"""Set a PIE breakpoint at an offset from the target binaries base address."""
_cmdline_ = "pie breakpoint"
_syntax_ = f"{_cmdline_} OFFSET"
@parse_arguments({"offset": ""}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not args.offset:
self.usage()
return
addr = parse_address(args.offset)
self.set_pie_breakpoint(lambda base: f"b *{base + addr}", addr)
# When the process is already on, set real breakpoints immediately
if is_alive():
vmmap = gef.memory.maps
base_address = [x.page_start for x in vmmap if x.path == get_filepath()][0]
for bp_ins in gef.session.pie_breakpoints.values():
bp_ins.instantiate(base_address)
return
@staticmethod
def set_pie_breakpoint(set_func: Callable[[int], str], addr: int) -> None:
gef.session.pie_breakpoints[gef.session.pie_counter] = PieVirtualBreakpoint(set_func, gef.session.pie_counter, addr)
gef.session.pie_counter += 1
return
@register
class PieInfoCommand(GenericCommand):
"""Display breakpoint info."""
_cmdline_ = "pie info"
_syntax_ = f"{_cmdline_} BREAKPOINT"
@parse_arguments({"breakpoints": [-1,]}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if args.breakpoints[0] == -1:
# No breakpoint info needed
bps = gef.session.pie_breakpoints.values()
else:
bps = [gef.session.pie_breakpoints[x]
for x in args.breakpoints
if x in gef.session.pie_breakpoints]
lines = [f"{'VNum':6s} {'Num':6s} {'Addr':18s}"]
lines += [
f"{x.vbp_num:6d} {str(x.bp_num) if x.bp_num else 'N/A':6s} {x.addr:18s}" for x in bps
]
gef_print("\n".join(lines))
return
@register
class PieDeleteCommand(GenericCommand):
"""Delete a PIE breakpoint."""
_cmdline_ = "pie delete"
_syntax_ = f"{_cmdline_} [BREAKPOINT]"
@parse_arguments({"breakpoints": [-1,]}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
global gef
args : argparse.Namespace = kwargs["arguments"]
if args.breakpoints[0] == -1:
# no arg, delete all
to_delete = list(gef.session.pie_breakpoints.values())
self.delete_bp(to_delete)
else:
self.delete_bp([gef.session.pie_breakpoints[x]
for x in args.breakpoints
if x in gef.session.pie_breakpoints])
return
@staticmethod
def delete_bp(breakpoints: list[PieVirtualBreakpoint]) -> None:
global gef
for bp in breakpoints:
# delete current real breakpoints if exists
if bp.bp_num:
gdb.execute(f"delete {bp.bp_num}")
# delete virtual breakpoints
del gef.session.pie_breakpoints[bp.vbp_num]
return
@register
class PieRunCommand(GenericCommand):
"""Run process with PIE breakpoint support."""
_cmdline_ = "pie run"
_syntax_ = _cmdline_
def do_invoke(self, argv: list[str]) -> None:
global gef
fpath = get_filepath()
if not fpath:
warn("No executable to debug, use `file` to load a binary")
return
if not os.access(fpath, os.X_OK):
warn(f"The file '{fpath}' is not executable.")
return
if is_alive():
warn("gdb is already running. Restart process.")
# get base address
gdb.execute("set stop-on-solib-events 1")
hide_context()
gdb.execute(f"run {' '.join(argv)}")
unhide_context()
gdb.execute("set stop-on-solib-events 0")
vmmap = gef.memory.maps
base_address = [x.page_start for x in vmmap if x.path == get_filepath()][0]
info(f"base address {hex(base_address)}")
# modify all breakpoints
for bp_ins in gef.session.pie_breakpoints.values():
bp_ins.instantiate(base_address)
try:
gdb.execute("continue")
except gdb.error as e:
err(str(e))
gdb.execute("kill")
return
@register
class PieAttachCommand(GenericCommand):
"""Do attach with PIE breakpoint support."""
_cmdline_ = "pie attach"
_syntax_ = f"{_cmdline_} PID"
def do_invoke(self, argv: list[str]) -> None:
try:
gdb.execute(f"attach {' '.join(argv)}", to_string=True)
except gdb.error as e:
err(str(e))
return
# after attach, we are stopped so that we can
# get base address to modify our breakpoint
vmmap = gef.memory.maps
base_address = [x.page_start for x in vmmap if x.path == get_filepath()][0]
for bp_ins in gef.session.pie_breakpoints.values():
bp_ins.instantiate(base_address)
gdb.execute("context")
return
@register
class PieRemoteCommand(GenericCommand):
"""Attach to a remote connection with PIE breakpoint support."""
_cmdline_ = "pie remote"
_syntax_ = f"{_cmdline_} REMOTE"
def do_invoke(self, argv: list[str]) -> None:
try:
gdb.execute(f"gef-remote {' '.join(argv)}")
except gdb.error as e:
err(str(e))
return
# after remote attach, we are stopped so that we can
# get base address to modify our breakpoint
vmmap = gef.memory.maps
base_address = [x.page_start for x in vmmap if x.realpath == get_filepath()][0]
for bp_ins in gef.session.pie_breakpoints.values():
bp_ins.instantiate(base_address)
gdb.execute("context")
return
@register
class SmartEvalCommand(GenericCommand):
"""SmartEval: Smart eval (vague approach to mimic WinDBG `?`)."""
_cmdline_ = "$"
_syntax_ = f"{_cmdline_} EXPR\n{_cmdline_} ADDRESS1 ADDRESS2"
_example_ = (f"\n{_cmdline_} $pc+1"
f"\n{_cmdline_} 0x00007ffff7a10000 0x00007ffff7bce000")
def do_invoke(self, argv: list[str]) -> None:
argc = len(argv)
if argc == 1:
self.evaluate(argv)
return
if argc == 2:
self.distance(argv)
return
def evaluate(self, expr: list[str]) -> None:
def show_as_int(i: int) -> None:
off = gef.arch.ptrsize*8
def comp2_x(x: Any) -> str: return f"{(x + (1 << off)) % (1 << off):x}"
def comp2_b(x: Any) -> str: return f"{(x + (1 << off)) % (1 << off):b}"
try:
s_i = comp2_x(res)
s_i = s_i.rjust(len(s_i)+1, "0") if len(s_i)%2 else s_i
gef_print(f"{i:d}")
gef_print("0x" + comp2_x(res))
gef_print("0b" + comp2_b(res))
gef_print(f"{binascii.unhexlify(s_i)}")
gef_print(f"{binascii.unhexlify(s_i)[::-1]}")
except Exception:
pass
return
parsed_expr = []
for xp in expr:
try:
xp = gdb.parse_and_eval(xp)
xp = int(xp)
parsed_expr.append(f"{xp:d}")
except gdb.error:
parsed_expr.append(str(xp))
try:
res = eval(" ".join(parsed_expr))
if isinstance(res, int):
show_as_int(res)
else:
gef_print(f"{res}")
except SyntaxError:
gef_print(" ".join(parsed_expr))
return
def distance(self, args: list[str]) -> None:
try:
x = int(args[0], 16) if is_hex(args[0]) else int(args[0])
y = int(args[1], 16) if is_hex(args[1]) else int(args[1])
gef_print(f"{abs(x - y)}")
except ValueError:
warn(f"Distance requires 2 numbers: {self._cmdline_} 0 0xffff")
return
@register
class CanaryCommand(GenericCommand):
"""Shows the canary value of the current process."""
_cmdline_ = "canary"
_syntax_ = _cmdline_
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
self.dont_repeat()
fname = get_filepath()
assert fname
has_canary = Elf(fname).checksec["Canary"]
if not has_canary:
warn("This binary was not compiled with SSP.")
return
res = gef.session.canary
if not res:
err("Failed to get the canary")
return
canary, location = res
info(f"The canary of process {gef.session.pid} is at {location:#x}, value is {canary:#x}")
return
@register
class ProcessStatusCommand(GenericCommand):
"""Extends the info given by GDB `info proc`, by giving an exhaustive description of the
process status (file descriptors, ancestor, descendants, etc.)."""
_cmdline_ = "process-status"
_syntax_ = _cmdline_
_aliases_ = ["status", ]
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_NONE)
return
@only_if_gdb_running
@only_if_gdb_target_local
def do_invoke(self, argv: list[str]) -> None:
self.show_info_proc()
self.show_ancestor()
self.show_descendants()
self.show_fds()
self.show_connections()
return
def get_state_of(self, pid: int) -> dict[str, str]:
res = {}
with open(f"/proc/{pid}/status", "r") as f:
file = f.readlines()
for line in file:
key, value = line.split(":", 1)
res[key.strip()] = value.strip()
return res
def get_cmdline_of(self, pid: int) -> str:
with open(f"/proc/{pid}/cmdline", "r") as f:
return f.read().replace("\x00", "\x20").strip()
def get_process_path_of(self, pid: int) -> str:
return os.readlink(f"/proc/{pid}/exe")
def get_children_pids(self, pid: int) -> list[int]:
cmd = [gef.session.constants["ps"], "-o", "pid", "--ppid", f"{pid}", "--noheaders"]
try:
return [int(x) for x in gef_execute_external(cmd, as_list=True)]
except Exception:
return []
def show_info_proc(self) -> None:
info("Process Information")
pid = gef.session.pid
cmdline = self.get_cmdline_of(pid)
gef_print(f"\tPID {RIGHT_ARROW} {pid}",
f"\tExecutable {RIGHT_ARROW} {self.get_process_path_of(pid)}",
f"\tCommand line {RIGHT_ARROW} '{cmdline}'", sep="\n")
return
def show_ancestor(self) -> None:
info("Parent Process Information")
ppid = int(self.get_state_of(gef.session.pid)["PPid"])
state = self.get_state_of(ppid)
cmdline = self.get_cmdline_of(ppid)
gef_print(f"\tParent PID {RIGHT_ARROW} {state['Pid']}",
f"\tCommand line {RIGHT_ARROW} '{cmdline}'", sep="\n")
return
def show_descendants(self) -> None:
info("Children Process Information")
children = self.get_children_pids(gef.session.pid)
if not children:
gef_print("\tNo child process")
return
for child_pid in children:
state = self.get_state_of(child_pid)
pid = int(state["Pid"])
gef_print(f"\tPID {RIGHT_ARROW} {pid} (Name: '{self.get_process_path_of(pid)}',"
f" CmdLine: '{self.get_cmdline_of(pid)}')")
return
def show_fds(self) -> None:
pid = gef.session.pid
path = f"/proc/{pid:d}/fd"
info("File Descriptors:")
items = os.listdir(path)
if not items:
gef_print("\tNo FD opened")
return
for fname in items:
fullpath = os.path.join(path, fname)
if os.path.islink(fullpath):
gef_print(f"\t{fullpath} {RIGHT_ARROW} {os.readlink(fullpath)}")
return
def list_sockets(self, pid: int) -> list[int]:
sockets = []
path = f"/proc/{pid:d}/fd"
items = os.listdir(path)
for fname in items:
fullpath = os.path.join(path, fname)
if os.path.islink(fullpath) and os.readlink(fullpath).startswith("socket:"):
p = os.readlink(fullpath).replace("socket:", "")[1:-1]
sockets.append(int(p))
return sockets
def parse_ip_port(self, addr: str) -> tuple[str, int]:
ip, port = addr.split(":")
return socket.inet_ntoa(struct.pack(" None:
# https://github.com/torvalds/linux/blob/v4.7/include/net/tcp_states.h#L16
tcp_states_str = {
0x01: "TCP_ESTABLISHED",
0x02: "TCP_SYN_SENT",
0x03: "TCP_SYN_RECV",
0x04: "TCP_FIN_WAIT1",
0x05: "TCP_FIN_WAIT2",
0x06: "TCP_TIME_WAIT",
0x07: "TCP_CLOSE",
0x08: "TCP_CLOSE_WAIT",
0x09: "TCP_LAST_ACK",
0x0A: "TCP_LISTEN",
0x0B: "TCP_CLOSING",
0x0C: "TCP_NEW_SYN_RECV",
}
udp_states_str = {
0x07: "UDP_LISTEN",
}
info("Network Connections")
pid = gef.session.pid
sockets = self.list_sockets(pid)
if not sockets:
gef_print("\tNo open connections")
return
entries = dict()
with open(f"/proc/{pid:d}/net/tcp", "r") as tcp:
entries["TCP"] = [x.split() for x in tcp.readlines()[1:]]
with open(f"/proc/{pid:d}/net/udp", "r") as udp:
entries["UDP"] = [x.split() for x in udp.readlines()[1:]]
for proto in entries:
for entry in entries[proto]:
local, remote, state = entry[1:4]
inode = int(entry[9])
if inode in sockets:
local = self.parse_ip_port(local)
remote = self.parse_ip_port(remote)
state = int(state, 16)
state_str = tcp_states_str[state] if proto == "TCP" else udp_states_str[state]
gef_print(f"\t{local[0]}:{local[1]} {RIGHT_ARROW} {remote[0]}:{remote[1]} ({state_str})")
return
@register
class GefThemeCommand(GenericCommand):
"""Customize GEF appearance."""
_cmdline_ = "theme"
_syntax_ = f"{_cmdline_} [KEY [VALUE]]"
_example_ = (f"{_cmdline_} address_stack green")
def __init__(self) -> None:
super().__init__(self._cmdline_)
self["context_title_line"] = ("gray", "Color of the borders in context window")
self["context_title_message"] = ("cyan", "Color of the title in context window")
self["default_title_line"] = ("gray", "Default color of borders")
self["default_title_message"] = ("cyan", "Default color of title")
self["table_heading"] = ("blue", "Color of the column headings to tables (e.g. vmmap)")
self["old_context"] = ("gray", "Color to use to show things such as code that is not immediately relevant")
self["disassemble_current_instruction"] = ("green", "Color to use to highlight the current $pc when disassembling")
self["dereference_string"] = ("yellow", "Color of dereferenced string")
self["dereference_code"] = ("gray", "Color of dereferenced code")
self["dereference_base_address"] = ("cyan", "Color of dereferenced address")
self["dereference_register_value"] = ("bold blue", "Color of dereferenced register")
self["registers_register_name"] = ("blue", "Color of the register name in the register window")
self["registers_value_changed"] = ("bold red", "Color of the changed register in the register window")
self["address_stack"] = ("pink", "Color to use when a stack address is found")
self["address_heap"] = ("green", "Color to use when a heap address is found")
self["address_code"] = ("red", "Color to use when a code address is found")
self["source_current_line"] = ("green", "Color to use for the current code line in the source window")
return
def do_invoke(self, args: list[str]) -> None:
self.dont_repeat()
argc = len(args)
if argc == 0:
for key in self.settings:
setting = self[key]
value = Color.colorify(setting, setting)
gef_print(f"{key:40s}: {value}")
return
setting_name = args[0]
if setting_name not in self:
err("Invalid key")
return
if argc == 1:
value = self[setting_name]
gef_print(f"{setting_name:40s}: {Color.colorify(value, value)}")
return
colors = (color for color in args[1:] if color in Color.colors)
self[setting_name] = " ".join(colors) # type: ignore // this is valid since we overwrote __setitem__()
class ExternalStructureManager:
class Structure:
def __init__(self, manager: "ExternalStructureManager", mod_path: pathlib.Path, struct_name: str) -> None:
self.manager = manager
self.module_path = mod_path
self.name = struct_name
self.class_type = self.__get_structure_class()
# if the symbol points to a class factory method and not a class
if not hasattr(self.class_type, "_fields_") and callable(self.class_type):
self.class_type = self.class_type(gef)
return
def __str__(self) -> str:
return self.name
def pprint(self) -> None:
res: list[str] = []
for _name, _type in self.class_type._fields_: # type: ignore
size = ctypes.sizeof(_type)
name = Color.colorify(_name, gef.config["pcustom.structure_name"])
type = Color.colorify(_type.__name__, gef.config["pcustom.structure_type"])
size = Color.colorify(hex(size), gef.config["pcustom.structure_size"])
offset = Color.boldify(f"{getattr(self.class_type, _name).offset:04x}")
res.append(f"{offset} {name:32s} {type:16s} /* size={size} */")
gef_print("\n".join(res))
return
def __get_structure_class(self) -> Type[ctypes.Structure]:
"""Returns a tuple of (class, instance) if modname!classname exists"""
fpath = self.module_path
spec = importlib.util.spec_from_file_location(fpath.stem, fpath)
assert spec and spec.loader, "Failed to determine module specification"
module = importlib.util.module_from_spec(spec)
sys.modules[fpath.stem] = module
spec.loader.exec_module(module)
_class = getattr(module, self.name)
return _class
def apply_at(self, address: int, max_depth: int, depth: int = 0) -> None:
"""Apply (recursively if possible) the structure format to the given address."""
if depth >= max_depth:
warn("maximum recursion level reached")
return
# read the data at the specified address
assert isinstance(self.class_type, type)
_structure = self.class_type()
_sizeof_structure = ctypes.sizeof(_structure)
try:
data = gef.memory.read(address, _sizeof_structure)
except gdb.MemoryError:
err(f"{' ' * depth}Cannot read memory {address:#x}")
return
# deserialize the data
length = min(len(data), _sizeof_structure)
ctypes.memmove(ctypes.addressof(_structure), data, length)
# pretty print all the fields (and call recursively if possible)
ptrsize = gef.arch.ptrsize
unpack = u32 if ptrsize == 4 else u64
for field in _structure._fields_:
assert len(field) == 2
_name, _type = field
_value = getattr(_structure, _name)
_offset = getattr(self.class_type, _name).offset
if ((ptrsize == 4 and _type is ctypes.c_uint32)
or (ptrsize == 8 and _type is ctypes.c_uint64)
or (ptrsize == ctypes.sizeof(ctypes.c_void_p) and _type is ctypes.c_void_p)):
# try to dereference pointers
_value = RIGHT_ARROW.join(dereference_from(_value))
line = f"{' ' * depth}"
line += f"{address:#x}+{_offset:#04x} {_name} : ".ljust(40)
line += f"{_value} ({_type.__name__})"
parsed_value = self.__get_ctypes_value(_structure, _name, _value)
if parsed_value:
line += f"{RIGHT_ARROW} {parsed_value}"
gef_print(line)
if issubclass(_type, ctypes.Structure):
self.apply_at(address + _offset, max_depth, depth + 1)
elif _type.__name__.startswith("LP_"):
# Pointer to a structure of a different type
__sub_type_name = _type.__name__.lstrip("LP_")
result = self.manager.find(__sub_type_name)
if result:
_, __structure = result
__address = unpack(gef.memory.read(address + _offset, ptrsize))
__structure.apply_at(__address, max_depth, depth + 1)
return
def __get_ctypes_value(self, struct, item, value) -> str:
if not hasattr(struct, "_values_"): return ""
default = ""
for name, values in struct._values_:
if name != item: continue
if callable(values):
return str(values(value))
try:
for val, desc in values:
if value == val: return desc
if val is None: default = desc
except Exception as e:
err(f"Error parsing '{name}': {e}")
return default
class Module(dict):
def __init__(self, manager: "ExternalStructureManager", path: pathlib.Path) -> None:
self.manager = manager
self.path = path
self.name = path.stem
self.raw = self.__load()
for entry in self:
structure = ExternalStructureManager.Structure(manager, self.path, entry)
self[structure.name] = structure
return
def __load(self) -> ModuleType:
"""Load a custom module, and return it."""
fpath = self.path
spec = importlib.util.spec_from_file_location(fpath.stem, fpath)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[fpath.stem] = module
spec.loader.exec_module(module)
return module
def __str__(self) -> str:
return self.name
def __iter__(self) -> Generator[str, None, None]:
_invalid = {"BigEndianStructure", "LittleEndianStructure", "Structure"}
for x in dir(self.raw):
if x in _invalid: continue
_attr = getattr(self.raw, x)
# if it's a ctypes.Structure class, add it
if inspect.isclass(_attr) and issubclass(_attr, ctypes.Structure):
yield x
continue
# also accept class factory functions
if callable(_attr) and _attr.__module__ == self.name and x.endswith("_t"):
yield x
continue
return
class Modules(dict):
def __init__(self, manager: "ExternalStructureManager") -> None:
self.manager: "ExternalStructureManager" = manager
self.root: pathlib.Path = manager.path
for entry in self.root.iterdir():
if not entry.is_file(): continue
if entry.suffix != ".py": continue
if entry.name == "__init__.py": continue
module = ExternalStructureManager.Module(manager, entry)
self[module.name] = module
return
def __contains__(self, structure_name: str) -> bool:
"""Return True if the structure name is found in any of the modules"""
for module in self.values():
if structure_name in module:
return True
return False
def __init__(self) -> None:
self.clear_caches()
return
def clear_caches(self) -> None:
self._path = None
self._modules = None
return
@property
def modules(self) -> "ExternalStructureManager.Modules":
if not self._modules:
self._modules = ExternalStructureManager.Modules(self)
return self._modules
@property
def path(self) -> pathlib.Path:
if not self._path:
self._path = gef.config["pcustom.struct_path"].expanduser().absolute()
return self._path
@property
def structures(self) -> Generator[tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"], None, None]:
for module in self.modules.values():
for structure in module.values():
yield module, structure
return
@lru_cache()
def find(self, structure_name: str) -> tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"] | None:
"""Return the module and structure for the given structure name; `None` if the structure name was not found."""
for module in self.modules.values():
if structure_name in module:
return module, module[structure_name]
return None
@register
class PCustomCommand(GenericCommand):
"""Dump user defined structure.
This command attempts to reproduce WinDBG awesome `dt` command for GDB and allows
to apply structures (from symbols or custom) directly to an address.
Custom structures can be defined in pure Python using ctypes, and should be stored
in a specific directory, whose path must be stored in the `pcustom.struct_path`
configuration setting."""
_cmdline_ = "pcustom"
_syntax_ = f"{_cmdline_} [list|edit |show ]| 0xADDRESS]"
def __init__(self) -> None:
global gef
super().__init__(prefix=True)
self["max_depth"] = (4, "Maximum level of recursion supported")
self["structure_name"] = ("bold blue", "Color of the structure name")
self["structure_type"] = ("bold red", "Color of the attribute type")
self["structure_size"] = ("green", "Color of the attribute size")
gef.config[f"{self._cmdline_}.struct_path"] = GefSetting( gef.config["gef.tempdir"] / "structs", pathlib.Path,
"Path to store/load the structure ctypes files",
hooks={"on_write": [GefSetting.create_folder_tree,]})
return
@parse_arguments({"type": "", "address": ""}, {})
def do_invoke(self, *_: Any, **kwargs: dict[str, Any]) -> None:
args = cast(argparse.Namespace, kwargs["arguments"])
if not args.type:
gdb.execute("pcustom list")
return
structname = self.explode_type(args.type)[1]
if not args.address:
gdb.execute(f"pcustom show {structname}")
return
if not is_alive():
err("Session is not active")
return
manager = ExternalStructureManager()
address = parse_address(args.address)
result = manager.find(structname)
if not result:
err(f"No structure named '{structname}' found")
return
structure = result[1]
structure.apply_at(address, self["max_depth"])
return
def explode_type(self, arg: str) -> tuple[str, str]:
modname, structname = arg.split(":", 1) if ":" in arg else (arg, arg)
structname = structname.split(".", 1)[0] if "." in structname else structname
return modname, structname
@register
class PCustomListCommand(PCustomCommand):
"""PCustom: list available structures"""
_cmdline_ = "pcustom list"
_syntax_ = f"{_cmdline_}"
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, _: list[str]) -> None:
"""Dump the list of all the structures and their respective."""
manager = ExternalStructureManager()
info(f"Listing custom structures from '{manager.path}'")
struct_color = gef.config["pcustom.structure_type"]
filename_color = gef.config["pcustom.structure_name"]
for module in manager.modules.values():
__modules = ", ".join([Color.colorify(str(structure), struct_color) for structure in module.values()])
__filename = Color.colorify(str(module.path), filename_color)
gef_print(f"{RIGHT_ARROW} {__filename} ({__modules})")
return
@register
class PCustomShowCommand(PCustomCommand):
"""PCustom: show the content of a given structure"""
_cmdline_ = "pcustom show"
_syntax_ = f"{_cmdline_} StructureName"
_aliases_ = ["pcustom create", "pcustom update"]
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, argv: list[str]) -> None:
if len(argv) == 0:
self.usage()
return
_, structname = self.explode_type(argv[0])
manager = ExternalStructureManager()
result = manager.find(structname)
if result:
_, structure = result
structure.pprint()
else:
err(f"No structure named '{structname}' found")
return
@register
class PCustomEditCommand(PCustomCommand):
"""PCustom: edit the content of a given structure"""
_cmdline_ = "pcustom edit"
_syntax_ = f"{_cmdline_} StructureName"
__aliases__ = ["pcustom create", "pcustom new", "pcustom update"]
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, argv: list[str]) -> None:
if len(argv) == 0:
self.usage()
return
modname, structname = self.explode_type(argv[0])
self.__create_or_edit_structure(modname, structname)
return
def __create_or_edit_structure(self, mod_name: str, struct_name: str) -> int:
path = gef.config["pcustom.struct_path"].expanduser() / f"{mod_name}.py"
if path.is_file():
info(f"Editing '{path}'")
else:
ok(f"Creating '{path}' from template")
self.__create_template(struct_name, path)
cmd = (os.getenv("EDITOR") or "nano").split()
cmd.append(str(path.absolute()))
return subprocess.call(cmd)
def __create_template(self, structname: str, fpath: pathlib.Path) -> None:
template = f"""from ctypes import *
class {structname}(Structure):
_fields_ = []
_values_ = []
"""
with fpath.open("w") as f:
f.write(template)
return
@register
class ChangeFdCommand(GenericCommand):
"""ChangeFdCommand: redirect file descriptor during runtime."""
_cmdline_ = "hijack-fd"
_syntax_ = f"{_cmdline_} FD_NUM NEW_OUTPUT"
_example_ = f"{_cmdline_} 2 /tmp/stderr_output.txt"
@only_if_gdb_running
@only_if_gdb_target_local
def do_invoke(self, argv: list[str]) -> None:
if len(argv) != 2:
self.usage()
return
if not os.access(f"/proc/{gef.session.pid:d}/fd/{argv[0]}", os.R_OK):
self.usage()
return
old_fd = int(argv[0])
new_output = argv[1]
if ":" in new_output:
address = socket.gethostbyname(new_output.split(":")[0])
port = int(new_output.split(":")[1])
AF_INET = 2
SOCK_STREAM = 1
res = gdb.execute(f"call (int)socket({AF_INET}, {SOCK_STREAM}, 0)", to_string=True) or ""
new_fd = self.get_fd_from_result(res)
# fill in memory with sockaddr_in struct contents
# we will do this in the stack, since connect() wants a pointer to a struct
vmmap = gef.memory.maps
stack_addr = [entry.page_start for entry in vmmap if entry.path == "[stack]"][0]
original_contents = gef.memory.read(stack_addr, 8)
gef.memory.write(stack_addr, b"\x02\x00", 2)
gef.memory.write(stack_addr + 0x2, struct.pack(" int:
# Output example: $1 = 3
res = gdb.execute(f"p/d {int(res.split()[2], 0)}", to_string=True) or ""
return int(res.split()[2], 0)
@register
class ScanSectionCommand(GenericCommand):
"""Search for addresses that are located in a memory mapping (haystack) that belonging
to another (needle)."""
_cmdline_ = "scan"
_syntax_ = f"{_cmdline_} HAYSTACK NEEDLE"
_aliases_ = ["lookup",]
_example_ = f"\n{_cmdline_} stack libc"
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if len(argv) != 2:
self.usage()
return
haystack = argv[0]
needle = argv[1]
info(f"Searching for addresses in '{Color.yellowify(haystack)}' "
f"that point to '{Color.yellowify(needle)}'")
fpath = get_filepath() or ""
if haystack == "binary":
haystack = fpath
if needle == "binary":
needle = fpath
needle_sections = []
haystack_sections = []
if "0x" in haystack:
start, end = parse_string_range(haystack)
haystack_sections.append((start, end, ""))
if "0x" in needle:
start, end = parse_string_range(needle)
needle_sections.append((start, end))
for sect in gef.memory.maps:
if sect.path is None:
continue
if haystack in sect.path:
haystack_sections.append((sect.page_start, sect.page_end, os.path.basename(sect.path)))
if needle in sect.path:
needle_sections.append((sect.page_start, sect.page_end))
step = gef.arch.ptrsize
unpack = u32 if step == 4 else u64
dereference_cmd = gef.gdb.commands["dereference"]
assert isinstance(dereference_cmd, DereferenceCommand)
for hstart, hend, hname in haystack_sections:
try:
mem = gef.memory.read(hstart, hend - hstart)
except gdb.MemoryError:
continue
for i in range(0, len(mem), step):
target = unpack(mem[i:i+step])
for nstart, nend in needle_sections:
if target >= nstart and target < nend:
deref = dereference_cmd.pprint_dereferenced(hstart, int(i / step))
if hname != "":
name = Color.colorify(hname, "yellow")
gef_print(f"{name}: {deref}")
else:
gef_print(f" {deref}")
return
@register
class SearchPatternCommand(GenericCommand):
"""SearchPatternCommand: search a pattern in memory. If given an hex value (starting with 0x)
the command will also try to look for upwards cross-references to this address."""
_cmdline_ = "search-pattern"
_syntax_ = f"{_cmdline_} PATTERN [little|big] [section]"
_aliases_ = ["grep", "xref"]
_example_ = [f"{_cmdline_} AAAAAAAA",
f"{_cmdline_} 0x555555554000 little stack",
f"{_cmdline_} AAAA 0x600000-0x601000",
f"{_cmdline_} --regex 0x401000 0x401500 ([\\\\x20-\\\\x7E]{{2,}})(?=\\\\x00) <-- It matches null-end-printable(from x20-x7e) C strings (min size 2 bytes)"]
def __init__(self) -> None:
super().__init__()
self["max_size_preview"] = (10, "max size preview of bytes")
self["nr_pages_chunk"] = (0x400, "number of pages readed for each memory read chunk")
return
def print_section(self, section: Section) -> None:
title = "In "
if section.path:
title += f"'{Color.blueify(section.path)}'"
title += f"({section.page_start:#x}-{section.page_end:#x})"
title += f", permission={section.permission}"
ok(title)
return
def print_loc(self, loc: tuple[int, int, str]) -> None:
gef_print(f""" {loc[0]:#x} - {loc[1]:#x} {RIGHT_ARROW} "{Color.pinkify(loc[2])}" """)
return
def search_pattern_by_address(self, pattern: str, start_address: int, end_address: int) -> list[tuple[int, int, str]]:
"""Search a pattern within a range defined by arguments."""
_pattern = gef_pybytes(pattern)
step = self["nr_pages_chunk"] * gef.session.pagesize
locations = []
for chunk_addr in range(start_address, end_address, step):
if chunk_addr + step > end_address:
chunk_size = end_address - chunk_addr
else:
chunk_size = step
try:
mem = gef.memory.read(chunk_addr, chunk_size)
except gdb.MemoryError:
return []
for match in re.finditer(_pattern, mem):
start = chunk_addr + match.start()
ustr = ""
if is_ascii_string(start):
ustr = gef.memory.read_ascii_string(start) or ""
end = start + len(ustr)
else:
ustr = gef_pystring(_pattern) + "[...]"
end = start + len(_pattern)
locations.append((start, end, ustr))
del mem
return locations
def search_binpattern_by_address(self, binpattern: bytes, start_address: int, end_address: int) -> list[tuple[int, int, str]]:
"""Search a binary pattern within a range defined by arguments."""
step = self["nr_pages_chunk"] * gef.session.pagesize
locations = []
for chunk_addr in range(start_address, end_address, step):
if chunk_addr + step > end_address:
chunk_size = end_address - chunk_addr
else:
chunk_size = step
try:
mem = gef.memory.read(chunk_addr, chunk_size)
except gdb.MemoryError:
return []
preview_size = self["max_size_preview"]
preview = ""
for match in re.finditer(binpattern, mem):
start = chunk_addr + match.start()
preview = str(mem[slice(*match.span())][0:preview_size]) + "..."
size_match = match.span()[1] - match.span()[0]
if size_match > 0:
size_match -= 1
end = start + size_match
locations.append((start, end, preview))
del mem
return locations
def search_pattern(self, pattern: str, section_name: str) -> None:
"""Search a pattern within the whole userland memory."""
for section in gef.memory.maps:
if not section.permission & Permission.READ: continue
if section.path == "[vvar]": continue
if section_name not in section.path: continue
start = section.page_start
end = section.page_end - 1
old_section = None
for loc in self.search_pattern_by_address(pattern, start, end):
addr_loc_start = lookup_address(loc[0])
if addr_loc_start and addr_loc_start.section:
if old_section != addr_loc_start.section:
self.print_section(addr_loc_start.section)
old_section = addr_loc_start.section
self.print_loc(loc)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
argc = len(argv)
if argc < 1:
self.usage()
return
if argc > 3 and argv[0].startswith("--regex"):
pattern = ' '.join(argv[3:])
pattern = ast.literal_eval("b'" + pattern + "'")
addr_start = parse_address(argv[1])
addr_end = parse_address(argv[2])
for loc in self.search_binpattern_by_address(pattern, addr_start, addr_end):
self.print_loc(loc)
return
pattern = argv[0]
endian = gef.arch.endianness
if argc >= 2:
if argv[1].lower() == "big": endian = Endianness.BIG_ENDIAN
elif argv[1].lower() == "little": endian = Endianness.LITTLE_ENDIAN
if is_hex(pattern):
if endian == Endianness.BIG_ENDIAN:
pattern = "".join(["\\x" + pattern[i:i + 2] for i in range(2, len(pattern), 2)])
else:
pattern = "".join(["\\x" + pattern[i:i + 2] for i in range(len(pattern) - 2, 0, -2)])
if argc == 3:
info(f"Searching '{Color.yellowify(pattern)}' in {argv[2]}")
if "0x" in argv[2]:
start, end = parse_string_range(argv[2])
loc = lookup_address(start)
if loc.valid:
self.print_section(loc.section)
for loc in self.search_pattern_by_address(pattern, start, end):
self.print_loc(loc)
else:
section_name = argv[2]
if section_name == "binary":
section_name = get_filepath() or ""
self.search_pattern(pattern, section_name)
else:
info(f"Searching '{Color.yellowify(pattern)}' in memory")
self.search_pattern(pattern, "")
return
@register
class FlagsCommand(GenericCommand):
"""Edit flags in a human friendly way."""
_cmdline_ = "edit-flags"
_syntax_ = f"{_cmdline_} [(+|-|~)FLAGNAME ...]"
_aliases_ = ["flags",]
_example_ = (f"\n{_cmdline_}"
f"\n{_cmdline_} +zero # sets ZERO flag")
def do_invoke(self, argv: list[str]) -> None:
if not gef.arch.flag_register:
warn(f"The architecture {gef.arch.arch}:{gef.arch.mode} doesn't have flag register.")
return
for flag in argv:
if len(flag) < 2:
continue
action = flag[0]
name = flag[1:].lower()
if action not in ("+", "-", "~"):
err(f"Invalid action for flag '{flag}'")
continue
if name not in gef.arch.flags_table.values():
err(f"Invalid flag name '{flag[1:]}'")
continue
for off in gef.arch.flags_table:
if gef.arch.flags_table[off] != name:
continue
old_flag = gef.arch.register(gef.arch.flag_register)
if action == "+":
new_flags = old_flag | (1 << off)
elif action == "-":
new_flags = old_flag & ~(1 << off)
else:
new_flags = old_flag ^ (1 << off)
gdb.execute(f"set ({gef.arch.flag_register}) = {new_flags:#x}")
gef_print(gef.arch.flag_register_to_human())
return
@register
class RemoteCommand(GenericCommand):
"""GDB `target remote` command on steroids. This command will use the remote procfs to create
a local copy of the execution environment, including the target binary and its libraries
in the local temporary directory (the value by default is in `gef.config.tempdir`). Additionally, it
will fetch all the /proc/PID/maps and loads all its information. If procfs is not available remotely, the command
will likely fail. You can however still use the limited command provided by GDB `target remote`."""
_cmdline_ = "gef-remote"
_syntax_ = f"{_cmdline_} [OPTIONS] TARGET"
_example_ = [f"{_cmdline_} localhost 1234",
f"{_cmdline_} --pid 6789 localhost 1234",
f"{_cmdline_} --qemu-user --qemu-binary /bin/debugme localhost 4444 "]
def __init__(self) -> None:
super().__init__(prefix=False)
return
@parse_arguments({"host": "", "port": 0}, {"--pid": -1, "--qemu-user": False, "--qemu-binary": ""})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
if gef.session.remote is not None:
err("You already are in remote session. Close it first before opening a new one...")
return
# argument check
args : argparse.Namespace = kwargs["arguments"]
if not args.host or not args.port:
err("Missing parameters")
return
# qemu-user support
qemu_binary: pathlib.Path | None = None
if args.qemu_user:
try:
qemu_binary = pathlib.Path(args.qemu_binary).expanduser().absolute() if args.qemu_binary else gef.session.file
if not qemu_binary or not qemu_binary.exists():
raise FileNotFoundError(f"{qemu_binary} does not exist")
except Exception as e:
err(f"Failed to initialize qemu-user mode, reason: {str(e)}")
return
# Try to establish the remote session, throw on error
# Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which
# calls `is_remote_debug` which checks if `remote_initializing` is True or `.remote` is None
# This prevents some spurious errors being thrown during startup
gef.session.remote_initializing = True
session = GefRemoteSessionManager(args.host, args.port, args.pid, qemu_binary)
dbg(f"[remote] initializing remote session with {session.target} under {session.root}")
if not session.connect(args.pid) or not session.setup():
gef.session.remote = None
gef.session.remote_initializing = False
raise EnvironmentError("Failed to setup remote target")
gef.session.remote_initializing = False
gef.session.remote = session
reset_all_caches()
gdb.execute("context")
return
@register
class SkipiCommand(GenericCommand):
"""Skip N instruction(s) execution"""
_cmdline_ = "skipi"
_syntax_ = (f"{_cmdline_} [LOCATION] [--n NUM_INSTRUCTIONS]"
"\n\tLOCATION\taddress/symbol from where to skip"
"\t--n NUM_INSTRUCTIONS\tSkip the specified number of instructions instead of the default 1.")
_example_ = [f"{_cmdline_}",
f"{_cmdline_} --n 3",
f"{_cmdline_} 0x69696969",
f"{_cmdline_} 0x69696969 --n 6",]
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
@parse_arguments({"address": "$pc"}, {"--n": 1})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
address = parse_address(args.address)
num_instructions = args.n
last_insn = gef_instruction_n(address, num_instructions-1)
total_bytes = (last_insn.address - address) + last_insn.size()
target_addr = address + total_bytes
info(f"skipping {num_instructions} instructions ({total_bytes} bytes) from {address:#x} to {target_addr:#x}")
gdb.execute(f"set $pc = {target_addr:#x}")
return
@register
class StepoverCommand(GenericCommand):
"""Breaks on the instruction immediately following this one. Ex: Step over call instruction"""
_cmdline_ = "stepover"
_syntax_ = (f"{_cmdline_}"
"\n\tBreaks on the instruction immediately following this one. Ex: Step over call instruction.")
_aliases_ = ["so",]
_example_ = [f"{_cmdline_}",]
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, _: list[str]) -> None:
target_addr = gef_next_instruction(parse_address("$pc")).address
JustSilentStopBreakpoint("".join(["*", str(target_addr)]))
gdb.execute("continue")
return
@register
class NopCommand(GenericCommand):
"""Patch the instruction(s) pointed by parameters with NOP. Note: this command is architecture
aware."""
_cmdline_ = "nop"
_syntax_ = (f"{_cmdline_} [LOCATION] [--i ITEMS] [--f] [--n] [--b]"
"\n\tLOCATION\taddress/symbol to patch (by default this command replaces whole instructions)"
"\t--i ITEMS\tnumber of items to insert (default 1)"
"\t--f\tForce patch even when the selected settings could overwrite partial instructions"
"\t--n\tInstead of replacing whole instructions, insert ITEMS nop instructions, no matter how many instructions it overwrites"
"\t--b\tInstead of replacing whole instructions, fill ITEMS bytes with nops")
_example_ = [f"{_cmdline_}",
f"{_cmdline_} $pc+3",
f"{_cmdline_} --i 2 $pc+3",
f"{_cmdline_} --b",
f"{_cmdline_} --b $pc+3",
f"{_cmdline_} --f --b --i 2 $pc+3"
f"{_cmdline_} --n --i 2 $pc+3",]
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
@parse_arguments({"address": "$pc"}, {"--i": 1, "--b": False, "--f": False, "--n": False})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
address = parse_address(args.address)
nop = gef.arch.nop_insn
num_items = int(args.i) or 1
fill_bytes = bool(args.b)
fill_nops = bool(args.n)
force_flag = bool(args.f) or False
if fill_nops and fill_bytes:
err("--b and --n cannot be specified at the same time.")
return
total_bytes = 0
if fill_bytes:
total_bytes = num_items
elif fill_nops:
total_bytes = num_items * len(nop)
else:
try:
last_insn = gef_instruction_n(address, num_items-1)
last_addr = last_insn.address
except Exception as e:
err(f"Cannot patch instruction at {address:#x} reaching unmapped area, reason: {e}")
return
total_bytes = (last_addr - address) + gef_get_instruction_at(last_addr).size()
if len(nop) > total_bytes or total_bytes % len(nop):
warn(f"Patching {total_bytes} bytes at {address:#x} will result in LAST-NOP "
f"(byte nr {total_bytes % len(nop):#x}) broken and may cause a crash or "
"break disassembly.")
if not force_flag:
warn("Use --f (force) to ignore this warning.")
return
target_end_address = address + total_bytes
curr_ins = gef_current_instruction(address)
while curr_ins.address + curr_ins.size() < target_end_address:
if not Address(value=curr_ins.address + 1).valid:
err(f"Cannot patch instruction at {address:#x}: reaching unmapped area")
return
curr_ins = gef_next_instruction(curr_ins.address)
final_ins_end_addr = curr_ins.address + curr_ins.size()
if final_ins_end_addr != target_end_address:
warn(f"Patching {total_bytes} bytes at {address:#x} will result in LAST-INSTRUCTION "
f"({curr_ins.address:#x}) being partial overwritten and may cause a crash or "
"break disassembly.")
if not force_flag:
warn("Use --f (force) to ignore this warning.")
return
nops = bytearray(nop * total_bytes)
end_address = Address(value=address + total_bytes - 1)
if not end_address.valid:
err(f"Cannot patch instruction at {address:#x}: reaching unmapped "
f"area: {end_address:#x}")
return
ok(f"Patching {total_bytes} bytes from {address:#x}")
gef.memory.write(address, nops, total_bytes)
return
@register
class StubCommand(GenericCommand):
"""Stub out the specified function. This function is useful when needing to skip one
function to be called and disrupt your runtime flow (ex. fork)."""
_cmdline_ = "stub"
_syntax_ = (f"{_cmdline_} [--retval RETVAL] [address]"
"\taddress\taddress/symbol to stub out"
"\t--retval RETVAL\tSet the return value")
_example_ = f"{_cmdline_} --retval 0 fork"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
@parse_arguments({"address": ""}, {("-r", "--retval"): 0})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
loc = args.address if args.address else f"*{gef.arch.pc:#x}"
StubBreakpoint(loc, args.retval)
return
@register
class GlibcHeapCommand(GenericCommand):
"""Base command to get information about the Glibc heap structure."""
_cmdline_ = "heap"
_syntax_ = f"{_cmdline_} (chunk|chunks|bins|arenas|set-arena)"
def __init__(self) -> None:
super().__init__(prefix=True)
return
@only_if_gdb_running
def do_invoke(self, _: list[str]) -> None:
self.usage()
return
@register
class GlibcHeapSetArenaCommand(GenericCommand):
"""Set the address of the main_arena or the currently selected arena."""
_cmdline_ = "heap set-arena"
_syntax_ = f"{_cmdline_} [address|&symbol]"
_example_ = f"{_cmdline_} 0x001337001337"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
@parse_arguments({"addr": ""}, {"--reset": False})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
global gef
args: argparse.Namespace = kwargs["arguments"]
if args.reset:
gef.heap.reset_caches()
return
if not args.addr:
ok(f"Current arena set to: '{gef.heap.selected_arena}'")
return
try:
new_arena_address = parse_address(args.addr)
except gdb.error:
err("Invalid symbol for arena")
return
new_arena = GlibcArena( f"*{new_arena_address:#x}")
if new_arena in gef.heap.arenas:
# if entered arena is in arena list then just select it
gef.heap.selected_arena = new_arena
else:
# otherwise set the main arena to the entered arena
gef.heap.main_arena = new_arena
return
@register
class GlibcHeapArenaCommand(GenericCommand):
"""Display information on a heap chunk."""
_cmdline_ = "heap arenas"
_syntax_ = _cmdline_
@only_if_gdb_running
def do_invoke(self, _: list[str]) -> None:
for arena in gef.heap.arenas:
gef_print(str(arena))
return
@register
class GlibcHeapChunkCommand(GenericCommand):
"""Display information on a heap chunk.
See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123."""
_cmdline_ = "heap chunk"
_syntax_ = f"{_cmdline_} [-h] [--allow-unaligned] [--number] address"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({"address": ""}, {"--allow-unaligned": False, "--number": 1})
@only_if_gdb_running
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not args.address:
err("Missing chunk address")
self.usage()
return
addr = parse_address(args.address)
current_chunk = GlibcChunk(addr, allow_unaligned=args.allow_unaligned)
if args.number > 1:
for _i in range(args.number):
if current_chunk.size == 0:
break
gef_print(str(current_chunk))
next_chunk_addr = current_chunk.get_next_chunk_addr()
if not Address(value=next_chunk_addr).valid:
break
next_chunk = current_chunk.get_next_chunk()
if next_chunk is None:
break
current_chunk = next_chunk
else:
gef_print(current_chunk.psprint())
return
class GlibcHeapChunkSummary:
def __init__(self, desc = ""):
self.desc = desc
self.count = 0
self.total_bytes = 0
def process_chunk(self, chunk: GlibcChunk) -> None:
self.count += 1
self.total_bytes += chunk.size
class GlibcHeapArenaSummary:
def __init__(self, resolve_type = False) -> None:
self.resolve_symbol = resolve_type
self.size_distribution = {}
self.flag_distribution = {
"PREV_INUSE": GlibcHeapChunkSummary(),
"IS_MMAPPED": GlibcHeapChunkSummary(),
"NON_MAIN_ARENA": GlibcHeapChunkSummary()
}
def process_chunk(self, chunk: GlibcChunk) -> None:
chunk_type = "" if not self.resolve_symbol else chunk.resolve_type()
per_size_summary = self.size_distribution.get((chunk.size, chunk_type), None)
if per_size_summary is None:
per_size_summary = GlibcHeapChunkSummary(desc=chunk_type)
self.size_distribution[(chunk.size, chunk_type)] = per_size_summary
per_size_summary.process_chunk(chunk)
if chunk.has_p_bit():
self.flag_distribution["PREV_INUSE"].process_chunk(chunk)
if chunk.has_m_bit():
self.flag_distribution["IS_MAPPED"].process_chunk(chunk)
if chunk.has_n_bit():
self.flag_distribution["NON_MAIN_ARENA"].process_chunk(chunk)
def print(self) -> None:
gef_print("== Chunk distribution by size ==")
gef_print(f"{'ChunkBytes':<10s}\t{'Count':<10s}\t{'TotalBytes':15s}\t{'Description':s}")
for chunk_info, chunk_summary in sorted(self.size_distribution.items(), key=lambda x: x[1].total_bytes, reverse=True):
gef_print(f"{chunk_info[0]:<10d}\t{chunk_summary.count:<10d}\t{chunk_summary.total_bytes:<15d}\t{chunk_summary.desc:s}")
gef_print("\n== Chunk distribution by flag ==")
gef_print(f"{'Flag':<15s}\t{'TotalCount':<10s}\t{'TotalBytes':s}")
for chunk_flag, chunk_summary in self.flag_distribution.items():
gef_print(f"{chunk_flag:<15s}\t{chunk_summary.count:<10d}\t{chunk_summary.total_bytes: None:
self.print_arena = print_arena
self.allow_unaligned = allow_unaligned
self.min_size = min_size
self.max_size = max_size
self.remaining_chunk_count = count
self.summary = summary
self.resolve_type = resolve_type
@register
class GlibcHeapChunksCommand(GenericCommand):
"""Display all heap chunks for the current arena. As an optional argument
the base address of a different arena can be passed"""
_cmdline_ = "heap chunks"
_syntax_ = f"{_cmdline_} [-h] [--all] [--allow-unaligned] [--summary] [--min-size MIN_SIZE] [--max-size MAX_SIZE] [--count COUNT] [--resolve] [arena_address]"
_example_ = (f"\n{_cmdline_}"
f"\n{_cmdline_} 0x555555775000")
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
self["peek_nb_byte"] = (16, "Hexdump N first byte(s) inside the chunk data (0 to disable)")
return
@parse_arguments({"arena_address": ""}, {("--all", "-a"): False, "--allow-unaligned": False, "--min-size": 0, "--max-size": 0, ("--count", "-n"): -1, ("--summary", "-s"): False, "--resolve": False})
@only_if_gdb_running
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args = kwargs["arguments"]
ctx = GlibcHeapWalkContext(print_arena=args.all, allow_unaligned=args.allow_unaligned, min_size=args.min_size, max_size=args.max_size, count=args.count, resolve_type=args.resolve, summary=args.summary)
if args.all or not args.arena_address:
for arena in gef.heap.arenas:
self.dump_chunks_arena(arena, ctx)
if not args.all:
return
try:
if not args.arena_address:
return
arena_addr = parse_address(args.arena_address)
arena = GlibcArena(f"*{arena_addr:#x}")
self.dump_chunks_arena(arena, ctx)
except gdb.error as e:
err(f"Invalid arena: {e}\nArena Address: {args.arena_address}")
return
def dump_chunks_arena(self, arena: GlibcArena, ctx: GlibcHeapWalkContext) -> None:
heap_addr = arena.heap_addr(allow_unaligned=ctx.allow_unaligned)
if heap_addr is None:
err("Could not find heap for arena")
return
if ctx.print_arena:
gef_print(str(arena))
if arena.is_main_arena():
heap_end = arena.top + GlibcChunk(arena.top, from_base=True).size
self.dump_chunks_heap(heap_addr, heap_end, arena, ctx)
else:
heap_info_structs = arena.get_heap_info_list() or []
for heap_info in heap_info_structs:
if not self.dump_chunks_heap(heap_info.heap_start, heap_info.heap_end, arena, ctx):
break
return
def dump_chunks_heap(self, start: int, end: int, arena: GlibcArena, ctx: GlibcHeapWalkContext) -> bool:
nb = self["peek_nb_byte"]
chunk_iterator = GlibcChunk(start, from_base=True, allow_unaligned=ctx.allow_unaligned)
heap_summary = GlibcHeapArenaSummary(resolve_type=ctx.resolve_type)
top_printed = False
for chunk in chunk_iterator:
heap_corrupted = chunk.base_address > end
should_process = self.should_process_chunk(chunk, ctx)
if not ctx.summary and chunk.base_address == arena.top:
if should_process:
gef_print(
f"{chunk!s} {LEFT_ARROW} {Color.greenify('top chunk')}")
top_printed = True
break
if heap_corrupted:
err("Corrupted heap, cannot continue.")
return False
if not should_process:
continue
if ctx.remaining_chunk_count == 0:
break
if ctx.summary:
heap_summary.process_chunk(chunk)
else:
line = str(chunk)
if nb:
line += f"\n [{hexdump(gef.memory.read(chunk.data_address, nb), nb, base=chunk.data_address)}]"
gef_print(line)
ctx.remaining_chunk_count -= 1
if not top_printed and ctx.print_arena:
top_chunk = GlibcChunk(arena.top, from_base=True, allow_unaligned=ctx.allow_unaligned)
gef_print(f"{top_chunk!s} {LEFT_ARROW} {Color.greenify('top chunk')}")
if ctx.summary:
heap_summary.print()
return True
def should_process_chunk(self, chunk: GlibcChunk, ctx: GlibcHeapWalkContext) -> bool:
if chunk.size < ctx.min_size:
return False
if 0 < ctx.max_size < chunk.size:
return False
return True
@register
class GlibcHeapBinsCommand(GenericCommand):
"""Display information on the bins on an arena (default: main_arena).
See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123."""
_bin_types_ = ("tcache", "fast", "unsorted", "small", "large")
_cmdline_ = "heap bins"
_syntax_ = f"{_cmdline_} [{'|'.join(_bin_types_)}]"
def __init__(self) -> None:
super().__init__(prefix=True, complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if not argv:
for bin_t in self._bin_types_:
gdb.execute(f"heap bins {bin_t}")
return
bin_t = argv[0]
if bin_t not in self._bin_types_:
self.usage()
return
gdb.execute(f"heap bins {bin_t}")
return
def pprint_bin(self, arena_addr: str, index: int, _type: str = "") -> int:
arena = GlibcArena(arena_addr)
fd, bk = arena.bin(index)
if (fd, bk) == (0x00, 0x00):
warn("Invalid backward and forward bin pointers(fw==bk==NULL)")
return -1
if _type == "tcache":
chunkClass = GlibcTcacheChunk
elif _type == "fast":
chunkClass = GlibcFastChunk
else:
chunkClass = GlibcChunk
nb_chunk = 0
head = chunkClass(bk, from_base=True).fd
if fd == head:
return nb_chunk
ok(f"{_type}bins[{index:d}]: fw={fd:#x}, bk={bk:#x}")
m = []
while fd != head:
chunk = chunkClass(fd, from_base=True)
m.append(f"{RIGHT_ARROW} {chunk!s}")
fd = chunk.fd
nb_chunk += 1
if m:
gef_print(" ".join(m))
return nb_chunk
@register
class GlibcHeapTcachebinsCommand(GenericCommand):
"""Display information on the Tcachebins on an arena (default: main_arena).
See https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc."""
_cmdline_ = "heap bins tcache"
_syntax_ = f"{_cmdline_} [all] [thread_ids...]"
TCACHE_MAX_BINS = 0x40
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
# Determine if we are using libc with tcache built in (2.26+)
if gef.libc.version and gef.libc.version < (2, 26):
info("No Tcache in this version of libc")
return
current_thread = gdb.selected_thread()
if current_thread is None:
err("Couldn't find current thread")
return
# As a nicety, we want to display threads in ascending order by gdb number
threads = sorted(gdb.selected_inferior().threads(), key=lambda t: t.num)
if argv:
if "all" in argv:
tids = [t.num for t in threads]
else:
tids = self.check_thread_ids([int(a) for a in argv])
else:
tids = [current_thread.num]
for thread in threads:
if thread.num not in tids:
continue
thread.switch()
tcache_addr = self.find_tcache()
if tcache_addr == 0:
info(f"Uninitialized tcache for thread {thread.num:d}")
continue
gef_print(titlify(f"Tcachebins for thread {thread.num:d}"))
tcache_empty = True
for i in range(self.TCACHE_MAX_BINS):
chunk, count = self.tcachebin(tcache_addr, i)
chunks = set()
msg = []
chunk_size = 0
# Only print the entry if there are valid chunks. Don't trust count
while True:
if chunk is None:
break
try:
msg.append(f"{LEFT_ARROW} {chunk!s} ")
if not chunk_size:
chunk_size = chunk.usable_size
if chunk.data_address in chunks:
msg.append(f"{RIGHT_ARROW} [loop detected]")
break
chunks.add(chunk.data_address)
next_chunk = chunk.fd
if next_chunk == 0:
break
chunk = GlibcTcacheChunk(next_chunk)
except gdb.MemoryError:
msg.append(f"{LEFT_ARROW} [Corrupted chunk at {chunk.data_address:#x}]")
break
if msg:
tcache_empty = False
tidx = gef.heap.csize2tidx(chunk_size)
size = gef.heap.tidx2size(tidx)
count = len(chunks)
gef_print(f"Tcachebins[idx={tidx:d}, size={size:#x}, count={count}]", end="")
gef_print("".join(msg))
if tcache_empty:
gef_print("All tcachebins are empty")
current_thread.switch()
return
def find_tcache(self) -> int:
"""Return the location of the current thread's tcache."""
try:
# For multithreaded binaries, the tcache symbol (in thread local
# storage) will give us the correct address.
tcache_addr = parse_address("(void *) tcache")
except gdb.error:
# In binaries not linked with pthread (and therefore there is only
# one thread), we can't use the tcache symbol, but we can guess the
# correct address because the tcache is consistently the first
# allocation in the main arena.
heap_base = gef.heap.base_address
if heap_base is None:
err("No heap section")
return 0x0
tcache_addr = heap_base + 0x10
return tcache_addr
def check_thread_ids(self, tids: list[int]) -> list[int]:
"""Return the subset of tids that are currently valid."""
existing_tids = set(t.num for t in gdb.selected_inferior().threads())
return list(set(tids) & existing_tids)
def tcachebin(self, tcache_base: int, i: int) -> tuple[GlibcTcacheChunk | None, int]:
"""Return the head chunk in tcache[i] and the number of chunks in the bin."""
if i >= self.TCACHE_MAX_BINS:
err("Incorrect index value, index value must be between 0 and "
f"{self.TCACHE_MAX_BINS}-1, given {i}"
)
return None, 0
tcache_chunk = GlibcTcacheChunk(tcache_base)
# Glibc changed the size of the tcache in version 2.30; this fix has
# been backported inconsistently between distributions. We detect the
# difference by checking the size of the allocated chunk for the
# tcache.
# Minimum usable size of allocated tcache chunk = ?
# For new tcache:
# TCACHE_MAX_BINS * _2_ + TCACHE_MAX_BINS * ptrsize
# For old tcache:
# TCACHE_MAX_BINS * _1_ + TCACHE_MAX_BINS * ptrsize
new_tcache_min_size = (
self.TCACHE_MAX_BINS * 2 +
self.TCACHE_MAX_BINS * gef.arch.ptrsize)
if tcache_chunk.usable_size < new_tcache_min_size:
tcache_count_size = 1
count = ord(gef.memory.read(tcache_base + tcache_count_size*i, 1))
else:
tcache_count_size = 2
count = u16(gef.memory.read(tcache_base + tcache_count_size*i, 2))
chunk = dereference(tcache_base + tcache_count_size*self.TCACHE_MAX_BINS + i*gef.arch.ptrsize)
chunk = GlibcTcacheChunk(int(chunk)) if chunk else None
return chunk, count
@register
class GlibcHeapFastbinsYCommand(GenericCommand):
"""Display information on the fastbinsY on an arena (default: main_arena).
See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123."""
_cmdline_ = "heap bins fast"
_syntax_ = f"{_cmdline_} [ARENA_ADDRESS]"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({"arena_address": ""}, {})
@only_if_gdb_running
def do_invoke(self, *_: Any, **kwargs: Any) -> None:
def fastbin_index(sz: int) -> int:
return (sz >> 4) - 2 if SIZE_SZ == 8 else (sz >> 3) - 2
args : argparse.Namespace = kwargs["arguments"]
if not gef.heap.main_arena:
err("Heap not initialized")
return
SIZE_SZ = gef.arch.ptrsize
MAX_FAST_SIZE = 80 * SIZE_SZ // 4
NFASTBINS = fastbin_index(MAX_FAST_SIZE) - 1
arena = GlibcArena(f"*{args.arena_address}") if args.arena_address else gef.heap.selected_arena
if arena is None:
err("Invalid Glibc arena")
return
gef_print(titlify(f"Fastbins for arena at {arena.addr:#x}"))
for i in range(NFASTBINS):
gef_print(f"Fastbins[idx={i:d}, size={(i+2)*SIZE_SZ*2:#x}] ", end="")
chunk = arena.fastbin(i)
chunks = set()
while True:
if chunk is None:
gef_print("0x00", end="")
break
try:
gef_print(f"{LEFT_ARROW} {chunk!s} ", end="")
if chunk.data_address in chunks:
gef_print(f"{RIGHT_ARROW} [loop detected]", end="")
break
if fastbin_index(chunk.size) != i:
gef_print("[incorrect fastbin_index] ", end="")
chunks.add(chunk.data_address)
next_chunk = chunk.fd
if next_chunk == 0:
break
chunk = GlibcFastChunk(next_chunk, from_base=True)
except gdb.MemoryError:
gef_print(f"{LEFT_ARROW} [Corrupted chunk at {chunk.data_address:#x}]", end="")
break
gef_print()
return
@register
class GlibcHeapUnsortedBinsCommand(GenericCommand):
"""Display information on the Unsorted Bins of an arena (default: main_arena).
See: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1689."""
_cmdline_ = "heap bins unsorted"
_syntax_ = f"{_cmdline_} [ARENA_ADDRESS]"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({"arena_address": ""}, {})
@only_if_gdb_running
def do_invoke(self, *_: Any, **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not gef.heap.main_arena or not gef.heap.selected_arena:
err("Heap not initialized")
return
arena_addr = args.arena_address if args.arena_address else f"{gef.heap.selected_arena.addr:#x}"
gef_print(titlify(f"Unsorted Bin for arena at {arena_addr}"))
heap_bins_cmd = gef.gdb.commands["heap bins"]
assert isinstance(heap_bins_cmd, GlibcHeapBinsCommand)
nb_chunk = heap_bins_cmd.pprint_bin(f"*{arena_addr}", 0, "unsorted_")
if nb_chunk >= 0:
info(f"Found {nb_chunk:d} chunks in unsorted bin.")
return
@register
class GlibcHeapSmallBinsCommand(GenericCommand):
"""Convenience command for viewing small bins."""
_cmdline_ = "heap bins small"
_syntax_ = f"{_cmdline_} [ARENA_ADDRESS]"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({"arena_address": ""}, {})
@only_if_gdb_running
def do_invoke(self, *_: Any, **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not gef.heap.main_arena or not gef.heap.selected_arena:
err("Heap not initialized")
return
arena_address = args.arena_address or f"{gef.heap.selected_arena.address:#x}"
gef_print(titlify(f"Small Bins for arena at {arena_address}"))
bins: dict[int, int] = {}
heap_bins_cmd = gef.gdb.commands["heap bins"]
assert isinstance (heap_bins_cmd, GlibcHeapBinsCommand)
for i in range(1, 63):
nb_chunk = heap_bins_cmd.pprint_bin(f"*{arena_address}", i, "small_")
if nb_chunk < 0:
break
if nb_chunk > 0:
bins[i] = nb_chunk
info(f"Found {sum(list(bins.values())):d} chunks in {len(bins):d} small non-empty bins.")
return
@register
class GlibcHeapLargeBinsCommand(GenericCommand):
"""Convenience command for viewing large bins."""
_cmdline_ = "heap bins large"
_syntax_ = f"{_cmdline_} [ARENA_ADDRESS]"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({"arena_address": ""}, {})
@only_if_gdb_running
def do_invoke(self, *_: Any, **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not gef.heap.main_arena or not gef.heap.selected_arena:
err("Heap not initialized")
return
arena_addr = args.arena_address if args.arena_address else f"{gef.heap.selected_arena.addr:#x}"
gef_print(titlify(f"Large Bins for arena at {arena_addr}"))
bins = {}
heap_bins_cmd = gef.gdb.commands["heap bins"]
assert isinstance(heap_bins_cmd, GlibcHeapBinsCommand)
for i in range(63, 126):
nb_chunk = heap_bins_cmd.pprint_bin(f"*{arena_addr}", i, "large_")
if nb_chunk < 0:
break
if nb_chunk > 0:
bins[i] = nb_chunk
info(f"Found {sum(bins.values()):d} chunks in {len(bins):d} large non-empty bins.")
return
@register
class DetailRegistersCommand(GenericCommand):
"""Display full details on one, many or all registers value from current architecture."""
_cmdline_ = "registers"
_syntax_ = f"{_cmdline_} [[Register1][Register2] ... [RegisterN]]"
_example_ = (f"\n{_cmdline_}"
f"\n{_cmdline_} $eax $eip $esp")
@only_if_gdb_running
@parse_arguments({"registers": [""]}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
unchanged_color = gef.config["theme.registers_register_name"]
changed_color = gef.config["theme.registers_value_changed"]
string_color = gef.config["theme.dereference_string"]
regs = gef.arch.all_registers
args : argparse.Namespace = kwargs["arguments"]
if args.registers and args.registers[0]:
all_regs = set(gef.arch.all_registers)
regs = [reg for reg in args.registers if reg in all_regs]
invalid_regs = [reg for reg in args.registers if reg not in all_regs]
if invalid_regs:
err(f"invalid registers for architecture: {', '.join(invalid_regs)}")
memsize = gef.arch.ptrsize
endian = str(gef.arch.endianness)
charset = string.printable
widest = max(map(len, gef.arch.all_registers))
special_line = ""
for regname in regs:
reg = gdb.parse_and_eval(regname)
if reg.type.code == gdb.TYPE_CODE_VOID:
continue
padreg = regname.ljust(widest, " ")
if str(reg) == "":
gef_print(f"{Color.colorify(padreg, unchanged_color)}: "
f"{Color.colorify('no value', 'yellow underline')}")
continue
value = align_address(int(reg))
ctx_cmd = gef.gdb.commands["context"]
assert isinstance(ctx_cmd, ContextCommand)
old_value = ctx_cmd.old_registers.get(regname, 0)
if value == old_value:
color = unchanged_color
else:
color = changed_color
# Special (e.g. segment) registers go on their own line
if regname in gef.arch.special_registers:
special_line += f"{Color.colorify(regname, color)}: "
special_line += f"{gef.arch.register(regname):#04x} "
continue
line = f"{Color.colorify(padreg, color)}: "
if regname == gef.arch.flag_register:
line += gef.arch.flag_register_to_human()
gef_print(line)
continue
addr = lookup_address(align_address(int(value)))
if addr.valid:
line += str(addr)
else:
line += format_address_spaces(value)
addrs = dereference_from(value)
if len(addrs) > 1:
sep = f" {RIGHT_ARROW} "
line += sep
line += sep.join(addrs[1:])
# check to see if reg value is ascii
try:
fmt = f"{endian}{'I' if memsize == 4 else 'Q'}"
last_addr = int(addrs[-1], 16)
val = gef_pystring(struct.pack(fmt, last_addr))
if all([_ in charset for _ in val]):
line += f" (\"{Color.colorify(val, string_color)}\"?)"
except ValueError:
pass
gef_print(line)
if special_line:
gef_print(special_line)
return
@register
class ShellcodeCommand(GenericCommand):
"""ShellcodeCommand uses @JonathanSalwan simple-yet-awesome shellcode API to
download shellcodes."""
_cmdline_ = "shellcode"
_syntax_ = f"{_cmdline_} (search|get)"
def __init__(self) -> None:
super().__init__(prefix=True)
return
def do_invoke(self, _: list[str]) -> None:
err("Missing sub-command (search|get)")
self.usage()
return
@register
class ShellcodeSearchCommand(GenericCommand):
"""Search pattern in shell-storm's shellcode database."""
_cmdline_ = "shellcode search"
_syntax_ = f"{_cmdline_} PATTERN1 PATTERN2"
_aliases_ = ["sc-search",]
api_base = "http://shell-storm.org"
search_url = f"{api_base}/api/?s="
def do_invoke(self, argv: list[str]) -> None:
if not argv:
err("Missing pattern to search")
self.usage()
return
# API : http://shell-storm.org/shellcode/
args = "*".join(argv)
res = http_get(self.search_url + args)
if res is None:
err("Could not query search page")
return
ret = gef_pystring(res)
# format: [author, OS/arch, cmd, id, link]
lines = ret.split("\\n")
refs = [line.split("::::") for line in lines]
if refs:
info("Showing matching shellcodes")
info("\t".join(["Id", "Platform", "Description"]))
for ref in refs:
try:
_, arch, cmd, sid, _ = ref
gef_print("\t".join([sid, arch, cmd]))
except ValueError:
continue
info("Use `shellcode get ` to fetch shellcode")
return
@register
class ShellcodeGetCommand(GenericCommand):
"""Download shellcode from shell-storm's shellcode database."""
_cmdline_ = "shellcode get"
_syntax_ = f"{_cmdline_} SHELLCODE_ID"
_aliases_ = ["sc-get",]
api_base = "http://shell-storm.org"
get_url = f"{api_base}/shellcode/files/shellcode-{{:d}}.html"
def do_invoke(self, argv: list[str]) -> None:
if len(argv) != 1:
err("Missing ID to download")
self.usage()
return
if not argv[0].isdigit():
err("ID is not a number")
self.usage()
return
self.get_shellcode(int(argv[0]))
return
def get_shellcode(self, sid: int) -> None:
info(f"Downloading shellcode id={sid}")
res = http_get(self.get_url.format(sid))
if res is None:
err(f"Failed to fetch shellcode #{sid}")
return
ok("Downloaded, written to disk...")
with tempfile.NamedTemporaryFile(prefix="sc-", suffix=".txt", mode='w+b', delete=False, dir=gef.config["gef.tempdir"]) as fd:
shellcode = res.split(b"")[1].split(b"
")[0]
shellcode = shellcode.replace(b""", b'"')
fd.write(shellcode)
ok(f"Shellcode written to '{fd.name}'")
return
@register
class ProcessListingCommand(GenericCommand):
"""List and filter process. If a PATTERN is given as argument, results shown will be grepped
by this pattern."""
_cmdline_ = "process-search"
_syntax_ = f"{_cmdline_} [-h] [--attach] [--smart-scan] [REGEX_PATTERN]"
_aliases_ = ["ps"]
_example_ = f"{_cmdline_} gdb.*"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
self["ps_command"] = (f"{gef.session.constants['ps']} auxww", "`ps` command to get process information")
return
@parse_arguments({"pattern": ""}, {"--attach": False, "--smart-scan": False})
def do_invoke(self, _: list, **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
do_attach = args.attach
smart_scan = args.smart_scan
pattern = args.pattern
pattern = re.compile("^.*$") if not args else re.compile(pattern)
for process in self.get_processes():
pid = int(process["pid"])
command = process["command"]
if not re.search(pattern, command):
continue
if smart_scan:
if command.startswith("[") and command.endswith("]"): continue
if command.startswith("socat "): continue
if command.startswith("grep "): continue
if command.startswith("gdb "): continue
if args and do_attach:
ok(f"Attaching to process='{process['command']}' pid={pid:d}")
gdb.execute(f"attach {pid:d}")
return None
line = [process[i] for i in ("pid", "user", "cpu", "mem", "tty", "command")]
gef_print("\t\t".join(line))
return None
def get_processes(self) -> Generator[dict[str, str], None, None]:
output = gef_execute_external(self["ps_command"].split(), True)
names = [x.lower().replace("%", "") for x in output[0].split()]
for line in output[1:]:
fields = line.split()
t = {}
for i, name in enumerate(names):
if i == len(names) - 1:
t[name] = " ".join(fields[i:])
else:
t[name] = fields[i]
yield t
return
@register
class ElfInfoCommand(GenericCommand):
"""Display a limited subset of ELF header information. If no argument is provided, the command will
show information about the current ELF being debugged."""
_cmdline_ = "elf-info"
_syntax_ = f"{_cmdline_} [FILE]"
_example_ = f"{_cmdline_} /bin/ls"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@parse_arguments({}, {"--filename": ""})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if is_qemu_system():
err("Unsupported")
return
filename = args.filename or get_filepath()
if filename is None:
return
try:
elf = Elf(filename)
except ValueError:
err(f"`{filename}` is an invalid value for ELF file")
return
data = [
("Magic", f"{hexdump(struct.pack('>I', elf.e_magic), show_raw=True)}"),
("Class", f"{elf.e_class.value:#x} - {elf.e_class.name}"),
("Endianness", f"{elf.e_endianness.value:#x} - {Endianness(elf.e_endianness).name}"),
("Version", f"{elf.e_eiversion:#x}"),
("OS ABI", f"{elf.e_osabi.value:#x} - {elf.e_osabi.name if elf.e_osabi else ''}"),
("ABI Version", f"{elf.e_abiversion:#x}"),
("Type", f"{elf.e_type.value:#x} - {elf.e_type.name}"),
("Machine", f"{elf.e_machine.value:#x} - {elf.e_machine.name}"),
("Program Header Table", f"{format_address(elf.e_phoff)}"),
("Section Header Table", f"{format_address(elf.e_shoff)}"),
("Header Table", f"{format_address(elf.e_phoff)}"),
("ELF Version", f"{elf.e_version:#x}"),
("Header size", f"{elf.e_ehsize} ({elf.e_ehsize:#x})"),
("Entry point", f"{format_address(elf.e_entry)}"),
]
for title, content in data:
gef_print(f"{Color.boldify(f'{title:<22}')}: {content}")
gef_print("")
gef_print(titlify("Program Header"))
gef_print(f" [{'#':>2s}] {'Type':12s} {'Offset':>8s} {'Virtaddr':>10s} {'Physaddr':>10s}"
f" {'FileSiz':>8s} {'MemSiz':>8s} {'Flags':5s} {'Align':>8s}")
for i, p in enumerate(elf.phdrs):
p_type = p.p_type.name if p.p_type else ""
p_flags = str(p.p_flags.name).lstrip("Flag.") if p.p_flags else "???"
gef_print(f" [{i:2d}] {p_type:12s} {p.p_offset:#8x} {p.p_vaddr:#10x} {p.p_paddr:#10x}"
f" {p.p_filesz:#8x} {p.p_memsz:#8x} {p_flags:5s} {p.p_align:#8x}")
gef_print("")
gef_print(titlify("Section Header"))
gef_print(f" [{'#':>2s}] {'Name':20s} {'Type':>15s} {'Address':>10s} {'Offset':>8s}"
f" {'Size':>8s} {'EntSiz':>8s} {'Flags':5s} {'Link':4s} {'Info':4s} {'Align':>8s}")
for i, s in enumerate(elf.shdrs):
sh_type = s.sh_type.name if s.sh_type else "UNKN"
sh_flags = str(s.sh_flags).lstrip("Flags.") if s.sh_flags else "UNKN"
gef_print(f" [{i:2d}] {s.name:20s} {sh_type:>15s} {s.sh_addr:#10x} {s.sh_offset:#8x} "
f"{s.sh_size:#8x} {s.sh_entsize:#8x} {sh_flags:5s} {s.sh_link:#4x} {s.sh_info:#4x} {s.sh_addralign:#8x}")
return
@register
class EntryPointBreakCommand(GenericCommand):
"""Tries to find best entry point and sets a temporary breakpoint on it. The command will test for
well-known symbols for entry points, such as `main`, `_main`, `__libc_start_main`, etc. defined by
the setting `entrypoint_symbols`."""
_cmdline_ = "entry-break"
_syntax_ = _cmdline_
_aliases_ = ["start",]
def __init__(self) -> None:
super().__init__()
self["entrypoint_symbols"] = ("main _main __libc_start_main __uClibc_main start _start", "Possible symbols for entry points")
return
def do_invoke(self, argv: list[str]) -> None:
fpath = get_filepath()
if fpath is None:
warn("No executable to debug, use `file` to load a binary")
return
if not os.access(fpath, os.X_OK):
warn(f"The file '{fpath}' is not executable.")
return
if is_alive() and not gef.session.qemu_mode:
warn("gdb is already running")
return
bp = None
entrypoints = self["entrypoint_symbols"].split()
for sym in entrypoints:
try:
value = parse_address(sym)
info(f"Breaking at '{value:#x}'")
bp = EntryBreakBreakpoint(sym)
gdb.execute(f"run {' '.join(argv)}")
return
except gdb.error as gdb_error:
if 'The "remote" target does not support "run".' in str(gdb_error):
# this case can happen when doing remote debugging
gdb.execute("continue")
return
continue
# if here, clear the breakpoint if any set
if bp:
bp.delete()
assert gef.binary
# break at entry point
entry = gef.binary.entry_point
if is_pie(fpath):
self.set_init_tbreak_pie(entry, argv)
gdb.execute("continue")
return
self.set_init_tbreak(entry)
gdb.execute(f"run {' '.join(argv)}")
return
def set_init_tbreak(self, addr: int) -> EntryBreakBreakpoint:
info(f"Breaking at entry-point: {addr:#x}")
bp = EntryBreakBreakpoint(f"*{addr:#x}")
return bp
def set_init_tbreak_pie(self, addr: int, argv: list[str]) -> EntryBreakBreakpoint:
warn("PIC binary detected, retrieving text base address")
gdb.execute("set stop-on-solib-events 1")
hide_context()
gdb.execute(f"run {' '.join(argv)}")
unhide_context()
gdb.execute("set stop-on-solib-events 0")
vmmap = gef.memory.maps
base_address = [x.page_start for x in vmmap if x.path == get_filepath()][0]
return self.set_init_tbreak(base_address + addr)
@register
class NamedBreakpointCommand(GenericCommand):
"""Sets a breakpoint and assigns a name to it, which will be shown, when it's hit."""
_cmdline_ = "name-break"
_syntax_ = f"{_cmdline_} name [address]"
_aliases_ = ["nb",]
_example = f"{_cmdline_} main *0x4008a9"
def __init__(self) -> None:
super().__init__()
return
@parse_arguments({"name": "", "address": "*$pc"}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not args.name:
err("Missing name for breakpoint")
self.usage()
return
NamedBreakpoint(args.address, args.name)
return
@register
class ContextCommand(GenericCommand):
"""Displays a comprehensive and modular summary of runtime context. Unless setting `enable` is
set to False, this command will be spawned automatically every time GDB hits a breakpoint, a
watchpoint, or any kind of interrupt. By default, it will show panes that contain the register
states, the stack, and the disassembly code around $pc."""
_cmdline_ = "context"
_syntax_ = f"{_cmdline_} [legend|regs|stack|code|args|memory|source|trace|threads|extra]"
_aliases_ = ["ctx",]
old_registers: dict[str, int | None] = {}
def __init__(self) -> None:
super().__init__()
self["enable"] = (True, "Enable/disable printing the context when breaking")
self["show_source_code_variable_values"] = (True, "Show extra PC context info in the source code")
self["show_full_source_file_name_max_len"] = (30, "Show full source path name, if less than this value")
self["show_basename_source_file_name_max_len"] = (20, "Show the source basename in full, if less than this value")
self["show_prefix_source_path_name_len"] = (10, "When truncating source path, show this many path prefix characters")
self["show_stack_raw"] = (False, "Show the stack pane as raw hexdump (no dereference)")
self["show_registers_raw"] = (False, "Show the registers pane with raw values (no dereference)")
self["show_opcodes_size"] = (0, "Number of bytes of opcodes to display next to the disassembly")
self["peek_calls"] = (True, "Peek into calls")
self["peek_ret"] = (True, "Peek at return address")
self["nb_lines_stack"] = (8, "Number of line in the stack pane")
self["grow_stack_down"] = (False, "Order of stack downward starts at largest down to stack pointer")
self["nb_lines_backtrace"] = (10, "Number of line in the backtrace pane")
self["nb_lines_backtrace_before"] = (2, "Number of line in the backtrace pane before selected frame")
self["nb_lines_threads"] = (-1, "Number of line in the threads pane")
self["nb_lines_code"] = (6, "Number of instruction after $pc")
self["nb_lines_code_prev"] = (3, "Number of instruction before $pc")
self["ignore_registers"] = ("", "Space-separated list of registers not to display (e.g. '$cs $ds $gs')")
self["clear_screen"] = (True, "Clear the screen before printing the context")
self["layout"] = ("legend regs stack code args source memory threads trace extra", "Change the order/presence of the context sections")
self["redirect"] = ("", "Redirect the context information to another TTY")
self["libc_args"] = (False, "[DEPRECATED - Unused] Show libc function call args description")
self["libc_args_path"] = ("", "[DEPRECATED - Unused] Path to libc function call args json files, provided via gef-extras")
self.layout_mapping: dict[str, tuple[Callable, Callable | None, Callable | None]] = {
"legend": (self.show_legend, None, None),
"regs": (self.context_regs, None, None),
"stack": (self.context_stack, None, None),
"code": (self.context_code, None, None),
"args": (self.context_args, None, None),
"memory": (self.context_memory, None, None),
"source": (self.context_source, None, None),
"trace": (self.context_trace, None, None),
"threads": (self.context_threads, None, None),
"extra": (self.context_additional_information, None, None),
}
self.instruction_iterator = gef_disassemble
return
def post_load(self) -> None:
gef_on_continue_hook(self.update_registers)
gef_on_continue_hook(self.empty_extra_messages)
return
def show_legend(self) -> None:
if gef.config["gef.disable_color"] is True:
return
changed_register_title = Color.colorify("Modified register", gef.config["theme.registers_value_changed"])
code_title = Color.colorify("Code", gef.config["theme.address_code"])
heap_title = Color.colorify("Heap", gef.config["theme.address_heap"])
stack_title = Color.colorify("Stack", gef.config["theme.address_stack"])
str_title = Color.colorify("String", gef.config["theme.dereference_string"])
gef_print(f"[ Legend: {changed_register_title} | {code_title} | {heap_title} | {stack_title} | {str_title} ]")
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if not self["enable"] or gef.ui.context_hidden:
return
if not all(_ in self.layout_mapping for _ in argv):
self.usage()
return
if len(argv) > 0:
current_layout = argv
else:
current_layout = self["layout"].strip().split()
if not current_layout:
return
self.tty_rows, self.tty_columns = get_terminal_size()
redirect = self["redirect"]
if redirect and os.access(redirect, os.W_OK):
enable_redirect_output(to_file=redirect)
if self["clear_screen"] and len(argv) == 0:
clear_screen(redirect)
for section in current_layout:
if section[0] == "-":
continue
try:
display_pane_function, pane_title_function, condition = self.layout_mapping[section]
if condition:
if not condition():
continue
if pane_title_function:
self.context_title(pane_title_function())
display_pane_function()
except gdb.MemoryError as e:
# a MemoryError will happen when $pc is corrupted (invalid address)
err(str(e))
except IndexError:
# the `section` is not present, just skip
pass
self.context_title("")
if redirect and os.access(redirect, os.W_OK):
disable_redirect_output()
return
def context_title(self, m: str | None) -> None:
# allow for not displaying a title line
if m is None:
return
line_color = gef.config["theme.context_title_line"]
msg_color = gef.config["theme.context_title_message"]
# print an empty line in case of ""
if not m:
gef_print(Color.colorify(HORIZONTAL_LINE * self.tty_columns, line_color))
return
trail_len = len(m) + 6
title = ""
width = max(self.tty_columns - trail_len, 0)
padd = HORIZONTAL_LINE
title += Color.colorify(f"{'':{padd}<{width}} ", line_color)
title += Color.colorify(m, msg_color)
title += Color.colorify(f" {'':{padd}<4}", line_color)
gef_print(title)
return
def context_regs(self) -> None:
self.context_title("registers")
ignored_registers = set(self["ignore_registers"].split())
# Defer to DetailRegisters by default
if self["show_registers_raw"] is False:
regs = [reg for reg in gef.arch.all_registers if reg not in ignored_registers]
printable_registers = " ".join(regs)
gdb.execute(f"registers {printable_registers}")
return
widest = curlen = max(map(len, gef.arch.all_registers))
curlen += 5
curlen += gef.arch.ptrsize * 2
nb = get_terminal_size()[1] // curlen
i = 1
line = ""
changed_color = gef.config["theme.registers_value_changed"]
regname_color = gef.config["theme.registers_register_name"]
for reg in gef.arch.all_registers:
if reg in ignored_registers:
continue
try:
r = gdb.parse_and_eval(reg)
if r.type.code == gdb.TYPE_CODE_VOID:
continue
new_value_type_flag = r.type.code == gdb.TYPE_CODE_FLAGS
new_value = int(r)
except (gdb.MemoryError, gdb.error):
# If this exception is triggered, it means that the current register
# is corrupted. Just use the register "raw" value (not eval-ed)
new_value = gef.arch.register(reg)
new_value_type_flag = False
except Exception:
new_value = 0
new_value_type_flag = False
old_value = self.old_registers.get(reg, 0)
padreg = reg.ljust(widest, " ")
value = align_address(new_value)
old_value = align_address(old_value or 0)
if value == old_value:
line += f"{Color.colorify(padreg, regname_color)}: "
else:
line += f"{Color.colorify(padreg, changed_color)}: "
if new_value_type_flag:
line += f"{format_address_spaces(value)} "
else:
addr = lookup_address(align_address(int(value)))
if addr.valid:
line += f"{addr!s} "
else:
line += f"{format_address_spaces(value)} "
if i % nb == 0:
gef_print(line)
line = ""
i += 1
if line:
gef_print(line)
gef_print(f"Flags: {gef.arch.flag_register_to_human()}")
return
def context_stack(self) -> None:
self.context_title("stack")
show_raw = self["show_stack_raw"]
nb_lines = self["nb_lines_stack"]
try:
sp = gef.arch.sp
if show_raw is True:
mem = gef.memory.read(sp, 0x10 * nb_lines)
gef_print(hexdump(mem, base=sp))
else:
gdb.execute(f"dereference -l {nb_lines:d} {sp:#x}")
except gdb.MemoryError:
err("Cannot read memory from $SP (corrupted stack pointer?)")
return
def addr_has_breakpoint(self, address: int, bp_locations: list[str]) -> bool:
return any(hex(address) in b for b in bp_locations)
def context_code(self) -> None:
nb_insn = self["nb_lines_code"]
nb_insn_prev = self["nb_lines_code_prev"]
show_opcodes_size = "show_opcodes_size" in self and self["show_opcodes_size"]
past_insns_color = gef.config["theme.old_context"]
cur_insn_color = gef.config["theme.disassemble_current_instruction"]
pc = gef.arch.pc
breakpoints = gdb.breakpoints() or []
# breakpoint.locations was introduced in gdb 13.1
if len(breakpoints) and hasattr(breakpoints[-1], "locations"):
bp_locations = [hex(location.address) for b in breakpoints for location in b.locations if location is not None] # type: ignore
else:
# location relies on the user setting the breakpoints with "b *{hex(address)}"
bp_locations = [b.location for b in breakpoints if b.location and b.location.startswith("*")]
frame = gdb.selected_frame()
arch_name = f"{gef.arch.arch.lower()}:{gef.arch.mode}"
self.context_title(f"code:{arch_name}")
try:
for insn in self.instruction_iterator(pc, nb_insn, nb_prev=nb_insn_prev):
line = []
is_taken = False
target = None
bp_prefix = Color.redify(BP_GLYPH) if self.addr_has_breakpoint(insn.address, bp_locations) else " "
if show_opcodes_size == 0:
text = str(insn)
else:
insn_fmt = f"{{:{show_opcodes_size}o}}"
text = insn_fmt.format(insn)
if insn.address < pc:
line += f"{bp_prefix} {Color.colorify(text, past_insns_color)}"
elif insn.address == pc:
line += f"{bp_prefix}{Color.colorify(f'{RIGHT_ARROW[1:]}{text}', cur_insn_color)}"
if gef.arch.is_conditional_branch(insn):
is_taken, reason = gef.arch.is_branch_taken(insn)
if is_taken:
target = insn.operands[-1].split()[0]
reason = f"[Reason: {reason}]" if reason else ""
line += Color.colorify(f"\tTAKEN {reason}", "bold green")
else:
reason = f"[Reason: !({reason})]" if reason else ""
line += Color.colorify(f"\tNOT taken {reason}", "bold red")
elif gef.arch.is_call(insn) and self["peek_calls"] is True:
target = insn.operands[-1].split()[0]
elif gef.arch.is_ret(insn) and self["peek_ret"] is True:
target = gef.arch.get_ra(insn, frame)
else:
line += f"{bp_prefix} {text}"
gef_print("".join(line))
if target:
try:
address = int(target, 0) if isinstance(target, str) else target
except ValueError:
# If the operand isn't an address right now we can't parse it
continue
for i, tinsn in enumerate(self.instruction_iterator(address, nb_insn)):
text= f" {DOWN_ARROW if i == 0 else ' '} {tinsn!s}"
gef_print(text)
break
except gdb.MemoryError:
err("Cannot disassemble from $PC")
return
def context_args(self) -> None:
insn = gef_current_instruction(gef.arch.pc)
if not gef.arch.is_call(insn):
return
self.size2type = {
1: "BYTE",
2: "WORD",
4: "DWORD",
8: "QWORD",
}
if insn.operands[-1].startswith(self.size2type[gef.arch.ptrsize]+" PTR"):
target = "*" + insn.operands[-1].split()[-1]
elif "$"+insn.operands[0] in gef.arch.all_registers:
target = f"*{gef.arch.register('$' + insn.operands[0]):#x}"
else:
# is there a symbol?
ops = " ".join(insn.operands)
if "<" in ops and ">" in ops:
# extract it
target = re.sub(r".*<([^\(> ]*).*", r"\1", ops)
else:
# it's an address, just use as is
target = re.sub(r".*(0x[a-fA-F0-9]*).*", r"\1", ops)
sym = gdb.lookup_global_symbol(target)
if sym is None:
self.print_guessed_arguments(target)
return
if sym.type and sym.type.code != gdb.TYPE_CODE_FUNC:
err(f"Symbol '{target}' is not a function: type={sym.type.code}")
return
self.print_arguments_from_symbol(target, sym)
return
def print_arguments_from_symbol(self, function_name: str, symbol: "gdb.Symbol") -> None:
"""If symbols were found, parse them and print the argument adequately."""
args = []
fields = symbol.type.fields() if symbol.type else []
for i, f in enumerate(fields):
if not f.type:
continue
_value = gef.arch.get_ith_parameter(i, in_func=False)[1]
_value = RIGHT_ARROW.join(dereference_from(_value))
_name = f.name or f"var_{i}"
_type = f.type.name or self.size2type[f.type.sizeof]
args.append(f"{_type} {_name} = {_value}")
self.context_title("arguments")
if not args:
gef_print(f"{function_name} ()")
return
gef_print(f"{function_name} (\n "+",\n ".join(args)+"\n)")
return
def print_guessed_arguments(self, function_name: str) -> None:
"""When no symbol, read the current basic block and look for "interesting" instructions."""
def __get_current_block_start_address() -> int | None:
pc = gef.arch.pc
max_distance = 10 * 16
try:
block = gdb.block_for_pc(pc)
block_start = block.start \
if block is not None and (pc - block.start) <= max_distance \
else gdb_get_nth_previous_instruction_address(pc, 5)
except RuntimeError:
block_start = gdb_get_nth_previous_instruction_address(pc, 5)
return block_start
block_start = __get_current_block_start_address()
if not block_start:
return
parameter_set: set[str] = set()
pc = gef.arch.pc
function_parameters = gef.arch.function_parameters
arg_key_color = gef.config["theme.registers_register_name"]
for insn in self.instruction_iterator(block_start, pc - block_start):
if not insn.operands:
continue
if is_x86_32():
if insn.mnemonic == "push":
parameter_set.add(insn.operands[0])
else:
op = "$" + insn.operands[0]
if op in function_parameters:
parameter_set.add(op)
if is_x86_64():
# also consider extended registers
extended_registers = {"$rdi": ["$edi", "$di"],
"$rsi": ["$esi", "$si"],
"$rdx": ["$edx", "$dx"],
"$rcx": ["$ecx", "$cx"],
}
for exreg in extended_registers:
if op in extended_registers[exreg]:
parameter_set.add(exreg)
if is_x86_32():
nb_argument = len(parameter_set)
else:
nb_argument = max([function_parameters.index(p)+1 for p in parameter_set], default=0)
args = []
for i in range(nb_argument):
_key, _values = gef.arch.get_ith_parameter(i, in_func=False)
_values = RIGHT_ARROW.join(dereference_from(_values))
args.append(f"{Color.colorify(_key, arg_key_color)} = {_values}")
self.context_title("arguments (guessed)")
gef_print(f"{function_name} (")
if args:
gef_print(" " + ",\n ".join(args))
gef_print(")")
return
def line_has_breakpoint(self, file_name: str, line_number: int, bp_locations: list[str]) -> bool:
filename_line = f"{file_name}:{line_number}"
return any(filename_line in loc for loc in bp_locations)
def context_source(self) -> None:
try:
pc = gef.arch.pc
symtabline = gdb.find_pc_line(pc)
symtab = symtabline.symtab
# we subtract one because the line number returned by gdb start at 1
line_num = symtabline.line - 1
if not symtab.is_valid():
return
fpath = pathlib.Path(symtab.fullname())
lines = [curline.rstrip() for curline in fpath.read_text().splitlines()]
except Exception:
return
file_base_name = os.path.basename(symtab.filename)
breakpoints = gdb.breakpoints() or []
bp_locations = [b.location for b in breakpoints if b.location and file_base_name in b.location]
past_lines_color = gef.config["theme.old_context"]
show_full_path_max = self["show_full_source_file_name_max_len"]
show_basename_path_max = self["show_basename_source_file_name_max_len"]
nb_line = self["nb_lines_code"]
fn = symtab.filename
if len(fn) > show_full_path_max:
base = os.path.basename(fn)
if len(base) > show_basename_path_max:
base = base[-show_basename_path_max:]
fn = fn[:15] + "[...]" + base
title = f"source:{fn}+{line_num + 1}"
cur_line_color = gef.config["theme.source_current_line"]
self.context_title(title)
show_extra_info = self["show_source_code_variable_values"]
for i in range(line_num - nb_line + 1, line_num + nb_line):
if i < 0:
continue
bp_prefix = Color.redify(BP_GLYPH) if self.line_has_breakpoint(file_base_name, i + 1, bp_locations) else " "
if i < line_num:
gef_print("{}{}".format(bp_prefix, Color.colorify(f" {i + 1:4d}\t {lines[i]}", past_lines_color)))
if i == line_num:
prefix = f"{bp_prefix}{RIGHT_ARROW[1:]}{i + 1:4d}\t "
leading = len(lines[i]) - len(lines[i].lstrip())
if show_extra_info:
extra_info = self.get_pc_context_info(pc, lines[i])
if extra_info:
gef_print(f"{' ' * (len(prefix) + leading)}{extra_info}")
gef_print(Color.colorify(f"{prefix}{lines[i]}", cur_line_color))
if i > line_num:
try:
gef_print(f"{bp_prefix} {i + 1:4d}\t {lines[i]}")
except IndexError:
break
return
def get_pc_context_info(self, pc: int, line: str) -> str:
try:
current_block = gdb.block_for_pc(pc)
if not current_block or not current_block.is_valid(): return ""
m = collections.OrderedDict()
while current_block and not current_block.is_static:
for sym in list(current_block):
symbol = sym.name
if not sym.is_function and re.search(fr"\W{symbol}\W", line):
val = gdb.parse_and_eval(symbol)
if val.type.code in (gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_ARRAY):
addr = int(val.address)
addrs = dereference_from(addr)
if len(addrs) > 2:
addrs = [addrs[0], "[...]", addrs[-1]]
f = f" {RIGHT_ARROW} "
val = f.join(addrs)
elif val.type.code == gdb.TYPE_CODE_INT:
val = hex(int(val))
else:
continue
if symbol not in m:
m[symbol] = val
current_block = current_block.superblock
if m:
return "// " + ", ".join([f"{Color.yellowify(a)}={b}" for a, b in m.items()])
except Exception:
pass
return ""
def context_trace(self) -> None:
self.context_title("trace")
nb_backtrace = self["nb_lines_backtrace"]
if nb_backtrace <= 0:
return
# backward compat for gdb (gdb < 7.10)
if not hasattr(gdb, "FrameDecorator"):
gdb.execute(f"backtrace {nb_backtrace:d}")
return
orig_frame: gdb.Frame = gdb.selected_frame()
current_frame: gdb.Frame = gdb.newest_frame()
frames = [current_frame,]
while current_frame != orig_frame and current_frame:
current_frame = current_frame.older()
if not current_frame: break
frames.append(current_frame)
nb_backtrace_before = self["nb_lines_backtrace_before"]
level = max(len(frames) - nb_backtrace_before - 1, 0)
current_frame: gdb.Frame = frames[level]
while current_frame:
current_frame.select()
if not current_frame.is_valid():
continue
pc = int(current_frame.pc())
name = current_frame.name()
items = []
items.append(f"{pc:#x}")
if name:
frame_args = gdb.FrameDecorator.FrameDecorator(current_frame).frame_args() or [] # type: ignore
symstr= ", ".join([f"{Color.yellowify(x.sym)}={x.sym.value(current_frame)!s}" for x in frame_args])
m = f"{Color.greenify(name)}({symstr})"
items.append(m)
else:
try:
insn = next(gef_disassemble(int(pc), 1))
except gdb.MemoryError:
break
# check if the gdb symbol table may know the address
sym_found = gdb_get_location_from_symbol(pc)
symbol = ""
if sym_found:
sym_name, offset = sym_found
symbol = f" <{sym_name}+{offset:x}> "
items.append(Color.redify(f"{symbol}{insn.mnemonic} {', '.join(insn.operands)}"))
title = Color.colorify(f"#{level}", "bold green" if current_frame == orig_frame else "bold pink")
gef_print(f"[{title}] {RIGHT_ARROW.join(items)}")
older = current_frame.older()
level += 1
nb_backtrace -= 1
if nb_backtrace == 0:
break
if not older:
break
current_frame = older
orig_frame.select()
return
def context_threads(self) -> None:
def reason() -> str:
res = gdb.execute("info program", to_string=True)
if not res:
return "NOT RUNNING"
for line in res.splitlines():
line = line.strip()
if line.startswith("It stopped with signal "):
return line.replace("It stopped with signal ", "").split(",", 1)[0]
if line == "The program being debugged is not being run.":
return "NOT RUNNING"
if line == "It stopped at a breakpoint that has since been deleted.":
return "TEMPORARY BREAKPOINT"
if line.startswith("It stopped at breakpoint "):
return "BREAKPOINT"
if line == "It stopped after being stepped.":
return "SINGLE STEP"
return "STOPPED"
self.context_title("threads")
threads = gdb.selected_inferior().threads()[::-1]
idx = self["nb_lines_threads"]
if idx > 0:
threads = threads[0:idx]
if idx == 0:
return
if not threads:
err("No thread selected")
return
selected_thread = gdb.selected_thread()
selected_frame = gdb.selected_frame()
for i, thread in enumerate(threads):
line = f"[{Color.colorify(f'#{i:d}', 'bold green' if thread == selected_thread else 'bold pink')}] Id {thread.num:d}, "
if thread.name:
line += f"""Name: "{thread.name}", """
if thread.is_running():
line += Color.colorify("running", "bold green")
elif thread.is_stopped():
line += Color.colorify("stopped", "bold red")
thread.switch()
frame = gdb.selected_frame()
frame_name = frame.name()
# check if the gdb symbol table may know the address
if not frame_name:
sym_found = gdb_get_location_from_symbol(int(frame.pc()))
if sym_found:
sym_name, offset = sym_found
frame_name = f"<{sym_name}+{offset:x}>"
line += (f" {Color.colorify(f'{frame.pc():#x}', 'blue')} in "
f"{Color.colorify(frame_name or '??', 'bold yellow')} (), "
f"reason: {Color.colorify(reason(), 'bold pink')}")
elif thread.is_exited():
line += Color.colorify("exited", "bold yellow")
gef_print(line)
i += 1
selected_thread.switch()
selected_frame.select()
return
def context_additional_information(self) -> None:
if not gef.ui.context_messages:
return
self.context_title("extra")
for level, text in gef.ui.context_messages:
if level == "error": err(text)
elif level == "warn": warn(text)
elif level == "success": ok(text)
else: info(text)
return
def context_memory(self) -> None:
for address, opt in sorted(gef.ui.watches.items()):
sz, fmt = opt[0:2]
self.context_title(f"memory:{address:#x}")
if fmt == "pointers":
gdb.execute(f"dereference -l {sz:d} {address:#x}")
else:
gdb.execute(f"hexdump {fmt} -s {sz:d} {address:#x}")
@classmethod
def update_registers(cls, _) -> None:
for reg in gef.arch.all_registers:
try:
cls.old_registers[reg] = gef.arch.register(reg)
except Exception:
cls.old_registers[reg] = 0
return
def empty_extra_messages(self, _) -> None:
gef.ui.context_messages.clear()
return
@register
class MemoryCommand(GenericCommand):
"""Add or remove address ranges to the memory view."""
_cmdline_ = "memory"
_syntax_ = f"{_cmdline_} (watch|unwatch|reset|list)"
def __init__(self) -> None:
super().__init__(prefix=True)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
self.usage()
return
@register
class MemoryWatchCommand(GenericCommand):
"""Adds address ranges to the memory view."""
_cmdline_ = "memory watch"
_syntax_ = f"{_cmdline_} ADDRESS [SIZE] [(qword|dword|word|byte|pointers)]"
_example_ = (f"\n{_cmdline_} 0x603000 0x100 byte"
f"\n{_cmdline_} $sp")
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if len(argv) not in (1, 2, 3):
self.usage()
return
address = parse_address(argv[0])
size = parse_address(argv[1]) if len(argv) > 1 else 0x10
group = "byte"
if len(argv) == 3:
group = argv[2].lower()
if group not in ("qword", "dword", "word", "byte", "pointers"):
warn(f"Unexpected grouping '{group}'")
self.usage()
return
else:
if gef.arch.ptrsize == 4:
group = "dword"
elif gef.arch.ptrsize == 8:
group = "qword"
gef.ui.watches[address] = (size, group)
ok(f"Adding memwatch to {address:#x}")
return
@register
class MemoryUnwatchCommand(GenericCommand):
"""Removes address ranges to the memory view."""
_cmdline_ = "memory unwatch"
_syntax_ = f"{_cmdline_} ADDRESS"
_example_ = (f"\n{_cmdline_} 0x603000"
f"\n{_cmdline_} $sp")
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if not argv:
self.usage()
return
address = parse_address(argv[0])
res = gef.ui.watches.pop(address, None)
if not res:
warn(f"You weren't watching {address:#x}")
else:
ok(f"Removed memwatch of {address:#x}")
return
@register
class MemoryWatchResetCommand(GenericCommand):
"""Removes all watchpoints."""
_cmdline_ = "memory reset"
_syntax_ = f"{_cmdline_}"
@only_if_gdb_running
def do_invoke(self, _: list[str]) -> None:
gef.ui.watches.clear()
ok("Memory watches cleared")
return
@register
class MemoryWatchListCommand(GenericCommand):
"""Lists all watchpoints to display in context layout."""
_cmdline_ = "memory list"
_syntax_ = f"{_cmdline_}"
@only_if_gdb_running
def do_invoke(self, _: list[str]) -> None:
if not gef.ui.watches:
info("No memory watches")
return
info("Memory watches:")
for address, opt in sorted(gef.ui.watches.items()):
gef_print(f"- {address:#x} ({opt[0]}, {opt[1]})")
return
@register
class HexdumpCommand(GenericCommand):
"""Display SIZE lines of hexdump from the memory location pointed by LOCATION."""
_cmdline_ = "hexdump"
_syntax_ = f"{_cmdline_} (qword|dword|word|byte) [LOCATION] [--size SIZE] [--reverse]"
_example_ = f"{_cmdline_} byte $rsp --size 16 --reverse"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION, prefix=True)
self["always_show_ascii"] = (False, "If true, hexdump will always display the ASCII dump")
self.format: str | None = None
self.__last_target = "$sp"
return
@only_if_gdb_running
@parse_arguments({"address": "",}, {("--reverse", "-r"): False, ("--size", "-s"): 0})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
valid_formats = ["byte", "word", "dword", "qword"]
if not self.format or self.format not in valid_formats:
err("Invalid command")
return
args : argparse.Namespace = kwargs["arguments"]
target = args.address or self.__last_target
start_addr = parse_address(target)
read_from = align_address(start_addr)
if self.format == "byte":
read_len = args.size or 0x40
read_from += self.repeat_count * read_len
mem = gef.memory.read(read_from, read_len)
lines = hexdump(mem, base=read_from).splitlines()
else:
read_len = args.size or 0x10
lines = self._hexdump(read_from, read_len, self.format, self.repeat_count * read_len)
if args.reverse:
lines.reverse()
self.__last_target = target
gef_print("\n".join(lines))
return
def _hexdump(self, start_addr: int, length: int, arrange_as: str, offset: int = 0) -> list[str]:
endianness = gef.arch.endianness
base_address_color = gef.config["theme.dereference_base_address"]
show_ascii = gef.config["hexdump.always_show_ascii"]
formats = {
"qword": ("Q", 8),
"dword": ("I", 4),
"word": ("H", 2),
}
formatter, width = formats[arrange_as]
fmt_str = f"{{base}}{VERTICAL_LINE}+{{offset:#06x}} {{sym}}{{val:#0{width*2+2}x}} {{text}}"
fmt_pack = f"{endianness!s}{formatter}"
lines = []
i = 0
text = ""
while i < length:
cur_addr = start_addr + (i + offset) * width
sym = gdb_get_location_from_symbol(cur_addr)
sym = f"<{sym[0]:s}+{sym[1]:04x}> " if sym else ""
mem = gef.memory.read(cur_addr, width)
val = struct.unpack(fmt_pack, mem)[0]
if show_ascii:
text = "".join([chr(b) if 0x20 <= b < 0x7F else "." for b in mem])
lines.append(fmt_str.format(base=Color.colorify(format_address(cur_addr), base_address_color),
offset=(i + offset) * width, sym=sym, val=val, text=text))
i += 1
return lines
@register
class HexdumpQwordCommand(HexdumpCommand):
"""Display SIZE lines of hexdump as QWORD from the memory location pointed by ADDRESS."""
_cmdline_ = "hexdump qword"
_syntax_ = f"{_cmdline_} [ADDRESS] [--size SIZE] [--reverse]"
_example_ = f"{_cmdline_} qword $rsp -s 16 --reverse"
def __init__(self) -> None:
super().__init__()
self.format = "qword"
return
@register
class HexdumpDwordCommand(HexdumpCommand):
"""Display SIZE lines of hexdump as DWORD from the memory location pointed by ADDRESS."""
_cmdline_ = "hexdump dword"
_syntax_ = f"{_cmdline_} [ADDRESS] [--size SIZE] [--reverse]"
_example_ = f"{_cmdline_} $esp -s 16 --reverse"
def __init__(self) -> None:
super().__init__()
self.format = "dword"
return
@register
class HexdumpWordCommand(HexdumpCommand):
"""Display SIZE lines of hexdump as WORD from the memory location pointed by ADDRESS."""
_cmdline_ = "hexdump word"
_syntax_ = f"{_cmdline_} [ADDRESS] [--size SIZE] [--reverse]"
_example_ = f"{_cmdline_} $esp -s 16 --reverse"
def __init__(self) -> None:
super().__init__()
self.format = "word"
return
@register
class HexdumpByteCommand(HexdumpCommand):
"""Display SIZE lines of hexdump as BYTE from the memory location pointed by ADDRESS."""
_cmdline_ = "hexdump byte"
_syntax_ = f"{_cmdline_} [ADDRESS] [--size SIZE] [--reverse]"
_example_ = f"{_cmdline_} $rsp -s 16"
def __init__(self) -> None:
super().__init__()
self.format = "byte"
return
@register
class PatchCommand(GenericCommand):
"""Write specified values to the specified address."""
_cmdline_ = "patch"
_syntax_ = (f"{_cmdline_} (qword|dword|word|byte) LOCATION VALUES\n"
f"{_cmdline_} string LOCATION \"double-escaped string\"")
SUPPORTED_SIZES = {
"qword": (8, "Q"),
"dword": (4, "L"),
"word": (2, "H"),
"byte": (1, "B"),
}
def __init__(self) -> None:
super().__init__(prefix=True, complete=gdb.COMPLETE_LOCATION)
self.format: str | None = None
return
@only_if_gdb_running
@parse_arguments({"location": "", "values": ["", ]}, {})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
if not self.format or self.format not in self.SUPPORTED_SIZES:
self.usage()
return
if not args.location or not args.values:
self.usage()
return
addr = align_address(parse_address(args.location))
size, fcode = self.SUPPORTED_SIZES[self.format]
values = args.values
if size == 1:
if values[0].startswith("$_gef"):
var_name = values[0]
try:
values = str(gdb.parse_and_eval(var_name)).lstrip("{").rstrip("}").replace(",","").split(" ")
except Exception:
gef_print(f"Bad variable specified, check value with command: p {var_name}")
return
d = str(gef.arch.endianness)
for value in values:
value = parse_address(value) & ((1 << size * 8) - 1)
vstr = struct.pack(d + fcode, value)
gef.memory.write(addr, vstr, length=size)
addr += size
return
@register
class PatchQwordCommand(PatchCommand):
"""Write specified QWORD to the specified address."""
_cmdline_ = "patch qword"
_syntax_ = f"{_cmdline_} LOCATION QWORD1 [QWORD2 [QWORD3..]]"
_example_ = f"{_cmdline_} $rip 0x4141414141414141"
def __init__(self) -> None:
super().__init__()
self.format = "qword"
return
@register
class PatchDwordCommand(PatchCommand):
"""Write specified DWORD to the specified address."""
_cmdline_ = "patch dword"
_syntax_ = f"{_cmdline_} LOCATION DWORD1 [DWORD2 [DWORD3..]]"
_example_ = f"{_cmdline_} $rip 0x41414141"
def __init__(self) -> None:
super().__init__()
self.format = "dword"
return
@register
class PatchWordCommand(PatchCommand):
"""Write specified WORD to the specified address."""
_cmdline_ = "patch word"
_syntax_ = f"{_cmdline_} LOCATION WORD1 [WORD2 [WORD3..]]"
_example_ = f"{_cmdline_} $rip 0x4141"
def __init__(self) -> None:
super().__init__()
self.format = "word"
return
@register
class PatchByteCommand(PatchCommand):
"""Write specified BYTE to the specified address."""
_cmdline_ = "patch byte"
_syntax_ = f"{_cmdline_} LOCATION BYTE1 [BYTE2 [BYTE3..]]"
_example_ = f"{_cmdline_} $pc 0x41 0x41 0x41 0x41 0x41"
def __init__(self) -> None:
super().__init__()
self.format = "byte"
return
@register
class PatchStringCommand(GenericCommand):
"""Write specified string to the specified memory location pointed by ADDRESS."""
_cmdline_ = "patch string"
_syntax_ = f"{_cmdline_} ADDRESS \"double backslash-escaped string\""
_example_ = [
f"{_cmdline_} $sp \"GEFROCKS\"",
f"{_cmdline_} $sp \"\\\\x41\\\\x41\\\\x41\\\\x41\""
]
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
argc = len(argv)
if argc != 2:
self.usage()
return
location, msg = argv[0:2]
addr = align_address(parse_address(location))
try:
msg_as_bytes = codecs.escape_decode(msg, "utf-8")[0]
gef.memory.write(addr, msg_as_bytes, len(msg_as_bytes)) # type: ignore
except (binascii.Error, gdb.error):
err(f"Could not decode '\\xXX' encoded string \"{msg}\"")
return
@lru_cache()
def dereference_from(address: int) -> list[str]:
if not is_alive():
return [format_address(address),]
code_color = gef.config["theme.dereference_code"]
string_color = gef.config["theme.dereference_string"]
max_recursion = gef.config["dereference.max_recursion"] or 10
addr = lookup_address(align_address(address))
msg = [format_address(addr.value),]
seen_addrs = set()
while addr.section and max_recursion:
if addr.value in seen_addrs:
msg.append("[loop detected]")
break
seen_addrs.add(addr.value)
max_recursion -= 1
# Is this value a pointer or a value?
# -- If it's a pointer, dereference
deref = addr.dereference()
if deref is None:
# if here, dereferencing addr has triggered a MemoryError, no need to go further
msg.append(str(addr))
break
new_addr = lookup_address(deref)
if new_addr.valid:
addr = new_addr
msg.append(str(addr))
continue
# -- Otherwise try to parse the value
if addr.section:
if addr.section.is_executable() and addr.is_in_text_segment() and not is_ascii_string(addr.value):
insn = gef_current_instruction(addr.value)
insn_str = f"{insn.location} {insn.mnemonic} {', '.join(insn.operands)}"
msg.append(Color.colorify(insn_str, code_color))
break
elif addr.section.permission & Permission.READ:
if is_ascii_string(addr.value):
s = gef.memory.read_cstring(addr.value)
if len(s) < gef.arch.ptrsize:
txt = f'{format_address(deref)} ("{Color.colorify(s, string_color)}"?)'
elif len(s) > GEF_MAX_STRING_LENGTH:
txt = Color.colorify(f'"{s[:GEF_MAX_STRING_LENGTH]}[...]"', string_color)
else:
txt = Color.colorify(f'"{s}"', string_color)
msg.append(txt)
break
# if not able to parse cleanly, simply display and break
msg.append(format_address(deref))
break
return msg
@register
class DereferenceCommand(GenericCommand):
"""Dereference recursively from an address and display information. This acts like WinDBG `dps`
command."""
_cmdline_ = "dereference"
_syntax_ = f"{_cmdline_} [-h] [--length LENGTH] [--reference REFERENCE] [address]"
_aliases_ = ["telescope", ]
_example_ = f"{_cmdline_} --length 20 --reference $sp+0x10 $sp"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
self["max_recursion"] = (7, "Maximum level of pointer recursion")
return
@staticmethod
def pprint_dereferenced(addr: int, idx: int, base_offset: int = 0) -> str:
base_address_color = gef.config["theme.dereference_base_address"]
registers_color = gef.config["theme.dereference_register_value"]
sep = f" {RIGHT_ARROW} "
memalign = gef.arch.ptrsize
offset = idx * memalign
current_address = align_address(addr + offset)
addrs = dereference_from(current_address)
addr_l = format_address(int(addrs[0], 16))
ma = (memalign*2 + 2)
line = (
f"{Color.colorify(addr_l, base_address_color)}{VERTICAL_LINE}"
f"{base_offset+offset:+#07x}: {sep.join(addrs[1:]):{ma}s}"
)
register_hints = []
for regname in gef.arch.all_registers:
regvalue = gef.arch.register(regname)
if current_address == regvalue:
register_hints.append(regname)
if register_hints:
m = f"\t{LEFT_ARROW}{', '.join(list(register_hints))}"
line += Color.colorify(m, registers_color)
offset += memalign
return line
@only_if_gdb_running
@parse_arguments({"address": "$sp"}, {("-r", "--reference"): "", ("-l", "--length"): 10})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
nb = args.length
target = args.address
target_addr = parse_address(target)
reference = args.reference or target
ref_addr = parse_address(reference)
if process_lookup_address(target_addr) is None:
err(f"Unmapped address: '{target}'")
return
if process_lookup_address(ref_addr) is None:
err(f"Unmapped address: '{reference}'")
return
if gef.config["context.grow_stack_down"] is True:
insnum_step = -1
if nb > 0:
from_insnum = nb * (self.repeat_count + 1) - 1
to_insnum = self.repeat_count * nb - 1
else:
from_insnum = self.repeat_count * nb
to_insnum = nb * (self.repeat_count + 1)
else:
insnum_step = 1
if nb > 0:
from_insnum = self.repeat_count * nb
to_insnum = nb * (self.repeat_count + 1)
else:
from_insnum = nb * (self.repeat_count + 1) + 1
to_insnum = (self.repeat_count * nb) + 1
start_address = align_address(target_addr)
base_offset = start_address - align_address(ref_addr)
dereference_cmd = gef.gdb.commands["dereference"]
assert isinstance(dereference_cmd, DereferenceCommand)
for i in range(from_insnum, to_insnum, insnum_step):
gef_print(dereference_cmd.pprint_dereferenced(start_address, i, base_offset))
return
@register
class ASLRCommand(GenericCommand):
"""View/modify the ASLR setting of GDB. By default, GDB will disable ASLR when it starts the process. (i.e. not
attached). This command allows to change that setting."""
_cmdline_ = "aslr"
_syntax_ = f"{_cmdline_} [(on|off)]"
def do_invoke(self, argv: list[str]) -> None:
argc = len(argv)
if argc == 0:
ret = gdb.execute("show disable-randomization", to_string=True) or ""
i = ret.find("virtual address space is ")
if i < 0:
return
msg = "ASLR is currently "
if ret[i + 25:].strip() == "on.":
msg += Color.redify("disabled")
else:
msg += Color.greenify("enabled")
gef_print(msg)
return
elif argc == 1:
if argv[0] == "on":
info("Enabling ASLR")
gdb.execute("set disable-randomization off")
return
elif argv[0] == "off":
info("Disabling ASLR")
gdb.execute("set disable-randomization on")
return
warn("Invalid command")
self.usage()
return
@register
class ResetCacheCommand(GenericCommand):
"""Reset cache of all stored data. This command is here for debugging and test purposes, GEF
handles properly the cache reset under "normal" scenario."""
_cmdline_ = "reset-cache"
_syntax_ = _cmdline_
def do_invoke(self, _: list[str]) -> None:
reset_all_caches()
return
@register
class VMMapCommand(GenericCommand):
"""Display a comprehensive layout of the virtual memory mapping. If a filter argument, GEF will
filter out the mapping whose pathname do not match that filter."""
_cmdline_ = "vmmap"
_syntax_ = f"{_cmdline_} [FILTER]"
_example_ = f"{_cmdline_} libc"
@only_if_gdb_running
@parse_arguments({"unknown_types": [""]}, {("--addr", "-a"): [""], ("--name", "-n"): [""]})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
vmmap = gef.memory.maps
if not vmmap:
err("No address mapping information found")
return
addrs: dict[str, int] = {x: parse_address(x) for x in args.addr}
names: list[str] = [x for x in args.name]
for arg in args.unknown_types:
if not arg:
continue
if self.is_integer(arg):
addr = int(arg, 0)
else:
addr = safe_parse_and_eval(arg)
if addr is None:
names.append(arg)
warn(f"`{arg}` has no type specified. We guessed it was a name filter.")
else:
addrs[arg] = int(addr)
warn(f"`{arg}` has no type specified. We guessed it was an address filter.")
warn("You can use --name or --addr before the filter value for specifying its type manually.")
gef_print()
if not gef.config["gef.disable_color"]:
self.show_legend()
color = gef.config["theme.table_heading"]
headers = ["Start", "End", "Offset", "Perm", "Path"]
gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<{w}s}{:<4s} {:s}".format(*headers, w=gef.arch.ptrsize*2+3), color))
last_printed_filter = None
for entry in vmmap:
names_filter = [f"name = '{x}'" for x in names if x in entry.path]
addrs_filter = [f"addr = {self.format_addr_filter(arg, addr)}" for arg, addr in addrs.items()
if entry.page_start <= addr < entry.page_end]
filter_content = f"[{' & '.join([*names_filter, *addrs_filter])}]"
if not names and not addrs:
self.print_entry(entry)
elif names_filter or addrs_filter:
if filter_content != last_printed_filter:
gef_print() # skip a line between different filters
gef_print(Color.greenify(filter_content))
last_printed_filter = filter_content
self.print_entry(entry)
gef_print()
return
def format_addr_filter(self, arg: str, addr: int):
return f"`{arg}`" if self.is_integer(arg) else f"`{arg}` ({addr:#x})"
def print_entry(self, entry: Section) -> None:
line_color = ""
if entry.path == "[stack]":
line_color = gef.config["theme.address_stack"]
elif entry.path == "[heap]":
line_color = gef.config["theme.address_heap"]
elif entry.permission & Permission.READ and entry.permission & Permission.EXECUTE:
line_color = gef.config["theme.address_code"]
line_parts = [
Color.colorify(format_address(entry.page_start), line_color),
Color.colorify(format_address(entry.page_end), line_color),
Color.colorify(format_address(entry.offset), line_color),
]
if entry.permission == Permission.ALL:
line_parts.append(Color.colorify(str(entry.permission), "underline " + line_color))
else:
line_parts.append(Color.colorify(str(entry.permission), line_color))
line_parts.append(Color.colorify(entry.path, line_color))
line = " ".join(line_parts)
gef_print(line)
return
def show_legend(self) -> None:
code_title = Color.colorify("Code", gef.config["theme.address_code"])
stack_title = Color.colorify("Stack", gef.config["theme.address_stack"])
heap_title = Color.colorify("Heap", gef.config["theme.address_heap"])
gef_print(f"[ Legend: {code_title} | {stack_title} | {heap_title} ]")
return
def is_integer(self, n: str) -> bool:
try:
int(n, 0)
except ValueError:
return False
return True
@register
class XFilesCommand(GenericCommand):
"""Shows all libraries (and sections) loaded by binary. This command extends the GDB command
`info files`, by retrieving more information from extra sources, and providing a better
display. If an argument FILE is given, the output will grep information related to only that file.
If an argument name is also given, the output will grep to the name within FILE."""
_cmdline_ = "xfiles"
_syntax_ = f"{_cmdline_} [FILE [NAME]]"
_example_ = f"\n{_cmdline_} libc\n{_cmdline_} libc IO_vtables"
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
color = gef.config["theme.table_heading"]
headers = ["Start", "End", "Name", "File"]
gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<21s} {:s}".format(*headers, w=gef.arch.ptrsize*2+3), color))
filter_by_file = argv[0] if argv and argv[0] else None
filter_by_name = argv[1] if len(argv) > 1 and argv[1] else None
for xfile in get_info_files():
if filter_by_file:
if filter_by_file not in xfile.filename:
continue
if filter_by_name and filter_by_name not in xfile.name:
continue
line_parts = [
format_address(xfile.zone_start),
format_address(xfile.zone_end),
f"{xfile.name:<21s}",
xfile.filename,
]
gef_print(" ".join(line_parts))
return
@register
class XAddressInfoCommand(GenericCommand):
"""Retrieve and display runtime information for the location(s) given as parameter."""
_cmdline_ = "xinfo"
_syntax_ = f"{_cmdline_} LOCATION"
_example_ = f"{_cmdline_} $pc"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_LOCATION)
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if not argv:
err("At least one valid address must be specified")
self.usage()
return
for sym in argv:
try:
addr = align_address(parse_address(sym))
gef_print(titlify(f"xinfo: {addr:#x}"))
self.infos(addr)
except gdb.error as gdb_err:
err(f"{gdb_err}")
return
def infos(self, address: int) -> None:
addr = lookup_address(address)
if not addr.valid:
warn(f"Cannot reach {address:#x} in memory space")
return
sect = addr.section
info = addr.info
if sect:
gef_print(f"Page: {format_address(sect.page_start)} {RIGHT_ARROW} "
f"{format_address(sect.page_end)} (size={sect.page_end-sect.page_start:#x})"
f"\nPermissions: {sect.permission}"
f"\nPathname: {sect.path}"
f"\nOffset (from page): {addr.value-sect.page_start:#x}"
f"\nInode: {sect.inode}")
if info:
gef_print(f"Segment: {info.name} "
f"({format_address(info.zone_start)}-{format_address(info.zone_end)})"
f"\nOffset (from segment): {addr.value-info.zone_start:#x}")
sym = gdb_get_location_from_symbol(address)
if sym:
name, offset = sym
msg = f"Symbol: {name}"
if offset:
msg += f"+{offset:d}"
gef_print(msg)
return
@register
class XorMemoryCommand(GenericCommand):
"""XOR a block of memory. The command allows to simply display the result, or patch it
runtime at runtime."""
_cmdline_ = "xor-memory"
_syntax_ = f"{_cmdline_} (display|patch) ADDRESS SIZE KEY"
def __init__(self) -> None:
super().__init__(prefix=True)
return
def do_invoke(self, _: list[str]) -> None:
self.usage()
return
@register
class XorMemoryDisplayCommand(GenericCommand):
"""Display a block of memory pointed by ADDRESS by xor-ing each byte with KEY. The key must be
provided in hexadecimal format."""
_cmdline_ = "xor-memory display"
_syntax_ = f"{_cmdline_} ADDRESS SIZE KEY"
_example_ = f"{_cmdline_} $sp 16 41414141"
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if len(argv) != 3:
self.usage()
return
address = parse_address(argv[0])
length = int(argv[1], 0)
key = argv[2]
block = gef.memory.read(address, length)
info(f"Displaying XOR-ing {address:#x}-{address + len(block):#x} with {key!r}")
gef_print(titlify("Original block"))
gef_print(hexdump(block, base=address))
gef_print(titlify("XOR-ed block"))
gef_print(hexdump(xor(block, key), base=address))
return
@register
class XorMemoryPatchCommand(GenericCommand):
"""Patch a block of memory pointed by ADDRESS by xor-ing each byte with KEY. The key must be
provided in hexadecimal format."""
_cmdline_ = "xor-memory patch"
_syntax_ = f"{_cmdline_} ADDRESS SIZE KEY"
_example_ = f"{_cmdline_} $sp 16 41414141"
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if len(argv) != 3:
self.usage()
return
address = parse_address(argv[0])
length = int(argv[1], 0)
key = argv[2]
block = gef.memory.read(address, length)
info(f"Patching XOR-ing {address:#x}-{address + len(block):#x} with {key!r}")
xored_block = xor(block, key)
gef.memory.write(address, xored_block, length)
return
@register
class TraceRunCommand(GenericCommand):
"""Create a runtime trace of all instructions executed from $pc to LOCATION specified. The
trace is stored in a text file that can be next imported in IDA Pro to visualize the runtime
path."""
_cmdline_ = "trace-run"
_syntax_ = f"{_cmdline_} LOCATION [MAX_CALL_DEPTH]"
_example_ = f"{_cmdline_} 0x555555554610"
def __init__(self) -> None:
super().__init__(self._cmdline_, complete=gdb.COMPLETE_LOCATION)
self["max_tracing_recursion"] = (1, "Maximum depth of tracing")
self["tracefile_prefix"] = ("./gef-trace-", "Specify the tracing output file prefix")
return
@only_if_gdb_running
def do_invoke(self, argv: list[str]) -> None:
if len(argv) not in (1, 2):
self.usage()
return
if len(argv) == 2 and argv[1].isdigit():
depth = int(argv[1])
else:
depth = 1
try:
loc_start = gef.arch.pc
loc_end = parse_address(argv[0])
except gdb.error as e:
err(f"Invalid location: {e}")
return
self.trace(loc_start, loc_end, depth)
return
def get_frames_size(self) -> int:
n = 0
f = gdb.newest_frame()
while f:
n += 1
f = f.older()
return n
def trace(self, loc_start: int, loc_end: int, depth: int) -> None:
info(f"Tracing from {loc_start:#x} to {loc_end:#x} (max depth={depth:d})")
logfile = f"{self['tracefile_prefix']}{loc_start:#x}-{loc_end:#x}.txt"
with RedirectOutputContext(to_file=logfile):
hide_context()
self.start_tracing(loc_start, loc_end, depth)
unhide_context()
ok(f"Done, logfile stored as '{logfile}'")
info("Hint: import logfile with `ida_color_gdb_trace.py` script in IDA to visualize path")
return
def start_tracing(self, loc_start: int, loc_end: int, depth: int) -> None:
loc_cur = loc_start
frame_count_init = self.get_frames_size()
gef_print("#",
f"# Execution tracing of {get_filepath()}",
f"# Start address: {format_address(loc_start)}",
f"# End address: {format_address(loc_end)}",
f"# Recursion level: {depth:d}",
"# automatically generated by gef.py",
"#\n", sep="\n")
while loc_cur != loc_end:
try:
delta = self.get_frames_size() - frame_count_init
if delta <= depth:
gdb.execute("stepi")
else:
gdb.execute("finish")
loc_cur = gef.arch.pc
gdb.flush()
except gdb.error as e:
gef_print("#",
f"# Execution interrupted at address {format_address(loc_cur)}",
f"# Exception: {e}",
"#\n", sep="\n")
break
return
@register
class PatternCommand(GenericCommand):
"""Generate or Search a De Bruijn Sequence of unique substrings of length N
and a total length of LENGTH. The default value of N is set to match the
currently loaded architecture."""
_cmdline_ = "pattern"
_syntax_ = f"{_cmdline_} (create|search) ARGS"
def __init__(self) -> None:
super().__init__(prefix=True)
self["length"] = (1024, "Default length of a cyclic buffer to generate")
return
def do_invoke(self, _: list[str]) -> None:
self.usage()
return
@register
class PatternCreateCommand(GenericCommand):
"""Generate a De Bruijn Sequence of unique substrings of length N and a
total length of LENGTH. The default value of N is set to match the currently
loaded architecture."""
_cmdline_ = "pattern create"
_syntax_ = f"{_cmdline_} [-h] [-n N] [length]"
_example_ = [
f"{_cmdline_} 4096",
f"{_cmdline_} -n 4 128"
]
@parse_arguments({"length": 0}, {"-n": 0,})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
length = args.length or gef.config["pattern.length"]
n = args.n or gef.arch.ptrsize
info(f"Generating a pattern of {length:d} bytes (n={n:d})")
pattern_str = gef_pystring(generate_cyclic_pattern(length, n))
gef_print(pattern_str)
ok(f"Saved as '{gef_convenience(pattern_str)}'")
return
@register
class PatternSearchCommand(GenericCommand):
"""Search a De Bruijn Sequence of unique substrings of length N and a
maximum total length of MAX_LENGTH. The default value of N is set to match
the currently loaded architecture. The PATTERN argument can be a GDB symbol
(such as a register name), a string or a hexadecimal value"""
_cmdline_ = "pattern search"
_syntax_ = f"{_cmdline_} [-h] [-n N] [--max-length MAX_LENGTH] [pattern]"
_example_ = [f"{_cmdline_} $pc",
f"{_cmdline_} 0x61616164",
f"{_cmdline_} aaab"]
_aliases_ = ["pattern offset"]
@only_if_gdb_running
@parse_arguments({"pattern": ""}, {("--period", "-n"): 0, ("--max-length", "-l"): 0})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args = kwargs["arguments"]
if not args.pattern:
warn("No pattern provided")
return
max_length = args.max_length or gef.config["pattern.length"]
n = args.period or gef.arch.ptrsize
if n not in (2, 4, 8) or n > gef.arch.ptrsize:
err("Incorrect value for period")
return
self.search(args.pattern, max_length, n)
return
def search(self, pattern: str, size: int, period: int) -> None:
pattern_be, pattern_le = None, None
# 1. check if it's a symbol (like "$sp" or "0x1337")
symbol = safe_parse_and_eval(pattern)
if symbol:
addr = int(abs(to_unsigned_long(symbol)))
dereferenced_value = dereference(addr)
if dereferenced_value:
addr = int(abs(to_unsigned_long(dereferenced_value)))
mask = (1<<(8 * period))-1
addr &= mask
pattern_le = addr.to_bytes(period, 'little')
pattern_be = addr.to_bytes(period, 'big')
else:
# 2. assume it's a plain string
pattern_be = gef_pybytes(pattern)
pattern_le = gef_pybytes(pattern[::-1])
info(f"Searching for '{pattern_le.hex()}'/'{pattern_be.hex()}' with period={period}")
cyclic_pattern = generate_cyclic_pattern(size, period)
off = cyclic_pattern.find(pattern_le)
if off >= 0:
ok(f"Found at offset {off:d} (little-endian search) "
f"{Color.colorify('likely', 'bold red') if gef.arch.endianness == Endianness.LITTLE_ENDIAN else ''}")
return
off = cyclic_pattern.find(pattern_be)
if off >= 0:
ok(f"Found at offset {off:d} (big-endian search) "
f"{Color.colorify('likely', 'bold green') if gef.arch.endianness == Endianness.BIG_ENDIAN else ''}")
return
err(f"Pattern '{pattern}' not found")
return
@register
class ChecksecCommand(GenericCommand):
"""Checksec the security properties of the current executable or passed as argument. The
command checks for the following protections:
- PIE
- NX
- RelRO
- Glibc Stack Canaries
- Fortify Source"""
_cmdline_ = "checksec"
_syntax_ = f"{_cmdline_} [FILENAME]"
_example_ = f"{_cmdline_} /bin/ls"
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_FILENAME)
return
def do_invoke(self, argv: list[str]) -> None:
argc = len(argv)
if argc == 0:
filename = get_filepath()
if filename is None:
warn("No executable/library specified")
return
elif argc == 1:
filename = os.path.realpath(os.path.expanduser(argv[0]))
if not os.access(filename, os.R_OK):
err("Invalid filename")
return
else:
self.usage()
return
info(f"{self._cmdline_} for '{filename}'")
self.print_security_properties(filename)
return
def print_security_properties(self, filename: str) -> None:
sec = Elf(filename).checksec
for prop in sec:
if prop in ("Partial RelRO", "Full RelRO"): continue
val = sec[prop]
msg = Color.greenify(Color.boldify(TICK)) if val is True else Color.redify(Color.boldify(CROSS))
if val and prop == "Canary" and is_alive():
canary = gef.session.canary[0] if gef.session.canary else 0
msg += f"(value: {canary:#x})"
gef_print(f"{prop:<30s}: {msg}")
if sec["Full RelRO"]:
gef_print(f"{'RelRO':<30s}: {Color.greenify('Full')}")
elif sec["Partial RelRO"]:
gef_print(f"{'RelRO':<30s}: {Color.yellowify('Partial')}")
else:
gef_print(f"{'RelRO':<30s}: {Color.redify(Color.boldify(CROSS))}")
return
@register
class GotCommand(GenericCommand):
"""Display current status of the got inside the process."""
_cmdline_ = "got"
_syntax_ = f"{_cmdline_} [FUNCTION_NAME ...] "
_example_ = "got read printf exit"
def __init__(self):
super().__init__()
self["function_resolved"] = ("green",
"Line color of the got command output for resolved function")
self["function_not_resolved"] = ("yellow",
"Line color of the got command output for unresolved function")
return
def build_line(self, name: str, _path: str, color: str, address_val: int, got_address: int) -> str:
line = f"[{hex(address_val)}] "
line += Color.colorify(f"{name} {RIGHT_ARROW} {hex(got_address)}", color)
return line
@only_if_gdb_running
@parse_arguments({"symbols": [""]}, {"--all": False})
def do_invoke(self, _: list[str], **kwargs: Any) -> None:
args : argparse.Namespace = kwargs["arguments"]
vmmap = gef.memory.maps
mapfiles = [mapfile for mapfile in vmmap if
(args.all or mapfile.path == str(gef.session.file)) and
pathlib.Path(mapfile.realpath).is_file() and
mapfile.permission & Permission.EXECUTE]
for mapfile in mapfiles:
self.print_got_for(mapfile.path, mapfile.realpath, args.symbols)
def print_got_for(self, file: str, realpath: str, argv: list[str]) -> None:
readelf = gef.session.constants["readelf"]
elf_file = realpath
elf_virtual_path = file
func_names_filter = argv if argv else []
vmmap = gef.memory.maps
base_address = min(x.page_start for x in vmmap if x.path == elf_virtual_path)
end_address = max(x.page_end for x in vmmap if x.path == elf_virtual_path)
# get the checksec output.
checksec_status = Elf(elf_file).checksec
relro_status = "Full RelRO"
full_relro = checksec_status["Full RelRO"]
pie = checksec_status["PIE"] # if pie we will have offset instead of abs address.
if not full_relro:
relro_status = "Partial RelRO"
partial_relro = checksec_status["Partial RelRO"]
if not partial_relro:
relro_status = "No RelRO"
# retrieve jump slots using readelf
lines = gef_execute_external([readelf, "--wide", "--relocs", elf_file], as_list=True)
jmpslots = [line for line in lines if "JUMP" in line]
gef_print(f"{titlify(file)}\n\nGOT protection: {relro_status} | GOT functions: {len(jmpslots)}\n ")
for line in jmpslots:
address, _, _, _, name = line.split()[:5]
# if we have a filter let's skip the entries that are not requested.
if func_names_filter:
if not any(map(lambda x: x in name, func_names_filter)):
continue
address_val = int(address, 16)
# address_val is an offset from the base_address if we have PIE.
if pie or is_remote_debug():
address_val = base_address + address_val
# read the address of the function.
got_address = gef.memory.read_integer(address_val)
# for the swag: different colors if the function has been resolved or not.
if base_address < got_address < end_address:
color = self["function_not_resolved"]
else:
color = self["function_resolved"]
line = self.build_line(name, elf_virtual_path, color, address_val, got_address)
gef_print(line)
return
@register
class HighlightCommand(GenericCommand):
"""Highlight user-defined text matches in GEF output universally."""
_cmdline_ = "highlight"
_syntax_ = f"{_cmdline_} (add|remove|list|clear)"
_aliases_ = ["hl"]
def __init__(self) -> None:
super().__init__(prefix=True)
self["regex"] = (False, "Enable regex highlighting")
def do_invoke(self, _: list[str]) -> None:
return self.usage()
@register
class HighlightListCommand(GenericCommand):
"""Show the current highlight table with matches to colors."""
_cmdline_ = "highlight list"
_aliases_ = ["highlight ls", "hll"]
_syntax_ = _cmdline_
def print_highlight_table(self) -> None:
if not gef.ui.highlight_table:
err("no matches found")
return
left_pad = max(map(len, gef.ui.highlight_table.keys()))
for match, color in sorted(gef.ui.highlight_table.items()):
print(f"{Color.colorify(match.ljust(left_pad), color)} {VERTICAL_LINE} "
f"{Color.colorify(color, color)}")
return
def do_invoke(self, _: list[str]) -> None:
return self.print_highlight_table()
@register
class HighlightClearCommand(GenericCommand):
"""Clear the highlight table, remove all matches."""
_cmdline_ = "highlight clear"
_aliases_ = ["hlc"]
_syntax_ = _cmdline_
def do_invoke(self, _: list[str]) -> None:
return gef.ui.highlight_table.clear()
@register
class HighlightAddCommand(GenericCommand):
"""Add a match to the highlight table."""
_cmdline_ = "highlight add"
_syntax_ = f"{_cmdline_} MATCH COLOR"
_aliases_ = ["highlight set", "hla"]
_example_ = f"{_cmdline_} 41414141 yellow"
def do_invoke(self, argv: list[str]) -> None:
if len(argv) < 2:
return self.usage()
match, color = argv
gef.ui.highlight_table[match] = color
return
@register
class HighlightRemoveCommand(GenericCommand):
"""Remove a match in the highlight table."""
_cmdline_ = "highlight remove"
_syntax_ = f"{_cmdline_} MATCH"
_aliases_ = [
"highlight delete",
"highlight del",
"highlight unset",
"highlight rm",
"hlr",
]
_example_ = f"{_cmdline_} remove 41414141"
def do_invoke(self, argv: list[str]) -> None:
if not argv:
return self.usage()
gef.ui.highlight_table.pop(argv[0], None)
return
@register
class FormatStringSearchCommand(GenericCommand):
"""Exploitable format-string helper: this command will set up specific breakpoints
at well-known dangerous functions (printf, snprintf, etc.), and check if the pointer
holding the format string is writable, and therefore susceptible to format string
attacks if an attacker can control its content."""
_cmdline_ = "format-string-helper"
_syntax_ = _cmdline_
_aliases_ = ["fmtstr-helper",]
def do_invoke(self, _: list[str]) -> None:
dangerous_functions = {
"printf": 0,
"sprintf": 1,
"fprintf": 1,
"snprintf": 2,
"vsnprintf": 2,
}
nb_installed_breaks = 0
with RedirectOutputContext(to_file="/dev/null"):
for function_name in dangerous_functions:
argument_number = dangerous_functions[function_name]
FormatStringBreakpoint(function_name, argument_number)
nb_installed_breaks += 1
ok(f"Enabled {nb_installed_breaks} FormatString "
f"breakpoint{'s' if nb_installed_breaks > 1 else ''}")
return
@register
class HeapAnalysisCommand(GenericCommand):
"""Heap vulnerability analysis helper: this command aims to track dynamic heap allocation
done through malloc()/free() to provide some insights on possible heap vulnerabilities. The
following vulnerabilities are checked:
- NULL free
- Use-after-Free
- Double Free
- Heap overlap"""
_cmdline_ = "heap-analysis-helper"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__(complete=gdb.COMPLETE_NONE)
self["check_free_null"] = (False, "Break execution when a free(NULL) is encountered")
self["check_double_free"] = (True, "Break execution when a double free is encountered")
self["check_weird_free"] = (True, "Break execution when free() is called against a non-tracked pointer")
self["check_uaf"] = (True, "Break execution when a possible Use-after-Free condition is found")
self["check_heap_overlap"] = (True, "Break execution when a possible overlap in allocation is found")
self.bp_malloc = None
self.bp_calloc = None
self.bp_free = None
self.bp_realloc = None
return
@only_if_gdb_running
@experimental_feature
def do_invoke(self, argv: list[str]) -> None:
if not argv:
self.setup()
return
if argv[0] == "show":
self.dump_tracked_allocations()
return
def setup(self) -> None:
ok("Tracking malloc() & calloc()")
self.bp_malloc = TraceMallocBreakpoint("__libc_malloc")
self.bp_calloc = TraceMallocBreakpoint("__libc_calloc")
ok("Tracking free()")
self.bp_free = TraceFreeBreakpoint()
ok("Tracking realloc()")
self.bp_realloc = TraceReallocBreakpoint()
ok("Disabling hardware watchpoints (this may increase the latency)")
gdb.execute("set can-use-hw-watchpoints 0")
info("Dynamic breakpoints correctly setup, "
"GEF will break execution if a possible vulnerability is found.")
warn(f"{Color.colorify('Note', 'bold underline yellow')}: "
"The heap analysis slows down the execution noticeably.")
# when inferior quits, we need to clean everything for a next execution
gef_on_exit_hook(self.clean)
return
def dump_tracked_allocations(self) -> None:
global gef
if gef.session.heap_allocated_chunks:
ok("Tracked as in-use chunks:")
for addr, sz in gef.session.heap_allocated_chunks:
gef_print(f"{CROSS} malloc({sz:d}) = {addr:#x}")
else:
ok("No malloc() chunk tracked")
if gef.session.heap_freed_chunks:
ok("Tracked as free-ed chunks:")
for addr, sz in gef.session.heap_freed_chunks:
gef_print(f"{TICK} free({sz:d}) = {addr:#x}")
else:
ok("No free() chunk tracked")
return
def clean(self, _: "gdb.ExitedEvent") -> None:
global gef
ok(f"{Color.colorify('Heap-Analysis', 'yellow bold')} - Cleaning up")
for bp in [self.bp_malloc, self.bp_calloc, self.bp_free, self.bp_realloc]:
if hasattr(bp, "retbp") and bp.retbp:
try:
bp.retbp.delete()
except RuntimeError:
# in some cases, gdb was found failing to correctly remove the retbp
# but they can be safely ignored since the debugging session is over
pass
bp.delete()
for wp in gef.session.heap_uaf_watchpoints:
wp.delete()
gef.session.heap_allocated_chunks = []
gef.session.heap_freed_chunks = []
gef.session.heap_uaf_watchpoints = []
ok(f"{Color.colorify('Heap-Analysis', 'yellow bold')} - Re-enabling hardware watchpoints")
gdb.execute("set can-use-hw-watchpoints 1")
gef_on_exit_unhook(self.clean)
return
#
# GDB Function declaration
#
@deprecated("")
def register_function(cls: Type["GenericFunction"]) -> Type["GenericFunction"]:
"""Decorator for registering a new convenience function to GDB."""
return cls
class GenericFunction(gdb.Function):
"""This is an abstract class for invoking convenience functions, should not be instantiated."""
_function_ : str
_syntax_: str = ""
_example_ : str = ""
def __init__(self) -> None:
super().__init__(self._function_)
def invoke(self, *args: Any) -> int:
if not is_alive():
raise gdb.GdbError("No debugging session active")
return self.do_invoke(args)
def arg_to_long(self, args: Any, index: int, default: int = 0) -> int:
try:
addr = args[index]
return int(addr) if addr.address is None else int(addr.address)
except IndexError:
return default
def do_invoke(self, args: Any) -> int:
raise NotImplementedError
@register
class StackOffsetFunction(GenericFunction):
"""Return the current stack base address plus an optional offset."""
_function_ = "_stack"
_syntax_ = f"${_function_}()"
def do_invoke(self, args: list) -> int:
base = get_section_base_address("[stack]")
if not base:
raise gdb.GdbError("Stack not found")
return self.arg_to_long(args, 0) + base
@register
class HeapBaseFunction(GenericFunction):
"""Return the current heap base address plus an optional offset."""
_function_ = "_heap"
_syntax_ = f"${_function_}()"
def do_invoke(self, args: list[str]) -> int:
base = gef.heap.base_address
if not base:
base = get_section_base_address("[heap]")
if not base:
raise gdb.GdbError("Heap not found")
return self.arg_to_long(args, 0) + base
@register
class SectionBaseFunction(GenericFunction):
"""Return the matching file's base address plus an optional offset.
Defaults to current file. Note that quotes need to be escaped"""
_function_ = "_base"
_syntax_ = "$_base([filepath])"
_example_ = "p $_base(\\\"/usr/lib/ld-2.33.so\\\")"
def do_invoke(self, args: list) -> int:
addr = 0
try:
name = args[0].string()
except IndexError:
assert gef.session.file
name = gef.session.file.name
except gdb.error:
err(f"Invalid arg: {args[0]}")
return 0
try:
base = get_section_base_address(name)
if base:
addr = int(base)
except TypeError:
err(f"Cannot find section {name}")
return 0
return addr
@register
class BssBaseFunction(GenericFunction):
"""Return the current bss base address plus the given offset."""
_function_ = "_bss"
_syntax_ = f"${_function_}([OFFSET])"
_example_ = "deref $_bss(0x20)"
def do_invoke(self, args: list) -> int:
base = get_zone_base_address(".bss")
if not base:
raise gdb.GdbError("BSS not found")
return self.arg_to_long(args, 0) + base
@register
class GotBaseFunction(GenericFunction):
"""Return the current GOT base address plus the given offset."""
_function_ = "_got"
_syntax_ = f"${_function_}([OFFSET])"
_example_ = "deref $_got(0x20)"
def do_invoke(self, args: list) -> int:
base = get_zone_base_address(".got")
if not base:
raise gdb.GdbError("GOT not found")
return base + self.arg_to_long(args, 0)
@register
class GefFunctionsCommand(GenericCommand):
"""List the convenience functions provided by GEF."""
_cmdline_ = "functions"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__()
self.docs = []
self.should_refresh = True
return
def __add__(self, function: GenericFunction):
"""Add function to documentation."""
doc = getattr(function, "__doc__", "").lstrip()
if not hasattr(function, "_syntax_"):
raise ValueError("Function is invalid")
syntax = getattr(function, "_syntax_").lstrip()
msg = f"{Color.colorify(syntax, 'bold cyan')}\n {doc}"
example = getattr(function, "_example_", "").strip()
if example:
msg += f"\n {Color.yellowify('Example:')} {example}"
self.docs.append(msg)
return self
def __radd__(self, function: GenericFunction):
return self.__add__(function)
def __str__(self) -> str:
if self.should_refresh:
self.__rebuild()
return self.__doc__ or ""
def __rebuild(self) -> None:
"""Rebuild the documentation for functions."""
for function in gef.gdb.functions.values():
self += function
self.command_size = len(gef.gdb.commands)
_, cols = get_terminal_size()
separator = HORIZONTAL_LINE*cols
self.__doc__ = f"\n{separator}\n".join(sorted(self.docs))
self.should_refresh = False
return
def do_invoke(self, argv) -> None:
self.dont_repeat()
gef_print(titlify("GEF - Convenience Functions"))
gef_print("These functions can be used as arguments to other "
"commands to dynamically calculate values\n")
gef_print(str(self))
return
#
# GEF internal command classes
#
class GefCommand(gdb.Command):
"""GEF main command: view all new commands by typing `gef`."""
_cmdline_ = "gef"
_syntax_ = f"{_cmdline_} (missing|config|save|restore|set|run)"
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True)
gef.config["gef.follow_child"] = GefSetting(True, bool, "Automatically set GDB to follow child when forking")
gef.config["gef.readline_compat"] = GefSetting(False, bool, "Workaround for readline SOH/ETX issue (SEGV)")
gef.config["gef.debug"] = GefSetting(False, bool, "Enable debug mode for gef")
gef.config["gef.autosave_breakpoints_file"] = GefSetting("", str, "Automatically save and restore breakpoints")
gef.config["gef.disable_target_remote_overwrite"] = GefSetting(False, bool, "Disable the overwrite of `target remote`")
plugins_dir = GefSetting("", str, "Autoload additional GEF commands from external directory", hooks={"on_write": [GefSetting.no_spaces, ]})
plugins_dir.add_hook("on_changed", [lambda _, new_val: GefSetting.must_exist(new_val), lambda _, new_val: self.load_extra_plugins(new_val), ])
gef.config["gef.extra_plugins_dir"] = plugins_dir
gef.config["gef.disable_color"] = GefSetting(False, bool, "Disable all colors in GEF")
gef.config["gef.tempdir"] = GefSetting(GEF_TEMP_DIR, pathlib.Path, "Directory to use for temporary/cache content", hooks={"on_write": [GefSetting.no_spaces, GefSetting.create_folder_tree]})
gef.config["gef.show_deprecation_warnings"] = GefSetting(True, bool, "Toggle the display of the `deprecated` warnings")
gef.config["gef.buffer"] = GefSetting(True, bool, "Internally buffer command output until completion")
gef.config["gef.bruteforce_main_arena"] = GefSetting(False, bool, "Allow bruteforcing main_arena symbol if everything else fails")
gef.config["gef.libc_version"] = GefSetting("", str, "Specify libc version when auto-detection fails")
gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). Set to empty string to disable.")
gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.")
self.commands : dict[str, GenericCommand] = {}
self.functions : dict[str, GenericFunction] = {}
self.missing: dict[str, Exception] = {}
return
@property
@deprecated()
def loaded_commands(self) -> list[tuple[str, Type[GenericCommand], Any]]:
raise ObsoleteException("Obsolete loaded_commands")
@property
@deprecated()
def loaded_functions(self) -> list[Type[GenericFunction]]:
raise ObsoleteException("Obsolete loaded_functions")
@property
@deprecated()
def missing_commands(self) -> dict[str, Exception]:
raise ObsoleteException("Obsolete missing_commands")
def setup(self) -> None:
self.load()
GefHelpCommand()
GefConfigCommand()
GefSaveCommand()
GefMissingCommand()
GefSetCommand()
GefRunCommand()
GefInstallExtraScriptCommand()
# At this point, commands (incl. extras) are loaded with default settings.
# Load custom settings from config file if any
GefRestoreCommand()
return
def load_extra_plugins(self, extra_plugins_dir: pathlib.Path | None = None) -> int:
"""Load the plugins from the gef-extras setting. Returns the number of new plugins added."""
def load_plugin(fpath: pathlib.Path) -> bool:
try:
dbg(f"Loading '{fpath}'")
gdb.execute(f"source {fpath}")
except AlreadyRegisteredException:
pass
except Exception as e:
warn(f"Exception while loading {fpath}: {str(e)}")
return False
return True
def load_plugins_from_directory(plugin_directory: pathlib.Path):
nb_added = -1
nb_initial = len(__registered_commands__)
start_time = time.perf_counter()
for entry in plugin_directory.glob("**/*.py"):
load_plugin(entry)
try:
nb_added = len(__registered_commands__) - nb_initial
if nb_added > 0:
self.load()
nb_failed = len(__registered_commands__) - len(self.commands)
load_time = time.perf_counter() - start_time
ok(f"{Color.colorify(str(nb_added), 'bold green')} extra commands added " \
f"in {load_time:.2f} seconds")
if nb_failed != 0:
warn(f"{Color.colorify(str(nb_failed), 'bold light_gray')} extra commands/functions failed to be added. "
"Check `gef missing` to know why")
except gdb.error as e:
err(f"failed: {e}")
return nb_added
directory = extra_plugins_dir or gef.config["gef.extra_plugins_dir"]
if not directory:
return 0
directory = pathlib.Path(directory).expanduser().absolute()
if not directory.exists():
return 0
dbg(f"Loading extra plugins from directory={directory}")
return load_plugins_from_directory(directory)
@property
def loaded_command_names(self) -> Iterable[str]:
print("obsolete loaded_command_names")
return self.commands.keys()
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
gdb.execute("gef help")
return
def add_context_layout_mapping(self, current_pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Callable | None) -> None:
"""Add a new context layout mapping."""
context = self.commands["context"]
assert isinstance(context, ContextCommand)
# overload the printing of pane title
context.layout_mapping[current_pane_name] = (display_pane_function, pane_title_function, condition)
def add_context_pane(self, pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Callable | None) -> None:
"""Add a new context pane to ContextCommand."""
# assure users can toggle the new context
corrected_settings_name: str = pane_name.replace(" ", "_")
if corrected_settings_name in gef.config["context.layout"]:
warn(f"Duplicate name for `{pane_name}` (`{corrected_settings_name}`), skipping")
return
gef.config["context.layout"] += f" {corrected_settings_name}"
self.add_context_layout_mapping(corrected_settings_name, display_pane_function, pane_title_function, condition)
def load(self) -> None:
"""Load all the commands and functions defined by GEF into GDB."""
current_commands = set(self.commands.keys())
new_commands = set(x._cmdline_ for x in __registered_commands__) - current_commands
current_functions = set(self.functions.keys())
new_functions = set(x._function_ for x in __registered_functions__) - current_functions
self.missing.clear()
self.__load_time_ms = time.time()* 1000
# load all new functions
for name in sorted(new_functions):
for function_cls in __registered_functions__:
if function_cls._function_ == name:
assert issubclass(function_cls, GenericFunction)
self.functions[name] = function_cls()
break
# load all new commands
for name in sorted(new_commands):
try:
for command_cls in __registered_commands__:
if command_cls._cmdline_ == name:
assert issubclass(command_cls, GenericCommand)
command_instance = command_cls()
# create the aliases if any
if hasattr(command_instance, "_aliases_"):
aliases = getattr(command_instance, "_aliases_")
for alias in aliases:
GefAlias(alias, name)
self.commands[name] = command_instance
break
except Exception as reason:
self.missing[name] = reason
self.__load_time_ms = (time.time()* 1000) - self.__load_time_ms
return
def show_banner(self) -> None:
gef_print(f"{Color.greenify('GEF')} for {gef.session.os} ready, "
f"type `{Color.colorify('gef', 'underline yellow')}' to start, "
f"`{Color.colorify('gef config', 'underline pink')}' to configure")
ver = f"{sys.version_info.major:d}.{sys.version_info.minor:d}"
gef_print(f"{Color.colorify(str(len(self.commands)), 'bold green')} commands loaded "
f"and {Color.colorify(str(len(self.functions)), 'bold blue')} functions added for "
f"GDB {Color.colorify(gdb.VERSION, 'bold yellow')} in {self.__load_time_ms:.2f}ms "
f"using Python engine {Color.colorify(ver, 'bold red')}")
nb_missing = len(self.missing)
if nb_missing:
warn(f"{Color.colorify(str(nb_missing), 'bold red')} "
f"command{'s' if nb_missing > 1 else ''} could not be loaded, "
f"run `{Color.colorify('gef missing', 'underline pink')}` to know why.")
return
class GefHelpCommand(gdb.Command):
"""GEF help sub-command."""
_cmdline_ = "gef help"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
self.docs = []
self.should_refresh = True
self.command_size = 0
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
gef_print(titlify("GEF - GDB Enhanced Features"))
gef_print(str(self))
return
def __rebuild(self) -> None:
"""Rebuild the documentation."""
for name, cmd in gef.gdb.commands.items():
self += (name, cmd)
self.command_size = len(gef.gdb.commands)
_, cols = get_terminal_size()
separator = HORIZONTAL_LINE*cols
self.__doc__ = f"\n{separator}\n".join(sorted(self.docs))
self.should_refresh = False
return
def __add__(self, command: tuple[str, GenericCommand]):
"""Add command to GEF documentation."""
cmd, class_obj = command
if " " in cmd:
# do not print subcommands in gef help
return self
doc = getattr(class_obj, "__doc__", "").lstrip()
aliases = f"Aliases: {', '.join(class_obj._aliases_)}" if hasattr(class_obj, "_aliases_") else ""
msg = f"{Color.colorify(cmd, 'bold red')}\n{doc}\n{aliases}"
self.docs.append(msg)
return self
def __radd__(self, command: tuple[str, GenericCommand]):
return self.__add__(command)
def __str__(self) -> str:
"""Lazily regenerate the `gef help` object if it was modified"""
# quick check in case the docs have changed
if self.should_refresh or self.command_size != len(gef.gdb.commands):
self.__rebuild()
return self.__doc__ or ""
class GefConfigCommand(gdb.Command):
"""GEF configuration sub-command
This command will help set/view GEF settings for the current debugging session.
It is possible to make those changes permanent by running `gef save` (refer
to this command help), and/or restore previously saved settings by running
`gef restore` (refer help).
"""
_cmdline_ = "gef config"
_syntax_ = f"{_cmdline_} [setting_name] [setting_value]"
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_NONE, prefix=False)
return
def invoke(self, args: str, from_tty: bool) -> None:
self.dont_repeat()
argv = gdb.string_to_argv(args)
argc = len(argv)
if not (0 <= argc <= 2):
err("Invalid number of arguments")
return
if argc == 0:
gef_print(titlify("GEF configuration settings"))
self.print_settings()
return
if argc == 1:
prefix = argv[0]
names = [x for x in gef.config.keys() if x.startswith(prefix)]
if names:
if len(names) == 1:
gef_print(titlify(f"GEF configuration setting: {names[0]}"))
self.print_setting(names[0], verbose=True)
else:
gef_print(titlify(f"GEF configuration settings matching '{argv[0]}'"))
for name in names: self.print_setting(name)
return
if not is_debug():
try:
self.set_setting(argv)
except (ValueError, KeyError) as e:
err(str(e))
else:
# Let exceptions (if any) propagate
self.set_setting(argv)
return
def print_setting(self, plugin_name: str, verbose: bool = False) -> None:
res = gef.config.raw_entry(plugin_name)
string_color = gef.config["theme.dereference_string"]
misc_color = gef.config["theme.dereference_base_address"]
if not res:
return
_setting = Color.colorify(plugin_name, "green")
_type = res.type.__name__
if _type == "str":
_value = f'"{Color.colorify(res.value, string_color)}"'
else:
_value = Color.colorify(res.value, misc_color)
gef_print(f"{_setting} ({_type}) = {_value}")
if verbose:
gef_print(Color.colorify("\nDescription:", "bold underline"))
gef_print(f"\t{res.description}")
return
def print_settings(self) -> None:
for x in sorted(gef.config):
self.print_setting(x)
return
def set_setting(self, argv: list[str]) -> bool:
global gef
key, new_value = argv
if "." not in key:
err("Invalid command format")
return False
loaded_commands = list( gef.gdb.commands.keys()) + ["gef"]
plugin_name = key.split(".", 1)[0]
if plugin_name not in loaded_commands:
err(f"Unknown plugin '{plugin_name}'")
return False
if key not in gef.config:
dbg(f"'{key}' is not a valid configuration setting")
return False
_type = gef.config.raw_entry(key).type
# Attempt to parse specific values for known types
if _type is bool:
if new_value.upper() in ("TRUE", "T", "1"):
_newval = True
elif new_value.upper() in ("FALSE", "F", "0"):
_newval = False
else:
raise ValueError(f"Cannot parse '{new_value}' as bool")
else:
_newval = _type(new_value)
gef.config[key] = _newval
reset_all_caches()
return True
def complete(self, text: str, word: str) -> list[str]:
settings = sorted(gef.config)
if text == "":
# no prefix: example: `gef config TAB`
return [s for s in settings if word in s]
if "." not in text:
# if looking for possible prefix
return [s for s in settings if s.startswith(text.strip())]
# finally, look for possible values for given prefix
return [s.split(".", 1)[1] for s in settings if s.startswith(text.strip())]
class GefSaveCommand(gdb.Command):
"""GEF save sub-command.
Saves the current configuration of GEF to disk (by default in file '~/.gef.rc')."""
_cmdline_ = "gef save"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
cfg = configparser.RawConfigParser()
old_sect = None
# save the configuration
for key in sorted(gef.config):
sect, optname = key.split(".", 1)
value = gef.config[key]
if old_sect != sect:
cfg.add_section(sect)
old_sect = sect
cfg.set(sect, optname, value)
# save the aliases
cfg.add_section("aliases")
for alias in gef.session.aliases:
cfg.set("aliases", alias.alias, alias.command)
with GEF_RC.open("w") as fd:
cfg.write(fd)
ok(f"Configuration saved to '{GEF_RC}'")
return
class GefRestoreCommand(gdb.Command):
"""GEF restore sub-command.
Loads settings from file '~/.gef.rc' and apply them to the configuration of GEF."""
_cmdline_ = "gef restore"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
self.reload(True)
return
def invoke(self, args: str, from_tty: bool) -> None:
self.dont_repeat()
if GEF_RC.is_file():
quiet = (args.lower() == "quiet")
self.reload(quiet)
return
def reload(self, quiet: bool):
cfg = configparser.ConfigParser()
cfg.read(GEF_RC)
for section in cfg.sections():
if section == "aliases":
# load the aliases
for key in cfg.options(section):
try:
GefAlias(key, cfg.get(section, key))
except Exception as e:
dbg(f"GefAlias() raised exception {e}")
continue
# load the other options
for optname in cfg.options(section):
key = f"{section}.{optname}"
try:
setting = gef.config.raw_entry(key)
except Exception:
continue
new_value = cfg.get(section, optname)
if setting.type is bool:
new_value = True if new_value.upper() in ("TRUE", "T", "1") else False
setting.value = setting.type(new_value)
if not quiet:
ok(f"Configuration from '{Color.colorify(str(GEF_RC), 'bold blue')}' restored")
return
class GefMissingCommand(gdb.Command):
"""GEF missing sub-command
Display the GEF commands that could not be loaded, along with the reason of why
they could not be loaded.
"""
_cmdline_ = "gef missing"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
missing_commands: dict[str, Exception] = gef.gdb.missing
if not missing_commands:
ok("No missing command")
return
for cmd, exc in missing_commands.items():
warn(f"Missing `{cmd}`: reason: {str(exc)})")
return
class GefSetCommand(gdb.Command):
"""Override GDB set commands with the context from GEF."""
_cmdline_ = "gef set"
_syntax_ = f"{_cmdline_} [GDB_SET_ARGUMENTS]"
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_SYMBOL, False)
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
args = args.split()
cmd = ["set", args[0],]
for p in args[1:]:
if p.startswith("$_gef"):
c = gdb.parse_and_eval(p)
cmd.append(c.string())
else:
cmd.append(p)
gdb.execute(" ".join(cmd))
return
class GefRunCommand(gdb.Command):
"""Override GDB run commands with the context from GEF.
Simple wrapper for GDB run command to use arguments set from `gef set args`."""
_cmdline_ = "gef run"
_syntax_ = f"{_cmdline_} [GDB_RUN_ARGUMENTS]"
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_FILENAME, False)
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
if is_alive():
gdb.execute("continue")
return
argv = args.split()
gdb.execute(f"gef set args {' '.join(argv)}")
gdb.execute("run")
return
class GefAlias(gdb.Command):
"""Simple aliasing wrapper because GDB doesn't do what it should."""
def __init__(self, alias: str, command: str, completer_class: int = gdb.COMPLETE_NONE, command_class: int = gdb.COMMAND_NONE) -> None:
p = command.split()
if not p:
return
if any(x for x in gef.session.aliases if x.alias == alias):
return
self.command = command
self.alias = alias
c = command.split()[0]
r = self.lookup_command(c)
self.__doc__ = f"Alias for '{Color.greenify(command)}'"
if r is not None:
_instance = r[1]
self.__doc__ += f": {_instance.__doc__}"
if hasattr(_instance, "complete"):
self.complete = _instance.complete
super().__init__(alias, command_class, completer_class=completer_class)
gef.session.aliases.append(self)
return
def __repr__(self) -> str:
return f"GefAlias(from={self.alias}, to={self.command})"
def __str__(self) -> str:
return f"GefAlias(from={self.alias}, to={self.command})"
def invoke(self, args: Any, from_tty: bool) -> None:
gdb.execute(f"{self.command} {args}", from_tty=from_tty)
return
def lookup_command(self, cmd: str) -> tuple[str, GenericCommand] | None:
global gef
for _name, _instance in gef.gdb.commands.items():
if cmd == _name:
return _name, _instance
return None
@register
class AliasesCommand(GenericCommand):
"""Base command to add, remove, or list aliases."""
_cmdline_ = "aliases"
_syntax_ = f"{_cmdline_} (add|rm|ls)"
def __init__(self) -> None:
super().__init__(prefix=True)
return
def do_invoke(self, _: list[str]) -> None:
self.usage()
return
@register
class AliasesAddCommand(AliasesCommand):
"""Command to add aliases."""
_cmdline_ = "aliases add"
_syntax_ = f"{_cmdline_} [ALIAS] [COMMAND]"
_example_ = f"{_cmdline_} scope telescope"
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, argv: list[str]) -> None:
if len(argv) < 2:
self.usage()
return
GefAlias(argv[0], " ".join(argv[1:]))
return
@register
class AliasesRmCommand(AliasesCommand):
"""Command to remove aliases."""
_cmdline_ = "aliases rm"
_syntax_ = f"{_cmdline_} [ALIAS]"
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, argv: list[str]) -> None:
global gef
if len(argv) != 1:
self.usage()
return
try:
alias_to_remove = next(filter(lambda x: x.alias == argv[0], gef.session.aliases))
gef.session.aliases.remove(alias_to_remove)
except (ValueError, StopIteration):
err(f"{argv[0]} not found in aliases.")
return
gef_print("You must reload GEF for alias removals to apply.")
return
@register
class AliasesListCommand(AliasesCommand):
"""Command to list aliases."""
_cmdline_ = "aliases ls"
_syntax_ = _cmdline_
def __init__(self) -> None:
super().__init__()
return
def do_invoke(self, _: list[str]) -> None:
ok("Aliases defined:")
for a in gef.session.aliases:
gef_print(f"{a.alias:30s} {RIGHT_ARROW} {a.command}")
return
class GefTmuxSetup(gdb.Command):
"""Setup a comfortable tmux debugging environment."""
def __init__(self) -> None:
super().__init__("tmux-setup", gdb.COMMAND_NONE, gdb.COMPLETE_NONE)
GefAlias("screen-setup", "tmux-setup")
return
def invoke(self, args: Any, from_tty: bool) -> None:
self.dont_repeat()
tmux = os.getenv("TMUX")
if tmux:
self.tmux_setup()
return
screen = os.getenv("TERM")
if screen is not None and screen == "screen":
self.screen_setup()
return
warn("Not in a tmux/screen session")
return
def tmux_setup(self) -> None:
"""Prepare the tmux environment by vertically splitting the current pane, and
forcing the context to be redirected there."""
tmux = which("tmux")
ok("tmux session found, splitting window...")
pane, pty = subprocess.check_output([tmux, "splitw", "-h", '-F#{session_name}:#{window_index}.#{pane_index}-#{pane_tty}', "-P"]).decode().strip().split("-")
atexit.register(lambda : subprocess.run([tmux, "kill-pane", "-t", pane]))
# clear the screen and let it wait for input forever
gdb.execute(f"!'{tmux}' send-keys -t {pane} 'clear ; cat' C-m")
gdb.execute(f"!'{tmux}' select-pane -L")
ok(f"Setting `context.redirect` to '{pty}'...")
gdb.execute(f"gef config context.redirect {pty}")
ok("Done!")
return
def screen_setup(self) -> None:
"""Hackish equivalent of the tmux_setup() function for screen."""
screen = which("screen")
sty = os.getenv("STY")
ok("screen session found, splitting window...")
fd_script, script_path = tempfile.mkstemp()
fd_tty, tty_path = tempfile.mkstemp()
os.close(fd_tty)
with os.fdopen(fd_script, "w") as f:
f.write("startup_message off\n")
f.write("split -v\n")
f.write("focus right\n")
f.write(f"screen bash -c 'tty > {tty_path}; clear; cat'\n")
f.write("focus left\n")
gdb.execute(f"!'{screen}' -r '{sty}' -m -d -X source {script_path}")
# artificial delay to make sure `tty_path` is populated
time.sleep(0.25)
with open(tty_path, "r") as f:
pty = f.read().strip()
ok(f"Setting `context.redirect` to '{pty}'...")
gdb.execute(f"gef config context.redirect {pty}")
ok("Done!")
os.unlink(script_path)
os.unlink(tty_path)
return
class GefInstallExtraScriptCommand(gdb.Command):
"""`gef install` command: installs one or more scripts from the `gef-extras` script repo. Note that the command
doesn't check for external dependencies the script(s) might require."""
_cmdline_ = "gef install"
_syntax_ = f"{_cmdline_} SCRIPTNAME [SCRIPTNAME [SCRIPTNAME...]]"
def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
self.branch = gef.config.get("gef.extras_default_branch", GEF_EXTRAS_DEFAULT_BRANCH)
return
def invoke(self, argv: str, from_tty: bool) -> None:
self.dont_repeat()
if not argv:
err("No script name provided")
return
args = argv.split()
if "--list" in args or "-l" in args:
subprocess.run(["xdg-open", f"https://github.com/hugsy/gef-extras/{self.branch}/"])
return
self.dirpath = gef.config["gef.tempdir"].expanduser().absolute()
if not self.dirpath.is_dir():
err("'gef.tempdir' is not a valid directory")
return
for script in args:
script = script.lower()
if not self.__install_extras_script(script):
warn(f"Failed to install '{script}', skipping...")
return
def __install_extras_script(self, script: str) -> bool:
fpath = self.dirpath / f"{script}.py"
if not fpath.exists():
url = f"https://raw.githubusercontent.com/hugsy/gef-extras/{self.branch}/scripts/{script}.py"
info(f"Searching for '{script}.py' in `gef-extras@{self.branch}`...")
data = http_get(url)
if not data:
warn("Not found")
return False
with fpath.open("wb") as fd:
fd.write(data)
fd.flush()
old_command_set = set(gef.gdb.commands)
gdb.execute(f"source {fpath}")
new_command_set = set(gef.gdb.commands)
new_commands = [f"`{c[0]}`" for c in (new_command_set - old_command_set)]
ok(f"Installed file '{fpath}', new command(s) available: {', '.join(new_commands)}")
return True
#
# GEF internal classes
#
def __gef_prompt__(current_prompt: Callable[[Callable], str]) -> str:
"""GEF custom prompt function."""
if gef.config["gef.readline_compat"] is True: return GEF_PROMPT
if gef.config["gef.disable_color"] is True: return GEF_PROMPT
prompt = gef.session.remote.mode.prompt_string() if gef.session.remote else ""
prompt += "(core) " if is_target_coredump() else ""
prompt += GEF_PROMPT_ON if is_alive() else GEF_PROMPT_OFF
return prompt
class GefManager(metaclass=abc.ABCMeta):
def reset_caches(self) -> None:
"""Reset the LRU-cached attributes"""
for attr in dir(self):
try:
obj = getattr(self, attr)
if not hasattr(obj, "cache_clear"):
continue
obj.cache_clear()
except Exception:
# we're resetting the cache here, we don't care if (or which) exception triggers
continue
return
class GefMemoryManager(GefManager):
"""Class that manages memory access for gef."""
def __init__(self) -> None:
self.reset_caches()
return
def reset_caches(self) -> None:
super().reset_caches()
self.__maps: list[Section] | None = None
return
def write(self, address: int, buffer: ByteString, length: int | None = None) -> None:
"""Write `buffer` at address `address`."""
length = length or len(buffer)
gdb.selected_inferior().write_memory(address, buffer, length)
def read(self, addr: int, length: int = 0x10) -> bytes:
"""Return a `length` long byte array with the copy of the process memory at `addr`."""
return gdb.selected_inferior().read_memory(addr, length).tobytes()
def read_integer(self, addr: int) -> int:
"""Return an integer read from memory."""
sz = gef.arch.ptrsize
mem = self.read(addr, sz)
unpack = u32 if sz == 4 else u64
return unpack(mem)
def read_cstring(self,
address: int,
max_length: int = GEF_MAX_STRING_LENGTH,
encoding: str | None = None) -> str:
"""Return a C-string read from memory."""
encoding = encoding or "unicode-escape"
length = min(address | (DEFAULT_PAGE_SIZE-1), max_length+1)
try:
res_bytes = self.read(address, length)
except gdb.error:
current_address = address
res_bytes = b""
while len(res_bytes) < length:
try:
# Calculate how many bytes there are until next page
next_page = current_address + DEFAULT_PAGE_SIZE
page_mask = ~(DEFAULT_PAGE_SIZE - 1)
size = (next_page & page_mask) - current_address
# Read until the end of the current page
res_bytes += self.read(current_address, size)
current_address += size
except gdb.error:
if not res_bytes:
err(f"Can't read memory at '{address:#x}'")
return ""
break
try:
with warnings.catch_warnings():
# ignore DeprecationWarnings (see #735)
warnings.simplefilter("ignore")
res = res_bytes.decode(encoding, "strict")
except UnicodeDecodeError:
# latin-1 as fallback due to its single-byte to glyph mapping
res = res_bytes.decode("latin-1", "replace")
res = res.split("\x00", 1)[0]
ustr = res.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
if max_length and len(res) > max_length:
return f"{ustr[:max_length]}[...]"
return ustr
def read_ascii_string(self, address: int) -> str | None:
"""Read an ASCII string from memory"""
cstr = self.read_cstring(address)
if isinstance(cstr, str) and cstr and all(x in string.printable for x in cstr):
return cstr
return None
@property
def maps(self) -> list[Section]:
if not self.__maps:
maps = self.__parse_maps()
if not maps:
raise RuntimeError("Failed to determine memory layout")
self.__maps = maps
return self.__maps
def __parse_maps(self) -> list[Section] | None:
"""Return the mapped memory sections. If the current arch has its maps
method defined, then defer to that to generated maps, otherwise, try to
figure it out from procfs, then info sections, then monitor info
mem."""
if gef.arch.maps is not None:
return list(gef.arch.maps())
# Coredumps are the only case where `maintenance info sections` collected more
# info than `info proc sections`.so use this unconditionally. See #1154
if is_target_coredump():
return list(self.parse_gdb_maintenance_info_sections())
try:
return list(self.parse_gdb_info_proc_maps())
except Exception:
pass
try:
return list(self.parse_procfs_maps())
except Exception:
pass
try:
return list(self.parse_monitor_info_mem())
except Exception:
pass
raise RuntimeError("Failed to get memory layout")
@staticmethod
def parse_procfs_maps() -> Generator[Section, None, None]:
"""Get the memory mapping from procfs."""
procfs_mapfile = gef.session.maps
if not procfs_mapfile:
is_remote = gef.session.remote is not None
raise FileNotFoundError(f"Missing {'remote ' if is_remote else ''}procfs map file")
with procfs_mapfile.open("r") as fd:
for line in fd:
line = line.strip()
addr, perm, off, _, rest = line.split(" ", 4)
rest = rest.split(" ", 1)
if len(rest) == 1:
inode = rest[0]
pathname = ""
else:
inode = rest[0]
pathname = rest[1].lstrip()
addr_start, addr_end = parse_string_range(addr)
off = int(off, 16)
perm = Permission.from_process_maps(perm)
inode = int(inode)
yield Section(page_start=addr_start,
page_end=addr_end,
offset=off,
permission=perm,
inode=inode,
path=pathname)
return
@staticmethod
def parse_gdb_info_proc_maps() -> Generator[Section, None, None]:
"""Get the memory mapping from GDB's command `info proc mappings`."""
if GDB_VERSION < (11, 0):
raise AttributeError("Disregarding old format")
output = (gdb.execute("info proc mappings", to_string=True) or "")
if not output:
raise AttributeError
start_idx = output.find("Start Addr")
if start_idx == -1:
raise AttributeError
output = output[start_idx:]
lines = output.splitlines()
if len(lines) < 2:
raise AttributeError
# The function assumes the following output format (as of GDB 11+) for `info proc mappings`:
# - live process (incl. remote)
# ```
# Start Addr End Addr Size Offset Perms objfile
# 0x555555554000 0x555555558000 0x4000 0x0 r--p /usr/bin/ls
# 0x555555558000 0x55555556c000 0x14000 0x4000 r-xp /usr/bin/ls
# [...]
# ```
# or
# - coredump & rr
# ```
# Start Addr End Addr Size Offset objfile
# 0x555555554000 0x555555558000 0x4000 0x0 /usr/bin/ls
# 0x555555558000 0x55555556c000 0x14000 0x4000 /usr/bin/ls
# ```
# In the latter case the 'Perms' header is missing, so mock the Permission to `rwx` so
# `dereference` will still work.
mock_permission = all(map(lambda x: x.strip() != "Perms", lines[0].split()))
for line in lines[1:]:
if not line:
break
parts = [x.strip() for x in line.split()]
addr_start, addr_end, _, offset = [int(x, 16) for x in parts[0:4]]
if mock_permission:
perm = Permission(7)
path = " ".join(parts[4:]) if len(parts) >= 4 else ""
else:
perm = Permission.from_process_maps(parts[4])
path = " ".join(parts[5:]) if len(parts) >= 5 else ""
yield Section(
page_start=addr_start,
page_end=addr_end,
offset=offset,
permission=perm,
path=path,
)
return
@staticmethod
def parse_monitor_info_mem() -> Generator[Section, None, None]:
"""Get the memory mapping from GDB's command `monitor info mem`
This can raise an exception, which the memory manager takes to mean
that this method does not work to get a map.
"""
stream = StringIO(gdb.execute("monitor info mem", to_string=True))
for line in stream:
try:
ranges, off, perms = line.split()
off = int(off, 16)
start, end = [int(s, 16) for s in ranges.split("-")]
except ValueError:
continue
perm = Permission.from_monitor_info_mem(perms)
yield Section(page_start=start,
page_end=end,
offset=off,
permission=perm)
@staticmethod
def parse_gdb_maintenance_info_sections() -> Generator[Section, None, None]:
"""Get the memory mapping from GDB's command `maintenance info sections` (limited info). In some cases (i.e. coredumps),
the memory info collected by `info proc sections` is insufficient."""
stream = StringIO(gdb.execute("maintenance info sections", to_string=True))
for line in stream:
if not line:
break
try:
parts = line.split()
addr_start, addr_end = [int(x, 16) for x in parts[1].split("->")]
off = int(parts[3][:-1], 16)
path = parts[4]
perm = Permission.NONE
if "DATA" in parts[5:]:
perm |= Permission.READ | Permission.WRITE
if "CODE" in parts[5:]:
perm |= Permission.READ | Permission.EXECUTE
yield Section(
page_start=addr_start,
page_end=addr_end,
offset=off,
permission=perm,
path=path,
)
except IndexError:
continue
except ValueError:
continue
@staticmethod
def parse_info_mem():
"""Get the memory mapping from GDB's command `info mem`. This can be
provided by certain gdbserver implementations."""
for line in StringIO(gdb.execute("info mem", to_string=True)):
# Using memory regions provided by the target.
# Num Enb Low Addr High Addr Attrs
# 0 y 0x10000000 0x10200000 flash blocksize 0x1000 nocache
# 1 y 0x20000000 0x20042000 rw nocache
_, en, start, end, *attrs = line.split()
if en != "y":
continue
if "flash" in attrs:
perm = Permission.from_info_mem("r")
else:
perm = Permission.from_info_mem("rw")
yield Section(page_start=int(start, 0),
page_end=int(end, 0),
permission=perm)
def append(self, section: Section):
if not self.maps:
raise AttributeError("No mapping defined")
if not isinstance(section, Section):
raise TypeError("section has an invalid type")
assert self.__maps
for s in self.__maps:
if section.overlaps(s):
raise RuntimeError(f"{section} overlaps {s}")
self.__maps.append(section)
return self
def __iadd__(self, section: Section):
return self.append(section)
class GefHeapManager(GefManager):
"""Class managing session heap."""
def __init__(self) -> None:
self.reset_caches()
return
def reset_caches(self) -> None:
self.__libc_main_arena: GlibcArena | None = None
self.__libc_selected_arena: GlibcArena | None = None
self.__heap_base = None
return
@property
def main_arena(self) -> GlibcArena | None:
if not self.__libc_main_arena:
try:
__main_arena_addr = GefHeapManager.find_main_arena_addr()
self.__libc_main_arena = GlibcArena(f"*{__main_arena_addr:#x}")
# the initialization of `main_arena` also defined `selected_arena`, so
# by default, `main_arena` == `selected_arena`
self.selected_arena = self.__libc_main_arena
except Exception:
# the search for arena can fail when the session is not started
pass
return self.__libc_main_arena
@main_arena.setter
def main_arena(self, value: GlibcArena) -> None:
self.__libc_main_arena = value
return
@staticmethod
@lru_cache()
def find_main_arena_addr() -> int:
assert gef.libc.version
"""A helper function to find the glibc `main_arena` address, either from
symbol, from its offset from `__malloc_hook` or by brute force."""
# Before anything else, use libc offset from config if available
if gef.config["gef.main_arena_offset"]:
try:
libc_base = get_section_base_address("libc")
offset: int = gef.config["gef.main_arena_offset"]
if libc_base:
dbg(f"Using main_arena_offset={offset:#x} from config")
addr = libc_base + offset
# Verify the found address before returning
if GlibcArena.verify(addr):
return addr
except gdb.error:
pass
# First, try to find `main_arena` symbol directly
try:
return parse_address(f"&{LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME}")
except gdb.error:
pass
# Second, try to find it by offset from `__malloc_hook`
if gef.libc.version < (2, 34):
try:
malloc_hook_addr = parse_address("(void *)&__malloc_hook")
struct_size = ctypes.sizeof(GlibcArena.malloc_state_t())
if is_x86():
addr = align_address_to_size(malloc_hook_addr + gef.arch.ptrsize, 0x20)
elif is_arch(Elf.Abi.AARCH64):
addr = malloc_hook_addr - gef.arch.ptrsize*2 - struct_size
elif is_arch(Elf.Abi.ARM):
addr = malloc_hook_addr - gef.arch.ptrsize - struct_size
else:
addr = None
# Verify the found address before returning
if addr and GlibcArena.verify(addr):
return addr
except gdb.error:
pass
# Last resort, try to find it via brute force if enabled in settings
if gef.config["gef.bruteforce_main_arena"]:
alignment = 0x8
try:
dbg("Trying to bruteforce main_arena address")
# setup search_range for `main_arena` to `.data` of glibc
def search_filter(zone: Zone) -> bool:
return "libc" in pathlib.Path(zone.filename).name and zone.name == ".data"
for dotdata in list(filter(search_filter, get_info_files())):
search_range = range(dotdata.zone_start, dotdata.zone_end, alignment)
# find first possible candidate
for addr in search_range:
if GlibcArena.verify(addr):
dbg(f"Found candidate at {addr:#x}")
return addr
dbg("Bruteforce not successful")
except Exception:
pass
# Nothing helped
err_msg = f"Cannot find main_arena for {gef.arch.arch}. You might want to set a manually found libc offset "
if not gef.config["gef.bruteforce_main_arena"]:
err_msg += "or allow bruteforcing "
err_msg += "through the GEF config."
raise OSError(err_msg)
@property
def selected_arena(self) -> GlibcArena | None:
if not self.__libc_selected_arena:
# `selected_arena` must default to `main_arena`
self.__libc_selected_arena = self.main_arena
return self.__libc_selected_arena
@selected_arena.setter
def selected_arena(self, value: GlibcArena) -> None:
self.__libc_selected_arena = value
return
@property
def arenas(self) -> list | Iterator[GlibcArena]:
if not self.main_arena:
return []
return iter(self.main_arena)
@property
def base_address(self) -> int | None:
if not self.__heap_base:
base = 0
try:
base = parse_address("mp_->sbrk_base")
base = self.malloc_align_address(base)
except gdb.error:
# missing symbol, try again
base = 0
if not base:
base = get_section_base_address("[heap]")
self.__heap_base = base
return self.__heap_base
@property
def chunks(self) -> list | Iterator:
if not self.base_address:
return []
return iter(GlibcChunk(self.base_address, from_base=True))
@property
def min_chunk_size(self) -> int:
return 4 * gef.arch.ptrsize
@property
def malloc_alignment(self) -> int:
assert gef.libc.version
__default_malloc_alignment = 0x10
if gef.libc.version >= (2, 26) and is_x86_32():
# Special case introduced in Glibc 2.26:
# https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/i386/malloc-alignment.h#L22
return __default_malloc_alignment
# Generic case:
# https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/generic/malloc-alignment.h#L22
return 2 * gef.arch.ptrsize
def csize2tidx(self, size: int) -> int:
return abs((size - self.min_chunk_size + self.malloc_alignment - 1)) // self.malloc_alignment
def tidx2size(self, idx: int) -> int:
return idx * self.malloc_alignment + self.min_chunk_size
def malloc_align_address(self, address: int) -> int:
"""Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github"""
def ceil(n: float) -> int:
return int(-1 * n // 1 * -1)
malloc_alignment = self.malloc_alignment
return malloc_alignment * ceil((address / malloc_alignment))
class GefSetting:
"""Basic class for storing gef settings as objects"""
def __init__(self, value: Any, cls: type | None = None, description: str | None = None, hooks: dict[str, list[Callable]] | None = None) -> None:
self.value = value
self.type = cls or type(value)
self.description = description or ""
self.hooks: dict[str, list[Callable]] = collections.defaultdict(list)
if not hooks:
hooks = {"on_read": [], "on_write": [], "on_changed": []}
for access, funcs in hooks.items():
self.add_hook(access, funcs)
return
def __str__(self) -> str:
return f"Setting(type={self.type.__name__}, value='{self.value}', desc='{self.description[:10]}...', " \
f"read_hooks={len(self.hooks['on_read'])}, write_hooks={len(self.hooks['on_write'])}, "\
f"changed_hooks={len(self.hooks['on_changed'])})"
def add_hook(self, access: str, funcs: list[Callable]):
if access not in ("on_read", "on_write", "on_changed"):
raise ValueError("invalid access type")
for func in funcs:
if not callable(func):
raise ValueError("hook is not callable")
self.hooks[access].append(func)
return self
@staticmethod
def no_spaces(value: pathlib.Path):
if " " in str(value):
raise ValidationError("setting cannot contain spaces")
@staticmethod
def must_exist(value: pathlib.Path):
if not value or not pathlib.Path(value).expanduser().absolute().exists():
raise ValidationError("specified path must exist")
@staticmethod
def create_folder_tree(value: pathlib.Path):
value.mkdir(0o755, exist_ok=True, parents=True)
class GefSettingsManager(dict):
"""
GefSettings acts as a dict where the global settings are stored and can be read, written or deleted as any other dict.
For instance, to read a specific command setting: `gef.config[mycommand.mysetting]`
"""
def __getitem__(self, name: str) -> Any:
setting : GefSetting = super().__getitem__(name)
self.__invoke_read_hooks(setting)
return setting.value
def __setitem__(self, name: str, value: Any) -> None:
# check if the key exists
if super().__contains__(name):
# if so, update its value directly
setting = super().__getitem__(name)
if not isinstance(setting, GefSetting): raise TypeError
new_value = setting.type(value)
dbg(f"in __invoke_changed_hooks(\"{name}\"), setting.value={setting.value} -> new_value={new_value}, changing={bool(setting.value != new_value)}")
self.__invoke_changed_hooks(setting, new_value)
self.__invoke_write_hooks(setting, new_value)
setting.value = new_value
return
# if not, assert `value` is a GefSetting, then insert it
if not isinstance(value, GefSetting): raise TypeError("Invalid argument")
if not value.type: raise TypeError("Invalid type")
if not value.description: raise AttributeError("Invalid description")
setting = value
value = setting.value
self.__invoke_write_hooks(setting, value)
super().__setitem__(name, setting)
return
def __delitem__(self, name: str) -> None:
return super().__delitem__(name)
def raw_entry(self, name: str) -> GefSetting:
return super().__getitem__(name)
def __invoke_read_hooks(self, setting: GefSetting) -> None:
for callback in setting.hooks["on_read"]:
callback()
return
def __invoke_changed_hooks(self, setting: GefSetting, new_value: Any) -> None:
old_value = setting.value
if old_value == new_value:
return
for callback in setting.hooks["on_changed"]:
callback(old_value, new_value)
def __invoke_write_hooks(self, setting: GefSetting, new_value: Any) -> None:
for callback in setting.hooks["on_write"]:
callback(new_value)
class GefSessionManager(GefManager):
"""Class managing the runtime properties of GEF. """
def __init__(self) -> None:
self.reset_caches()
self.remote: "GefRemoteSessionManager | None" = None
self.remote_initializing: bool = False
self.qemu_mode: bool = False
self.coredump_mode: bool | None = None
self.convenience_vars_index: int = 0
self.heap_allocated_chunks: list[tuple[int, int]] = []
self.heap_freed_chunks: list[tuple[int, int]] = []
self.heap_uaf_watchpoints: list[UafWatchpoint] = []
self.pie_breakpoints: dict[int, PieVirtualBreakpoint] = {}
self.pie_counter: int = 1
self.aliases: list[GefAlias] = []
self.modules: list[FileFormat] = []
self.constants = {} # a dict for runtime constants (like 3rd party file paths)
for constant in ("python3", "readelf", "nm", "file", "ps"):
self.constants[constant] = which(constant)
return
def reset_caches(self) -> None:
super().reset_caches()
self._auxiliary_vector = None
self._pagesize = None
self._os = None
self._pid = None
self._file = None
self._maps: pathlib.Path | None = None
self._root: pathlib.Path | None = None
return
def __str__(self) -> str:
_type = "Local" if self.remote is None else f"Remote/{self.remote.mode}"
return f"Session(type={_type}, pid={self.pid or 'Not running'}, os='{self.os}')"
def __repr__(self) -> str:
return str(self)
@property
def auxiliary_vector(self) -> dict[str, int] | None:
if not is_alive():
return None
if is_qemu_system():
return None
if not self._auxiliary_vector:
auxiliary_vector = {}
auxv_info = gdb.execute("info auxv", to_string=True) or ""
if not auxv_info or "failed" in auxv_info:
err("Failed to query auxiliary variables")
return None
for line in auxv_info.splitlines():
line = line.split('"')[0].strip() # remove the ending string (if any)
line = line.split() # split the string by whitespace(s)
if len(line) < 4:
continue
__av_type = line[1]
__av_value = line[-1]
auxiliary_vector[__av_type] = int(__av_value, base=0)
self._auxiliary_vector = auxiliary_vector
return self._auxiliary_vector
@property
def os(self) -> str:
"""Return the current OS."""
if not self._os:
self._os = platform.system().lower()
return self._os
@property
def pid(self) -> int:
"""Return the PID of the target process."""
if not self._pid:
pid = gdb.selected_inferior().pid if not self.qemu_mode else gdb.selected_thread().ptid[1]
if not pid:
raise RuntimeError("cannot retrieve PID for target process")
self._pid = pid
return self._pid
@property
def file(self) -> pathlib.Path | None:
"""Return a Path object of the target process."""
if self.remote is not None:
return self.remote.file
progspace = gdb.current_progspace()
assert progspace
fpath: str = progspace.filename or ""
if fpath and not self._file:
self._file = pathlib.Path(fpath).expanduser()
return self._file
@property
def cwd(self) -> pathlib.Path | None:
if self.remote is not None:
return self.remote.root
return self.file.parent if self.file else None
@property
def pagesize(self) -> int:
"""Get the system page size"""
auxval = self.auxiliary_vector
if not auxval:
return DEFAULT_PAGE_SIZE
self._pagesize = auxval["AT_PAGESZ"]
return self._pagesize
@property
def canary(self) -> tuple[int, int] | None:
"""Return a tuple of the canary address and value, read from the canonical
location if supported by the architecture. Otherwise, read from the auxiliary
vector."""
try:
canary_location = gef.arch.canary_address()
canary = gef.memory.read_integer(canary_location)
except (NotImplementedError, gdb.error):
# Fall back to `AT_RANDOM`, which is the original source
# of the canary value but not the canonical location
return self.original_canary
return canary, canary_location
@property
def original_canary(self) -> tuple[int, int] | None:
"""Return a tuple of the initial canary address and value, read from the
auxiliary vector."""
auxval = self.auxiliary_vector
if not auxval:
return None
canary_location = auxval["AT_RANDOM"]
canary = gef.memory.read_integer(canary_location)
canary &= ~0xFF
return canary, canary_location
@property
def maps(self) -> pathlib.Path | None:
"""Returns the Path to the procfs entry for the memory mapping."""
if not is_alive():
return None
if not self._maps:
if self.remote is not None:
self._maps = self.remote.maps
else:
self._maps = pathlib.Path(f"/proc/{self.pid}/maps")
return self._maps
@property
def root(self) -> pathlib.Path | None:
"""Returns the path to the process's root directory."""
if not is_alive():
return None
if not self._root:
self._root = pathlib.Path(f"/proc/{self.pid}/root")
return self._root
class GefRemoteSessionManager(GefSessionManager):
"""Class for managing remote sessions with GEF. It will create a temporary environment
designed to clone the remote one."""
class RemoteMode(enum.IntEnum):
GDBSERVER = 0
QEMU = 1
RR = 2
def __str__(self):
return self.name
def __repr__(self):
return f"RemoteMode = {str(self)} ({int(self)})"
def prompt_string(self) -> str:
match self:
case GefRemoteSessionManager.RemoteMode.QEMU:
return Color.boldify("(qemu) ")
case GefRemoteSessionManager.RemoteMode.RR:
return Color.boldify("(rr) ")
case GefRemoteSessionManager.RemoteMode.GDBSERVER:
return Color.boldify("(remote) ")
raise AttributeError("Unknown value")
def __init__(self, host: str, port: int, pid: int =-1, qemu: pathlib.Path | None = None) -> None:
super().__init__()
self.__host = host
self.__port = port
self.__local_root_fd = tempfile.TemporaryDirectory()
self.__local_root_path = pathlib.Path(self.__local_root_fd.name)
self.__qemu = qemu
if pid > 0:
self._pid = pid
if self.__qemu is not None:
self._mode = GefRemoteSessionManager.RemoteMode.QEMU
elif os.environ.get("GDB_UNDER_RR", None) == "1":
self._mode = GefRemoteSessionManager.RemoteMode.RR
else:
self._mode = GefRemoteSessionManager.RemoteMode.GDBSERVER
def close(self) -> None:
self.__local_root_fd.cleanup()
try:
gef_on_new_unhook(self.remote_objfile_event_handler)
gef_on_new_hook(new_objfile_handler)
except Exception as e:
warn(f"Exception while restoring local context: {str(e)}")
raise
def __str__(self) -> str:
return f"RemoteSession(target='{self.target}', local='{self.root}', pid={self.pid}, mode={self.mode})"
def __repr__(self) -> str:
return str(self)
@property
def target(self) -> str:
return f"{self.__host}:{self.__port}"
@property
def root(self) -> pathlib.Path:
return self.__local_root_path.absolute()
@property
def file(self) -> pathlib.Path:
"""Path to the file being debugged as seen by the remote endpoint."""
if not self._file:
progspace = gdb.current_progspace()
if not progspace:
raise RuntimeError("No session started")
filename = progspace.filename
if not filename:
raise RuntimeError("No session started")
start_idx = len("target:") if filename.startswith("target:") else 0
self._file = pathlib.Path(filename[start_idx:])
return self._file
@property
def lfile(self) -> pathlib.Path:
"""Local path to the file being debugged."""
return self.root / str(self.file).lstrip("/")
@property
def maps(self) -> pathlib.Path:
if not self._maps:
self._maps = self.root / f"proc/{self.pid}/maps"
return self._maps
@property
def mode(self) -> RemoteMode:
return self._mode
def sync(self, src: str, dst: str | None = None) -> bool:
"""Copy the `src` into the temporary chroot. If `dst` is provided, that path will be
used instead of `src`."""
if not dst:
dst = src
tgt = self.root / dst.lstrip("/")
if tgt.exists():
return True
tgt.parent.mkdir(parents=True, exist_ok=True)
dbg(f"[remote] downloading '{src}' -> '{tgt}'")
gdb.execute(f"remote get '{src}' '{tgt.absolute()}'")
return tgt.exists()
def connect(self, pid: int) -> bool:
"""Connect to remote target. If in extended mode, also attach to the given PID."""
# before anything, register our new hook to download files from the remote target
dbg("[remote] Installing new objfile handlers")
try:
gef_on_new_unhook(new_objfile_handler)
except SystemError:
# the default objfile handler might already have been removed, ignore failure
pass
gef_on_new_hook(self.remote_objfile_event_handler)
# then attempt to connect
is_extended_mode = (pid > -1)
dbg(f"[remote] Enabling extended remote: {bool(is_extended_mode)}")
try:
with DisableContextOutputContext():
cmd = f"target {'extended-' if is_extended_mode else ''}remote {self.target}"
dbg(f"[remote] Executing '{cmd}'")
gdb.execute(cmd)
if is_extended_mode:
gdb.execute(f"attach {pid:d}")
return True
except Exception as e:
err(f"Failed to connect to {self.target}: {e}")
# a failure will trigger the cleanup, deleting our hook anyway
return False
def setup(self) -> bool:
# setup remote adequately depending on remote or qemu mode
match self.mode:
case GefRemoteSessionManager.RemoteMode.QEMU:
dbg(f"Setting up as qemu session, target={self.__qemu}")
self.__setup_qemu()
case GefRemoteSessionManager.RemoteMode.RR:
dbg("Setting up as rr session")
self.__setup_rr()
case GefRemoteSessionManager.RemoteMode.GDBSERVER:
dbg("Setting up as remote session")
self.__setup_remote()
case _:
raise ValueError
# refresh gef to consider the binary
reset_all_caches()
gef.binary = Elf(self.lfile)
reset_architecture()
return True
def __setup_qemu(self) -> bool:
# setup emulated file in the chroot
assert self.__qemu
target = self.root / str(self.__qemu.parent).lstrip("/")
target.mkdir(parents=True, exist_ok=False)
shutil.copy2(self.__qemu, target)
self._file = self.__qemu
assert self.lfile.exists()
# create a procfs
procfs = self.root / f"proc/{self.pid}/"
procfs.mkdir(parents=True, exist_ok=True)
## /proc/pid/cmdline
cmdline = procfs / "cmdline"
if not cmdline.exists():
with cmdline.open("w") as fd:
fd.write("")
## /proc/pid/environ
environ = procfs / "environ"
if not environ.exists():
with environ.open("wb") as fd:
fd.write(b"PATH=/bin\x00HOME=/tmp\x00")
## /proc/pid/maps
maps = procfs / "maps"
if not maps.exists():
with maps.open("w") as fd:
fname = self.file.absolute()
mem_range = "00000000-ffffffff" if is_32bit() else "0000000000000000-ffffffffffffffff"
fd.write(f"{mem_range} rwxp 00000000 00:00 0 {fname}\n")
return True
def __setup_remote(self) -> bool:
# get the file
fpath = f"/proc/{self.pid}/exe"
if not self.sync(fpath, str(self.file)):
err(f"'{fpath}' could not be fetched on the remote system.")
return False
# pseudo procfs
for _file in ("maps", "environ", "cmdline"):
fpath = f"/proc/{self.pid}/{_file}"
if not self.sync(fpath):
err(f"'{fpath}' could not be fetched on the remote system.")
return False
return True
def __setup_rr(self) -> bool:
#
# Simply override the local root path, the binary must exist
# on the host.
#
self.__local_root_path = pathlib.Path("/")
return True
def remote_objfile_event_handler(self, evt: "gdb.NewObjFileEvent") -> None:
dbg(f"[remote] in remote_objfile_handler({evt.new_objfile.filename if evt else 'None'}))")
if not evt or not evt.new_objfile.filename:
return
if not evt.new_objfile.filename.startswith("target:") and not evt.new_objfile.filename.startswith("/"):
warn(f"[remote] skipping '{evt.new_objfile.filename}'")
return
if evt.new_objfile.filename.startswith("target:"):
src: str = evt.new_objfile.filename[len("target:"):]
if not self.sync(src):
raise FileNotFoundError(f"Failed to sync '{src}'")
return
class GefUiManager(GefManager):
"""Class managing UI settings."""
def __init__(self) -> None:
self.redirect_fd : TextIOWrapper | None = None
self.context_hidden = False
self.stream_buffer : StringIO | None = None
self.highlight_table: dict[str, str] = {}
self.watches: dict[int, tuple[int, str]] = {}
self.context_messages: list[tuple[str, str]] = []
return
class GefLibcManager(GefManager):
"""Class managing everything libc-related (except heap)."""
PATTERN_LIBC_VERSION_MEMORY = re.compile(rb"glibc (\d+)\.(\d+)")
PATTERN_LIBC_VERSION_FILENAME = re.compile(r"libc6?[-_](\d+)\.(\d+)\.so")
def __init__(self) -> None:
self._version : tuple[int, int] | None = None
self._patch: int | None = None
self._release: str | None = None
return
def __str__(self) -> str:
return f"Libc(version='{self.version}')"
@property
def version(self) -> tuple[int, int] | None:
if not is_alive():
return None
if not self._version:
self._version = GefLibcManager.find_libc_version()
# Whenever auto-detection fails, try use the user-provided version.
if self._version == (0, 0):
if gef.config["gef.libc_version"]:
ver = [int(v) for v in gef.config["gef.libc_version"].split(".", 1)]
assert len(ver) >= 2
self._version = ver[0], ver[1]
return self._version
@staticmethod
@lru_cache()
def find_libc_version() -> tuple[int, int]:
"""Attempt to determine the libc version. This operation can be long."""
libc_sections = (m for m in gef.memory.maps if "libc" in m.path and m.permission & Permission.READ)
for section in libc_sections:
# Try to determine from the filepath
match = re.search(GefLibcManager.PATTERN_LIBC_VERSION_FILENAME, section.path)
if match:
return int(match.group(1)), int(match.group(2))
# Try to determine from memory
try:
mem = gef.memory.read(section.page_start, section.size)
match = re.search(GefLibcManager.PATTERN_LIBC_VERSION_MEMORY, mem)
if match:
return int(match.group(1)), int(match.group(2))
except gdb.MemoryError:
continue
return 0, 0
class Gef:
"""The GEF root class, which serves as a entrypoint for all the debugging session attributes (architecture,
memory, settings, etc.)."""
binary: FileFormat | None
arch: Architecture
config : GefSettingsManager
ui: GefUiManager
libc: GefLibcManager
memory : GefMemoryManager
heap : GefHeapManager
session : GefSessionManager
gdb: GefCommand
def __init__(self) -> None:
self.binary: FileFormat | None = None
self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler`
self.arch_reason: str = "This is the default architecture"
self.config = GefSettingsManager()
self.ui = GefUiManager()
self.libc = GefLibcManager()
return
def __str__(self) -> str:
return f"Gef(binary='{self.binary or 'None'}', arch={self.arch})"
def reinitialize_managers(self) -> None:
"""Reinitialize the managers. Avoid calling this function directly, using `pi reset()` is preferred"""
self.memory = GefMemoryManager()
self.heap = GefHeapManager()
self.session = GefSessionManager()
return
def setup(self) -> None:
"""Setup initialize the runtime setup, which may require for the `gef` to be not None."""
self.reinitialize_managers()
self.gdb = GefCommand()
self.gdb.setup()
gdb.execute(f"save gdb-index '{self.config['gef.tempdir']}'")
return
def reset_caches(self) -> None:
"""Recursively clean the cache of all the managers. Avoid calling this function directly, using `reset-cache`
is preferred"""
for mgr in (self.memory, self.heap, self.session, self.arch):
mgr.reset_caches()
return
def target_remote_posthook():
if gef.session.remote_initializing:
return
gef.session.remote = GefRemoteSessionManager("", 0)
if not gef.session.remote.setup():
raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote}")
if __name__ == "__main__":
if sys.version_info[0] == 2:
err("GEF has dropped Python2 support for GDB when it reached EOL on 2020/01/01.")
err("If you require GEF for GDB+Python2, use https://github.com/hugsy/gef-legacy.")
exit(1)
if GDB_VERSION < GDB_MIN_VERSION or PYTHON_VERSION < PYTHON_MIN_VERSION:
err("You're using an old version of GDB. GEF will not work correctly. "
f"Consider updating to GDB {'.'.join(map(str, GDB_MIN_VERSION))} or higher "
f"(with Python {'.'.join(map(str, PYTHON_MIN_VERSION))} or higher).")
exit(1)
# setup config
gdb_initial_settings = (
"set confirm off",
"set verbose off",
"set pagination off",
"set print elements 0",
"set history save on",
f"set history filename {os.getenv('GDBHISTFILE', '~/.gdb_history')}",
"set output-radix 0x10",
"set print pretty on",
"set disassembly-flavor intel",
"handle SIGALRM print nopass",
)
for cmd in gdb_initial_settings:
try:
gdb.execute(cmd)
except gdb.error:
pass
# set fallback 'debug-file-directory' for gdbs that installed outside `/usr`.
try:
default_dbgsym_path = "/usr/lib/debug"
param_name = "debug-file-directory"
dbgsym_paths = gdb.parameter(param_name)
if not isinstance(dbgsym_paths, str):
raise TypeError
if default_dbgsym_path not in dbgsym_paths:
newpath = f"{dbgsym_paths}:" if dbgsym_paths else ""
newpath += default_dbgsym_path
gdb.execute(f"set {param_name} {newpath}")
except gdb.error as e:
warn(f"Failed to set {param_name}, reason: {str(e)}")
# load GEF, set up the managers and load the plugins, functions,
gef = Gef()
reset()
assert isinstance(gef, Gef)
gef.gdb.load()
gef.gdb.show_banner()
# load config
if gef.gdb.load_extra_plugins():
# reload settings
gdb.execute("gef restore")
# setup gdb prompt
gdb.prompt_hook = __gef_prompt__
# gdb events configuration
gef_on_continue_hook(continue_handler)
gef_on_stop_hook(hook_stop_handler)
gef_on_new_hook(new_objfile_handler)
gef_on_exit_hook(exit_handler)
gef_on_memchanged_hook(memchanged_handler)
gef_on_regchanged_hook(regchanged_handler)
progspace = gdb.current_progspace()
if progspace and progspace.filename:
# if here, we are sourcing gef from a gdb session already attached, force call to new_objfile (see issue #278)
new_objfile_handler(None)
GefTmuxSetup()
if GDB_VERSION > (9, 0):
disable_tr_overwrite_setting = "gef.disable_target_remote_overwrite"
if not gef.config[disable_tr_overwrite_setting]:
warnmsg = ("Using `target remote` with GEF should work in most cases, "
"but use `gef-remote` if you can. You can disable the "
"overwrite of the `target remote` command by toggling "
f"`{disable_tr_overwrite_setting}` in the config.")
hook = f"""
define target hookpost-{{}}
pi target_remote_posthook()
context
pi if calling_function() != "connect": warn("{warnmsg}")
end
"""
# Register a post-hook for `target remote` that initialize the remote session
gdb.execute(hook.format("remote"))
gdb.execute(hook.format("extended-remote"))
else:
errmsg = ("Using `target remote` does not work, use `gef-remote` "
f"instead. You can toggle `{disable_tr_overwrite_setting}` "
"if this is not desired.")
hook = f"""pi if calling_function() != "connect": err("{errmsg}")"""
gdb.execute(f"define target hook-remote\n{hook}\nend")
gdb.execute(f"define target hook-extended-remote\n{hook}\nend")
# restore saved breakpoints (if any)
bkp_fpath = pathlib.Path(gef.config["gef.autosave_breakpoints_file"]).expanduser().absolute()
if bkp_fpath.is_file():
gdb.execute(f"source {bkp_fpath}")
# Add a `source` post hook to force gef to recheck the registered plugins and
# eventually load the missing one(s)
gdb.execute("define hookpost-source\npi gef.gdb.load()\nend")
================================================
FILE: get-pip.py
================================================
#!/usr/bin/env python
#
# Hi There!
#
# You may be wondering what this giant blob of binary data here is, you might
# even be worried that we're up to something nefarious (good for you for being
# paranoid!). This is a base85 encoding of a zip file, this zip file contains
# an entire copy of pip (version 26.0.1).
#
# Pip is a thing that installs packages, pip itself is a package that someone
# might want to install, especially if they're looking to run this get-pip.py
# script. Pip has a lot of code to deal with the security of installing
# packages, various edge cases on various platforms, and other such sort of
# "tribal knowledge" that has been encoded in its code base. Because of this
# we basically include an entire copy of pip inside this blob. We do this
# because the alternatives are attempt to implement a "minipip" that probably
# doesn't do things correctly and has weird edge cases, or compress pip itself
# down into a single file.
#
# If you're wondering how this is created, it is generated using
# `scripts/generate.py` in https://github.com/pypa/get-pip.
import sys
this_python = sys.version_info[:2]
min_version = (3, 9)
if this_python < min_version:
message_parts = [
"This script does not work on Python {}.{}.".format(*this_python),
"The minimum supported Python version is {}.{}.".format(*min_version),
"Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python),
]
print("ERROR: " + " ".join(message_parts))
sys.exit(1)
import os.path
import pkgutil
import shutil
import tempfile
import argparse
import importlib
from base64 import b85decode
def include_setuptools(args):
"""
Install setuptools only if absent, not excluded and when using Python <3.12.
"""
cli = not args.no_setuptools
env = not os.environ.get("PIP_NO_SETUPTOOLS")
absent = not importlib.util.find_spec("setuptools")
python_lt_3_12 = this_python < (3, 12)
return cli and env and absent and python_lt_3_12
def include_wheel(args):
"""
Install wheel only if absent, not excluded and when using Python <3.12.
"""
cli = not args.no_wheel
env = not os.environ.get("PIP_NO_WHEEL")
absent = not importlib.util.find_spec("wheel")
python_lt_3_12 = this_python < (3, 12)
return cli and env and absent and python_lt_3_12
def determine_pip_install_arguments():
pre_parser = argparse.ArgumentParser()
pre_parser.add_argument("--no-setuptools", action="store_true")
pre_parser.add_argument("--no-wheel", action="store_true")
pre, args = pre_parser.parse_known_args()
args.append("pip")
if include_setuptools(pre):
args.append("setuptools")
if include_wheel(pre):
args.append("wheel")
return ["install", "--upgrade", "--force-reinstall"] + args
def monkeypatch_for_cert(tmpdir):
"""Patches `pip install` to provide default certificate with the lowest priority.
This ensures that the bundled certificates are used unless the user specifies a
custom cert via any of pip's option passing mechanisms (config, env-var, CLI).
A monkeypatch is the easiest way to achieve this, without messing too much with
the rest of pip's internals.
"""
from pip._internal.commands.install import InstallCommand
# We want to be using the internal certificates.
cert_path = os.path.join(tmpdir, "cacert.pem")
with open(cert_path, "wb") as cert:
cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem"))
install_parse_args = InstallCommand.parse_args
def cert_parse_args(self, args):
if not self.parser.get_default_values().cert:
# There are no user provided cert -- force use of bundled cert
self.parser.defaults["cert"] = cert_path # calculated above
return install_parse_args(self, args)
InstallCommand.parse_args = cert_parse_args
def bootstrap(tmpdir):
monkeypatch_for_cert(tmpdir)
# Execute the included pip and use it to install the latest pip and
# any user-requested packages from PyPI.
from pip._internal.cli.main import main as pip_entry_point
args = determine_pip_install_arguments()
sys.exit(pip_entry_point(args))
def main():
tmpdir = None
try:
# Create a temporary working directory
tmpdir = tempfile.mkdtemp()
# Unpack the zipfile into the temporary directory
pip_zip = os.path.join(tmpdir, "pip.zip")
with open(pip_zip, "wb") as fp:
fp.write(b85decode(DATA.replace(b"\n", b"")))
# Add the zipfile to sys.path so that we can import it
sys.path.insert(0, pip_zip)
# Run the bootstrap
bootstrap(tmpdir=tmpdir)
finally:
# Clean up our temporary working directory
if tmpdir:
shutil.rmtree(tmpdir, ignore_errors=True)
DATA = b"""
P)h>@6aWAK2modQMO=`r3jO*3003hF000jF003}la4%n9X>MtBUtcb8c|DLpOT<77h41q#LNB_YQx$P
_LBWgQMLc*D8D`sbcc9G-N$OJY$D3Aa7|8JQcznE$^8g`qqmGOrKpIMBg-Db&YRV+eh476m_P6^ZR5y
42%3oK`xfVMZVxsfN2iZZNL_bCO3x41&6PkHm8@POeM7nceQ&rW+F$vg|&=^hb5v1W%RJ>aPU(6cYHqEdW)S|}J%LND`994_#>iUrMIPQIuhq_E9)|Oc(krAcd-
ag#>m2BDFT!}e+Sn2$z7U_|cr;<&TwWo06SPJqV3Zu11|Kd{1#{B|NO9KQH000080A>M2TyOWU*SrA$
09FG401p5F0B~t=FJE76VQFq(UoLQYT~bSL+b|5i`&SU@!Oq~iIS)&L9d|8u8wNv=>6nNu38Ea&`}HF
gyJ@GB9{e8sD4K$g2|O2c-|@;t@dR%;`5Qu6f^i+#IYx8|79X$VF3?d#n|xfMkA8wQAoLVDffU76;J#
O)CYUtTKs|(rtOUt}xq0efX64y=-}wYe4gv+Rewsv@!47DzwFn{pMIm#X%sAFClIW>99{f@Za2e3a^U
Yte1H%y3GHUTlK2Lp_T}m3nsgC)$#bX09kug6MU#nM~&r24-0~c2yu2!TgU+z6
-O~;x-O@YkJ|0dA=sY-F^F})aITrzTyS?O7N5T~%P_vE*{#XPt(tDzVC+>eZ42i!91eGvPx8>ysJFuZ
iRYzlCqu4no3L)R_c2M{&P)haML0zYtRpKw0?HZ~t=E9}0<93*a^reKp2wsiXosqFv#$q{3!PIV@d3Fa6TvSqmUyJeY&DcVg-E}?
LbjUB1cn%!C6%kRp-;$05^P^$8se4pYUP)h>@6aWAK2modQMO-Bu+MCq^00625000#L003}la4%n9aA
|NYa&>NQWpZC%E^v8$RKZT$KoGtAD+Y6@Eg2UujN70@J3Ari?w5UCxep1%xiq>fmEIFl6B?A3x
Sz!*+f%5d4fHxHF8jFN1eGgxmyrJk!>JJ`PlvdTodP;jzGLU-v4$e!DeT^^-gWwoG9{qx@+7nu*j3%O
0ExlT)Qu!9LHDH>L!H(!_=TwzaVV^*pl)kx9tbG)rYeKk&*k^&wU|
$S++58k3m8Lmp1|ig2!b4ofsJt9M%#5f0yqZW);I!nuZ_h3=&|S?TPxwZ$eyWfgq*)nizNtGA>RzSi7l{Y^XYU+CfY_u!@m%Gi-d@I{#hxVgPE$YI@mc<6gykr_e}b+f9t|3q=xfugZ)@gfN(Wtk;Ae`plH
gH3naii$Ja*F(o1`fzI*wHS@|Is4%u)thLF4yj@}qyYiY&_b_ZOX8r%fBrv1Y-*UW9nT9_LmT00^xl2ROe(l1bGhRx+qTh)+F%!n@6aWAK2modQMO@9tz%{@C
0015V000aC003}la4&FqE_8WtWn?9eu};M>3`O^T#obt*`VR~YY;QnfHmzwbC3ciJh5S8E87&>(bBYv
517WkANp~bsMyYmG$}2ukNeuDHNG^#ptMd*~JcpmA56q`#0W5TpB>IYnZ>tlx>JJR-$h|q#9KFT3l$T
hGovM`Z`h1@k{0zqrjTIlGh#re*%w%#go%(3HWDhs}=jz2OtQ*5LjXNW#DIpx4DusYoy!{s5eCbN6)&
t+MoumghxP_Ew!2Ru`@Lf_lF*R=M@&`~$0|XQR000O8W&uT94Q=skH~|0v{{a9182|tPaA|NaUukZ1W
pZv|Y%gD5X>MtBUtcb8d3902O2j}6z0X&K^|A$>UIcYPkDkPnH<2>irfpz4GbE`?!H+ksuHwO5@(7QY
mzSRN1WnV=Ix{v6&{Q%jgp{%drJTgMe&rM@zM-VZDMUXjMLCn3mGKjpdOy)FFJHi-B!6Xp$ye9WBWs{t8H%Aoj)?NPZQs-QU#tW48fd@H2B+*w7B)Fj?>
6vg5~1s84KhUNP=n=m34#ctYLP=-52WLK^lhH%vXoF;&^3w7DD}j7@R*dBl5P6?mJ_cT@
|P7x`*8XhZs}C+6HrS71QY-O00;nP0YzLt7mdF;7ytm5RR91Q0001RX>c!JX>N37a&BR4FJg6RY-C?$
ZgwtkdEGo~Z{s+U-}5WDYAjlwwKb0qhl4S`LqF2f!ObQ?cY5~*L7qOaJxGb9Xs2g
p?|``g-H9c#Sgdyyiw=U|b)OfRI4$q;rYd)`>_p$nOl+z~zlm16>XNcomk@x_!tDi$;q
avb|Q{9eb+ZKjzzw!tELl4Symm*ZBI_rv!c4ag=zX@)#|_Mq}vwxT7PWwP7Z(Xhf$q;=;ljn$huKT@B3VB|w8Ym2GjiGUvx(zg<3XTY!hS=n|;QJgkS)y!$Y+dR1~tG3J2%e*$8tC4@-N?8
yPt>i{+?y490ligVlF=(MFl98_
C#dp!$)_4o_1GI57V7!|RKWr*n}eHEFL_(1QR588WxhfD+}6JdSbOuj{5tWjjza1-8WFqR0sx}l
uslU%K>7a|?aSv86v^lnKRW~o2#){Y0m=4Y*7)NGgB=F}fMM?umuQ`|0})=s-yiT57>E;^n-&Q$?DPS
UR1P8>Obk#SEPSzgmlUm>F`4m4;%F_#OOe6QYp|ko0sq~coSdML!G2fT9Yi2QKhm=>j>}{R24@DtKtT
j(N!Hh4vJ?bhO}}Si@o%E*>p~*f8}Z-c2VR3wX(WhrDZ&Ki%>+nABL1`zYasz!e2Q!E_TtM%Zt@47M#
}=F@-B|URu-E%d8cvZ^Qx5NW^qJ>17JV+%YM-U4mG?X5{<9OP{8BSYJ@=m>kt|%1T(YYtn-o-V5R0pZ
QA5@*V?1N65*&8hAG0S(Sw=NpV@|KDw7WPWr;2^20;mFpRixvCV2TDBsg?|&H#80`vjuF??hbgyDeBC
`5@E2V~+~Y0hZ`MwbH(6KnRQd0->YPKq+Lwy4vPo2wDy`)SQHT&u9_m9D}e_<8;H&z~mC9;WDH~1k^B
{kw%?eA&VZeWHsc9$196N35alfrI5dR)1@Gj(>LG^OCN(%HoYPi2339
?aR6A!ygze;+guVWPPk^Ek=q>Cc=?s`DDB@h)0|}E7Nks2KlhEcB`)C-cWE@x(GKRAgQ}(Mi+O(NWt4
yl*;%gDa$YWxOY~nbo_raiUB#`?SENYA~m@;;)5(r6pd}B!kkEV!C=;4gRr?v;S?RnSu9`)RW%077Ac
VYtrG29U#OaCzZYB>AaaJk{^A4uB1Ozx7rKxrcYApnkmtUyxWeg}(!7>Kly=nBPpZ#@0}yKfYMJ`jOg
;BB)@iu@NC3ELCZ25AZWYHj&}Ww%M2bVz5|!CVB+UIIfT3<9Dm`nDdjexoTeMajVbd-2^jq5?a7-w+&
vQt!om4jR{!-!%amKuls9v@|Ih8N)lJh@Z>qzC=yj<*40TB*L7y({53?%a3sJX=5y%
XXOspj+1gI=*B@^Kw{Nqk%hv;@~3Bp`hl5k^+`D4|R367APh+Ie}l&lAtX29D7{xn#A4hTo9p*$#fA{
|oCmX;u#p$FYSbncoaGC~-Gae$gjWuQQRIf+79JZ`84_oKjumUW*SN(;Vw}U#`y8|#v0&DUU
^J6vL#+f!z92O;ngDH$vuR$!6aTBiACFQ&_Iq_$X0#0LoH!4r4(`qSBuZ;ky$;;Mo;twqMO
YgM8>IBpN1r1W8f@yKEU!QVp3)9TCcdWm>_`gj_@JAHNa>BH$NaOVXtk5TXzbQdh?D){bSqVEFx9fSt
GT-i2o_6K!bj+s1a*k+?93})Ie+QZAFxwYH|L@^fqAPR|$WXY!cBnz|O2a385OM=BBzlFd@F4TLOO-G
a!3Q{wD!PIu1^Fs=$e2`G07o31Dbow-ftdwAZxrVAYE{K@U&==xWJEn1_g-07KYfM<1q?n7mdx_cvVc
e!KAW(In0Ma1hyBc#;2@CEigq;gX`c9chqHH?8RjI23WLIT9%qJtYmXw@+t$=1|g?3fjarhf~2%|<$H
h2ZQW11@ClVi?%UxA>M5a=Xp2#z6f-uA6Cu>*MmE1+x3oBWn@aL{|q5$=*k1?Pw?OF`!1#V}m{7@x;@
0lWs+X3)Ewvg^H*KLWi9h~~c^3pQ_0Uw`_+b{Hu65v9Lb7%cAY3}*xZ{BwX+A@V3l10seG8_e7~RMoD
55DS|mMbun)v+E&Hqg1V$ZU$9eCQCpi4mSYQS1oBUmU2Ba#>*zBif
6=(feWYJc$0Q%1g-P8}!3^6F`f{U|FI`3!Y}4$u`=CDFe2seM6E3j$Dq^WKFT{B>udBv>xS;&RK{AR1
j(;SeH9VdW?qVC8CvstRw{p?rsNx)&hl{XHL|EGf*x)t+3)1k}U=03>@!9c
>FL2y~#sNn7`^GEJkR(3rk3*s%0K}YeW8=6*Yd_-HH9a_=EXK#rps9u~a44*Sziwt8JX07}?c!86-En
|8Vno)M<_8PPfw9Z;U}>NWeVpxuj;>5L%CsKkJyBRq2Ig?JApp^($C_t&Ra$9HJNi2P}q^5FU
r$iaNWp?)^|Ogi%Y7Fqh)L%^Y6CaoiPZ^Jtw=Li?-rgFBrC=WYj|BoJ#PR4v53V(-Le_4Fd-(gnh{ly
|OYAZSDeg?2m8ygz`Qhqgk=fkjw)pu%VAnzaa}pf>SB~%UFE2L2uSb*ocIAo&*hRqhsKDnA1k_LF3Y4
mEvBq)NtSiJihIVq&WgerNMSR(Ttd_@fHb@Xo2-4Si+MSbH%fBmBya^aDMUO)W~K(!l2;W;OWU{WBa<
Ok__x&-Q|S_@b*ugEN5?!4^5Z31^0AIo2g`-l)aQEYno(3?4w`fG2yt_8(!+M=)5#!LGwpSaYrht3&@e$m|ErMxFNv|I@YAl74k%i@QV^nn*9xl)2QDN
B0)SC_pc|h5?2^wwa#^-~{NX9uhrpNnZJut`5hhMPVOf=8n>6SFGTd7$B`rorxocTe_p{$n(l=^CUsT
;!WM5ic7ULjdL`;lW8XX}=&j@q}+4Z#Wg!Lb@iUV9*S8d+ymxAlL-yEz-9d93jxT+?8a@&TnrtZWg(ABYA9&8d_=VNt&Wjk60uOy1STkv>L|EP#3!QMMDS2F0q0w1My-36xq5e|gGs
mTa%APxin;09V6b(^{2E#v9C)?=Tw&i;{P248i#psOFIK%PglNrd8FoiP1JG;T}vXV($k_Fwiq3+pfx
4cu2E0?A6Q$j8+^&27+!^qdlx8QL#r*AySDw*speFoX_bW@F=$pFeP3mu!U-~nb!3#-d-&6lzYpSO=^
%G>Tw*7aCggKDbqwqrLyYK1?}oss#8{l6LvY`KBK
c*{dhLBHZ<7>*2JR1Yhy3A>0l@L-(`Xpo?CT0o#{@U{g1+}%-FK=}5}AN*QeyWgNC4!rSQj;q2aT3WG
^dz(s2XVAo^l%Su4+P6`J53r%VMI*)W9GBHQ~9r%6qJdsX(P2eY@fVAF)E%;E7Wv+AsG*^&3PPf+f;>
?4B#tZmTry8)v3QF71G2c4zdp?sG5@N31?5*iTwffk=oP?ae+zQeU7mSrs|7s+f@D
SRv_DndLf1FiP%a=Nt)TgeaqAc<|h8!VnYQuJlu?tzI1M>#gvu5ti-
q0OWMQGb8TQv$wmiuWH(Lt8KM%J7-EaU%B6IFqLrm%v$m)88kd2WF7e+{PAA%Ymt&?&ErhDjVYCO6n9
WsJQMYxg9#|Tt$d?uZSH*I=$(y#rihTjGBsYE;r%E_BI>-Qhu^<9-4OFO~+8HLmt{m#=7wg>h*2(uutKGNtu%>b3Tn+uy!R6So2O0Zx|$4ulW-3-lP_y=t7lWw=56w!hJaFL#s2?RypT?Z#A|~GLB$aRwm<
_mW_^|o;vVc{L4zAC_?`P3EV8W+)D}3!w;Z_d#sFu>ZR9lG4=3-j?tYBh9)>piW6-diWCMtA^E-5L^5
^!Mi4iAeJ*Ez=3k9zR;I!D1eh(nd+K49O1qAL=977Mp1{)c?&_farjw*Xf4pe4FF)Zz{w0L)81J*^m1
9_++Lptu%ofk0{BZg?yuqx_N7Gnd=YM3|lchkCal>#8rbezg@Z+kSY)Ok0hZZO5%SG)u9a(kmjL?SCo
BC}{(LTjHtw{cf7EhY^l4bzpWdH_7m9t4!lD+Zms!vWgJh*%Y+;!TnlU{8naKeTX}PZR$+W|aA6?@%q
@H~^O(L*&1g71|T6LsX?>fZbGGyp*SQgV>CM`LH)MNjGz|KE8u-s0HSC=dMjvb)KAcTkYJ&D*VA-o0x
lThN}v{DYyIIZL@)p4m=?D^?}rJ8%=BV(b!a|_X^0b&AiQ^rMy#jXpG&27@vfSefmPo%#uxIn#FQ;#B
hhht2r(-n}k9`7^fx}`LAP|hOSuidX?>(sCG-i@cn4$P}4EM>hc|t(DF_%#E+DZ8^!Sw`Jpb-Qxn{8GAIg^Sxq=HY|ti8F%INBMV!3n`hx$}Ubsw$QSk51A%6bdy)6`Kx27&n{wG
8>JMKCxClNj;umGCgGRZRhjm
%|nG1gice@_=wYnWJhm+@aE^bU2d7cxBxkg5s6W*#a{VfyHN?OmB(n#)h7dZ&Rd9k?lpDmGq{BqB{9O
t+vnjVe7sz$1VkC4`C)PXiYP2Ar94Pm_ZP9!@q4lZ(NMkr7j6VmG6}A4{sV**qvw*r|tK(Nc92Hh!@L
PPypQ(3xs14^2s5nIEv3mC;?75sv?W6L3#(*ysmX!K@YYWtULwzP(6Yx&^jP#Wo<
TSa~P~u&Nlrw<1m9*eVrw)5r3S@0%5!q~KtQW1Xq$(GV6bg*DDQtXnVbFFJbefEI=DE@@ask*`B{zbz;caBeqg4GxRzs
t$+crYhmD(2w)4)m%Eb)C-Sf=klXjwsROjg+Qm$JyJ$}U$Svq%
QIYd4Tf54xz7fGJh1@44W|f2-Bheb5@T&t3g0ZUFrj^vQ+*rXx%zc+LJevV*=B9q#EfF2FunPF_Z4bn
XU>F?uk7^Emqf_XbZ}XF?kYiG-s^p%dFD#+2+;3S5)wC%xFT~&F%LbcIz}%&ISL}sE$xAwY2bB);@m4
dRR`P9?xhmj|-%wx!HSb-khUg9*@m-!#XZ2p}BsQhw?T#`CiA_!VvRle4d3-!`G1?^!eF4&V>1b%y9$
!alA2L*p%xD~grvv9~c<7#&{q)F=zQ1mT8=~Scer)nJm7GG|)9aN8#0E$E*Hbc#;lW|%97W7NOT?A**
R;*`7qlJN>sZ9p(zFtr;Bn|*UacNYa8J|tWY>+i8;t!Z5fE-
;RMaO)~uONN8X+N=U-2$%>=)WL>D~8rw>2=yIWE;mYQoWPdc5B*>MBl7q>mdNvrCSPJ7euZGN)8z{2B
VRpDdhRD7;ow8kLJ45GPQLEm}hKpegZ3z_QX_7-6*ZmDk}FzGoK!1Q0bJW5x@;^{Z0|XQR000O8W&uT9#Uk5%2MhoJX(#{y761SMaA|NaUukZ1WpZv|Y%gPBV`ybAa
CzNYZI9c=5&nL^VynOiRmeQoaNJgaiaO3YcHx)6cHj?(1F;fU()uVa!zE8v+Wz-GGrJ_0B6U7DOnz&ig#G)MPSwDpy-2Uddc;)K2BqWHLEBTbE`hl4M<1b*Yj>=v`sTO2|Anm8`VM?b(?h+e*95^s;&
UXieUH&&+0{^G)+;tfO1Vigq+af54-gs15~|H{(z8gJ-1BMV#EIJT+xp$knwZs&OSZwpsgLZe~K~l`4
x;;ct@5o$8O=sM3P*+bGtR&RUWynVVd%WTt;sOfF=G3Fk74)u)vzSRu{67x@kLq#w3QWzTe`c+@<5ZVwRV8(qH3Yunksr(KmR*_7ubcHR@%@&idt#(n%Uq2uX3^tH?TQ2(wQ9w)W5<+-nV@ORCf7Po?
P)5IrPT)6I;jlFcXmepph~f593$SS>Lrgw39C3sre|kw-oE($#p~p6@7}ylUO#ztf%OwpYHWzfx!|2=
salJK91qN{swkZc`v1~aXSGwbUsKoB{$i^yP!AQ
}B*H5O?c&k38dZTO=K@n%GOj;{AKwC+kK#CN3?EPR$?IrSFP7+ITAq_UENtwnBw$R
$#9;(zCMkjdxbJ+_v{RHT&(-+3x-&UL$3@vyh9k|eS!ThBrYV2d^X+T@;LR%Ttmz|*+hH*KfDzJik=s
dQunI5svW$BDIttWi7FZKR{)Zp)&-oz4&Vg_k96p1l!*SJpF63Yl{RG2@xG>_t}{_YnPiMShG;q#61G
^~g%>YFmk=YMK3Lbgojd*VVRTr_~&Nz7ijl*vlLQjM%7L!Gjc%s-Wn{P;K#qQ|i)N5)L;Ldsx)uUsuh
}fZRu7p;WYwEcWBumtbfm=~;t8z1dQJz5$PzMpPq!5t%tJUYBx1`92^g^Rzo{B};I#G&PB9toH=^fGe
j&=>6{#@IINdA}R0_5+8e#hWtO%4DS>
n!E$wO*?dtO!o@0hv@4C~#Czjk9=hfNApfbPOR}EK*Yu7Y9=LQqk(N&aq>50
KreNstAn7@90vrpRaMye`S}JC)yo)*&x=DL&rvae?v|S$0nbX7|Xfvm?EH-VNurGzbD2hNx9_4zFKq1LVWgCf
*u_fPpGmrk_H`NON37xVJr{89JtX#Ti+cs#$l>ZJ8mokg|H<-LUd&eY*TVPs)4YetD|GYn^|Hb%#J;}{eQtP35L9lR{Itp(kBFoZJy^S#TO5T(yoFMgIpFG$jGcnjnH
{*su#N=<7P^w7iDS6CcSv(q8^UvGHu`_fzFRyMErxxVz&bLilDYi6H2>t=RRnNuG|t9m1vI0YAG#HD*
-uWC?lB<=}dj1f-`gQVNb%9ejf$`76IdY^$yRtn_fK1eWz)T`x6Gd5&xZbxYw6TL^>Bb2W?%03WEUGX
J|eCvp_Wfp1*-_1!8fSw%fn;^`+w6N3iTwxLD=~K?sV`0mDV#4}_<+#0XrlB0&vi&SzZR@jx{k-Am@t
ZO7-H<}zchDN)x&kduhaR#zzX?I7~M$+H7lL@`jtCJGoF+x8UXTzEJ*Xj)_tPE%9Xbm$GOyxuJ_M9r7
usl%owuOzS(C5MhI*HC|=Ute}!O6jl8^-r~m#YMb{MSSjKD+J1^`58SNd0FFAbfhbgy2Hw(vj%}RcnW
(S#1G;)rb&nhZfQxyi@lAdGaN&RuBHQ~L9r;lq+ycBQp$FXJC#YVFzBH!QLG7MEER@Tidanp?fa(A`6
Bgm9NfO2WB*`5_FKK$I_$CszHHRTE;Sy(O}XUMKi*LF)4%WR`ZX%3#qZejy%&?=d{@E^=x~Ei@?onL^
=YF6cht~}|31Ve|06lKLj;66f}#rBl56(P9IKN3oN%JdFNW7vQMjNpkn;m5%wLtc?|k=@_srY^nd}zu
a#Yx>^3WfkkMrk+yMv0lv!}h4?m}(!7Hvq9@_^23)tk`1Ld2|*-z`qztpPQ6Jr$>FS#P4jEPW;~Ht63
dO2eEj?zu#a{}7odo0IX)sZp-2-N}mMPeIxOSr&vSHcO38fHDa)eh<}bHcBWk+vt4ObLQ|9%0FB&y|a
Ev2&dyV5TbraOo!_IN_@-dkZV@Zx4O3UT$ZctxvExoW1{oRSQgah4h=SL!XDcw^r`xk>?oV`+5h)6-v
-g=lsg^7e<#o#qCc6Ph}{L?}tG&No^slb_u0Yq`gD0&5O1WS=qC_0iStMc$^%o}^z=un2rmD<7h0{8+TB
oF;B?UZ?3kkhWhO%?zw_>{<8h^H(yAIEiob;b5k9rSfB@V77S=ym(4Wx()JU>NKr*PVxLr{%))oAp7G
EE$n0(JeH1(WvOp3ohVVaB^x||6(AV~&{}`g3z^OcUJ{e{`7Y_WVQ-0)j%mS787v-lUK8oD`3yGh}%p
QqMo4Oa*f3@^Go7GOCH%i-JLs>Runxw$ZnF0K8^*ZVokC|xab-p$MVjC$dzT$@vtt$|BLu6kH!)`@jA
0F@$R0*t+N^C>5V-_dn3pnyd)=^ZPJ!qso1$>3v3Zs%HH<
`R>h}-|uy5S3lJnp6Op6=OeprdvRL;N1gxS!8eZ|Q7|BnZRjrYm`3X-^y>h+jp;cj4Gy`b&s~f~pUE3
%A^PpZ&_MU-`nGrL(j3nl#x2KW@7dUaNc_w6wE|?6^lK-vtT%#hFx)u^$g*}`$8WMvxdRhrmk9l!xwB
RxS9<|lfWQgYpiigC(`&jL(UVICCKz8j9m^6iv+lyOPp2|2g!yMs#~snX08mQ<1QY-O00;nP0YzN!qS
k*(5dZ+_H~;`10001RX>c!JX>N37a&BR4FJo_RW@%@2a$$67Z*DGddBt1*Z`(MQ|J{EDSH++<)=_uv7
Ka-HzTM0u?F5r1MbdU=5Exp;qGN4kNf2eH-fZq~zt4LlB~p@;Y_Y%vMPp0kzDny_<|56-k^zZTnn@L6(Js!mwCXe
c7O_&rGL37Qi2Jl&i&z6W5?|yVi!Sg55H`1IZdauBYXu~hjMvzF2x-@Zp;u{~*Mx;oX?~YUabHx4NTg
a;DZt!``?aiq`dS*)ER8tvh>;UzoK|8832ITEux?)VyDfM&_9YTg
H`=m3@{K%Q%zvTcPYHwO1PSvdA(`iwfi0rG(h(l_orAF5O!&v1|nTqLWRtf)NknAV03!%IimH~(b>C;%d6
89+PIUIf(!sVSj()0{=E_(p|M|Zpc2K(S6xNg44sZE8IJUlIa8~}{ptGjY94Gs4d8*EPej*sDVOOgUG
j=|=a)YopU;E4thkLcz3O_eYPk`=ritV=*JmG3=YdLVso&pxPOEwwXA5zf@6xKs**kb(LE1c!`A%gw#|G`hSxIlTh4Om(Q~n3``yxBX1m+s`=9
`AA2PJ|CZ-okZuC$0yO{#rf|IKo^7$oB)nS8LJ|eH)=VKBJxF11dV{uE-pWw&cyq8e=7wfz;35toBS%
`x`s>I)_7Woyr?#Dmi|MMA0h~FmWf=6sCmI~$MoNfHBKD;5B;7o58t+c|v}UU@?R_|AO`1gQiJX$uf(VQ2B^-@+g?}R#tLT>||NVRr+-t9AODbvB_CWLy|Z9imqx3KIZAaupUTJs6YIGm5hq@h^7?9gK6m(-0j)tx^3m89)Z*Uhb
a)(IZ2CEg@9$hU4IWgUPg6!07cobx4ofRUI_+3u6uP{wYi>U+8FuFNQ4#2sA=}XwG+i(QHU|)6Pp1NFUd!5*jHiLKLkPyiW`L_`?}oFtU#t&QgiMrjL~Skzob!Hv3EmJUml;uBwYla*Wl<
YcFX{k@Wyr3uK!o@ORjtwcPj;ds^Tk8)tB_iVY|R=F(#Ur>Zb1I{LQ2N%X;WP4)jDPix8G%T&72q}5Cnqqa45r<|#yvX2nkU&
Nh4Q7I~sDFx!A}1T6U+kjjL+irTw&cLmfSrt*cd*e09HB*SoS_lg?HdysI2WRa^e?g1zy
hXm{?JM!4T*ef~?kN?KrKqcT`Gu&_g%cj%ffJ2(aEaud5b0D}&3aJ>m18C;_Dn@5&t5DRz~{lE%+zvl
N(*MS`A#zR^S@@-V+Rqf-X0$OkuhjLp^$p={D;SHJbzOpNQPz7a^zVyQ0QH>Rz(tGiyANym>-p8s%yo
%oa`AkTZfbP*>tZ(m({_ICF6-}b2(>7j*j`J@!km&s>5daGBan$nahQp+AE^sDj2Kc{|z-lJT(Peb3h1Xyi~j<>})K6i=&(X;`h}os
1e+BDhmg+TPYvp2$n6%nrvVcD*~R@6^`(!?Niph2avh#=y6U<**wuxDW=W9k-5EWRAAI-
T|{Qi{m8eEglW5gV@I&Jx1OR{%-zom-w%&nISy~V;q?nPQ!vr?!rhIVxcU--YMVQZ
J&`)fyUh=&@36{#qV+G=*l+is|G$dtXWE3WsRLdh7jLU+{k&v*+u3DhY+{c`GN
?BG!Z|!q-CX0f_EOubl3aNmZNQdl4=;z*DD0Oo&w3rs&phAK*mkd4^GMUJ{xHo~#W?u2N_B!4t_
?1@wj_RB;GRU(k2#>IqUs3)kUpd+vsDuazk2B399r(Kv7G7`la(R2=wP)6)a`;i9T2
Wp1@6xDO^|D69Tj`Y~F{M~qkg0Y*hGuUSI~i0rEh{rqbdz6y?WW7frzRU#4hYpw`eaSV60MBmUg#0RG
K5j8#oemv+9Ug`hEhbrAc^+Z){ainRNy>i-M!y0%%$QRE7z%}W?i}ktB#LgOc=BRGJ2
dy_&+Fenq{miMfHU*$4L#oQ%$dfaH1Pd4@tw4zm^pgrmFWX@okaQQhc!E;@SGKX!jl}LZ8T1>`mK=1(
H(316IP0wl}L$!$X?$V)Ri|U*wT#?XH_esn;^t4Y?~(t`#SL6uB6s;V%NB#~R;y{h
8GHRDEqNA#$Ukr=Ad)RxLf@tXr$Ghp*}0U4JrKkeH6OrG5k4sYsX?RZOldyYMu<;u6PIW?uNz9A&Q|)
WpGIJ;!CP&k>>hjn)|lzvbk|*Yd36hm>+%NG-1Kw{g0K3?*r2EK8t)d5Kg>HThXaW3hZ=dZ`+EJIdM(
CH|MfdHu`zaPkLkC?>4z?J(|jvOy|{Y3P%sek4&I3AbK23MVB21_(uZ857&=Q&3LTz
tXsT?#@O~}xHu2E+r}55OwwkuLNQ{~T&L*x6_mI^X+TD=S-s4j)ZS<}iJ80Mrnb@*mdgtazcqeN;i-O
eAz8$|s;D1Sm+|*@pQ(;*c^UjHb>F}pK#anqI>HmS+Nps;^O0ll%QqA7HxkECxy`_PTH^z0Lc-T#C)G&>BcDl2}*Ic~0Q)kc%kJ%UYL}emP$IUpU2NysshCBrlwl1G%HcahDUauV
{=9xXDj!PPhZe~ZQfY?Br^yG$~kG6_5Fqd~Un8(5v9IXgl%9_s(%HEw6#o?yKO_5kLwAUn^H_`f!JXi
O}!O274blWqXhIZFd+C~1&pQkBLUS~S9YKCfiof!jCjtw=4)Z^nwY8+O9Vgj$<=|M71_FcvigMfX*AH
){}VK`Ao2%>>NRmWAW2=F*ykOQ4O)04&Wn?KJMc9wD|wdR$k(cBvd9?qZlsYG0EL~FVf;Oke#5kq%y(3i&Vgh|^nAhjPHKj=+Ob?vI1pQ$-DN%Z}+Hdz>nYp9`aC07Z&
um!@lNQ0e}^Tewa6dDE^^Uyc|ARDgI1c9IS(gZsD-@2m7`!wQ
#4FN?9Edhwa~+nCjFtjyNmr5~MT8Hu|vk=|JwxXwrOhckqREynl<*J%Cnq($&rqH$Xb>-~cM!3w*KwJ
+JBhl?{>J>N@E&hbMXOF7hC`T-Out13ae}9>;fj{vQ;X<%3exgZBgQ=*7aj>*})a;H8%fpNIKxI$NFF
3gq_BZC?0+Nsn4Yshz19s>Gj5IA~hM41kzUCeLEJ5;!zaOqIw|(8_g6%8Kx@s^`i12KYRVmUR`eTU$*M|^Bf4O%#{`3Xf@E
Y3_>w$JFhKMc%%xJOycP|cKSHXimBH{t?C<^VL;yjKYx-(dRs2(T5=>GsvO9KQH000080A>M2T%{KmV
jUy^0Hu8Z02=@R0B~t=FJEbHbY*gGVQepDcw=R7bZKvHb1ras-97DcBe`+^^%U@IQX+li$al7{T#R{T
omP9h<^513?b$A!LJem?a;)JDJs3*MV_22SGdPvk$UEds{3PiQ0E79WsP$E)64l+390CoX(dh36JivB_mNR@@zd{Wa~n#g77|=OaJKTdJ!wJ%%XK7#k-GJ*W&$$YfXKgw1z4+`lEf(if0Pb(P~IC3VYQiBVoFp>dz5+sS3#fek~Zn2O@YC
Y8xgf`Ito~_kDD42N?i8L!jBv&$xL`Kb*{*mW-M@Iy6GMTN5buK3pGy5Qg9s!}@=tw^cvvd~ES3$01?
mR2xry_}`=1G!;K_bgf^Es^Fd|U;^!fBh^c50){+aeD_Ifa$*+`=qL03SFLKbVGQf^PtYh?D`pE3%xM
$MC7hvP7AN;Ub9BftbTCuBMv-jJc9Q!HtW}3O7f$y#CY0`Q+^N`PuK^y#H<>-Xf?V(I}#gdjR}pxzA{
Zo_;R>vW|1PlxblHe3L!|NgQ34U+shNAaez4t9AaKpEf(@ak%gS=_*SMX^1oaI52NMF!>I|CG?T`rBv
&rWex;t8RYltm4PHx7_4MRQ`~2ZDtHfcF{|QibR^)9dM^Tn8*SQ8Uc=|3^6Lt4K$KL4^5Hzma^Egsqk
>!#9^q?TjeVn5us&IbXO5M>ltmB)MPNXE8z}iQRz)69*NB?Vel!6;iPL-2^)0;bYM#pCG0X4$D0@tkE
Qsu4U%}CQsULQAhE?PL@IV!KobCS8N0)ex@C?aZjLl*4=dyT9&w7(d8Z6=1_K%K^UL3s;S79)lWl4lb
?!{3gXJQho$vqsW=_CX!^wdZI9{OVVjhMnI;T_8*9R3tufE`rC56UbADksqp~#v(+_CI2#;qBlou;PT^h
oo2J}^*zr#PG)v-|Y0QOWizS47nJ-{Qd+|KYa_RNO7j5nBpfm4>bj
u6cc`f~^Vc-9A7NO9Z0yl3T=UOsU3>xC3k*li>^A
+eQDyk2*G83!<`D&o)rF>gv3rz!VDIT94OJBpZGWIMwK&9Zz+LIPzK2o;o=xzAW6afadn*sl_Sn6A@s
A+i}#aU2e?hfGPV0qmmKm?0P*w9D2xPl3QKrBWc;lrPQ$fJ0vhI0y)OoyNc#VBcmEkRQrQn=nX4kSLJ
LX_%}dNzj5QigBDEF%oP5a6poxp%
~n;Y}i4#K3>RqE-ltB@rNxL4cyMT5}JQO`tTW70m3@5T&*pPS$YVwhCU`%|ee&No
joJ_TiIWuEJCDpGrbuh~=Nbjh8Z}%Kp*!R^YmUy6veP07ZLVrv|;e4}(ZSM7bdx&PSrIU=Fct8D1L;d)-JsU3G*o5#=c9c*i@wZ^q>t3S22)M(c&^{
3XB7X7-d{oLBx?u2e_KSS$kTd)63`JjqYV9gUPc)NLAyWW9+8LP^py6Rl=09S_|IS>;RPh&i+k%uSUI
R}s?P{-%ekoKi*|GlF_|0h|T(LH{W#r^F+{_Agl{kLzPw?cS&&$D(0XPw~&ED>5Hg89rvHde{biYKX{`EcgFB?WkE!R$QkMK}$MHCRSu5h{4XX5$&T|
JFGfXkBLGcQQrmK}chjq5%`SJzYywdpn*9F;)G74jD06xY
^f@-%lI@xfYW)Ij5{lwgn=1dQ=FkI(KJCe2E7m*}`$Utea*$FJ$?HUMy}X6B~or0SS{K%?uF|U5>jNpT+g-~{0zcOjx?FfpS`>}4b)RsZEOYK>;i!
(knc0O66d>1R&;rYepBG3LT!{S?9j`s4YUehDfPMWLDio5$owoK4&6nBJ}q1wQ3%ew*KsQ?E1>eb%H4Ui%)EBg(VqlWahzfH70Hd55<;!(mf3x_O;3qjdg;Sev@l^bQVll
1o0IhM2*jwHi_>?LZjP8_L`!8PB{b_JP*7yz1Tm1Ny}MdZ#6Dmnz{>Vi4j6;mHkz8yh-S!h
3gqefskB`qV>(+q?Mvcf&XDUw!agxvt`Q3R9ALORTFm+*pY$D{uu&GhQkbWsBf}MGo-{6RSe@1Ih)N?
?8??RdIKRp``a&@hV%V5v}I#&dBsWGy9l>9Hdu}-UdnrF(=YrvS^LY$AVq)usK>iSOyzZE}}KJlR5D*
9U3FeLLIIT*iR8Gc+?2Dxo%)ytfMt_DFdFPw2nXt3zi!CX%(&6lnA=swl)%Sa1#94tcvxiBoZKEkWqz
1dl)$Y9CeXH-*25PDFirG;8XZgcXm4SBA2K0*+n!gvC~)hLVQHxKU0<4DHhqta~kI9+{`x<`Q=ffg=sm;f|aQGzJJh+m%W@EHHQVbuE7O>F
}pwI7B^G*pR9cI<_n+>jAb?rVnwRrO2g-cVSis^-*yl{Ti+B+XB6C59i6Twg}j7NWx!RW14Hp$g2y{s
}LhOXW4QZr?isJWj#HSshr{FflM3-j{}Gpc?|l5=>W-t@EhcFbu$`%dE0Mp-Wg?kK)XV08QzZedt3H|
jyvEKa|tw&yP?E#Ck>lA)XoYjbbuBXP67@b6AG{;1t8Q6*qN(;yQZEKCRfNQFEBTSx&OhFm>c_&qi`QatfKvrltfI_i&;%wn@T>
C(BA_W$LZ28G&HxGuI1GYlB;;O(a0&k|U=8&Uv5Mp&wTi$PCHtTH4-s<$$Z1X3bVmSN{UOfQinPj?ae
nYn$BM#n+?yN?NgNiqb!;%&Q|XTOo)b6DvNYw)qORdyUteD!P~D7I^el-;Jtj*vccdMkJdE~0k2|gmRkMV
Vh|eG_pTePsyA21bn{i*R=65wdF2Ee4NO?pK%>wsHFbOo{ZZTSW<{0RGzMx|6?T0CQ4Xqm^`9x4lnyA
D2y7U0e^^MFAm2nuy^mw+_IU0j8_L+D!hAlauo_HZk_>hqIHxUxtUz)ncgUoA(x3ZqO7ncZD*7(K5?>
YD&!{ol^0T_Avz6=<)$J(rs-`@#+naT`=csO0=F;-S@zw&`9rGw?Iu*t%tB2jOACcDUmzXm4&YRNyDE
46txoc4>WHvd+v0o}4!kQy9uS>25AebPKs@@U84xS?|(fZovfz?GBoj%GoBT%ke(r|OIW3nm2H90bhv
}!|r1M9dPA~hwTXo0=-LeA%7u#spc0}^kkRR|qfPP2Q4#}m5!Wz>?LH-P!bBr=}P35=`XFYWtIe`k@PzLDCHC3r7uvgptN>}T^=uBY5B<#f4El@>MdR$+!z)+osdh@SveW
d~4VvK?*SJy^0I2Gie@?!c4*y))cql)CX%!7opBS?K^jXPiT8Er~xf5c@>t8(#%aC$E%Ij-;-0->Pl^502VyY
rA%*)Pql?2`j{O9IzKCS`5LsKHMw?X>m=EvC{b)p`6YJ*r(kUu}%
$@f1al
N#(6&0{uBjYx{1|(3F!5QOuNo!O5QlRMa>!ip^H`b*L9(A)?e~Wsc5J+Jkj!hWF#rTH5j`%@X=ehH
>et=jGI0^M<&qp$jw1eixaO3!lT&KgFFyXmok=;d6?xL`}sA&hQRXF=$ua2=!ig*RcwatYoN
dRRT%a$V8+@xp?XC1`{4DtnIAH!fPsY8nMB(P!7S4jl?NhQiV`V!_>~n%At7yF-$igfM>Hfgwp{Q+{
{^1h;=U;(W4X%9eP;=L=FQ0Yb!{=K9njwE@1uW4{3kmxX6rL^-9KjVsZ
1Z0&28_h3Q8D$7j=nll)#!+c)Eh+`BfLq!e`%#R8klBLKt)}V1rgi~3#GJ%InKWc;O&w8WgCsLp6i1Z
G6fUJ18_W@!4V>`OF*(>Q94x2|IRt4HU>W3KoSU*B$6jbPxT?-fYKe$L`OEaIi4iGrEQVi}XNt~nJDW
n!F`p<@p+Kd`kupKPR5oLcnclQ?y;Xjx5D~&)lmMt2YxNSEEK3iCpRs-^PN^Kp)t5NlSmK9w1IfT~Mm
Xc>@a2=+iWMN)ePvfUal68qF#Jaef;xBrl(t$Ya7Dp7C4OxfU#Yl!A1ag#j)S#V%74e0C*J)SlvPVQTBu!hL(~C5gE9Fu7~qUtq>=lEz&%R
f*2t?ds{tT@dbP+H91g;%QJHdrt8{?j?Eb<%+@(5;(MSNNB^3)JlMFN1A1{=<~fCZ
b-nqD1obrlz2W`Ri8HJDClQ%8qU8~5!8k}M37E-d;(dilq
IU%W68NKAR#etwYk!-I#&9c7l&E98X79$VrX*0hI;i_>&;F`S|GM}7HP8j*zcSzt90j!*CxtI3OS$nO
jSTskQoq@um+Bv1H6q<+2KlYG;{KB5kJ5;4jh}*vj`&4Orl6D-6lgrkz%*Oa->GAn$nHjz02hTvAkfp
A}QpH-kTYAwoVgWliR?Ti<}^(84b8ZmiT83K#3co3b8IS^yRZ6z+v`uV^z+|8ATN02!7v^kY)8%5A8f
FKueln<1TM(OU_WG*R@8y;m|!e{wki5)|JE|1|9ll58x11MCqTg3bNd))-j1oJ0k*RI-d^GR9=XeSp-
N5;~W%dQ1JL(fHJ*iF5g_APhOv1y)I>q*@JO4B|tQIj}lrtdI8J*+YlBPlT+qmh
Qponxh45|%IQXh90SRx%~&h_N2TVOrKP2!d95*`}fY_~Q!`?bhQ2DL<`it`yOnT~a<(rnqUI=1Zk_4(DgxHvug{pok-SK{Rdy1D7y>Gj!b0pAHEN_hV2^
20m&e0l!QAKzS_zdL__edTW2t2b}YeW7J1?dy6KkzwWH$ZYLEfaQRvo*TJ<$N5RxyrZkfB&TSP-|0y9$7mmni1e`RaFxj9CnB9^LR_=Fnt>^NWwL*0vZCx
)XP4cXP`0DdF3{SCkpbkR+XMi`m^Q
KgeC@6m@c2MKq9D$MB2)wQu#uq2SDEMT}sB4nXQluXC|>ml^dncT^(`Y1_10^whS7NBuV%mKZyV4#wJ
QUSlgELXY)MpZQlb2So9uyv@cKukQpC8WFz}lFz!I7=UtD8DmJ><}mjd!
sD(=^rj$%kg;t*BF({2zuOST)x)MPEaPY{V0RDPxM2^kJv`7t;;Tur&33R(vbpk}=h5mKK-O`a_vkj7
B;KLUdFUZ81*Mu9O%Pvwl{Zu2cPmp61ZrnKLFo(JNGbF(db?wss{W*UsXoXDAFSN?8e9N$Pj&Vh1IPFd#A
pb>jx4^_c!U8s~vFnM|Y>h$B=>&f{aug@>vpT2$jr^&n1_nHuJ@1Dj)ROgiaBEChU)ZX
R@|mkpCg(CpVHgydY{8=|1w|-oZcKDX&?K(8>ZwLsTxdlSO-OCL2)ysodaFn(7`-2pNM=VxIXSmzo-Q
jzCjv-uqa$K4!I{4hpYVr~Qr>a$fe>TM3Ft3fFm#kSn7o2Nec8Pb8ln36d0D!huBTJX09dbDF2~c#s^$~djruK&*6?<=P
p#|nH@czCqF|^|Fm#aAodQ`VI%+P}o)l*n*gXB^Rb7-<_T=f=ml9WlRcC;QWy4%I=(&wTI`UsyFX!2t
x~0v*sZ3&NZfPJVG1$UIa4)SHND0$$=*Vl^lC#xzSOMuDuBs(dI?w=Ip#{dDU?!;cVaz>;Ry3Uf=9ZH
~ELQiZz)ykF?vZ!URP?wl(3(>j;6Yji>?a=%36P0xn1KBL!uPHkU^tl1tO3TC-zkjCg&Zp>X&lwqNxA
0}z2_6Iinx~>P$`kl%`L(;wflv5dp3D@esx9e0t$qSiw{?C{%CGmV04}d439^kMKeR8qApBctJGTpuP
D%AW|H6~OE()=WQxHWfVr+C{6m~Z*`uPG;#@kQ)`WIWkI)@r9n&$l0~=mK7c$2`S|$eS8)Cu*mgYdRe
48@c3p2DY@D66fyZBb;VS?$ud~dJyzz~aepz(#9U|L`d30TumqA?g0WyzfefVU`Sm@*QGR6ar<+N2_W
aJ;icUc0!=VrQ)9uIy_!G?)fsw9)=e9`Hg#
Px+PIbO+{yO-9jOkr!%3;cEM!Y=in(Cr2l{ek?hW@Z;MKiRn2zo~eWY9&tKoc&WT}<2PDpj5d5@H#Rm
NOyT=j`J2vGw-N!??RAVO2Np~v33m<@kZOM*6uKx5tarbSeHw+#}|7dl`b+dd1mGV3x90hG)#6xCR0{vt83w
^1Ll_!jqPqlx}xl@P3K{n4Rh)_B+|kxLz?oO3bk{E$+q#IL5
Tk>Bz8&Tlw};iswVeH{BH(fYQ=cZj`j!9Xz{%;y=VmT#jJ-NPMWRf*?lo?)O!cs%>Ro5t$z)dBQY3u|
zCHX1v;9zGgfs!>V}1dqtKPQzN*J4FhADlaq>R9sq?(CML3x8{Vn;xbMQm=Z(n(ckEDwioWje>Wo5HVRe#>5^>-3JH-&8(EBpISoLDqb@sG~$1?T{QnTwC#w#EodiwQb~)~b=%1yth<(6a
yuzY~r0sJ}aDZ-$@xl@fLH4o;qJ1?E9f$mObN)(hpEa%MA+vtYI@9pPwxxfAwNtHz6WP*EO|S}#!(Ey
xoDqRhCjDa(J2moYFLk;Kcm*c*w#J=8yHM6-=cPX;H5Mvb_zyCkoDqIk(Z`YW~fs%_3X{WK~^hZZaL`
B|9dFGe(Z>&&w==c+!jbtktoe2$lbzwD+DRsG_EM@fidN)Wwmjih${#!nL9-(e!5^$v8yBxl~o%eNzk
95CMyXd((Q_&izXN&ObdE=3goo-DZ3zhY&tIeLhTO;@F*y>?4w3F!ydS@u%O)n~9cQ+>6uB`D-0z`#F
WrTQseuCWjX@8hCdpXI7pba1lVPH0$UXnx7na|$dE3nsj*O__c0JZY>O(KDubc7zmKxt}@a$+X8-!s^2Z7xt~t8
Y32yGW$JzIbqtNGxlM!!**8s`qY+d*(RM9C^(J7>Dc6tGLCR!#;bG1HO=IFKAh>Ud#S=8p=q#=@$6l}lpw+z8XO1T>@
;&Vc$vm}BB;1ZJ^Gjs$(eTOlBmcslbar3
UN<>b$GDdD?%!>4mt$-x(GBlcn~`7%H2}@j3*?p}8p;^*Ks31MKtQd*-FnY#S|TtF_>^KlQBZh$glQg
i+A%^ADjC#9q^J)j!&^8GE8YGf$s&T+H@Z9P@O_(@nXztU<+d&C$-fKNZ$fY~b}fjOaiCf4
POT1@4hCvgvGFe&?|hTfI@{Z=1QY-
O00;nP0YzNwp3pDp0000|0RR9M0001RX>c!JX>N37a&BR4FKuCIZZ2?nJ&-|51ThSS@BI~FPA+JB5!3
-adi3N?q;#k0OrX=2B%=)YDA&~G0FCQ;)UBUab>Bi_i(Ap|1gi@*orCP)o6EVRWsN4jeWrR$kAE
NvTKk!xy!t*gsaQ6aKbTrUw7>4(S1bR0{LH@!BsWQ|YVd}m$Bgn+mR6v)Y{lgBTlrx+W7!xv*suiYZVNEfz{A)*G#atne8i)xe6xtVm?&UUJ+YkLc>QFA1+p}2?pr$>7QJZ8Ei&@Zg-?8x2JZk<9b
7UjLwuF5qQ|IMK_c!JX>N3
7a&BR4FK~Hqa&Ky7V{|TXd6idPi`+I8eV<>UIuARBS(8Fbmw^eiG!KEaEJ@zNVl=j#QP!4RN#2bK`R_
efvTS)~H))qZ#`gWzx#wOrMt7_%o52l6lqHj0uZ?3|DebtETG`EJdZ+ECA?ZrDt*HFBD6V)XYB%()2q
JFmrTP@V{;tMNc;CyuC?ABXwJCaD-SbZnD~$MOkVbSux!K|y^}t(Mzgs?~CZux0DBc$0OC|bMDBBIg-
y3BeZ(E7IUVP3!YJ3u|
G-sh-#0v21o#Q0X
!X!`J@(Xc63>!f{lM9TKdCqs}g+vK{-FO{JuV>_}c!O+TxXsP2<@KC$d>6Vq-ruamSXmf&ao&L@llBF
P@&(<6MA6rX?G_M@vPfv@a?%^gg*GA&3d}AdOa@8B(FSg?!=`yfZTp>4pG9X4~oMnk4hxg+C*RFdqcjNMOK>)~=
YP^9W!6|B2hBThN`c-K8bOY}Z)LXBCgDOYw-w2?023jG;3hL}u}=mtO#4I34%}_peX9$-YMXeq*&3)?
4F!FSw!1j|7?9(Vfr_lg~ZN&P*sF31I9hHpmsM;BgI}D{$+e>LN>u7ZY10wWRO}yG-{&ot16}007>A$
srqDbA6M6)`8glDzLr}oN$Qaryx4B+Xge1*V{awr9G%h>v;*{lVX7xw9Wjt>+mfLDx$Sw|C}=Q<(${*
o3#GpqWJ=+$x*4#x7nN*e;H4efC^5mU9lFr?zWfeT(9W9fgdyB%>sZxlD_R)
u2A}~44UNQ%GrX2$p0`sMEp#Woh-wI~32#$Nv&WMVExk#jVGp3^i9M5ed>nS@sTZfGDa47}pQz;5)oC
sxY>#=in0|Hmz=^NSy?3T
LyW(hBU}*~tnTDUjq%5e>ihM>^6VxUlQ^v87keH)AqLEV~yY;YfznEy8T6jn_atX(`Vs;k!%SHf&)>2bngZzfh>Jo#6>;$(4YCs8?V#&nZ79CjAD9Ab`HDaD!#KVc$@rQ*ek64Ph1ix4!=#TjsEW
%{{d_YkfkR@3bF>}&G$llhjd&E;W4``AOtBcH8-lrnT|^gNaK`|Q(7$@HfU`7Y@By=3
n(@DTvGgYL%}qcMo5h&(KOSiubFvsPDbBJB}~ii;8LKMP%t?2#+F8j)7Y#7{DLf!wNLFz&4yaAU1U
o1GKzmfx4z>6%Wc+xNfG@FeE5jgIyu8=z@j@3L7(#3KO(^Vb6CI)sD@X(Nzz+S?m}ZF)gzaNQ3~`T3H
n<{@{6I$W4^|!dZME;kM{VvG_tZ;1w{f0~D2o-oMrV}-ErU#H`0hg3B_||iXu=Set#l)W8IOLo{5qYO3L{m_GjGK3b;HWs`k)a3c!~qjO@nyo7Q@GUIj^NFe>_
}grQq`L%n;wC*X7IW;ye>}OILY=yH(5Qn|}dNO9KQH000080A>M2TqZRYD6I+r09+sd03-ka0B~t=FJ
EbHbY*gGVQepRWo%|&Z*_EJVRU6=Ut?%xV{0yOd8JufZ`;Tfe%G&<$|yn=G9MNNS_tKKw~5mRo9+g2c
Jp8u#F!jf6H^@SaA;Xg{onhYxsV)*_9k0B*y8YH@l&Uo@V!>Ix3aknkE%5-*WEx$b!p4ZHavERZc!0qxRMt&?>f=3kqdX
^Ak3i!-SF}yuWNo&i-mjH$~MjKMN=vb`o$e59@2ZEI|vqVesPaqfnix^ymMdj`=d+s0D{!p*f>f9SFU5$1mOKGx(h#(O{9h)uI|4=
Q13>!>Q1sDMU9%r4eSY^c~1_Q=`9=?WH@JL+Xvh&4t>*jRVHVH$T4m>Eq?o;31$QzshB&@*&io#Dxm=
p;b5?1WXnTAJX@Y=nN33V797RFIG=qU&&e^N{!gXxAXE7FTh)Fc5O~T?z&<
tio2XbJXiZhXn(5OLtaX49r2L2Lhq#2aOd7@(bk~$EF-`uViqnQ96){eH*eso<;CcAQ*Oinm+u^)dn=
lbQX%-Fix>IFi;EvvHe|n){=m-+PfC91vcCX^vKvAq3u!Kedf(c!B+nbZLt+Kg0%5>%(tL;1Do8u_Wp
XDD8`c@UV&~t||BdAXr}@pnAoFGP9TqR(-^t3pBs`q^jGJ~_{>?XkP6m`!x)l$lyoH55f)+^djl}~eH
i1$^R4+*vm&A{|)$#}>OOxjbBB@%lfBYQsudm@B}o3HTa3etw(-w9=D90D`D3
2B0(cK_8e=P$n;#*$O5eWM?SLTg(AaRuk5S(`WASRVMv}ut_2W>lK04nk|Wd^^)a!)?)SoXnPe=iJuU
ko0CiS5}c@3G|3!~Yi5lMQL|-ls`I}tS5#&Opi_EB(n3h8x+$qH8PHipbcAwgF>*qV5Mqa7`Eq)fhlKb3A%-;-jEr|$X5M~*kuwat-
0s5EYlOrmMXtJ{jSweq&A1aZ($It);Nynr&SvJbkaXMtS8p|CDXmTT1T=+0K*lU8-!U8w+9u^UzCUP~h
q;%8{f;v)NWYddGwHLE7voiq+->@t+q5azasop-G$WF-sl47T4UsDd}_Q{R~*DhkTAoWuTrzo)6LBin
1BHEPJ$){N;<&9+#w|LVpqb;s^#Y|7?P6JBRjW~$ymuTgNmo9{?>GsO#A>zP
8Jm6%c~p6dwq3H9FTOD(xjEUO~h%B45hexAKCY$cIIbw
rYf6F;7=2&9mTLeK1vXHi3IqzyM?O#~a@D
A`6%nCZ$_{~gu-QNGxkI0h@s_AI!Aj`vDqgq*5mEO;yIr-==|CJ)NgRz0qQaYDPK^b;S9DU*&8jOPn?BA
SoYjhGk@G2cd2;+vuOMx58Lm$15;WdM8{)wOX&?Uyy?IJgf~nOQM#|T|RE(vUm}fx>jIxV9)gEQ=Z|>
{~uq3VJvS{Zg+RC3@*GQI`-dDCWYqIK0om0PL$bejzmTM!7#3M{VlBwyywtt2kE#c;IM*FsaI^gYRhJ
~Y@yAtYRvme=Z57EUjJa^m;}PP`TJ8?)!f69
-hIpw;7C^UJi8qbT*!IRJ!Z{ngd=dJ=EfYZrl0)sJE^CX^Fu=D4R6lT4Xi1?A4*%qVz2H(ekIExPiK0?{Q!e99a%%O#JFToVP@di%&yuUfjK+ppI|y`2Wwkiv`MqN}5OF1c!~WQz
!Hy}+VZSBsI6-#Uk>_}pDTyCfPB1=OP+eHfT>6oIq@jv;R6zgk?wz}1@E9sWa{#jeNeS|ODDb#L
4hLRiekSDw#eqONzR&ru)iHCdrYnHIWbC0|v;kMAp5OiHsr@F
E@a8D-wzE`I-E=z_i6IXP6}PxsC^Rs67(N9S=yUoLL&12EY@SF%p>$^eaVI&)>HVjk-Uzln%`)7*F)z
R!eg9D)^V60>2}h2Zzj^%gu3UtK|`1
y?APN8gG9v&09smFUaA|NaUukZ1WpZv|Y%h0cWo2w%Vs&Y3WMy(LaCzNXTaVku6@J&R7}E$!AzC&z;$
8$OKx?lH14$g%PV%rYh*@eRu}hIOXSnN)oB!VL%$*l$7XkWG0(;@iocnbSlO%cD6$c?z$5qp@9e%F+v
f7sQp4}aUs3hBU&5^ySrQ%ifPJHUiP8>z8WR@h!Vxf2QeAlbK6M4?cW7~9!@w#plS7lSn#lo&t&3+HO
?zNFw%hkcXcEaE)nyM0ohRpbN;Sj!2qT|<TT13T5-d
PgREC21hcROuba9ocvb!;@|qvTYPfVOItgyh;j>3Puk|fJx4)Rx6|?`uER{9nsd<$Z9O`#~zmmpZ(#q
UX@wpJKR?LWy273*4818?rW#7`iFgtU&jzaM*SKMJDXn8N8R}Hdad!cge0Qt6rca+z?8Y&^6YPN8btS
ajphwXd(f99yhJCJ$qaMQx##t*bh4#D)2<**g)i)Vj*JdRv!OGVB!hs^EGq3gsa`oV&ZhG2$qaLfT~r
^{>*IMA}*;&_hL(;&xE`Y;f%E#hojb}M$^@{m&$oCfW?;O7)mpN{v`fgEQnFjv5gDc3?A{l
UQ2TwCV9SGEON2m1IMn(JAt#E$cpCJf~T}g(kp)P+cy_~&$EwTFE7H|ldmtZkgVwRn}7ZI_TA;nf4sj
0vVW2p-U-=P3b<6?R^qCH_gzgD{Bgxj?R;D;7F)4ndC}C0!&IKfRIV9(dC4xmW7kbntrrXr(%35jB^=
9IP|i~ah>c%#lq^*>&5Z=L8-c9G9IkAkf!8hX_>lqehXA1(D7%v)W0%=JLsD%no@dV-G5xm_$d|R|B8
_#JNx_kjQ@LC^G324?;hj2Bq!#$oddBHi3ojY_5BssH#d<*VouTPOeT9HhouUCpQAn^OV&BY1X3kP+e4z
{8_8q*SwNq5fjrniq~8Jl9e)tx5s3DdHJ4!L~+yfDr2u2&6TDa9qMbOOT-JJszC%vGC8OGf9wrkh=L<
_DMT|52Q3MNmlM-xn$iAH1%4NN=GQ1WC_ab|yir8Dl^m5jmtgXU$aBJ(_vILWgvf6}LD4d)#mfS(yv?
0P0&=90)ri$)&|_qV4_zjL`b{K9(0
w0S|tj;|VvzZridD1XY`WO*~(=bN?A~J!;3X(B3QmZaW41!mr>rx|h}%nP2-dr1hlxeZ`UyvOU7*i{TFY$;xjV)~R)br1QOGI_f!N$yXym13K
M~D{=$;PHDw;-^<{k)9yWuV3z|$#)s}auK%rOBWBL2)iN`uS=YZi)^o7elmBUrjCY-8PC%hZ2agUikM
9v_@CP1E
^wbb%bq7oo%g7Elv3XY>Ou^>(ck(et~4P$GQJaufet%@3|B(3pDur5-+ucI^XL&oV@D!Gf~6$#6`SbH
1&8w7!}SYQzU`Inz9`1bqREtNT{O&!>t5WNt8U}gQ#bXjscm2A`eu`oP20S)0mYhKC~fBWin1o*BdfS
QL=~#fxWe&Hl{;Qg!bu!+34!@$kxB|nG~Q(Du;FTz1{DmP^6mS}t^+@a@F-Z!MJc=EU5(fnTGL2RA@h
~g!xj3Yd0*q-D5_%thO4f83|GZCA(*4O%#pfWw_}ZL)wUHan${X!Lg+9;Z;_IZy?FLFTcS#J(8P`Ji2
62&95OYO9h{5q#pPDeS*m)g$dy*!3ZbY4Bos^5!#s_FZRi$oxp6Z^l#km(>RL-)I;D&whDaE3}D5Og7zAw+B3yhkmw`TRE5FVp0kdgYnPtvYbUWM33W1a=;zk@2XWsz;zO`u6aX(XE|f2GxwUdG&FHAJ>K&iTr
R_8ho>1IdTBgciE4E;K+P*6LRDV@ZQ8^Yd>xwIJ}Uf$DA%##E`mgVV`neBNi6M9YQ}8KFXxg?ah=|ty
{D#a%C4tQ@A%sq;@N_131CJFfL9tPxQn?uhAGXYJm0PFksDw9zAR(m%sQSG?PV7Wx8b?c)C8Ak9RL6TaA|NaUukZ1WpZv|Y%gPMX)j-2X>MtBUtcb8c_oZ74g(rfczF|*SohJr-pVWe08mQ<1QY-O00;nP0YzNLRWc!JX>N37a&BR4FJo
+JFJX0bZ)0z5aBO9CX>V>WaCx0s-H+Qg5`Xt!!BZbrLcMB}M_*kaNEdg(<(fs3;vNh`=E}6qMkaM6wY
vuHkG~myNTeihlKLTQlbr9F-wc^7%ihZ)uZcNPLt6R7lqR$@vX<8)oceUrWSq3J
4b&kQA8M>#21_noFsy#cVX*$i@F9~Q$Zt~Im_W%0KadW+a-tg7wd_=lNkH|fir61`!%E8m2(tTk_tl#}K7Xj*e)
#wO`@7F~8G#Id9D*>H-YTaweiVar5m&{J5;1ZLd8CSCDriqk6|;g`#WXt2>w`;!;1XP%XQ)~ApAR2@`
;-*~Ty^!#TRfB%v+CAQQbFw35#=W4ye)OxaFZ9CSF7zn0R#}Cd_v?zZ3}x6B00#cJEhRzwHLW@d~V4r
$Cf?UtcB7Ld-i;%lvH!1SfRM$^L#y%;<=MOBnTSUaBcD=vE3=3J8)vNUHAwLlp{u!pl7xs+9>iro3uc
Nf9x^LRcjDrh%<1|AL19bBDIG788N^bNs=QEVC$EOlEH?MDPu-l+r$`(%rj_N&1pkws==n46KmR=m@5
hRk_YTosF9A&sm7l`j!I?_pV6kLr_rvi05xzuohKt*+Tu$f6oZ*Vb5{Fe
R)iqo24r<=HJ1Ns1iaKZ0x$#WNI|Ez`ALezdVoyfUXw>Jg|E(C-R5=0$S0sWG{|84b3x(Sg|HRDb-S2
g{lYDL6RS8ag?H{>5|X0Z^pGHZ-VS;g;eLosEBPgH%b;T-Kje$BGFzaKVhwI08*QrNV&BkOipqND+`{
>UjM8fU?br0dP9-^!d}9vLpxi3wU3DAtt1B)L2r#9>|sOjeYjQG5oX&0TK^S&5mp1rG&PP0Ro&S9KJW
hu|Pxi_Kb?JB(D{nT875A)SS?G>}a8RvI(LaFk4%iHP)UezinX
O{4W$dcvlDDwjUx~HB|;hb+q$3b%1?jX-+#RO^r`yi-TV8gO`(PSI9G@G$>Br=wY0SC1yx5~1T}Hp&J
?gyHBuZa_y)Y(0)#~wyE0+CT|}S?lMB=?k)0hI9=Yr^;Hib$1804QAa-`6kCfZ!POu$moqRCm+4ucyo
fVs(w^rfjxF*Sp3>|!^$f%>{&r9;?O!Z%#rFI{@{T#}?7rdP@%AbdkJ&XZdw4Iq1_@x(!-$vrT#Kq!y
6|qD>=6KwM(VLMYe`LXCTU>+tuC!qM3|uP9#i2gteKp!jE8EFgPS?_Io0xP(rNX40
T)NVIYa<_viB4V`L{er~5?siN34&?9^Cs6o-M#8TcT}Ir}60*<%4tfz#MjTS`h!X(mmgMIm!79saJ4O
h1BMj85G0O^q%o?)x2cQH-O^3EE1K-`OZ-%C#$UpEsRKZjn0T&~UuwfKt;-(P=yV>&Nz-00{oNu&6lWd?E1-a2H8_o`%OVCJn|M5~
Kx--|J*%*HRI+Pz0+qUSX_Qavcu4X?LychqW->3%z7Ld$<`wrZ2^fBz^z1z$G%t56f4#N3Z(xAgdf%+@Nx)7yuo<4XHgCjbyRh#(!^OFO?fC~p6GnFzMtN|M9fddF?X
c6hMgx7ueZf<%;UF{bkQJmOJ@?9Kgc!KPBKPQ;1y|4T;sT6@cMjjNVkY0i!#;bYJe(Uo1L-6av0>CFR
=dmm%Du*{CdON*e47WnJHs1*8_a$oufwy@=*i{v_Bt#$ZPJGg4-qLa#DK8=HbAJQFkE!QUDPtS`DzqG
SP`bqpTef?p9_VxDe+H^-knz=E~<=4_Z&>)&BcI+B(ogEr2xAEX;6AIs|F{e<9({s
1`JCS%hf8v_CW@?#6qz;6xk(X(C2kWu0Y$8A{J+S_@b)Os_J7=!Kk^&p*$`@OamgpORMgfjBn74=2E9
jA*?E64!S*Xjfep%Y{{*_Pa5{3FK3efCu*8Q+?k51ADjeql=Uorpg>?UjXHUgI?iksjmTH_Ss9W}fxy
X8EVxy1F4)rWu0zZ}mPA*4Fv%U&M#f@*ypIZxUDq_GhX;SK~IL-D7ZrQ*krsK0?k+y);cDi;=@aj=
G2ErP6+Oru>=YCP4<;k{4JO)7n|kRm6h1n?LLPHYNm#_$P{g;iK7MI(*$LFWa`_kC;pRmT!%caPQ(UG
gpV3XuU)~`9)7zetGb?5~^5Ar6@zPyzywO~vAdB%SC|)>g_JiZ1;V%j$*BI$*#A$2-CqQk0M2T!gicbBGH70MH`<03iSX0B~t=FJEbHbY*gGVQepBY-ulIVRL0)V{dJ3VQyqDaCyyIZI
9cy5&rI9L1>Z22E0PskBjpa`<9$>IqzcEn`;pS1TE1v8(Gwm(mHOM|K4Xvy<6Vo3iMFnkd^r|Gn|)aW
~d8-;7VA@uJvvw(wIewuy)Gm)yPET5`T4(t5n(`2u7o|(L2WZy2uM7IcI8@X_I4ps&kPmo!Zf;982_O
qtZ?9XBg>pt?E&o<(V*6*1v7_W)JmiZbT$kBKqXnS9>bv??qBb>*w-A1}4?awMY`NN@OW_rm~Pf%QV&
|G%DIw%Z&6w;YcqptOa4hGMeCxXsw+p&81025=M!twnn=caytbLFN9*82{>fQ<>$OUgCD;3qra%n^Yj
!2rxnv|q%++)ruS}U%AT?qR!TQFaxID^w?D}wd!x-xFYcE7&E3=DW;&V-2_Zj6((A6v{z}B9c09@Q$
E`8i_>mhWHmSC`itbg`8lR^)6nuk-wuTon-)r*;noDKWo4YsiS?R)F6)K5ulqshM1xCOmHe9Pj!i#gc
tDRsgqfa~&`IhT_I;?TOw~Ur*goUDevpXw&jE0J&-#wdp=#fF@=8Zh6a_^eD`h(xB(b!Z
o93dQgujiuqu{Ak2E8~_M5V;_Rjae|Md5or=JG^_Cn+Ir{4#+;{O2hf|0Mk9xlmBWn(Vsvuhyr!@?!v
%xbAPE4aF$I?dcpCXz0UZ*~JYTSt)(Mup5edN1EsNzENUKtnb3V2*S;Jo7?})+f1z4Ix`Ab>tz`9Fk!
p?tW3!O?Y3q7{TaFR(_6P}EkIq(RH1@_aQ?nKEDbLCriy2PH@0PKwGm9<7@ZjXDNc4o;hXY1fg<)h=t
SN4U3`5JT=t}E19DKB>YK7zXsdvt=xq(?tf
cbNsF-wjikc2AdSsjQe#SDuz7P&uU74T;z|C*YUBOGLO#(sfe01HO4y&NFoH#4<%6ADoL*(>zO?q^JZ
NM@v!Y2I*yk|e;KMBECCz&7J@=adDh=8k00(mY^)SZ640%?7^`MA!!e*+-{Dq>)C4Mkv&l3!Tq*St55
3MEa7V1S!@$mwB2r4=QPH2^h2a1?|2}maaz#ixRIvLvOlsOJ=t
Ev}b`fP{y3r~&HP^r411GqZ7FaKZ#`_d7R?{HWFdm-SkX!5@j_f+%HKS3AoPZaUh)9((d=cOW_p2`1(
N(Rs0Zg9R!%G&+@P%%$+D_vR7*wum3AJc^?C35!;dk8P_b^wo^P*lFNowQUdQgzd#VRIDrhOJ~I3c$8
?Opz%-F(ry*Y8!SoWd?DNW0U9?46!V!XexK^q1Kg?9}1ujZiW^v^jx}11pKu@pZ(DE3^%t|Pj8O_wgE
yX0*AWBM=ZWu%RkG*N(&RusRuGemVd()?vSjUX<0-dm?Sc$5&&8=!5`}$!pfTHH-v81eILWlMMIOYT|
vhw7nbRIC>5iTPUMFwS=VzP8xhagT2Rjm&5^G_8)!}eLMxT1ylIHq$+#>fTLSouT#H67{(?}Q^naB6_
A1|cK18OvIvP)2E)clm*{uu)tAfg(?Y2O4?9+B}%YTpHXY5zWc7;V2M%$@eli}Nxm4p_hD-6qqh}Dh1
#;Dm-HHMcKt?IVUd-LA4KvWy902@&Rs8Fj!l5abzYUj$*D3Ky%s5!f|dQ2On80^ug`=xOz3Qeu<{;F{
+E1WTk&y*x3{nCAnLvCRbcUzL_Qe2eh{sJU9oO@wnC!99zUi*y2<-Lmn8d9+=o`rIkoWApuur$=HeXU
J*cD*BYMIXL%p#!vg#-^046fo@1UcyHl5w+t=TLeYjcBq_ihinAIiJ3&-y@z@G;x!v*b#>{-Bs;H1ek
+vBe@`KLMEkFuI`+7$pvwnn((;u6Ji5eG@n}!6*^C9E$aT==H@ubwIt_?lPn;iT?9WakSg>9PqZX3XH
`8PEIgw7ZnN_r_fyUW7`DoU?8|>CjHSbwhl-^ayDVE)3ltPFNcgd_;i-Ez*50UJ}!E%|B)5d%)1$|5K
o|RV?2&>_0-Gu$g&7bP{FW>!o`Hx3_ySTi1cYDL%++Hr99&R58I((@Agb0JVXfI59q&|9rB?4@%lSJ>
SM5)i=g3>WyC4&q0%7(0Wf#pcR(ZYXf!clF|W{yt?HtF4Bb;7M_zFnMA*q@(ghkOfcL~It+!J_#|U+2
tbGE!@W@DppGKo1ISu`2HdLV|0G)*NNRv@WioxtcQ7Nj14?iwdB&yp_^vLN%O-wjSy;95g2)d-)pDmR
GaL2p+1i5-RA*gYY3dpR)ayKEohP-CBM>zvmB6i^Y8Lmd_Xb@#*UR;qLnO@ewM2Rz|0E=oYDGVU)v2x
KKER8@>9!%R4%k7y*n~g8DFSr$o9$Y0ToTCWD?Qw0}_@DL<=x?8={x
mn`pY?x^WJP@33C=O-{J72sq9%I?g6L33%t@%vkAzkjISpvnh635TVPpw=jZerZ
8a|-FQ@yU)SXpN=5()f(8eEc1CP{e;HQ-PQqSy(Q(wFuYkjK8X{
*kX?Dt5_esGOKco{XD^Cz}xu-rzNX{xh%cyW5K``c9l_J(tcF;wYBTi6rtvuG
gw(R^Twob$*97vz^99ijj*|GZ#IZgK)3!I{B%>bC=lhL_@s`FT8HgLJ%M8(p*gCrH!kU}37#|~Dm?-q
~Chs*h5Nwq*PQd~Cj+)xIhvp~m{H#z8Ad?&xNo|&iT*bUZdnN9WYz(G%$CPAYVUAx{
Hwx?OnfoO!=jRcIu2FUA?FA_*grWcJbV5dD}Pk?J{}FdROIjgX?d?sVB;suQ#!ZT#q}g|KE}i12+a!=
S}UKgZB3l5Trn&WbQuKm5i8A9M-9+{zy}X&FZ|I9i&*E^vl=Le*jQR0|XQR000O8W&uT9Gpdeu86^M!
M2!Fd9{>OVaA|NaUukZ1WpZv|Y%gPMX)j}KWN&bEX>V?GE^v9>eQR^u#+KlB{ffSDZHb%_U^|{nrnpg
RbrmJ{j$^ANC3o*?RR}~jB}5>=0YJ&TUjO$!uYLib9#p)$HLgS?f$r1Yr%#_(pFSN$(N1%n)q19?Wt&
%Jqq1_QuIg&FY<70euC-d$c{$COg;v+P)+%pQTdA3z=cTrFs&cCL)a9yG|5R4(KdHQI+N^BztktT$){
C91qPonAVx6duP_5QkQV*p%c^c+P=NM~dbzVB&x-2m3i@GwR*m_#%-ZWBzcfEv{i(gHvn9
ixugYm#RYjw0=^Ey-;J45lLbQ2#`s?xW$@!N;eG6v}~Zw^b
8Ot@77C1qP#gMVdf5DF0#u)?{Ep2^(4Io0;%d`nN4pp9Br~{bJuquoT|*HS&{!kr)9PXsv4q5rbV96s
A^O1uqx;ImEZB9div8;3uV}Piv`fvQC(MqHHvDQ1r>fhJvvDb4-VfRr9Zwq*#q)z)25!L%No|#ry&rF
YNm@OnOF59Yg3@qwyp{T?k9SB$Pe$SotHEH$y7RCAHTb&Y^@7et-9OP2|s?gtv0K1RLgaH4eLy5pV7l
(dNx#-W-*z;nr!oh_DB&rk~dXq-fXUWoi*3m;`#LK*H)LX5_WcQB>{
2ln