From d0e00cb8710be008a0b25f37ce254b5622bd051b Mon Sep 17 00:00:00 2001 From: Matt Strapp Date: Mon, 6 Nov 2023 11:18:18 -0600 Subject: Migrate to chezmoi (https://chezmoi.io) --- .chezmoiignore | 1 + alacritty/.config/alacritty.yml | 590 - alacritty/.config/alacritty/nord.yml | 58 - bat/.config/bat/config | 27 - dot_config/alacritty.yml | 590 + dot_config/alacritty/nord.yml | 58 + dot_config/bat/config | 27 + dot_config/lvim/config.lua | 197 + dot_config/shell/aliasrc | 63 + dot_config/tmux/tmux.conf | 30 + dot_config/zsh/dot_p10k.zsh | 1624 +++ dot_config/zsh/dot_zdirs | 20 + dot_config/zsh/dot_zshenv | 167 + dot_config/zsh/dot_zshrc | 128 + dot_config/zsh/executable_dot_zshopts | 71 + dot_config/zsh/keybindings.zsh | 138 + ...gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py | 11629 ------------------- gdb/.config/gdb/init | 1 - lvim/.config/lvim/config.lua | 197 - sh/.config/shell/aliasrc | 63 - tmux/.config/tmux/tmux.conf | 30 - zsh/.config/zsh/.p10k.zsh | 1624 --- zsh/.config/zsh/.zshenv | 167 - zsh/.config/zsh/.zshopts | 71 - zsh/.config/zsh/.zshrc | 128 - zsh/.config/zsh/keybindings.zsh | 138 - 26 files changed, 3114 insertions(+), 14723 deletions(-) create mode 100644 .chezmoiignore delete mode 100644 alacritty/.config/alacritty.yml delete mode 100644 alacritty/.config/alacritty/nord.yml delete mode 100644 bat/.config/bat/config create mode 100644 dot_config/alacritty.yml create mode 100644 dot_config/alacritty/nord.yml create mode 100644 dot_config/bat/config create mode 100644 dot_config/lvim/config.lua create mode 100644 dot_config/shell/aliasrc create mode 100644 dot_config/tmux/tmux.conf create mode 100644 dot_config/zsh/dot_p10k.zsh create mode 100644 dot_config/zsh/dot_zdirs create mode 100644 dot_config/zsh/dot_zshenv create mode 100644 dot_config/zsh/dot_zshrc create mode 100644 dot_config/zsh/executable_dot_zshopts create mode 100644 dot_config/zsh/keybindings.zsh delete mode 100644 gdb/.config/gdb/.gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py delete mode 100644 gdb/.config/gdb/init delete mode 100644 lvim/.config/lvim/config.lua delete mode 100644 sh/.config/shell/aliasrc delete mode 100644 tmux/.config/tmux/tmux.conf delete mode 100644 zsh/.config/zsh/.p10k.zsh delete mode 100644 zsh/.config/zsh/.zshenv delete mode 100755 zsh/.config/zsh/.zshopts delete mode 100644 zsh/.config/zsh/.zshrc delete mode 100644 zsh/.config/zsh/keybindings.zsh diff --git a/.chezmoiignore b/.chezmoiignore new file mode 100644 index 0000000..6b1d0bf --- /dev/null +++ b/.chezmoiignore @@ -0,0 +1 @@ +LICENSE diff --git a/alacritty/.config/alacritty.yml b/alacritty/.config/alacritty.yml deleted file mode 100644 index d2889fd..0000000 --- a/alacritty/.config/alacritty.yml +++ /dev/null @@ -1,590 +0,0 @@ -# Configuration for Alacritty, the GPU enhanced terminal emulator. -import: - - ~/.config/alacritty/nord.yml - -# Any items in the `env` entry below will be added as -# environment variables. Some entries may override variables -# set by alacritty itself. -#env: - # TERM variable - # - # This value is used to set the `$TERM` environment variable for - # each instance of Alacritty. If it is not present, alacritty will - # check the local terminfo database and use `alacritty` if it is - # available, otherwise `xterm-256color` is used. - #TERM: xterm-256color - -window: - # Window dimensions (changes require restart) - # - # Specified in number of columns/lines, not pixels. - # If both are `0`, this setting is ignored. - dimensions: - columns: 100 - lines: 24 - - # Window position (changes require restart) - # - # Specified in number of pixels. - # If the position is not set, the window manager will handle the placement. - #position: - # x: 0 - # y: 0 - - # Window padding (changes require restart) - # - # Blank space added around the window in pixels. This padding is scaled - # by DPI and the specified value is always added at both opposing sides. - #padding: - # x: 6 - # y: 6 - - # Spread additional padding evenly around the terminal content. - dynamic_padding: false - #opacity: 1.0 - opacity: 0.90 - # Window decorations - # - # Values for `decorations`: - # - full: Borders and title bar - # - none: Neither borders nor title bar - # - # Values for `decorations` (macOS only): - # - transparent: Title bar, transparent background and title bar buttons - # - buttonless: Title bar, transparent background, but no title bar buttons - #decorations: full - - # Startup Mode (changes require restart) - # - # Values for `startup_mode`: - # - Windowed - # - Maximized - # - Fullscreen - # - # Values for `startup_mode` (macOS only): - # - SimpleFullscreen - #startup_mode: Windowed - - # Window title - title: Terminal - - # Window class (Linux/BSD only): - class: - # Application instance name - instance: Alacritty - # General application class - general: Alacritty - - # GTK theme variant (Linux/BSD only) - # - # Override the variant of the GTK theme. Commonly supported values are `dark` and `light`. - # Set this to `None` to use the default theme variant. - gtk_theme_variant: None - -scrolling: - # Maximum number of lines in the scrollback buffer. - # Specifying '0' will disable scrolling. - history: 5000 - - # Number of lines the viewport will move for every line scrolled when - # scrollback is enabled (history > 0). - #multiplier: 3 - - # Scroll to the bottom when new text is written to the terminal. - #auto_scroll: false - -# Spaces per Tab (changes require restart) -# -# This setting defines the width of a tab in cells. -# -# Some applications, like Emacs, rely on knowing about the width of a tab. -# To prevent unexpected behavior in these applications, it's also required to -# change the `it` value in terminfo when altering this setting. -#tabspaces: 8 - -# Font configuration -font: - # Normal (roman) font face - normal: - # Font family - # - # Default: - # - (macOS) Menlo - # - (Linux/BSD) monospace - # - (Windows) Consolas - family: Terminus - #family: Source Code Pro - #Family: TerminessTTF Nerd Font Mono - # family: CodeNewRoman Nerd Font - # family: RobotoMono Nerd Font - # family: Hack - # family: JetBrains Mono - # family: UbuntuMono Nerd Font - # family: Monofur Nerd Font - #family: TerminessTTF Nerd Font - # family: Mononoki Nerd Font - - # The `style` can be specified to pick a specific face. - style: Regular - - # Bold font face - bold: - # Font family - # - # If the bold family is not specified, it will fall back to the - # value specified for the normal font. - family: Terminus - #Family: TerminessTTF Nerd Font Mono - # family: Source Code Pro - # family: CodeNewRoman Nerd Font - # family: RobotoMono Nerd Font - # family: Hack - # family: JetBrains Mono - # family: UbuntuMono Nerd Font - # family: Monofur Nerd Font - #family: TerminessTTF Nerd Font - # family: Mononoki Nerd Font - - # The `style` can be specified to pick a specific face. - style: Bold - - # Italic font face - italic: - # Font family - # - # If the italic family is not specified, it will fall back to the - # value specified for the normal font. - family: Terminus - #family: TerminessTTF Nerd Font Mono - # family: CodeNewRoman Nerd Font - # family: RobotoMono Nerd Font - # family: Hack - # family: JetBrains Mono - # family: UbuntuMono Nerd Font - # family: Monofuritalic Nerd Font Mono - # family: TerminessTTF Nerd Font - # family: Mononoki Nerd Font - - # The `style` can be specified to pick a specific face. - style: Italic - - # Bold italic font face - bold_italic: - # Font family - # - # If the bold italic family is not specified, it will fall back to the - # value specified for the normal font. - family: Terminus - # family: Source Code Pro - # family: CodeNewRoman Nerd Font - # family: RobotoMono Nerd Font - # family: Hack - # family: JetBrains Mono - # family: UbuntuMono Nerd Font - # family: Monofuritalic Nerd Font Mono - # family: TerminessTTF Nerd Font - # family: Mononoki Nerd Font - - # The `style` can be specified to pick a specific face. - style: Bold Italic - - # Point size - size: 7.0 - - # Offset is the extra space around each character. `offset.y` can be thought of - # as modifying the line spacing, and `offset.x` as modifying the letter spacing. - offset: - x: 0 - y: 1 - - # Glyph offset determines the locations of the glyphs within their cells with - # the default being at the bottom. Increasing `x` moves the glyph to the right, - # increasing `y` moves the glyph upwards. - #glyph_offset: - # x: 0 - # y: 0 - - # Thin stroke font rendering (macOS only) - # - # Thin strokes are suitable for retina displays, but for non-retina screens - # it is recommended to set `use_thin_strokes` to `false` - # - # macOS >= 10.14.x: - # - # If the font quality on non-retina display looks bad then set - # `use_thin_strokes` to `true` and enable font smoothing by running the - # following command: - # `defaults write -g CGFontRenderingFontSmoothingDisabled -bool NO` - # - # This is a global setting and will require a log out or restart to take - # effect. - #use_thin_strokes: true - -# If `true`, bold text is drawn using the bright color variants. -draw_bold_text_with_bright_colors: true - -# Colors (Tomorrow Night Bright) -colors: - # Default colors - primary: - background: '0x0c0f14' - foreground: '0xbbc2cf' - - # Bright and dim foreground colors - # - # The dimmed foreground color is calculated automatically if it is not present. - # If the bright foreground color is not set, or `draw_bold_text_with_bright_colors` - # is `false`, the normal foreground color will be used. - #dim_foreground: '0x9a9a9a' - #bright_foreground: '0xffffff' - - # Cursor colors - # - # Colors which should be used to draw the terminal cursor. If these are unset, - # the cursor color will be the inverse of the cell color. - #cursor: - # text: '0x000000' - # cursor: '0xffffff' - - # Selection colors - # - # Colors which should be used to draw the selection area. If selection - # background is unset, selection color will be the inverse of the cell colors. - # If only text is unset the cell text color will remain the same. - selection: - text: '0xbbc2cf' - - # Normal colors - # normal: - # black: '0x1c1f24' - # red: '0xff6c6b' - # green: '0x98be65' - # yellow: '0xda8548' - # blue: '0x51afef' - # magenta: '0xc678dd' - # cyan: '0x5699af' - # white: '0x202328' - - # Bright colors - # bright: - # black: '0x5b6268' - # red: '0xda8548' - # green: '0x4db5bd' - # yellow: '0xecbe7b' - # blue: '0x3071db' # This is 2257a0 in Doom Emacs but I lightened it. - # magenta: '0xa9a1e1' - # cyan: '0x46d9ff' - # white: '0xdfdfdf' - - # Dim colors - # - # If the dim colors are not set, they will be calculated automatically based - # on the `normal` colors. - #dim: - # black: '0x000000' - # red: '0x8c3336' - # green: '0x7a8530' - # yellow: '0x97822e' - # blue: '0x506d8f' - # magenta: '0x80638e' - # cyan: '0x497e7a' - # white: '0x9a9a9a' - - # Indexed Colors - # - # The indexed colors include all colors from 16 to 256. - # When these are not set, they're filled with sensible defaults. - # - # Example: - # `- { index: 16, color: '0xff00ff' }` - # - #indexed_colors: [] - -# Visual Bell -# -# Any time the BEL code is received, Alacritty "rings" the visual bell. Once -# rung, the terminal background will be set to white and transition back to the -# default background color. You can control the rate of this transition by -# setting the `duration` property (represented in milliseconds). You can also -# configure the transition function by setting the `animation` property. -# -# Values for `animation`: -# - Ease -# - EaseOut -# - EaseOutSine -# - EaseOutQuad -# - EaseOutCubic -# - EaseOutQuart -# - EaseOutQuint -# - EaseOutExpo -# - EaseOutCirc -# - Linear -# -# Specifying a `duration` of `0` will disable the visual bell. -#visual_bell: -# animation: EaseOutExpo -# duration: 0 -# color: '0xffffff' - -# Background opacity -# -# Window opacity as a floating point number from `0.0` to `1.0`. -# The value `0.0` is completely transparent and `1.0` is opaque. -window.opacity: 0.85 - - -#selection: - #semantic_escape_chars: ",│`|:\"' ()[]{}<>\t" - - # When set to `true`, selected text will be copied to the primary clipboard. - #save_to_clipboard: false - -# Allow terminal applications to change Alacritty's window title. -#dynamic_title: true - -cursor: - # Cursor style - # - # Values for `style`: - # - ▇ Block - # - _ Underline - # - | Beam - #style: Block - - # If this is `true`, the cursor will be rendered as a hollow box when the - # window is not focused. - unfocused_hollow: true - -# Live config reload (changes require restart) -live_config_reload: true - -# Shell -# -# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`. -# Entries in `shell.args` are passed unmodified as arguments to the shell. -# -# Default: -# - (macOS) /bin/bash --login -# - (Linux/BSD) user login shell -# - (Windows) powershell -shell: - program: /usr/bin/zsh - args: - - -l - - -c - - "tmux" - -# Startup directory -# -# Directory the shell is started in. If this is unset, or `None`, the working -# directory of the parent process will be used. -#working_directory: None - -# WinPTY backend (Windows only) -# -# Alacritty defaults to using the newer ConPTY backend if it is available, -# since it resolves a lot of bugs and is quite a bit faster. If it is not -# available, the the WinPTY backend will be used instead. -# -# Setting this option to `true` makes Alacritty use the legacy WinPTY backend, -# even if the ConPTY backend is available. -#winpty_backend: false - -# Send ESC (\x1b) before characters when alt is pressed. -#alt_send_esc: true - -#debug: - # Display the time it takes to redraw each frame. - #render_timer: false - - # Keep the log file after quitting Alacritty. - #persistent_logging: false - - # Log level - # - # Values for `log_level`: - # - None - # - Error - # - Warn - # - Info - # - Debug - # - Trace - #log_level: Warn - - # Print all received window events. - #print_events: false - - # Record all characters and escape sequences as test data. - #ref_test: false - -#mouse: - # Click settings - # - # The `double_click` and `triple_click` settings control the time - # alacritty should wait for accepting multiple clicks as one double - # or triple click. - #double_click: { threshold: 300 } - #triple_click: { threshold: 300 } - - # If this is `true`, the cursor is temporarily hidden when typing. - hide_when_typing: true - - url: - # URL launcher - # - # This program is executed when clicking on a text which is recognized as a URL. - # The URL is always added to the command as the last parameter. - # - # When set to `None`, URL launching will be disabled completely. - # - # Default: - # - (macOS) open - # - (Linux/BSD) xdg-open - # - (Windows) explorer - launcher: - - program: xdg-open - - args: [] - - # URL modifiers - # - # These are the modifiers that need to be held down for opening URLs when clicking - # on them. The available modifiers are documented in the key binding section. - modifiers: Control - -# Mouse bindings -# -# Mouse bindings are specified as a list of objects, much like the key -# bindings further below. -# -# Each mouse binding will specify a: -# -# - `mouse`: -# -# - Middle -# - Left -# - Right -# - Numeric identifier such as `5` -# -# - `action` (see key bindings) -# -# And optionally: -# -# - `mods` (see key bindings) -#mouse_bindings: -# - { mouse: Middle, action: PasteSelection } - -# Key bindings -# -# Key bindings are specified as a list of objects. For example, this is the -# default paste binding: -# -# `- { key: V, mods: Control|Shift, action: Paste }` -# -# Each key binding will specify a: -# -# - `key`: Identifier of the key pressed -# -# - A-Z -# - F1-F24 -# - Key0-Key9 -# -# A full list with available key codes can be found here: -# https://docs.rs/glutin/*/glutin/event/enum.VirtualKeyCode.html#variants -# -# Instead of using the name of the keys, the `key` field also supports using -# the scancode of the desired key. Scancodes have to be specified as a -# decimal number. This command will allow you to display the hex scancodes -# for certain keys: -# -# `showkey --scancodes`. -# -# Then exactly one of: -# -# - `chars`: Send a byte sequence to the running application -# -# The `chars` field writes the specified string to the terminal. This makes -# it possible to pass escape sequences. To find escape codes for bindings -# like `PageUp` (`"\x1b[5~"`), you can run the command `showkey -a` outside -# of tmux. Note that applications use terminfo to map escape sequences back -# to keys. It is therefore required to update the terminfo when changing an -# escape sequence. -# -# - `action`: Execute a predefined action -# -# - Copy -# - Paste -# - PasteSelection -# - IncreaseFontSize -# - DecreaseFontSize -# - ResetFontSize -# - ScrollPageUp -# - ScrollPageDown -# - ScrollLineUp -# - ScrollLineDown -# - ScrollToTop -# - ScrollToBottom -# - ClearHistory -# - Hide -# - Minimize -# - Quit -# - ToggleFullscreen -# - SpawnNewInstance -# - ClearLogNotice -# - ReceiveChar -# - None -# -# (macOS only): -# - ToggleSimpleFullscreen: Enters fullscreen without occupying another space -# -# - `command`: Fork and execute a specified command plus arguments -# -# The `command` field must be a map containing a `program` string and an -# `args` array of command line parameter strings. For example: -# `{ program: "alacritty", args: ["-e", "vttest"] }` -# -# And optionally: -# -# - `mods`: Key modifiers to filter binding actions -# -# - Command -# - Control -# - Option -# - Super -# - Shift -# - Alt -# -# Multiple `mods` can be combined using `|` like this: -# `mods: Control|Shift`. -# Whitespace and capitalization are relevant and must match the example. -# -# - `mode`: Indicate a binding for only specific terminal reported modes -# -# This is mainly used to send applications the correct escape sequences -# when in different modes. -# -# - AppCursor -# - AppKeypad -# - Alt -# -# A `~` operator can be used before a mode to apply the binding whenever -# the mode is *not* active, e.g. `~Alt`. -# -# Bindings are always filled by default, but will be replaced when a new -# binding with the same triggers is defined. To unset a default binding, it can -# be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for -# a no-op if you do not wish to receive input characters for that binding. -key_bindings: - # (Windows, Linux, and BSD only) - - { key: V, mods: Control|Shift, action: Paste } - - { key: C, mods: Control|Shift, action: Copy } - - { key: Insert, mods: Shift, action: PasteSelection } - - { key: Key0, mods: Control, action: ResetFontSize } - - { key: Equals, mods: Control, action: IncreaseFontSize } - - { key: Plus, mods: Control, action: IncreaseFontSize } - - { key: Minus, mods: Control, action: DecreaseFontSize } - - { key: Minus, mods: Control, action: DecreaseFontSize } - - { key: F11, mods: None, action: ToggleFullscreen } - - { key: Paste, mods: None, action: Paste } - - { key: Copy, mods: None, action: Copy } - - { key: L, mods: Control, action: ClearLogNotice } - - { key: L, mods: Control, chars: "\x0c" } diff --git a/alacritty/.config/alacritty/nord.yml b/alacritty/.config/alacritty/nord.yml deleted file mode 100644 index c36675c..0000000 --- a/alacritty/.config/alacritty/nord.yml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2017-present Arctic Ice Studio -# Copyright (c) 2017-present Sven Greb - -# Project: Nord Alacritty -# Version: 0.1.0 -# Repository: https://github.com/arcticicestudio/nord-alacritty -# License: MIT -# References: -# https://github.com/alacritty/alacritty - -colors: - primary: - background: '#2e3440' - foreground: '#d8dee9' - dim_foreground: '#a5abb6' - cursor: - text: '#2e3440' - cursor: '#d8dee9' - vi_mode_cursor: - text: '#2e3440' - cursor: '#d8dee9' - selection: - text: CellForeground - background: '#4c566a' - search: - matches: - foreground: CellBackground - background: '#88c0d0' - footer_bar: - background: '#434c5e' - foreground: '#d8dee9' - normal: - black: '#3b4252' - red: '#bf616a' - green: '#a3be8c' - yellow: '#ebcb8b' - blue: '#81a1c1' - magenta: '#b48ead' - cyan: '#88c0d0' - white: '#e5e9f0' - bright: - black: '#4c566a' - red: '#bf616a' - green: '#a3be8c' - yellow: '#ebcb8b' - blue: '#81a1c1' - magenta: '#b48ead' - cyan: '#8fbcbb' - white: '#eceff4' - dim: - black: '#373e4d' - red: '#94545d' - green: '#809575' - yellow: '#b29e75' - blue: '#68809a' - magenta: '#8c738c' - cyan: '#6d96a5' - white: '#aeb3bb' diff --git a/bat/.config/bat/config b/bat/.config/bat/config deleted file mode 100644 index 0fd7580..0000000 --- a/bat/.config/bat/config +++ /dev/null @@ -1,27 +0,0 @@ -# This is `bat`s configuration file. Each line either contains a comment or -# a command-line option that you want to pass to `bat` by default. You can -# run `bat --help` to get a list of all possible configuration options. - -# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes` -# for a list of all available themes ---theme="Nord" - -# Enable this to use italic text on the terminal. This is not supported on all -# terminal emulators (like tmux, by default): ---italic-text=always - -# Uncomment the following line to disable automatic paging: -#--paging=never - -# Uncomment the following line if you are using less version >= 551 and want to -# enable mouse scrolling support in `bat` when running inside tmux. This might -# disable text selection, unless you press shift. ---pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse" - -# Syntax mappings: map a certain filename pattern to a language. -# Example 1: use the C++ syntax for Arduino .ino files -# Example 2: Use ".gitignore"-style highlighting for ".ignore" files -#--map-syntax "*.ino:C++" -#--map-syntax ".ignore:Git Ignore" - ---style="numbers,changes" diff --git a/dot_config/alacritty.yml b/dot_config/alacritty.yml new file mode 100644 index 0000000..d2889fd --- /dev/null +++ b/dot_config/alacritty.yml @@ -0,0 +1,590 @@ +# Configuration for Alacritty, the GPU enhanced terminal emulator. +import: + - ~/.config/alacritty/nord.yml + +# Any items in the `env` entry below will be added as +# environment variables. Some entries may override variables +# set by alacritty itself. +#env: + # TERM variable + # + # This value is used to set the `$TERM` environment variable for + # each instance of Alacritty. If it is not present, alacritty will + # check the local terminfo database and use `alacritty` if it is + # available, otherwise `xterm-256color` is used. + #TERM: xterm-256color + +window: + # Window dimensions (changes require restart) + # + # Specified in number of columns/lines, not pixels. + # If both are `0`, this setting is ignored. + dimensions: + columns: 100 + lines: 24 + + # Window position (changes require restart) + # + # Specified in number of pixels. + # If the position is not set, the window manager will handle the placement. + #position: + # x: 0 + # y: 0 + + # Window padding (changes require restart) + # + # Blank space added around the window in pixels. This padding is scaled + # by DPI and the specified value is always added at both opposing sides. + #padding: + # x: 6 + # y: 6 + + # Spread additional padding evenly around the terminal content. + dynamic_padding: false + #opacity: 1.0 + opacity: 0.90 + # Window decorations + # + # Values for `decorations`: + # - full: Borders and title bar + # - none: Neither borders nor title bar + # + # Values for `decorations` (macOS only): + # - transparent: Title bar, transparent background and title bar buttons + # - buttonless: Title bar, transparent background, but no title bar buttons + #decorations: full + + # Startup Mode (changes require restart) + # + # Values for `startup_mode`: + # - Windowed + # - Maximized + # - Fullscreen + # + # Values for `startup_mode` (macOS only): + # - SimpleFullscreen + #startup_mode: Windowed + + # Window title + title: Terminal + + # Window class (Linux/BSD only): + class: + # Application instance name + instance: Alacritty + # General application class + general: Alacritty + + # GTK theme variant (Linux/BSD only) + # + # Override the variant of the GTK theme. Commonly supported values are `dark` and `light`. + # Set this to `None` to use the default theme variant. + gtk_theme_variant: None + +scrolling: + # Maximum number of lines in the scrollback buffer. + # Specifying '0' will disable scrolling. + history: 5000 + + # Number of lines the viewport will move for every line scrolled when + # scrollback is enabled (history > 0). + #multiplier: 3 + + # Scroll to the bottom when new text is written to the terminal. + #auto_scroll: false + +# Spaces per Tab (changes require restart) +# +# This setting defines the width of a tab in cells. +# +# Some applications, like Emacs, rely on knowing about the width of a tab. +# To prevent unexpected behavior in these applications, it's also required to +# change the `it` value in terminfo when altering this setting. +#tabspaces: 8 + +# Font configuration +font: + # Normal (roman) font face + normal: + # Font family + # + # Default: + # - (macOS) Menlo + # - (Linux/BSD) monospace + # - (Windows) Consolas + family: Terminus + #family: Source Code Pro + #Family: TerminessTTF Nerd Font Mono + # family: CodeNewRoman Nerd Font + # family: RobotoMono Nerd Font + # family: Hack + # family: JetBrains Mono + # family: UbuntuMono Nerd Font + # family: Monofur Nerd Font + #family: TerminessTTF Nerd Font + # family: Mononoki Nerd Font + + # The `style` can be specified to pick a specific face. + style: Regular + + # Bold font face + bold: + # Font family + # + # If the bold family is not specified, it will fall back to the + # value specified for the normal font. + family: Terminus + #Family: TerminessTTF Nerd Font Mono + # family: Source Code Pro + # family: CodeNewRoman Nerd Font + # family: RobotoMono Nerd Font + # family: Hack + # family: JetBrains Mono + # family: UbuntuMono Nerd Font + # family: Monofur Nerd Font + #family: TerminessTTF Nerd Font + # family: Mononoki Nerd Font + + # The `style` can be specified to pick a specific face. + style: Bold + + # Italic font face + italic: + # Font family + # + # If the italic family is not specified, it will fall back to the + # value specified for the normal font. + family: Terminus + #family: TerminessTTF Nerd Font Mono + # family: CodeNewRoman Nerd Font + # family: RobotoMono Nerd Font + # family: Hack + # family: JetBrains Mono + # family: UbuntuMono Nerd Font + # family: Monofuritalic Nerd Font Mono + # family: TerminessTTF Nerd Font + # family: Mononoki Nerd Font + + # The `style` can be specified to pick a specific face. + style: Italic + + # Bold italic font face + bold_italic: + # Font family + # + # If the bold italic family is not specified, it will fall back to the + # value specified for the normal font. + family: Terminus + # family: Source Code Pro + # family: CodeNewRoman Nerd Font + # family: RobotoMono Nerd Font + # family: Hack + # family: JetBrains Mono + # family: UbuntuMono Nerd Font + # family: Monofuritalic Nerd Font Mono + # family: TerminessTTF Nerd Font + # family: Mononoki Nerd Font + + # The `style` can be specified to pick a specific face. + style: Bold Italic + + # Point size + size: 7.0 + + # Offset is the extra space around each character. `offset.y` can be thought of + # as modifying the line spacing, and `offset.x` as modifying the letter spacing. + offset: + x: 0 + y: 1 + + # Glyph offset determines the locations of the glyphs within their cells with + # the default being at the bottom. Increasing `x` moves the glyph to the right, + # increasing `y` moves the glyph upwards. + #glyph_offset: + # x: 0 + # y: 0 + + # Thin stroke font rendering (macOS only) + # + # Thin strokes are suitable for retina displays, but for non-retina screens + # it is recommended to set `use_thin_strokes` to `false` + # + # macOS >= 10.14.x: + # + # If the font quality on non-retina display looks bad then set + # `use_thin_strokes` to `true` and enable font smoothing by running the + # following command: + # `defaults write -g CGFontRenderingFontSmoothingDisabled -bool NO` + # + # This is a global setting and will require a log out or restart to take + # effect. + #use_thin_strokes: true + +# If `true`, bold text is drawn using the bright color variants. +draw_bold_text_with_bright_colors: true + +# Colors (Tomorrow Night Bright) +colors: + # Default colors + primary: + background: '0x0c0f14' + foreground: '0xbbc2cf' + + # Bright and dim foreground colors + # + # The dimmed foreground color is calculated automatically if it is not present. + # If the bright foreground color is not set, or `draw_bold_text_with_bright_colors` + # is `false`, the normal foreground color will be used. + #dim_foreground: '0x9a9a9a' + #bright_foreground: '0xffffff' + + # Cursor colors + # + # Colors which should be used to draw the terminal cursor. If these are unset, + # the cursor color will be the inverse of the cell color. + #cursor: + # text: '0x000000' + # cursor: '0xffffff' + + # Selection colors + # + # Colors which should be used to draw the selection area. If selection + # background is unset, selection color will be the inverse of the cell colors. + # If only text is unset the cell text color will remain the same. + selection: + text: '0xbbc2cf' + + # Normal colors + # normal: + # black: '0x1c1f24' + # red: '0xff6c6b' + # green: '0x98be65' + # yellow: '0xda8548' + # blue: '0x51afef' + # magenta: '0xc678dd' + # cyan: '0x5699af' + # white: '0x202328' + + # Bright colors + # bright: + # black: '0x5b6268' + # red: '0xda8548' + # green: '0x4db5bd' + # yellow: '0xecbe7b' + # blue: '0x3071db' # This is 2257a0 in Doom Emacs but I lightened it. + # magenta: '0xa9a1e1' + # cyan: '0x46d9ff' + # white: '0xdfdfdf' + + # Dim colors + # + # If the dim colors are not set, they will be calculated automatically based + # on the `normal` colors. + #dim: + # black: '0x000000' + # red: '0x8c3336' + # green: '0x7a8530' + # yellow: '0x97822e' + # blue: '0x506d8f' + # magenta: '0x80638e' + # cyan: '0x497e7a' + # white: '0x9a9a9a' + + # Indexed Colors + # + # The indexed colors include all colors from 16 to 256. + # When these are not set, they're filled with sensible defaults. + # + # Example: + # `- { index: 16, color: '0xff00ff' }` + # + #indexed_colors: [] + +# Visual Bell +# +# Any time the BEL code is received, Alacritty "rings" the visual bell. Once +# rung, the terminal background will be set to white and transition back to the +# default background color. You can control the rate of this transition by +# setting the `duration` property (represented in milliseconds). You can also +# configure the transition function by setting the `animation` property. +# +# Values for `animation`: +# - Ease +# - EaseOut +# - EaseOutSine +# - EaseOutQuad +# - EaseOutCubic +# - EaseOutQuart +# - EaseOutQuint +# - EaseOutExpo +# - EaseOutCirc +# - Linear +# +# Specifying a `duration` of `0` will disable the visual bell. +#visual_bell: +# animation: EaseOutExpo +# duration: 0 +# color: '0xffffff' + +# Background opacity +# +# Window opacity as a floating point number from `0.0` to `1.0`. +# The value `0.0` is completely transparent and `1.0` is opaque. +window.opacity: 0.85 + + +#selection: + #semantic_escape_chars: ",│`|:\"' ()[]{}<>\t" + + # When set to `true`, selected text will be copied to the primary clipboard. + #save_to_clipboard: false + +# Allow terminal applications to change Alacritty's window title. +#dynamic_title: true + +cursor: + # Cursor style + # + # Values for `style`: + # - ▇ Block + # - _ Underline + # - | Beam + #style: Block + + # If this is `true`, the cursor will be rendered as a hollow box when the + # window is not focused. + unfocused_hollow: true + +# Live config reload (changes require restart) +live_config_reload: true + +# Shell +# +# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`. +# Entries in `shell.args` are passed unmodified as arguments to the shell. +# +# Default: +# - (macOS) /bin/bash --login +# - (Linux/BSD) user login shell +# - (Windows) powershell +shell: + program: /usr/bin/zsh + args: + - -l + - -c + - "tmux" + +# Startup directory +# +# Directory the shell is started in. If this is unset, or `None`, the working +# directory of the parent process will be used. +#working_directory: None + +# WinPTY backend (Windows only) +# +# Alacritty defaults to using the newer ConPTY backend if it is available, +# since it resolves a lot of bugs and is quite a bit faster. If it is not +# available, the the WinPTY backend will be used instead. +# +# Setting this option to `true` makes Alacritty use the legacy WinPTY backend, +# even if the ConPTY backend is available. +#winpty_backend: false + +# Send ESC (\x1b) before characters when alt is pressed. +#alt_send_esc: true + +#debug: + # Display the time it takes to redraw each frame. + #render_timer: false + + # Keep the log file after quitting Alacritty. + #persistent_logging: false + + # Log level + # + # Values for `log_level`: + # - None + # - Error + # - Warn + # - Info + # - Debug + # - Trace + #log_level: Warn + + # Print all received window events. + #print_events: false + + # Record all characters and escape sequences as test data. + #ref_test: false + +#mouse: + # Click settings + # + # The `double_click` and `triple_click` settings control the time + # alacritty should wait for accepting multiple clicks as one double + # or triple click. + #double_click: { threshold: 300 } + #triple_click: { threshold: 300 } + + # If this is `true`, the cursor is temporarily hidden when typing. + hide_when_typing: true + + url: + # URL launcher + # + # This program is executed when clicking on a text which is recognized as a URL. + # The URL is always added to the command as the last parameter. + # + # When set to `None`, URL launching will be disabled completely. + # + # Default: + # - (macOS) open + # - (Linux/BSD) xdg-open + # - (Windows) explorer + launcher: + - program: xdg-open + - args: [] + + # URL modifiers + # + # These are the modifiers that need to be held down for opening URLs when clicking + # on them. The available modifiers are documented in the key binding section. + modifiers: Control + +# Mouse bindings +# +# Mouse bindings are specified as a list of objects, much like the key +# bindings further below. +# +# Each mouse binding will specify a: +# +# - `mouse`: +# +# - Middle +# - Left +# - Right +# - Numeric identifier such as `5` +# +# - `action` (see key bindings) +# +# And optionally: +# +# - `mods` (see key bindings) +#mouse_bindings: +# - { mouse: Middle, action: PasteSelection } + +# Key bindings +# +# Key bindings are specified as a list of objects. For example, this is the +# default paste binding: +# +# `- { key: V, mods: Control|Shift, action: Paste }` +# +# Each key binding will specify a: +# +# - `key`: Identifier of the key pressed +# +# - A-Z +# - F1-F24 +# - Key0-Key9 +# +# A full list with available key codes can be found here: +# https://docs.rs/glutin/*/glutin/event/enum.VirtualKeyCode.html#variants +# +# Instead of using the name of the keys, the `key` field also supports using +# the scancode of the desired key. Scancodes have to be specified as a +# decimal number. This command will allow you to display the hex scancodes +# for certain keys: +# +# `showkey --scancodes`. +# +# Then exactly one of: +# +# - `chars`: Send a byte sequence to the running application +# +# The `chars` field writes the specified string to the terminal. This makes +# it possible to pass escape sequences. To find escape codes for bindings +# like `PageUp` (`"\x1b[5~"`), you can run the command `showkey -a` outside +# of tmux. Note that applications use terminfo to map escape sequences back +# to keys. It is therefore required to update the terminfo when changing an +# escape sequence. +# +# - `action`: Execute a predefined action +# +# - Copy +# - Paste +# - PasteSelection +# - IncreaseFontSize +# - DecreaseFontSize +# - ResetFontSize +# - ScrollPageUp +# - ScrollPageDown +# - ScrollLineUp +# - ScrollLineDown +# - ScrollToTop +# - ScrollToBottom +# - ClearHistory +# - Hide +# - Minimize +# - Quit +# - ToggleFullscreen +# - SpawnNewInstance +# - ClearLogNotice +# - ReceiveChar +# - None +# +# (macOS only): +# - ToggleSimpleFullscreen: Enters fullscreen without occupying another space +# +# - `command`: Fork and execute a specified command plus arguments +# +# The `command` field must be a map containing a `program` string and an +# `args` array of command line parameter strings. For example: +# `{ program: "alacritty", args: ["-e", "vttest"] }` +# +# And optionally: +# +# - `mods`: Key modifiers to filter binding actions +# +# - Command +# - Control +# - Option +# - Super +# - Shift +# - Alt +# +# Multiple `mods` can be combined using `|` like this: +# `mods: Control|Shift`. +# Whitespace and capitalization are relevant and must match the example. +# +# - `mode`: Indicate a binding for only specific terminal reported modes +# +# This is mainly used to send applications the correct escape sequences +# when in different modes. +# +# - AppCursor +# - AppKeypad +# - Alt +# +# A `~` operator can be used before a mode to apply the binding whenever +# the mode is *not* active, e.g. `~Alt`. +# +# Bindings are always filled by default, but will be replaced when a new +# binding with the same triggers is defined. To unset a default binding, it can +# be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for +# a no-op if you do not wish to receive input characters for that binding. +key_bindings: + # (Windows, Linux, and BSD only) + - { key: V, mods: Control|Shift, action: Paste } + - { key: C, mods: Control|Shift, action: Copy } + - { key: Insert, mods: Shift, action: PasteSelection } + - { key: Key0, mods: Control, action: ResetFontSize } + - { key: Equals, mods: Control, action: IncreaseFontSize } + - { key: Plus, mods: Control, action: IncreaseFontSize } + - { key: Minus, mods: Control, action: DecreaseFontSize } + - { key: Minus, mods: Control, action: DecreaseFontSize } + - { key: F11, mods: None, action: ToggleFullscreen } + - { key: Paste, mods: None, action: Paste } + - { key: Copy, mods: None, action: Copy } + - { key: L, mods: Control, action: ClearLogNotice } + - { key: L, mods: Control, chars: "\x0c" } diff --git a/dot_config/alacritty/nord.yml b/dot_config/alacritty/nord.yml new file mode 100644 index 0000000..c36675c --- /dev/null +++ b/dot_config/alacritty/nord.yml @@ -0,0 +1,58 @@ +# Copyright (c) 2017-present Arctic Ice Studio +# Copyright (c) 2017-present Sven Greb + +# Project: Nord Alacritty +# Version: 0.1.0 +# Repository: https://github.com/arcticicestudio/nord-alacritty +# License: MIT +# References: +# https://github.com/alacritty/alacritty + +colors: + primary: + background: '#2e3440' + foreground: '#d8dee9' + dim_foreground: '#a5abb6' + cursor: + text: '#2e3440' + cursor: '#d8dee9' + vi_mode_cursor: + text: '#2e3440' + cursor: '#d8dee9' + selection: + text: CellForeground + background: '#4c566a' + search: + matches: + foreground: CellBackground + background: '#88c0d0' + footer_bar: + background: '#434c5e' + foreground: '#d8dee9' + normal: + black: '#3b4252' + red: '#bf616a' + green: '#a3be8c' + yellow: '#ebcb8b' + blue: '#81a1c1' + magenta: '#b48ead' + cyan: '#88c0d0' + white: '#e5e9f0' + bright: + black: '#4c566a' + red: '#bf616a' + green: '#a3be8c' + yellow: '#ebcb8b' + blue: '#81a1c1' + magenta: '#b48ead' + cyan: '#8fbcbb' + white: '#eceff4' + dim: + black: '#373e4d' + red: '#94545d' + green: '#809575' + yellow: '#b29e75' + blue: '#68809a' + magenta: '#8c738c' + cyan: '#6d96a5' + white: '#aeb3bb' diff --git a/dot_config/bat/config b/dot_config/bat/config new file mode 100644 index 0000000..0fd7580 --- /dev/null +++ b/dot_config/bat/config @@ -0,0 +1,27 @@ +# This is `bat`s configuration file. Each line either contains a comment or +# a command-line option that you want to pass to `bat` by default. You can +# run `bat --help` to get a list of all possible configuration options. + +# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes` +# for a list of all available themes +--theme="Nord" + +# Enable this to use italic text on the terminal. This is not supported on all +# terminal emulators (like tmux, by default): +--italic-text=always + +# Uncomment the following line to disable automatic paging: +#--paging=never + +# Uncomment the following line if you are using less version >= 551 and want to +# enable mouse scrolling support in `bat` when running inside tmux. This might +# disable text selection, unless you press shift. +--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse" + +# Syntax mappings: map a certain filename pattern to a language. +# Example 1: use the C++ syntax for Arduino .ino files +# Example 2: Use ".gitignore"-style highlighting for ".ignore" files +#--map-syntax "*.ino:C++" +#--map-syntax ".ignore:Git Ignore" + +--style="numbers,changes" diff --git a/dot_config/lvim/config.lua b/dot_config/lvim/config.lua new file mode 100644 index 0000000..1a8d8f6 --- /dev/null +++ b/dot_config/lvim/config.lua @@ -0,0 +1,197 @@ +--[[ +lvim is the global options object + +Linters should be +filled in as strings with either +a global executable or a path to +an executable +]] +-- THESE ARE EXAMPLE CONFIGS FEEL FREE TO CHANGE TO WHATEVER YOU WANT + +-- general +lvim.log.level = "warn" +lvim.format_on_save = true +lvim.colorscheme = "nord" +-- to disable icons and use a minimalist setup, uncomment the following +-- lvim.use_icons = false + +-- keymappings [view all the defaults by pressing Lk] +lvim.leader = "space" +-- add your own keymapping +lvim.keys.normal_mode[""] = ":w" +-- lvim.keys.normal_mode[""] = ":BufferLineCycleNext" +-- lvim.keys.normal_mode[""] = ":BufferLineCyclePrev" +-- unmap a default keymapping +-- vim.keymap.del("n", "") +-- override a default keymapping +-- lvim.keys.normal_mode[""] = ":q" -- or vim.keymap.set("n", "", ":q" ) + +-- Change Telescope navigation to use j and k for navigation and n and p for history in both input and normal mode. +-- we use protected-mode (pcall) just in case the plugin wasn't loaded yet. +-- local _, actions = pcall(require, "telescope.actions") +-- lvim.builtin.telescope.defaults.mappings = { +-- -- for input mode +-- i = { +-- [""] = actions.move_selection_next, +-- [""] = actions.move_selection_previous, +-- [""] = actions.cycle_history_next, +-- [""] = actions.cycle_history_prev, +-- }, +-- -- for normal mode +-- n = { +-- [""] = actions.move_selection_next, +-- [""] = actions.move_selection_previous, +-- }, +-- } + +-- Change theme settings +-- lvim.builtin.theme.options.dim_inactive = true +-- lvim.builtin.theme.options.style = "storm" + +-- Use which-key to add extra bindings with the leader-key prefix +-- lvim.builtin.which_key.mappings["P"] = { "Telescope projects", "Projects" } +-- lvim.builtin.which_key.mappings["t"] = { +-- name = "+Trouble", +-- r = { "Trouble lsp_references", "References" }, +-- f = { "Trouble lsp_definitions", "Definitions" }, +-- d = { "Trouble document_diagnostics", "Diagnostics" }, +-- q = { "Trouble quickfix", "QuickFix" }, +-- l = { "Trouble loclist", "LocationList" }, +-- w = { "Trouble workspace_diagnostics", "Workspace Diagnostics" }, +-- } + +-- TODO: User Config for predefined plugins +-- After changing plugin config exit and reopen LunarVim, Run :PackerInstall :PackerCompile +lvim.builtin.alpha.active = true +lvim.builtin.alpha.mode = "dashboard" +lvim.builtin.terminal.active = true +lvim.builtin.nvimtree.setup.view.side = "left" +lvim.builtin.nvimtree.setup.renderer.icons.show.git = false + +-- if you don't want all the parsers change this to a table of the ones you want +lvim.builtin.treesitter.ensure_installed = { + "bash", + "c", + "javascript", + "json", + "lua", + "python", + "typescript", + "tsx", + "css", + "rust", + "java", + "yaml", +} + +lvim.builtin.treesitter.ignore_install = { "haskell" } +lvim.builtin.treesitter.highlight.enable = true + +-- generic LSP settings + +-- -- make sure server will always be installed even if the server is in skipped_servers list +-- lvim.lsp.installer.setup.ensure_installed = { +-- "sumneko_lua", +-- "jsonls", +-- } +-- -- change UI setting of `LspInstallInfo` +-- -- see +-- lvim.lsp.installer.setup.ui.check_outdated_servers_on_open = false +-- lvim.lsp.installer.setup.ui.border = "rounded" +-- lvim.lsp.installer.setup.ui.keymaps = { +-- uninstall_server = "d", +-- toggle_server_expand = "o", +-- } + +-- ---@usage disable automatic installation of servers +-- lvim.lsp.installer.setup.automatic_installation = false + +-- ---configure a server manually. !!Requires `:LvimCacheReset` to take effect!! +-- ---see the full default list `:lua print(vim.inspect(lvim.lsp.automatic_configuration.skipped_servers))` +-- vim.list_extend(lvim.lsp.automatic_configuration.skipped_servers, { "pyright" }) +-- local opts = {} -- check the lspconfig documentation for a list of all possible options +-- require("lvim.lsp.manager").setup("pyright", opts) + +-- ---remove a server from the skipped list, e.g. eslint, or emmet_ls. !!Requires `:LvimCacheReset` to take effect!! +-- ---`:LvimInfo` lists which server(s) are skipped for the current filetype +-- lvim.lsp.automatic_configuration.skipped_servers = vim.tbl_filter(function(server) +-- return server ~= "emmet_ls" +-- end, lvim.lsp.automatic_configuration.skipped_servers) + +-- -- you can set a custom on_attach function that will be used for all the language servers +-- -- See +-- lvim.lsp.on_attach_callback = function(client, bufnr) +-- local function buf_set_option(...) +-- vim.api.nvim_buf_set_option(bufnr, ...) +-- end +-- --Enable completion triggered by +-- buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc") +-- end + +-- -- set a formatter, this will override the language server formatting capabilities (if it exists) +-- local formatters = require "lvim.lsp.null-ls.formatters" +-- formatters.setup { +-- { command = "black", filetypes = { "python" } }, +-- { command = "isort", filetypes = { "python" } }, +-- { +-- -- each formatter accepts a list of options identical to https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#Configuration +-- command = "prettier", +-- ---@usage arguments to pass to the formatter +-- -- these cannot contain whitespaces, options such as `--line-width 80` become either `{'--line-width', '80'}` or `{'--line-width=80'}` +-- extra_args = { "--print-with", "100" }, +-- ---@usage specify which filetypes to enable. By default a providers will attach to all the filetypes it supports. +-- filetypes = { "typescript", "typescriptreact" }, +-- }, +-- } + +-- -- set additional linters +-- local linters = require "lvim.lsp.null-ls.linters" +-- linters.setup { +-- { command = "flake8", filetypes = { "python" } }, +-- { +-- -- each linter accepts a list of options identical to https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#Configuration +-- command = "shellcheck", +-- ---@usage arguments to pass to the formatter +-- -- these cannot contain whitespaces, options such as `--line-width 80` become either `{'--line-width', '80'}` or `{'--line-width=80'}` +-- extra_args = { "--severity", "warning" }, +-- }, +-- { +-- command = "codespell", +-- ---@usage specify which filetypes to enable. By default a providers will attach to all the filetypes it supports. +-- filetypes = { "javascript", "python" }, +-- }, +-- } + +-- Additional Plugins +lvim.plugins = { + { + "editorconfig/editorconfig-vim", + "shaunsingh/nord.nvim", + "sheerun/vim-polyglot", + "edluffy/hologram.nvim", + "lepture/vim-jinja", + "google/vim-jsonnet", + "elixir-editors/vim-elixir", + "alaviss/nim.nvim" + } +} +vim.scrolloff = 99999 + +vim.g.nord_disable_background = true +vim.g.nord_borders = true +vim.g.nord_contrast = true +vim.g.nord_cursorline_transparent = true + +-- Autocommands (https://neovim.io/doc/user/autocmd.html) +-- vim.api.nvim_create_autocmd("BufEnter", { +-- pattern = { "*.json", "*.jsonc" }, +-- -- enable wrap mode for json files only +-- command = "setlocal wrap", +-- }) +-- vim.api.nvim_create_autocmd("FileType", { +-- pattern = "zsh", +-- callback = function() +-- -- let treesitter use bash highlight for zsh files as well +-- require("nvim-treesitter.highlight").attach(0, "bash") +-- end, +-- }) diff --git a/dot_config/shell/aliasrc b/dot_config/shell/aliasrc new file mode 100644 index 0000000..4d0851d --- /dev/null +++ b/dot_config/shell/aliasrc @@ -0,0 +1,63 @@ +#!/bin/sh + +# Use neovim for vim if present. +[ -x "$(command -v nvim)" ] && alias vim="nvim" vimdiff="nvim -d" + +# Use $XINITRC variable if file exists. +[ -f "$XINITRC" ] && alias startx="startx $XINITRC" + +# sudo not required for some system commands +for x in mount umount sv emerge apt pacman updatedb su ; do + alias $x="sudo $x" +done + +if command -v doas &>/dev/null +then + alias sudo='doas' +else + alias sudo='sudo ' +fi + +# Verbosity and settings that you pretty much just always are going to want. +alias \ + cp="cp -iv" \ + mv="mv -iv" \ + rm="rm -vI" \ + bc="bc -ql" \ + info="pinfo"\ + mkdir="mkdir -pv" \ + yt="yt-dlp --add-metadata -i" \ + nvim="lvim" \ + yta="yt -x -f bestaudio/best" \ + ffmpeg="ffmpeg -hide_banner" \ + df="df -h" \ + du="dust -r" \ + free="free -m" \ + gdb='gdb -q -nh -x "$XDG_CONFIG_HOME/gdb/init"' \ + +# Colorize commands when possible. +alias \ + ls="exa -aF --group-directories-first" \ + ll="exa -alg --group-directories-first" \ + lt='exa -aTF --group-directories-first' \ + lr='exa -aRF' \ + grep="rg --color=auto" \ + diff="delta" \ + cat='bat --paging=never --style=plain' \ + +# These common commands are just too long! Abbreviate them. +alias \ + ka="killall" \ + g="git" \ + trem="transmission-remote" \ + YT="youtube-viewer" \ + sdn="sudo shutdown -h now" \ + e="$EDITOR" \ + v="$EDITOR" \ + em="sudo emerge" \ + p="sudo pacman" \ + xi="sudo xbps-install" \ + xr="sudo xbps-remove -R" \ + xq="xbps-query" \ + z="zathura" + diff --git a/dot_config/tmux/tmux.conf b/dot_config/tmux/tmux.conf new file mode 100644 index 0000000..5c017d8 --- /dev/null +++ b/dot_config/tmux/tmux.conf @@ -0,0 +1,30 @@ +set-option -g default-shell "/bin/zsh" + +# https://github.com/microsoft/WSL/issues/5931 +set -sg escape-time 50 + +set -g mouse on +set -ga terminal-overrides ",xterm-256color:Tc" +set -g default-terminal "tmux-256color" +set -g @plugin "arcticicestudio/nord-tmux" +set -g @plugin 'tmux-plugins/tmux-sensible' +set -g @plugin 'tmux-plugins/tmux-yank' +set -g default-command "${SHELL} -l" +set -g @plugin 'jaclu/tmux-menus' +set -g @plugin 'schasse/tmux-jump' +set -g @plugin 'MunifTanjim/tmux-mode-indicator' + +set-environment -g TMUX_PLUGIN_MANAGER_PATH "$XDG_DATA_HOME/tmux/plugins/" + +#ctrl-a for prefix +unbind C-b +set-option -g prefix C-a +bind-key C-a send-prefix + +bind -n M-Left select-pane -L +bind -n M-Right select-pane -R +bind -n M-Up select-pane -U +bind -n M-Down select-pane -D + +#Make sure you have tmux-plugin-manager installed! +run '/usr/share/tmux-plugin-manager/tpm' diff --git a/dot_config/zsh/dot_p10k.zsh b/dot_config/zsh/dot_p10k.zsh new file mode 100644 index 0000000..733b8e6 --- /dev/null +++ b/dot_config/zsh/dot_p10k.zsh @@ -0,0 +1,1624 @@ +# Generated by Powerlevel10k configuration wizard on 2022-08-31 at 16:10 CDT. +# Based on romkatv/powerlevel10k/config/p10k-lean.zsh. +# Wizard options: nerdfont-complete + powerline, small icons, unicode, lean, 24h time, +# 1 line, compact, few icons, concise, transient_prompt, instant_prompt=quiet. +# Type `p10k configure` to generate another config. +# +# Config for Powerlevel10k with lean prompt style. Type `p10k configure` to generate +# your own config based on it. +# +# Tip: Looking for a nice color? Here's a one-liner to print colormap. +# +# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done + +# Temporarily change options. +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + # Unset all configuration options. This allows you to apply configuration changes without + # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + # Zsh >= 5.1 is required. + autoload -Uz is-at-least && is-at-least 5.1 || return + + # The list of segments shown on the left. Fill it with the most important segments. + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + # os_icon # os identifier + dir # current directory + vcs # git status + prompt_char # prompt symbol + ) + + # The list of segments shown on the right. Fill it with less important segments. + # Right prompt on the last prompt line (where you are typing your commands) gets + # automatically hidden when the input line reaches it. Right prompt above the + # last prompt line gets hidden if it would overlap with left prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + status # exit code of the last command + command_execution_time # duration of the last command + background_jobs # presence of background jobs + direnv # direnv status (https://direnv.net/) + #asdf # asdf version manager (https://github.com/asdf-vm/asdf) + virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) + #anaconda # conda environment (https://conda.io/) + #pyenv # python environment (https://github.com/pyenv/pyenv) + goenv # go environment (https://github.com/syndbg/goenv) + #nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) + #nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) + #nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) + node_version # node.js version + go_version # go version (https://golang.org) + rust_version # rustc version (https://www.rust-lang.org) + dotnet_version # .NET version (https://dotnet.microsoft.com) + php_version # php version (https://www.php.net/) + laravel_version # laravel php framework version (https://laravel.com/) + java_version # java version (https://www.java.com/) + #package # name@version from package.json (https://docs.npmjs.com/files/package.json) + #rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) + #rvm # ruby version from rvm (https://rvm.io) + #fvm # flutter version management (https://github.com/leoafarias/fvm) + #luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) + #jenv # java version from jenv (https://github.com/jenv/jenv) + #plenv # perl version from plenv (https://github.com/tokuhirom/plenv) + #perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew) + #phpenv # php version from phpenv (https://github.com/phpenv/phpenv) + #scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) + #haskell_stack # haskell version from stack (https://haskellstack.org/) + kubecontext # current kubernetes context (https://kubernetes.io/) + terraform # terraform workspace (https://www.terraform.io) + # terraform_version # terraform version (https://www.terraform.io) + aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) + aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) + azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) + gcloud # google cloud cli account and project (https://cloud.google.com/) + google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) + toolbox # toolbox name (https://github.com/containers/toolbox) + context # user@hostname + #nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) + ranger # ranger shell (https://github.com/ranger/ranger) + nnn # nnn shell (https://github.com/jarun/nnn) + xplr # xplr shell (https://github.com/sayanarijit/xplr) + vim_shell # vim shell indicator (:sh) + midnight_commander # midnight commander shell (https://midnight-commander.org/) + nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) + # vpn_ip # virtual private network indicator + # load # CPU load + # disk_usage # disk usage + # ram # free RAM + # swap # used swap + todo # todo items (https://github.com/todotxt/todo.txt-cli) + #timewarrior # timewarrior tracking status (https://timewarrior.net/) + #taskwarrior # taskwarrior task count (https://taskwarrior.org/) + time # current time + # ip # ip address and bandwidth usage for a specified network interface + # public_ip # public IP address + # proxy # system-wide http/https/ftp proxy + # battery # internal battery + # wifi # wifi speed + # example # example user-defined segment (see prompt_example function below) + ) + + # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. + typeset -g POWERLEVEL9K_MODE=nerdfont-complete + # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid + # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. + typeset -g POWERLEVEL9K_ICON_PADDING=none + + # Basic style options that define the overall look of your prompt. You probably don't want to + # change them. + typeset -g POWERLEVEL9K_BACKGROUND= # transparent background + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol + + # When set to true, icons appear before content on both sides of the prompt. When set + # to false, icons go after content. If empty or not set, icons go before content in the left + # prompt and after content in the right prompt. + # + # You can also override it for a specific segment: + # + # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false + # + # Or for a specific segment in specific state: + # + # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true + + # Add an empty line before each prompt. + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + + # Connect left prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX= + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX= + # Connect right prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX= + + # The left end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + # The right end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL= + + # Ruler, a.k.a. the horizontal line before each prompt. If you set it to true, you'll + # probably want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false above and + # POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' below. + typeset -g POWERLEVEL9K_SHOW_RULER=false + typeset -g POWERLEVEL9K_RULER_CHAR='─' # reasonable alternative: '·' + typeset -g POWERLEVEL9K_RULER_FOREGROUND=242 + + # Filler between left and right prompt on the first prompt line. You can set it to '·' or '─' + # to make it easier to see the alignment between left and right prompt and to separate prompt + # from command output. It serves the same purpose as ruler (see above) without increasing + # the number of prompt lines. You'll probably want to set POWERLEVEL9K_SHOW_RULER=false + # if using this. You might also like POWERLEVEL9K_PROMPT_ADD_NEWLINE=false for more compact + # prompt. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' + if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then + # The color of the filler. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=242 + # Add a space between the end of left prompt and the filler. + typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=' ' + # Add a space between the filler and the start of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL=' ' + # Start filler from the edge of the screen if there are no left segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' + # End filler on the edge of the screen if there are no right segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' + fi + + #################################[ os_icon: os identifier ]################################## + # OS identifier color. + typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND= + # Custom icon. + # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' + + ################################[ prompt_char: prompt symbol ]################################ + # Green prompt symbol if the last command succeeded. + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 + # Red prompt symbol if the last command failed. + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 + # Default prompt symbol. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + # Prompt symbol in command vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + # Prompt symbol in visual vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + # Prompt symbol in overwrite vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + # No line terminator if prompt_char is the last segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # No line introducer if prompt_char is the first segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + + ##################################[ dir: current directory ]################################## + # Default current directory color. + typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 + # If directory is too long, shorten some of its segments to the shortest possible unique + # prefix. The shortened directory can be tab-completed to the original. + typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique + # Replace removed segment suffixes with this symbol. + typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= + # Color of the shortened directory segments. + typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 + # Color of the anchor directory segments. Anchor segments are never shortened. The first + # segment is always an anchor. + typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 + # Display anchor directory segments in bold. + typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true + # Don't shorten directories that contain any of these files. They are anchors. + local anchor_files=( + .bzr + .citc + .git + .hg + .node-version + .python-version + .go-version + .ruby-version + .lua-version + .java-version + .perl-version + .php-version + .tool-version + .shorten_folder_marker + .svn + .terraform + CVS + Cargo.toml + composer.json + go.mod + package.json + stack.yaml + ) + typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" + # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains + # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is + # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) + # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers + # and other directories don't. + # + # Optionally, "first" and "last" can be followed by ":" where is an integer. + # This moves the truncation point to the right (positive offset) or to the left (negative offset) + # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" + # respectively. + typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false + # Don't shorten this many last directory segments. They are anchors. + typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 + # Shorten directory if it's longer than this even if there is space for it. The value can + # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, + # directory will be shortened only when prompt doesn't fit or when other parameters demand it + # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). + # If set to `0`, directory will always be shortened to its minimum length. + typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this + # many columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least + # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 + # If set to true, embed a hyperlink into the directory. Useful for quickly + # opening a directory in the file manager simply by clicking the link. + # Can also be handy when the directory is shortened, as it allows you to see + # the full directory that was used in previous commands. + typeset -g POWERLEVEL9K_DIR_HYPERLINK=false + + # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON + # and POWERLEVEL9K_DIR_CLASSES below. + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + + # The default icon shown next to non-writable and non-existent directories when + # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. + # typeset -g POWERLEVEL9K_LOCK_ICON='⭐' + + # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different + # directories. It must be an array with 3 * N elements. Each triplet consists of: + # + # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with + # extended_glob option enabled. + # 2. Directory class for the purpose of styling. + # 3. An empty string. + # + # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. + # + # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories + # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_DIR_CLASSES=( + # '~/work(|/*)' WORK '' + # '~(|/*)' HOME '' + # '*' DEFAULT '') + # + # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one + # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or + # WORK_NON_EXISTENT. + # + # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an + # option to define custom colors and icons for different directory classes. + # + # # Styling for WORK. + # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NOT_WRITABLE. + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NON_EXISTENT. + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 + # + # If a styling parameter isn't explicitly defined for some class, it falls back to the classless + # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls + # back to POWERLEVEL9K_DIR_FOREGROUND. + # + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + # Custom prefix. + # typeset -g POWERLEVEL9K_DIR_PREFIX='%fin ' + + #####################################[ vcs: git status ]###################################### + # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon. + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + # Untracked files icon. It's really a question mark, your font isn't broken. + # Change the value of this parameter to show a different icon. + typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' + + # Formatter for Git status. + # + # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. + # + # You can edit the function to customize how Git status looks. + # + # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: + # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from + # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + # Styling for up-to-date Git status. + local meta='%f' # default foreground + local clean='%76F' # green foreground + local modified='%178F' # yellow foreground + local untracked='%39F' # blue foreground + local conflicted='%196F' # red foreground + else + # Styling for incomplete and stale Git status. + local meta='%244F' # grey foreground + local clean='%244F' # grey foreground + local modified='%244F' # grey foreground + local untracked='%244F' # grey foreground + local conflicted='%244F' # grey foreground + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + # If local branch name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show local branch name in full without truncation, delete the next line. + (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG + # Show tag only if not on a branch. + # Tip: To always show tag, delete the next line. + && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line + ]]; then + local tag=${(V)VCS_STATUS_TAG} + # If tag name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show tag name in full without truncation, delete the next line. + (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line + res+="${meta}#${clean}${tag//\%/%%}" + fi + + # Display the current Git commit if there is no branch and no tag. + # Tip: To always display the current Git commit, delete the next line. + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + # Show tracking branch name if it differs from local branch. + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + # Display "wip" if the latest commit's summary contains "wip" or "WIP". + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + # ⇣42 if behind the remote. + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + # ⇠42 if behind the push remote. + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + # *42 if have stashes. + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + # 'merge' if the repo is in an unusual state. + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + # ~42 if have merge conflicts. + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + # +42 if have staged changes. + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + # !42 if have unstaged changes. + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + # ?42 if have untracked files. It's really a question mark, your font isn't broken. + # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. + # Remove the next line if you don't want to see untracked files at all. + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" + # "─" if the number of unstaged files is unknown. This can happen due to + # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower + # than the number of files in the Git index, or due to bash.showDirtyState being set to false + # in the repository config. The number of staged and untracked files may also be unknown + # in this case. + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + # Don't count the number of unstaged, untracked and conflicted files in Git repositories with + # more than this many files in the index. Negative value means infinity. + # + # If you are working in Git repositories with tens of millions of files and seeing performance + # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output + # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's + # config: `git config bash.showDirtyState false`. + typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 + + # Don't show Git status in prompt for repositories whose workdir matches this pattern. + # For example, if set to '~', the Git repository at $HOME/.git will be ignored. + # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + # Disable the default Git status formatting. + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + # Install our own Git status formatter. + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + # Enable counters for staged, unstaged, etc. + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + # Icon color. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 + typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 + # Custom icon. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_VCS_PREFIX='%fon ' + + # Show status of repositories of these types. You can add svn and/or hg if you are + # using them. If you do, your prompt may become slow even when your current directory + # isn't in an svn or hg repository. + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + # These settings are used for repositories other than Git or when gitstatusd fails and + # Powerlevel10k has to fall back to using vcs_info. + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 + + ##########################[ status: exit code of the last command ]########################### + # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and + # style them independently from the regular OK and ERROR state. + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as + # it will signify success by turning green. + typeset -g POWERLEVEL9K_STATUS_OK=false + typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when some part of a pipe command fails but the overall exit status is zero. It may look + # like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as + # it will signify error by turning red. + typeset -g POWERLEVEL9K_STATUS_ERROR=true + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when the last command was terminated by a signal. + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 + # Use terse signal names: "INT" instead of "SIGINT(2)". + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when some part of a pipe command fails and the overall exit status is also non-zero. + # It may look like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘' + + ###################[ command_execution_time: duration of the last command ]################### + # Show duration of the last command if takes at least this many seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + # Show this many fractional digits. Zero means round to seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + # Execution time color. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=101 + # Duration format: 1d 2h 3m 4s. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + # Custom icon. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%ftook ' + + #######################[ background_jobs: presence of background jobs ]####################### + # Don't show the number of background jobs. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false + # Background jobs color. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=70 + # Custom icon. + # typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ direnv: direnv status (https://direnv.net/) ]######################## + # Direnv color. + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### + # Default asdf color. Only used to display tools for which there is no color override (see below). + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. + typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 + + # There are four parameters that can be used to hide asdf tools. Each parameter describes + # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at + # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to + # hide a tool, it gets shown. + # + # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and + # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: + # + # asdf local python 3.8.1 + # asdf global python 3.8.1 + # + # After running both commands the current python version is 3.8.1 and its source is "local" as + # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, + # it'll hide python version in this case because 3.8.1 is the same as the global version. + # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't + # contain "local". + + # Hide tool versions that don't come from one of these sources. + # + # Available sources: + # + # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" + # - local `asdf current` says "set by /some/not/home/directory/file" + # - global `asdf current` says "set by /home/username/file" + # + # Note: If this parameter is set to (shell local global), it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. + typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) + + # If set to false, hide tool versions that are the same as global. + # + # Note: The name of this parameter doesn't reflect its meaning at all. + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. + typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false + + # If set to false, hide tool versions that are equal to "system". + # + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. + typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true + + # If set to non-empty value, hide tools unless there is a file matching the specified file pattern + # in the current directory, or its parent directory, or its grandparent directory, and so on. + # + # Note: If this parameter is set to empty value, it won't hide tools. + # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. + # + # Example: Hide nodejs version when there is no package.json and no *.js files in the current + # directory, in `..`, in `../..` and so on. + # + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' + typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= + + # Ruby version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 + # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Python version from asdf. + typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Go version from asdf. + typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Node.js version from asdf. + typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Rust version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' + + # .NET Core version from asdf. + typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_DOTNET_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Flutter version from asdf. + typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Lua version from asdf. + typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Java version from asdf. + typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Perl version from asdf. + typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 + # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Erlang version from asdf. + typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 + # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Elixir version from asdf. + typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Postgres version from asdf. + typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' + + # PHP version from asdf. + typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 + # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Haskell version from asdf. + typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 + # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Julia version from asdf. + typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' + + ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### + # NordVPN connection indicator color. + typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 + # Hide NordVPN connection indicator when not connected. + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= + # Custom icon. + # typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## + # Ranger shell color. + typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### + # Nnn shell color. + typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## + # xplr shell color. + typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########################[ vim_shell: vim shell indicator (:sh) ]########################### + # Vim shell indicator color. + typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 + # Custom icon. + # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### + # Midnight Commander shell color. + typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## + # Nix shell color. + typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 + + # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. + # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ disk_usage: disk usage ]################################## + # Colors for different levels of disk usage. + typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 + # Thresholds for different levels of disk usage (percentage points). + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 + # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. + typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false + # Custom icon. + # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ ram: free RAM ]####################################### + # RAM color. + typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################################[ swap: used swap ]###################################### + # Swap color. + typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 + # Custom icon. + # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ load: CPU load ]###################################### + # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. + typeset -g POWERLEVEL9K_LOAD_WHICH=5 + # Load color when load is under 50%. + typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 + # Load color when load is between 50% and 70%. + typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 + # Load color when load is over 70%. + typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 + # Custom icon. + # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ + # Todo color. + typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 + # Hide todo when the total number of tasks is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true + # Hide todo when the number of tasks after filtering is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false + + # Todo format. The following parameters are available within the expansion. + # + # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. + # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. + # + # These variables correspond to the last line of the output of `todo.sh -p ls`: + # + # TODO: 24 of 42 tasks shown + # + # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. + # + # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ + # Timewarrior color. + typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 + # If the tracked task is longer than 24 characters, truncate and append "…". + # Tip: To always display tasks without truncation, delete the following parameter. + # Tip: To hide task names and display just the icon when time tracking is enabled, set the + # value of the following parameter to "". + typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' + + # Custom icon. + # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## + # Taskwarrior color. + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 + + # Taskwarrior segment format. The following parameters are available within the expansion. + # + # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. + # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. + # + # Zero values are represented as empty parameters. + # + # The default format: + # + # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + # + # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ context: user@hostname ]################################## + # Context color when running with privileges. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 + # Context color in SSH without privileges. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 + # Default context color (no privileges, no SSH). + typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 + + # Context format when running with privileges: bold user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' + # Context format when in SSH without privileges: user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' + # Default context format (no privileges, no SSH): user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' + + # Don't show context unless running with privileges or in SSH. + # Tip: Remove the next line to always show context. + typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%fwith ' + + ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 + # Don't show Python version next to the virtual environment name. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + # If set to "false", won't show virtualenv if pyenv is already shown. + # If set to "if-different", won't show virtualenv if it's the same as pyenv. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false + # Separate environment name from Python version only with a space. + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ anaconda: conda environment (https://conda.io/) ]###################### + # Anaconda environment color. + typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 + + # Anaconda segment format. The following parameters are available within the expansion. + # + # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. + # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. + # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). + # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). + # + # CONDA_PROMPT_MODIFIER can be configured with the following command: + # + # conda config --set env_prompt '({default_env}) ' + # + # The last argument is a Python format string that can use the following variables: + # + # - prefix The same as CONDA_PREFIX. + # - default_env The same as CONDA_DEFAULT_ENV. + # - name The last segment of CONDA_PREFIX. + # - stacked_env Comma-separated list of names in the environment stack. The first element is + # always the same as default_env. + # + # Note: '({default_env}) ' is the default value of env_prompt. + # + # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER + # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former + # is empty. + typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' + + # Custom icon. + # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ + # Pyenv color. + typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 + # Hide python version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) + # If set to false, hide python version if it's the same as global: + # $(pyenv version-name) == $(pyenv global). + typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide python version if it's equal to "system". + typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true + + # Pyenv segment format. The following parameters are available within the expansion. + # + # - P9K_CONTENT Current pyenv environment (pyenv version-name). + # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). + # + # The default format has the following logic: + # + # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or + # starts with "$P9K_PYENV_PYTHON_VERSION/". + # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". + typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' + + # Custom icon. + # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ + # Goenv color. + typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 + # Hide go version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) + # If set to false, hide go version if it's the same as global: + # $(goenv version-name) == $(goenv global). + typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide go version if it's equal to "system". + typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## + # Nodenv color. + typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 + # Hide node version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) + # If set to false, hide node version if it's the same as global: + # $(nodenv version-name) == $(nodenv global). + typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### + # Nvm color. + typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 + # Custom icon. + # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ + # Nodeenv color. + typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 + # Don't show Node version next to the environment name. + typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false + # Separate environment name from Node version only with a space. + typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############################[ node_version: node.js version ]############################### + # Node version color. + typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 + # Show node version only when in a directory tree containing package.json. + typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ go_version: go version (https://golang.org) ]######################## + # Go version color. + typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 + # Show go version only when in a go project subdirectory. + typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## + # Rust version color. + typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 + # Show rust version only when in a rust project subdirectory. + typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ + # .NET version color. + typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 + # Show .NET version only when in a .NET project subdirectory. + typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ php_version: php version (https://www.php.net/) ]###################### + # PHP version color. + typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 + # Show PHP version only when in a PHP project subdirectory. + typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### + # Laravel version color. + typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 + # Custom icon. + # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ####################[ java_version: java version (https://www.java.com/) ]#################### + # Java version color. + typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 + # Show java version only when in a java project subdirectory. + typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true + # Show brief version. + typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false + # Custom icon. + # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### + # Package color. + typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 + # Package format. The following parameters are available within the expansion. + # + # - P9K_PACKAGE_NAME The value of `name` field in package.json. + # - P9K_PACKAGE_VERSION The value of `version` field in package.json. + # + # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' + # Custom icon. + # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## + # Rbenv color. + typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 + # Hide ruby version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) + # If set to false, hide ruby version if it's the same as global: + # $(rbenv version-name) == $(rbenv global). + typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide ruby version if it's equal to "system". + typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## + # Rvm color. + typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 + # Don't show @gemset at the end. + typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false + # Don't show ruby- at the front. + typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ + # Fvm color. + typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### + # Lua color. + typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 + # Hide lua version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) + # If set to false, hide lua version if it's the same as global: + # $(luaenv version-name) == $(luaenv global). + typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide lua version if it's equal to "system". + typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ + # Java color. + typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 + # Hide java version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) + # If set to false, hide java version if it's the same as global: + # $(jenv version-name) == $(jenv global). + typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide java version if it's equal to "system". + typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ + # Perl color. + typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 + # Hide perl version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) + # If set to false, hide perl version if it's the same as global: + # $(plenv version-name) == $(plenv global). + typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide perl version if it's equal to "system". + typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############ + # Perlbrew color. + typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67 + # Show perlbrew version only when in a perl project subdirectory. + typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true + # Don't show "perl-" at the front. + typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ + # PHP color. + typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 + # Hide php version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) + # If set to false, hide php version if it's the same as global: + # $(phpenv version-name) == $(phpenv global). + typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide php version if it's equal to "system". + typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### + # Scala color. + typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 + # Hide scala version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) + # If set to false, hide scala version if it's the same as global: + # $(scalaenv version-name) == $(scalaenv global). + typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide scala version if it's equal to "system". + typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### + # Haskell color. + typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 + # Hide haskell version if it doesn't come from one of these sources. + # + # shell: version is set by STACK_YAML + # local: version is set by stack.yaml up the directory tree + # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) + typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) + # If set to false, hide haskell version if it's the same as in the implicit global project. + typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true + # Custom icon. + # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# + # Show kubecontext only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show kubecontext. + typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold' + + # Kubernetes context classes for the purpose of using different colors, icons and expansions with + # different contexts. + # + # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current kubernetes context gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current kubernetes context is "deathray-testing/default", its class is TEST + # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 + # typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext + # segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # Within the expansion the following parameters are always available: + # + # - P9K_CONTENT The content that would've been displayed if there was no content + # expansion defined. + # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE + # in the output of `kubectl config get-contexts`. If there is no + # namespace, the parameter is set to "default". + # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the + # output of `kubectl config get-contexts`. + # + # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), + # the following extra parameters are available: + # + # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. + # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. + # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. + # + # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, + # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=gke + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + # + # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=eks + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= + # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' + # Append the current context's namespace if it's not "default". + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' + + # Custom prefix. + # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%fat ' + + ################[ terraform: terraform workspace (https://www.terraform.io) ]################# + # Don't show terraform workspace if it's literally "default". + typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false + # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current terraform workspace gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current terraform workspace is "project_test", its class is TEST because "project_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ terraform_version: terraform version (https://www.terraform.io) ]############## + # Terraform version color. + typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# + # Show aws only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show aws. + typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current AWS profile gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current AWS profile is "company_test", its class is TEST + # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 + # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # AWS segment format. The following parameters are available within the expansion. + # + # - P9K_AWS_PROFILE The name of the current AWS profile. + # - P9K_AWS_REGION The region associated with the current AWS profile. + typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' + + #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# + # AWS Elastic Beanstalk environment color. + typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 + # Custom icon. + # typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## + # Show azure only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show azure. + typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' + # Azure account name color. + typeset -g POWERLEVEL9K_AZURE_FOREGROUND=32 + # Custom icon. + # typeset -g POWERLEVEL9K_AZURE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### + # Show gcloud only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show gcloud. + typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil' + # Google cloud color. + typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 + + # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or + # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative + # enough. You can use the following parameters in the expansions. Each of them corresponds to the + # output of `gcloud` tool. + # + # Parameter | Source + # -------------------------|-------------------------------------------------------------------- + # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' + # P9K_GCLOUD_ACCOUNT | gcloud config get-value account + # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project + # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. + # + # Obtaining project name requires sending a request to Google servers. This can take a long time + # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud + # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets + # set and gcloud prompt segment transitions to state COMPLETE. + # + # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL + # and COMPLETE. You can also hide gcloud in state PARTIAL by setting + # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and + # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. + typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' + typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' + + # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name + # this often. Negative value disables periodic polling. In this mode project name is retrieved + # only when the current configuration, account or project id changes. + typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 + + # Custom icon. + # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# + # Show google_app_cred only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show google_app_cred. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' + + # Google application credentials classes for the purpose of using different colors, icons and + # expansions with different credentials. + # + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first + # element in each pair defines a pattern against which the current kubernetes context gets + # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion + # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION + # parameters, you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. + # The first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD + # '*:*test*:*' TEST + # '*' DEFAULT) + # + # If your current Google application credentials is "service_account deathray-testing x@y.com", + # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD # These values are examples that are unlikely + # '*:*test*:*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by + # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # You can use the following parameters in the expansion. Each of them corresponds to one of the + # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. + # + # Parameter | JSON key file field + # ---------------------------------+--------------- + # P9K_GOOGLE_APP_CRED_TYPE | type + # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id + # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' + + ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### + # Toolbox color. + typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 + # Don't display the name of the toolbox if it matches fedora-toolbox-*. + typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' + # Custom icon. + # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%fin ' + + ###############################[ public_ip: public IP address ]############################### + # Public IP color. + typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 + # Custom icon. + # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ########################[ vpn_ip: virtual private network indicator ]######################### + # VPN IP color. + typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 + # When on VPN, show just an icon without the IP address. + # Tip: To display the private IP address when on VPN, remove the next line. + typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= + # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN + # to see the name of the interface. + typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*' + # If set to true, show one segment per matching network interface. If set to false, show only + # one segment corresponding to the first matching network interface. + # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. + typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false + # Custom icon. + # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### + # IP color. + typeset -g POWERLEVEL9K_IP_FOREGROUND=38 + # The following parameters are accessible within the expansion: + # + # Parameter | Meaning + # ----------------------+------------------------------------------- + # P9K_IP_IP | IP address + # P9K_IP_INTERFACE | network interface + # P9K_IP_RX_BYTES | total number of bytes received + # P9K_IP_TX_BYTES | total number of bytes sent + # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt + # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt + # P9K_IP_RX_RATE | receive rate (since last prompt) + # P9K_IP_TX_RATE | send rate (since last prompt) + typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='$P9K_IP_IP${P9K_IP_RX_RATE:+ %70F⇣$P9K_IP_RX_RATE}${P9K_IP_TX_RATE:+ %215F⇡$P9K_IP_TX_RATE}' + # Show information for the first network interface whose name matches this regular expression. + # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. + typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' + # Custom icon. + # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #########################[ proxy: system-wide http/https/ftp proxy ]########################## + # Proxy color. + typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ battery: internal battery ]################################# + # Show battery in red when it's below this level and not connected to power supply. + typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 + typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 + # Show battery in green when it's charging or fully charged. + typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 + # Show battery in yellow when it's discharging. + typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 + # Battery pictograms going from low to high level of charge. + typeset -g POWERLEVEL9K_BATTERY_STAGES='\uf58d\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf578' + # Don't show the remaining time to charge/discharge. + typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false + + #####################################[ wifi: wifi speed ]##################################### + # WiFi color. + typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). + # + # # Wifi colors and icons for different signal strength levels (low to high). + # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values + # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values + # + # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' + # + # The following parameters are accessible within the expansions: + # + # Parameter | Meaning + # ----------------------+--------------- + # P9K_WIFI_SSID | service set identifier, a.k.a. network name + # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown + # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second + # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 + # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 + # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) + + ####################################[ time: current time ]#################################### + # Current time color. + typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 + # Format for the current time: 09:51:02. See `man 3 strftime`. + typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}' + # If set to true, time will update when you hit enter. This way prompts for the past + # commands will contain the start times of their commands as opposed to the default + # behavior where they contain the end times of their preceding commands. + typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false + # Custom icon. + typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_TIME_PREFIX='%fat ' + + # Example of a user-defined prompt segment. Function prompt_example will be called on every + # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or + # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. + # + # Type `p10k help segment` for documentation and a more sophisticated example. + function prompt_example() { + p10k segment -f 208 -i '⭐' -t 'hello, %n' + } + + # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job + # is to generate the prompt segment for display in instant prompt. See + # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. + # + # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function + # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k + # will replay these calls without actually calling instant_prompt_*. It is imperative that + # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this + # rule is not observed, the content of instant prompt will be incorrect. + # + # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If + # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. + function instant_prompt_example() { + # Since prompt_example always makes the same `p10k segment` calls, we can call it from + # instant_prompt_example. This will give us the same `example` prompt segment in the instant + # and regular prompts. + prompt_example + } + + # User-defined prompt segments can be customized the same way as built-in segments. + # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 + # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt + # when accepting a command line. Supported values: + # + # - off: Don't change prompt when accepting a command line. + # - always: Trim down prompt when accepting a command line. + # - same-dir: Trim down prompt when accepting a command line unless this is the first command + # typed after changing current working directory. + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + + # Instant prompt mode. + # + # - off: Disable instant prompt. Choose this if you've tried instant prompt and found + # it incompatible with your zsh configuration files. + # - quiet: Enable instant prompt and don't print warnings when detecting console output + # during zsh initialization. Choose this if you've read and understood + # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. + # - verbose: Enable instant prompt and print a warning when detecting console output during + # zsh initialization. Choose this if you've never tried instant prompt, haven't + # seen the warning, or if you are unsure what this all means. + typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet + + # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. + # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload + # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you + # really need it. + typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true + + # If p10k is already loaded, reload configuration. + # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. + (( ! $+functions[p10k] )) || p10k reload +} + +typeset -g POWERLEVEL9K_TERM_SHELL_INTEGRATION=true + +# Tell `p10k configure` which file it should overwrite. +typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +'builtin' 'unset' 'p10k_config_opts' diff --git a/dot_config/zsh/dot_zdirs b/dot_config/zsh/dot_zdirs new file mode 100644 index 0000000..35a023c --- /dev/null +++ b/dot_config/zsh/dot_zdirs @@ -0,0 +1,20 @@ +/home/user +/home/user/.config +/home/user/.config/alacritty +/home/user/.config/lvim +/home/user/dotfiles/lvim/.config +/home/user/dotfiles/lvim +/home/user/dotfiles +/home/user/dotfiles/sh +/home/user/dotfiles/sh/.config +/home/user/dotfiles/tmux +/home/user/dotfiles/tmux/.config +/home/user/.config/zsh +/home/user/.local/share/chezmoi +/home/user/.local/share +/usr/lib64 +/home/user/.config/zsh/.cache +/home/user/.qucs +/mnt +/mnt/distrod_root +/mnt/distrod_root/proc diff --git a/dot_config/zsh/dot_zshenv b/dot_config/zsh/dot_zshenv new file mode 100644 index 0000000..85815a6 --- /dev/null +++ b/dot_config/zsh/dot_zshenv @@ -0,0 +1,167 @@ +#!/bin/zsh +# profile file. Runs on login. Environmental variables are set here. + +# If you don't plan on reverting to bash, you can remove the link in ~/.profile +# to clean up. + +export FPATH="$FPATH:${XDG_DATA_HOME:-$HOME/.local/share/zsh/site-functions}" + +unsetopt PROMPT_SP + +# Default programs: +export EDITOR="lvim +star" +export BROWSER="chromium" + +# ~/ Clean-up: +[ -z "$XDG_CONFIG_HOME" ] && export XDG_CONFIG_HOME="$HOME/.config" +[ -z "$XDG_DATA_HOME" ] && export XDG_DATA_HOME="$HOME/.local/share" +[ -z "$XDG_CACHE_HOME" ] && export XDG_CACHE_HOME="$HOME/.cache" +[ -z "$XDG_STATE_HOME" ] && export XDG_STATE_HOME="$HOME/.local/state" +[ -z "$XDG_BIN_HOME" ] && export XDG_BIN_HOME="$HOME/.local/bin" + +export USERXSESSION="$XDG_CACHE_HOME/X11/xsession" +export USERXSESSIONRC="$XDG_CACHE_HOME/X11/xsessionrc" +export ALTUSERXSESSION="$XDG_CACHE_HOME/X11/Xsession" +export ERRFILE="$XDG_CACHE_HOME/X11/xsession-errors" +export GPG_TTY=$TTY +export XINITRC="${XDG_CONFIG_HOME:-$HOME/.config}/x11/xinitrc" +# this breaks X forwarding over SSH +#export XAUTHORITY="$XDG_RUNTIME_DIR/Xauthority" # This line will break some DMs. +export NOTMUCH_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/notmuch-config" +export GTK2_RC_FILES="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-2.0/gtkrc-2.0" +export LESSHISTFILE="-" +export WGETRC="${XDG_CONFIG_HOME:-$HOME/.config}/wget/wgetrc" +export INPUTRC="${XDG_CONFIG_HOME:-$HOME/.config}/shell/inputrc" +export ZDOTDIR="${XDG_CONFIG_HOME:-$HOME/.config}/zsh" +export NUGET_PACKAGES="$XDG_CACHE_HOME/NuGetPackages" +export AWS_SHARED_CREDENTIALS_FILE="$XDG_CONFIG_HOME/aws/credentials" +export AWS_CONFIG_FILE="$XDG_CONFIG_HOME/aws/config" +export ALSA_CONFIG_PATH="$XDG_CONFIG_HOME/alsa/asoundrc" +export AZURE_CONFIG_DIR="$XDG_DATA_HOME/azure" +export MIX_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/mix" +export GNUPGHOME="${XDG_DATA_HOME:-$HOME/.local/share}/gnupg" +export WINEPREFIX="${XDG_DATA_HOME:-$HOME/.local/share}/wineprefixes/default" +export KODI_DATA="${XDG_DATA_HOME:-$HOME/.local/share}/kodi" +export PASSWORD_STORE_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/password-store" +export TMUX_TMPDIR="$XDG_RUNTIME_DIR" +export ANDROID_SDK_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/android" +export CARGO_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/cargo" +export GOBIN="${XDG_BIN_HOME:-$HOME/.local/bin}" +export GOPATH="${XDG_DATA_HOME:-$HOME/.local/share}/go" +export GOMODCACHE="$XDG_CACHE_HOME"/go/mod +export VOLTA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/volta" +export PNPM_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/pnpm" +export DENO_INSTALL_ROOT="${XDG_BIN_HOME:-$HOME/.local/bin}" +export ANSIBLE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/ansible/ansible.cfg" +export UNISON="${XDG_DATA_HOME:-$HOME/.local/share}/unison" +export HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/history" +export WEECHAT_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/weechat" +export MBSYNCRC="${XDG_CONFIG_HOME:-$HOME/.config}/mbsync/config" +export ELECTRUMDIR="${XDG_DATA_HOME:-$HOME/.local/share}/electrum" +export OCTAVE_SITE_INITFILE="$XDG_CONFIG_HOME/octave/octaverc" +export OCTAVE_HISTFILE="$XDG_CACHE_HOME/octave/octave_hist" +export ICEAUTHORITY="$XDG_CACHE_HOME/ICEauthority" +export HISTORY_IGNORE="(ls|cd|pwd|exit|sudo reboot|history|cd -|cd ..|clear|fg)" +export GDBHISTFILE="$XDG_DATA_HOME"/gdb/history +export SONARLINT_USER_HOME="$XDG_DATA_HOME/sonarlint" +export OMNISHARPHOME="$XDG_CONFIG_HOME"/omnisharp +export MIX_XDG=true + +# Other program settings: +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DICS="/usr/share/stardict/dic/" +export SUDO_ASKPASS="$HOME/.local/bin/dmenupass" +export FZF_DEFAULT_OPTS="--layout=reverse --height 40%" +export LESS=-R +export LESS_TERMCAP_mb="$(printf '%b' '')" +export LESS_TERMCAP_md="$(printf '%b' '')" +export LESS_TERMCAP_me="$(printf '%b' '')" +export LESS_TERMCAP_so="$(printf '%b' '')" +export LESS_TERMCAP_se="$(printf '%b' '')" +export LESS_TERMCAP_us="$(printf '%b' '')" +export LESS_TERMCAP_ue="$(printf '%b' '')" +export LESSOPEN="| /usr/bin/highlight -O ansi %s 2>/dev/null" +export QT_QPA_PLATFORMTHEME="qt5ct" # Have QT use gtk2 theme. +export MOZ_USE_XINPUT2="1" # Mozilla smooth scrolling/touchpads. +export AWT_TOOLKIT="MToolkit wmname LG3D" #May have to install wmname +export _JAVA_AWT_WM_NONREPARENTING=1 # Fix for Java applications in dwm +export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME"/java + +# This is the list for lf icons: +export LF_ICONS="di=📁:\ +fi=📃:\ +tw=🤝:\ +ow=📂:\ +ln=⛓:\ +or=❌:\ +ex=🎯:\ +*.txt=✍:\ +*.mom=✍:\ +*.me=✍:\ +*.ms=✍:\ +*.png=🖼:\ +*.webp=🖼:\ +*.ico=🖼:\ +*.jpg=📸:\ +*.jpe=📸:\ +*.jpeg=📸:\ +*.gif=🖼:\ +*.svg=🗺:\ +*.tif=🖼:\ +*.tiff=🖼:\ +*.xcf=🖌:\ +*.html=🌎:\ +*.xml=📰:\ +*.gpg=🔒:\ +*.css=🎨:\ +*.pdf=📚:\ +*.djvu=📚:\ +*.epub=📚:\ +*.csv=📓:\ +*.xlsx=📓:\ +*.tex=📜:\ +*.md=📘:\ +*.r=📊:\ +*.R=📊:\ +*.rmd=📊:\ +*.Rmd=📊:\ +*.m=📊:\ +*.mp3=🎵:\ +*.opus=🎵:\ +*.ogg=🎵:\ +*.m4a=🎵:\ +*.flac=🎼:\ +*.wav=🎼:\ +*.mkv=🎥:\ +*.mp4=🎥:\ +*.webm=🎥:\ +*.mpeg=🎥:\ +*.avi=🎥:\ +*.mov=🎥:\ +*.mpg=🎥:\ +*.wmv=🎥:\ +*.m4b=🎥:\ +*.flv=🎥:\ +*.zip=📦:\ +*.rar=📦:\ +*.7z=📦:\ +*.tar.gz=📦:\ +*.z64=🎮:\ +*.v64=🎮:\ +*.n64=🎮:\ +*.gba=🎮:\ +*.nes=🎮:\ +*.gdi=🎮:\ +*.1=ℹ:\ +*.nfo=ℹ:\ +*.info=ℹ:\ +*.log=📙:\ +*.iso=📀:\ +*.img=📀:\ +*.bib=🎓:\ +*.ged=👪:\ +*.part=💔:\ +*.torrent=🔽:\ +*.jar=♨:\ +*.java=♨:\ +" diff --git a/dot_config/zsh/dot_zshrc b/dot_config/zsh/dot_zshrc new file mode 100644 index 0000000..bdd34e1 --- /dev/null +++ b/dot_config/zsh/dot_zshrc @@ -0,0 +1,128 @@ +# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.config/zsh/.zshrc. +# Initialization code that may require console input (password prompts, [y/n] +# confirmations, etc.) must go above this block; everything else may go below. +[[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]] && source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" +# add .local/bin to PATH +export PATH="${XDG_BIN_HOME:-$HOME/.local/bin}:$PATH" +#export PATH="${$(find ~/.local/bin -type d -printf %p:)%%:}:$PATH" + +eval "$(antidot init)" +#eval "$(lesspipe.sh)" + +# Luke's config for the Zoomer Shell + +if [ -f /etc/os-release ] +then + . /etc/os-release +else + ID=`uname -s` +fi +case "$ID" in + Darwin ) + source $HOME/.nix-profile/share/zsh-powerlevel10k/powerlevel10k.zsh-theme + ;; + * ) + [ -f /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme ] && source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme || source ${XDG_DATA_HOME:-$HOME/.local/share}/powerlevel10k/powerlevel10k.zsh-theme + ;; +esac + + +# Enable colors and change prompt: +autoload -U colors && colors # Load colors +setopt autocd # Automatically cd into typed directory. +#stty stop undef # Disable ctrl-s to freeze terminal. +setopt interactive_comments +setopt complete_aliases + +# History in cache directory: +HISTSIZE=100000000 +SAVEHIST=100000000 +HISTFILE="$XDG_STATE_HOME"/zsh/history +setopt BANG_HIST # Treat the '!' character specially during expansion. +setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format. +setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits. +setopt SHARE_HISTORY # Share history between all sessions. +setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history. +setopt HIST_IGNORE_DUPS # Don't record an entry that was just recorded again. +setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate. +setopt HIST_FIND_NO_DUPS # Do not display a line previously found. +setopt HIST_IGNORE_SPACE # Don't record an entry starting with a space. +setopt HIST_SAVE_NO_DUPS # Don't write duplicate entries in the history file. +setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry. +setopt HIST_VERIFY # Don't execute immediately upon history expansion. +setopt HIST_BEEP # Beep when accessing nonexistent history. + +# Load aliases and shortcuts if existent. +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshopts" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshopts" + +# Basic auto/tab complete: +fpath=(~/.config/zsh/completions $fpath) +autoload -U compinit +zstyle ':completion:*' menu select +zmodload zsh/complist +#compinit +compinit -d ~/.cache/zsh/zcompdump-$ZSH_VERSION +_comp_options+=(globdots) # Include hidden files. + +# To customize prompt, run `p10k configure` or edit ~/.config/zsh/. p10k.zsh. +[[ ! -f ~/.config/zsh/.p10k.zsh ]] || source ~/.config/zsh/.p10k.zsh +[[ ! -f ~/.config/zsh/keybindings.zsh ]] || source ~/.config/zsh/keybindings.zsh + +# zsh parameter completion for the dotnet CLI +_dotnet_zsh_complete() +{ + local completions=("$(dotnet complete "$words")") + + # If the completion list is empty, just continue with filename selection + if [ -z "$completions" ] + then + _arguments '*::arguments: _normal' + return + fi + + # This is not a variable assignment, don't remove spaces! + _values = "${(ps:\n:)completions}" +} + +compdef _dotnet_zsh_complete dotnet + +[[ -f ~/.config/tabtab/zsh/__tabtab.zsh ]] && . ~/.config/tabtab/zsh/__tabtab.zsh || true + +if [ -n $WSL_DISTRO_NAME ] +then + command_not_found_handler() { + if [ -x "$(command -v $@.exe)" ]; then + $@.exe + else + echo >&2 "zsh: command not found: $@" + return 127 + fi +} +fi + + +case "$ID" in + debian | ubuntu ) + source /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh + source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + ;; + arch | artix | msys2 ) + source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh + source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + ;; + gentoo ) + source /usr/share/zsh/site-functions/zsh-syntax-highlighting.zsh + source /usr/share/zsh/site-functions/zsh-autosuggestions.zsh + ;; + Darwin ) + source $HOME/.nix-profile/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + source $HOME/.nix-profile/share/zsh-autosuggestions/zsh-autosuggestions.zsh + ;; + * ) + echo "no highlight for you" + ;; +esac + diff --git a/dot_config/zsh/executable_dot_zshopts b/dot_config/zsh/executable_dot_zshopts new file mode 100644 index 0000000..6756bfd --- /dev/null +++ b/dot_config/zsh/executable_dot_zshopts @@ -0,0 +1,71 @@ +#! /usr/bin/env zsh +# History settings +HISTSIZE=50000 +SAVEHIST=10000 + +setopt append_history # append the history +setopt inc_append_history # append to history in the current + # session and not just when the session ends +setopt share_history # share history between sessions +setopt extended_history # include statistics of when/how long/etc a + # command has run +setopt hist_ignore_dups # do not store dupes executed after eachother +setopt hist_ignore_all_dups # removes copies of the same line +setopt hist_expire_dups_first # removes copies when the histfile fills up +setopt hist_save_no_dups # don't save dupes from the same session +setopt hist_find_no_dups # if we find dupes in the history, don't show + # them in editor commands) +setopt hist_reduce_blanks # remove blank lines from the command which + # mean nothing to the shell + +# Disable this on boxes that are affected by bug +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924736 +# is-at-least 5.5 && unsetopt hist_reduce_blanks +# bugfix is incoming, lets see what it does +# is-at-least 5.7.2 && setopt hist_reduce_blanks + +setopt hist_ignore_space # lines starting with space don't go into the + # history +setopt no_hist_beep # silence..! +setopt hist_verify +setopt hist_no_store # don't store history/fc commands +#setopt hist_no_functions # don't show history of function definitions + +setopt bg_nice # nice bg commands +setopt notify # notify when a command returns exit code + +setopt no_beep # silence..! + +#unsetopt auto_cd # disable $ ./bin as cd ./bin +setopt extendedglob # ls ^bla.* will not show ^bla.txt for example + +setopt correct # correct incorrent cmd's +setopt correctall # correct everything, use + # `nocorrect mv foo bar` to negate this feature + # for a command + +setopt hash_list_all # fill the lookup table for tab completions + +unsetopt promptcr # prevent the prompt overwriting output when + # there is no newline + +unsetopt nomatch # +setopt prompt_subst # Enable prompt substition + +setopt glob_subst # global substitution +setopt globdots # Also look for . files + +setopt longlistjobs +setopt completeinword + +# Directories +setopt auto_pushd # cd foo == pushd foo +setopt pushd_ignore_dups # no duplicates in the list +setopt pushdminus +setopt auto_name_dirs # foo=/path/to/foo is the same as + # hash -d foo=/path/to/foo + +# Misc +setopt interactivecomments # $ # foo doesn't become an error when hitting + # enter + diff --git a/dot_config/zsh/keybindings.zsh b/dot_config/zsh/keybindings.zsh new file mode 100644 index 0000000..33b8195 --- /dev/null +++ b/dot_config/zsh/keybindings.zsh @@ -0,0 +1,138 @@ +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Standard-Widgets + +# Make sure that the terminal is in application mode when zle is active, since +# only then values from $terminfo are valid +if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then + function zle-line-init() { + echoti smkx + } + function zle-line-finish() { + echoti rmkx + } + zle -N zle-line-init + zle -N zle-line-finish +fi + +# Use emacs key bindings +bindkey -e + +# [PageUp] - Up a line of history +if [[ -n "${terminfo[kpp]}" ]]; then + bindkey -M emacs "${terminfo[kpp]}" up-line-or-history + bindkey -M viins "${terminfo[kpp]}" up-line-or-history + bindkey -M vicmd "${terminfo[kpp]}" up-line-or-history +fi +# [PageDown] - Down a line of history +if [[ -n "${terminfo[knp]}" ]]; then + bindkey -M emacs "${terminfo[knp]}" down-line-or-history + bindkey -M viins "${terminfo[knp]}" down-line-or-history + bindkey -M vicmd "${terminfo[knp]}" down-line-or-history +fi + +# Start typing + [Up-Arrow] - fuzzy find history forward +if [[ -n "${terminfo[kcuu1]}" ]]; then + autoload -U up-line-or-beginning-search + zle -N up-line-or-beginning-search + + bindkey -M emacs "${terminfo[kcuu1]}" up-line-or-beginning-search + bindkey -M viins "${terminfo[kcuu1]}" up-line-or-beginning-search + bindkey -M vicmd "${terminfo[kcuu1]}" up-line-or-beginning-search +fi +# Start typing + [Down-Arrow] - fuzzy find history backward +if [[ -n "${terminfo[kcud1]}" ]]; then + autoload -U down-line-or-beginning-search + zle -N down-line-or-beginning-search + + bindkey -M emacs "${terminfo[kcud1]}" down-line-or-beginning-search + bindkey -M viins "${terminfo[kcud1]}" down-line-or-beginning-search + bindkey -M vicmd "${terminfo[kcud1]}" down-line-or-beginning-search +fi + +# [Home] - Go to beginning of line +if [[ -n "${terminfo[khome]}" ]]; then + bindkey -M emacs "${terminfo[khome]}" beginning-of-line + bindkey -M viins "${terminfo[khome]}" beginning-of-line + bindkey -M vicmd "${terminfo[khome]}" beginning-of-line +fi +# [End] - Go to end of line +if [[ -n "${terminfo[kend]}" ]]; then + bindkey -M emacs "${terminfo[kend]}" end-of-line + bindkey -M viins "${terminfo[kend]}" end-of-line + bindkey -M vicmd "${terminfo[kend]}" end-of-line +fi + +# [Shift-Tab] - move through the completion menu backwards +if [[ -n "${terminfo[kcbt]}" ]]; then + bindkey -M emacs "${terminfo[kcbt]}" reverse-menu-complete + bindkey -M viins "${terminfo[kcbt]}" reverse-menu-complete + bindkey -M vicmd "${terminfo[kcbt]}" reverse-menu-complete +fi + +# [Backspace] - delete backward +bindkey -M emacs '^?' backward-delete-char +bindkey -M viins '^?' backward-delete-char +bindkey -M vicmd '^?' backward-delete-char +# [Delete] - delete forward +if [[ -n "${terminfo[kdch1]}" ]]; then + bindkey -M emacs "${terminfo[kdch1]}" delete-char + bindkey -M viins "${terminfo[kdch1]}" delete-char + bindkey -M vicmd "${terminfo[kdch1]}" delete-char +else + bindkey -M emacs "^[[3~" delete-char + bindkey -M viins "^[[3~" delete-char + bindkey -M vicmd "^[[3~" delete-char + + bindkey -M emacs "^[3;5~" delete-char + bindkey -M viins "^[3;5~" delete-char + bindkey -M vicmd "^[3;5~" delete-char +fi + +# [Ctrl-Delete] - delete whole forward-word +bindkey -M emacs '^[[3;5~' kill-word +bindkey -M viins '^[[3;5~' kill-word +bindkey -M vicmd '^[[3;5~' kill-word + +# [Ctrl-RightArrow] - move forward one word +bindkey -M emacs '^[[1;5C' forward-word +bindkey -M viins '^[[1;5C' forward-word +bindkey -M vicmd '^[[1;5C' forward-word +# [Ctrl-LeftArrow] - move backward one word +bindkey -M emacs '^[[1;5D' backward-word +bindkey -M viins '^[[1;5D' backward-word +bindkey -M vicmd '^[[1;5D' backward-word + + +bindkey '\ew' kill-region # [Esc-w] - Kill from the cursor to the mark +bindkey -s '\el' 'ls\n' # [Esc-l] - run command: ls +bindkey '^r' history-incremental-search-backward # [Ctrl-r] - Search backward incrementally for a specified string. The string may begin with ^ to anchor the search to the beginning of the line. +bindkey ' ' magic-space # [Space] - don't do history expansion + + +# Edit the current command line in $EDITOR +autoload -U edit-command-line +zle -N edit-command-line +bindkey '\C-x\C-e' edit-command-line + +# file rename magick +bindkey "^[m" copy-prev-shell-word + +# consider emacs keybindings: + +#bindkey -e ## emacs key bindings +# +#bindkey '^[[A' up-line-or-search +#bindkey '^[[B' down-line-or-search +#bindkey '^[^[[C' emacs-forward-word +#bindkey '^[^[[D' emacs-backward-word +# +#bindkey -s '^X^Z' '%-^M' +#bindkey '^[e' expand-cmd-path +#bindkey '^[^I' reverse-menu-complete +#bindkey '^X^N' accept-and-infer-next-history +#bindkey '^W' kill-region +#bindkey '^I' complete-word +## Fix weird sequence that rxvt produces +#bindkey -s '^[[Z' '\t' +# \ No newline at end of file diff --git a/gdb/.config/gdb/.gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py b/gdb/.config/gdb/.gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py deleted file mode 100644 index 8386154..0000000 --- a/gdb/.config/gdb/.gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py +++ /dev/null @@ -1,11629 +0,0 @@ -####################################################################################### -# 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-2022 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 binascii -import codecs -import collections -import configparser -import ctypes -import enum -import functools -import hashlib -import importlib -import inspect -import itertools -import json -import os -import pathlib -import platform -import re -import shutil -import site -import socket -import string -import struct -import subprocess -import sys -import tempfile -import time -import traceback -import warnings -import xmlrpc.client as xmlrpclib -from functools import lru_cache -from io import StringIO, TextIOWrapper -from types import ModuleType -from typing import (Any, ByteString, Callable, Dict, Generator, IO, Iterator, List, - NoReturn, Optional, Sequence, Tuple, Type, Union) -from urllib.request import urlopen - - -def 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.""" - try: - http = urlopen(url) - if http.getcode() != 200: - return None - return http.read() - except Exception: - return None - - -def update_gef(argv: List[str]) -> int: - """Try to update `gef` to the latest version pushed on GitHub master branch. - Return 0 on success, 1 on failure. """ - ver = "dev" if "--dev" in argv[2:] else "master" - latest_gef_data = http_get(f"https://raw.githubusercontent.com/hugsy/gef/{ver}/scripts/gef.sh") - if latest_gef_data is None: - print("[-] Failed to get remote gef") - return 1 - - fd, fname = tempfile.mkstemp(suffix=".sh") - os.write(fd, latest_gef_data) - os.close(fd) - retcode = subprocess.run(["bash", fname, ver], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode - os.unlink(fname) - return retcode - - -try: - import gdb # pylint: disable= -except ImportError: - # if out of gdb, the only action allowed is to update gef.py - if len(sys.argv) == 2 and sys.argv[1].lower() in ("--update", "--upgrade"): - sys.exit(update_gef(sys.argv)) - print("[-] gef cannot run as standalone") - sys.exit(0) - - -GDB_MIN_VERSION = (8, 0) -GDB_VERSION = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) -PYTHON_MIN_VERSION = (3, 6) -PYTHON_VERSION = sys.version_info[0:2] - -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 = os.path.join(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 = " \u2190 " -RIGHT_ARROW = " \u2192 " -DOWN_ARROW = "\u21b3" -HORIZONTAL_LINE = "\u2500" -VERTICAL_LINE = "\u2502" -CROSS = "\u2718 " -TICK = "\u2713 " -BP_GLYPH = "\u25cf" -GEF_PROMPT = "gef\u27a4 " -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" - -PATTERN_LIBC_VERSION = re.compile(rb"glibc (\d+)\.(\d+)") - - -gef : "Gef" = None -__registered_commands__ : List[Type["GenericCommand"]] = [] -__registered_functions__ : List[Type["GenericFunction"]] = [] -__registered_architectures__ : Dict[Union["Elf.Abi", str], Type["Architecture"]] = {} - - -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: - 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 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 - - -# -# Helpers -# - -def p8(x: int, s: bool = False) -> bytes: - """Pack one byte respecting the current architecture endianness.""" - return struct.pack(f"{gef.arch.endianness}B", x) if not s else struct.pack(f"{gef.arch.endianness}b", x) - - -def p16(x: int, s: bool = False) -> bytes: - """Pack one word respecting the current architecture endianness.""" - return struct.pack(f"{gef.arch.endianness}H", x) if not s else struct.pack(f"{gef.arch.endianness}h", x) - - -def p32(x: int, s: bool = False) -> bytes: - """Pack one dword respecting the current architecture endianness.""" - return struct.pack(f"{gef.arch.endianness}I", x) if not s else struct.pack(f"{gef.arch.endianness}i", x) - - -def p64(x: int, s: bool = False) -> bytes: - """Pack one qword respecting the current architecture endianness.""" - return struct.pack(f"{gef.arch.endianness}Q", x) if not s else struct.pack(f"{gef.arch.endianness}q", x) - - -def u8(x: bytes, s: bool = False) -> int: - """Unpack one byte respecting the current architecture endianness.""" - return struct.unpack(f"{gef.arch.endianness}B", x)[0] if not s else struct.unpack(f"{gef.arch.endianness}b", x)[0] - - -def u16(x: bytes, s: bool = False) -> int: - """Unpack one word respecting the current architecture endianness.""" - return struct.unpack(f"{gef.arch.endianness}H", x)[0] if not s else struct.unpack(f"{gef.arch.endianness}h", x)[0] - - -def u32(x: bytes, s: bool = False) -> int: - """Unpack one dword respecting the current architecture endianness.""" - return struct.unpack(f"{gef.arch.endianness}I", x)[0] if not s else struct.unpack(f"{gef.arch.endianness}i", x)[0] - - -def u64(x: bytes, s: bool = False) -> int: - """Unpack one qword respecting the current architecture endianness.""" - return struct.unpack(f"{gef.arch.endianness}Q", x)[0] if not s else struct.unpack(f"{gef.arch.endianness}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 - - -# -# 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: - if gef.config["gef.show_deprecation_warnings"] is True: - msg = f"'{f.__name__}' is deprecated and will be removed in a feature release. " - 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_gdb_version_higher_than(required_gdb_version: Tuple[int, ...]) -> Callable: - """Decorator to check whether current GDB version requirements.""" - - def wrapper(f: Callable) -> Callable: - def inner_f(*args: Any, **kwargs: Any) -> None: - if GDB_VERSION >= required_gdb_version: - f(*args, **kwargs) - else: - reason = f"GDB >= {required_gdb_version} for this command" - raise OSError(reason) - return inner_f - return wrapper - - -def only_if_current_arch_in(valid_architectures: List["Architecture"]) -> Callable: - """Decorator to allow commands for only a subset of the architectured supported by GEF. - This decorator is to use lightly, as it goes against the purpose of GEF to support all - architectures GDB does. However in some cases, it is necessary.""" - - def wrapper(f: Callable) -> Callable: - def inner_f(*args: Any, **kwargs: Any) -> None: - if gef.arch in valid_architectures: - f(*args, **kwargs) - else: - reason = f"This command cannot work for the '{gef.arch.arch}' architecture" - raise OSError(reason) - return inner_f - 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): - 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[Union[str, Tuple[str, str]], Any], - optional_arguments: Dict[Union[str, Tuple[str, str]], Any]) -> Optional[Callable]: - """Argument parsing decorator.""" - - def int_wrapper(x: str) -> int: return int(x, 0) - - def decorator(f: Callable) -> Optional[Callable]: - def wrapper(*args: Any, **kwargs: Any) -> Optional[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 = isinstance(argname, list) or isinstance(argname, tuple) - 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: - argname_is_list = isinstance(argname, list) or isinstance(argname, tuple) - if not argname_is_list and not argname.startswith("-"): - # refuse positional arguments - continue - argvalue = optional_arguments[argname] - argtype = type(argvalue) - if not argname_is_list: - argname = [argname,] - if argtype is int: - argtype = int_wrapper - if argtype is bool: - parser.add_argument(*argname, action="store_true" if argvalue else "store_false") - else: - parser.add_argument(*argname, type=argtype, default=argvalue) - - try: - parsed_args = parser.parse_args(*(args[1:])) - except RuntimeWarning: - return - kwargs["arguments"] = parsed_args - return f(*args, **kwargs) - return wrapper - return decorator - - -class Color: - """Used to colorify terminal output.""" - colors = { - "normal" : "\033[0m", - "gray" : "\033[1;38;5;240m", - "light_gray" : "\033[0;37m", - "red" : "\033[31m", - "green" : "\033[32m", - "yellow" : "\033[33m", - "blue" : "\033[34m", - "pink" : "\033[35m", - "cyan" : "\033[36m", - "bold" : "\033[1m", - "underline" : "\033[4m", - "underline_off" : "\033[24m", - "highlight" : "\033[3m", - "highlight_off" : "\033[23m", - "blink" : "\033[5m", - "blink_off" : "\033[25m", - } - - @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) - self.valid: bool = kwargs.get("valid", True) - 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) -> Optional[int]: - addr = align_address(int(self.value)) - derefed = dereference(addr) - return None if derefed is None else int(derefed) - - -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 - - @staticmethod - def from_info_sections(*args: str) -> "Permission": - perm = Permission(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 - - @staticmethod - def from_process_maps(perm_str: str) -> "Permission": - perm = Permission(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 - - -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 (self.permission & Permission.READ) != 0 - - def is_writable(self) -> bool: - return (self.permission & Permission.WRITE) != 0 - - def is_executable(self) -> bool: - return (self.permission & Permission.EXECUTE) != 0 - - @property - def size(self) -> int: - if self.page_end is None or self.page_start is None: - return -1 - return self.page_end - self.page_start - - @property - def realpath(self) -> str: - # when in a `gef-remote` session, realpath returns the path to the binary on the local disk, not remote - return self.path if gef.session.remote is None else f"/tmp/gef/{gef.session.remote:d}/{self.path}" - - def __str__(self) -> str: - return (f"Section(page_start={self.page_start:#x}, page_end={self.page_end:#x}, " - f"permissions={self.permission!s})") - - -Zone = collections.namedtuple("Zone", ["name", "zone_start", "zone_end", "filename"]) - - -class Endianness(enum.Enum): - LITTLE_ENDIAN = 1 - BIG_ENDIAN = 2 - - def __str__(self) -> str: - if self == Endianness.LITTLE_ENDIAN: - return "<" - return ">" - - def __repr__(self) -> str: - return self.name - - def __int__(self) -> int: - return self.value - - -class Elf: - """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: Class = Class.ELF_32_BITS - e_endianness: Endianness = Endianness.LITTLE_ENDIAN - e_eiversion: int - e_osabi: OsAbi - e_abiversion: int - e_pad: bytes - e_type: 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: Optional[pathlib.Path] = None - - def __init__(self, path: str = "", minimalist: bool = False) -> None: - """Instantiate an ELF object. The default behavior is to create the object by parsing the ELF file. - But in some cases (QEMU-stub), we may just want a simple minimal object with default values.""" - if minimalist: - return - - self.fpath = pathlib.Path(path).expanduser() - if not os.access(self.fpath, os.R_OK): - raise FileNotFoundError(f"'{self.fpath}' not found/readable, most gef features will not work") - - with self.fpath.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 : List["Phdr"] = [] - for i in range(self.e_phnum): - self.phdrs.append(Phdr(self, self.e_phoff + self.e_phentsize * i)) - - self.shdrs : List["Shdr"] = [] - 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.fpath.absolute()}', {self.e_class.name}, {self.e_machine.name})" - - @property - def entry_point(self) -> int: - return self.e_entry - - @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) -> Type: - return cls.UNKNOWN_PHDR - - class Flags(enum.IntFlag): - PF_X = 1 - PF_W = 2 - PF_R = 4 - - p_type: Type - p_flags: 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 = gef.arch.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) -> 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: Type - sh_flags: 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: Optional[Elf], off: int) -> None: - if elf is None: - return - elf.seek(off) - endian = gef.arch.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: bytearray) -> 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 - - -@lru_cache() -def search_for_main_arena() -> int: - """A helper function to find the libc `main_arena` address, either from symbol or from its offset - from `__malloc_hook`.""" - try: - addr = parse_address(f"&{LIBC_HEAP_MAIN_ARENA_DEFAULT_NAME}") - - except gdb.error: - malloc_hook_addr = parse_address("(void *)&__malloc_hook") - - 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 - MallocStateStruct("*0").struct_size - elif is_arch(Elf.Abi.ARM): - addr = malloc_hook_addr - gef.arch.ptrsize - MallocStateStruct("*0").struct_size - else: - raise OSError(f"Cannot find main_arena for {gef.arch.arch}") - - return addr - - -class MallocStateStruct: - """GEF representation of malloc_state - from https://github.com/bminor/glibc/blob/glibc-2.28/malloc/malloc.c#L1658""" - - def __init__(self, addr: str) -> None: - try: - self.__addr = parse_address(f"&{addr}") - except gdb.error: - self.__addr = search_for_main_arena() - # if `search_for_main_arena` throws `gdb.error` on symbol lookup: - # it means the session is not started, so just propagate the exception - - self.num_fastbins = 10 - self.num_bins = 254 - - self.int_size = cached_lookup_type("int").sizeof - self.size_t = cached_lookup_type("size_t") - if not self.size_t: - ptr_type = "unsigned long" if gef.arch.ptrsize == 8 else "unsigned int" - self.size_t = cached_lookup_type(ptr_type) - - # Account for separation of have_fastchunks flag into its own field - # within the malloc_state struct in GLIBC >= 2.27 - # https://sourceware.org/git/?p=glibc.git;a=commit;h=e956075a5a2044d05ce48b905b10270ed4a63e87 - # Be aware you could see this change backported into GLIBC release - # branches. - if get_libc_version() >= (2, 27): - self.fastbin_offset = align_address_to_size(self.int_size * 3, 8) - else: - self.fastbin_offset = self.int_size * 2 - return - - # struct offsets - @property - def addr(self) -> int: - return self.__addr - - @property - def fastbins_addr(self) -> int: - return self.__addr + self.fastbin_offset - - @property - def top_addr(self) -> int: - return self.fastbins_addr + self.num_fastbins * gef.arch.ptrsize - - @property - def last_remainder_addr(self) -> int: - return self.top_addr + gef.arch.ptrsize - - @property - def bins_addr(self) -> int: - return self.last_remainder_addr + gef.arch.ptrsize - - @property - def next_addr(self) -> int: - return self.bins_addr + self.num_bins * gef.arch.ptrsize + self.int_size * 4 - - @property - def next_free_addr(self) -> int: - return self.next_addr + gef.arch.ptrsize - - @property - def system_mem_addr(self) -> int: - return self.next_free_addr + gef.arch.ptrsize * 2 - - @property - def struct_size(self) -> int: - return self.system_mem_addr + gef.arch.ptrsize * 2 - self.__addr - - # struct members - @property - def fastbinsY(self) -> "gdb.Value": - return self.get_size_t_array(self.fastbins_addr, self.num_fastbins) - - @property - def top(self) -> "gdb.Value": - return self.get_size_t_pointer(self.top_addr) - - @property - def last_remainder(self) -> "gdb.Value": - return self.get_size_t_pointer(self.last_remainder_addr) - - @property - def bins(self) -> "gdb.Value": - return self.get_size_t_array(self.bins_addr, self.num_bins) - - @property - def next(self) -> "gdb.Value": - return self.get_size_t_pointer(self.next_addr) - - @property - def next_free(self) -> "gdb.Value": - return self.get_size_t_pointer(self.next_free_addr) - - @property - def system_mem(self) -> "gdb.Value": - return self.get_size_t(self.system_mem_addr) - - # helper methods - def get_size_t(self, addr: int) -> "gdb.Value": - return dereference(addr).cast(self.size_t) - - def get_size_t_pointer(self, addr: int) -> "gdb.Value": - size_t_pointer = self.size_t.pointer() - return dereference(addr).cast(size_t_pointer) - - def get_size_t_array(self, addr: int, length: int) -> "gdb.Value": - size_t_array = self.size_t.array(length) - return dereference(addr).cast(size_t_array) - - def __getitem__(self, item: str) -> Any: - return getattr(self, item) - - -class GlibcHeapInfo: - """Glibc heap_info struct - See https://github.com/bminor/glibc/blob/glibc-2.34/malloc/arena.c#L64""" - - def __init__(self, addr: Union[int, str]) -> None: - self.__addr = addr if type(addr) is int else parse_address(addr) - self.size_t = cached_lookup_type("size_t") - if not self.size_t: - ptr_type = "unsigned long" if gef.arch.ptrsize == 8 else "unsigned int" - self.size_t = cached_lookup_type(ptr_type) - - @property - def addr(self) -> int: - return self.__addr - - @property - def ar_ptr_addr(self) -> int: - return self.addr - - @property - def prev_addr(self) -> int: - return self.ar_ptr_addr + gef.arch.ptrsize - - @property - def size_addr(self) -> int: - return self.prev_addr + gef.arch.ptrsize - - @property - def mprotect_size_addr(self) -> int: - return self.size_addr + self.size_t.sizeof - - @property - def ar_ptr(self) -> "gdb.Value": - return self._get_size_t_pointer(self.ar_ptr_addr) - - @property - def prev(self) -> "gdb.Value": - return self._get_size_t_pointer(self.prev_addr) - - @property - def size(self) -> "gdb.Value": - return self._get_size_t(self.size_addr) - - @property - def mprotect_size(self) -> "gdb.Value": - return self._get_size_t(self.mprotect_size_addr) - - # helper methods - def _get_size_t_pointer(self, addr: int) -> "gdb.Value": - size_t_pointer = self.size_t.pointer() - return dereference(addr).cast(size_t_pointer) - - def _get_size_t(self, addr: int) -> "gdb.Value": - return dereference(addr).cast(self.size_t) - - -class GlibcArena: - """Glibc arena class - Ref: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1671""" - - def __init__(self, addr: str) -> None: - self.__arena: Union["gdb.Value", MallocStateStruct] - try: - arena = gdb.parse_and_eval(addr) - malloc_state_t = cached_lookup_type("struct malloc_state") - self.__arena = arena.cast(malloc_state_t) # here __arena becomes a "gdb.Value" - self.__addr = int(arena.address) - self.struct_size: int = malloc_state_t.sizeof - except: - self.__arena = MallocStateStruct(addr) # here __arena becomes MallocStateStruct - self.__addr = self.__arena.addr - - try: - self.top = int(self.top) - self.last_remainder = int(self.last_remainder) - self.n = int(self.next) - self.nfree = int(self.next_free) - self.sysmem = int(self.system_mem) - except gdb.error as e: - err("Glibc arena: {}".format(e)) - return - - def __getitem__(self, item: Any) -> Any: - return self.__arena[item] - - def __getattr__(self, item: Any) -> Any: - return self.__arena[item] - - def __int__(self) -> int: - return self.__addr - - def __iter__(self) -> Generator["GlibcArena", None, None]: - yield self - current_arena = self - - while True: - next_arena_address = int(current_arena.next) - if next_arena_address == int(gef.heap.main_arena): - break - - current_arena = GlibcArena(f"*{next_arena_address:#x} ") - yield current_arena - return - - def __eq__(self, other: "GlibcArena") -> bool: - # You cannot have 2 arenas at the same address, so this check should be enough - return self.__addr == int(other) - - def fastbin(self, i: int) -> Optional["GlibcChunk"]: - """Return head chunk in fastbinsY[i].""" - addr = int(self.fastbinsY[i]) - if addr == 0: - return None - return GlibcChunk(addr + 2 * gef.arch.ptrsize) - - def bin(self, i: int) -> Tuple[int, int]: - idx = i * 2 - fd = int(self.bins[idx]) - bw = int(self.bins[idx + 1]) - return fd, bw - - def is_main_arena(self) -> bool: - return int(self) == int(gef.heap.main_arena) - - def heap_addr(self, allow_unaligned: bool = False) -> Optional[int]: - if self.is_main_arena(): - heap_section = gef.heap.base_address - if not heap_section: - return None - return heap_section - _addr = int(self) + self.struct_size - if allow_unaligned: - return _addr - return malloc_align_address(_addr) - - def get_heap_info_list(self) -> Optional[List[GlibcHeapInfo]]: - 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 != 0: - 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 - default_mmap_threshold_max = 4 * 1024 * 1024 * cached_lookup_type("long").sizeof - heap_max_size = 2 * default_mmap_threshold_max - return ptr & ~(heap_max_size - 1) - - def __str__(self) -> str: - return (f"{Color.colorify('Arena', 'blue bold underline')}(base={self.__addr:#x}, top={self.top:#x}, " - f"last_remainder={self.last_remainder:#x}, next={self.n:#x}, next_free={self.nfree:#x}, " - f"system_mem={self.sysmem:#x})") - - @property - def addr(self) -> int: - return int(self) - - -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/.""" - - def __init__(self, addr: int, from_base: bool = False, allow_unaligned: bool = True) -> None: - self.ptrsize = gef.arch.ptrsize - if from_base: - self.data_address = addr + 2 * self.ptrsize - else: - self.data_address = addr - if not allow_unaligned: - self.data_address = malloc_align_address(self.data_address) - self.base_address = addr - 2 * self.ptrsize - - self.size_addr = int(self.data_address - self.ptrsize) - self.prev_size_addr = self.base_address - return - - def get_chunk_size(self) -> int: - return gef.memory.read_integer(self.size_addr) & (~0x07) - - @property - def size(self) -> int: - return self.get_chunk_size() - - def get_usable_size(self) -> int: - # https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L4537 - cursz = self.get_chunk_size() - if cursz == 0: return cursz - if self.has_m_bit(): return cursz - 2 * self.ptrsize - return cursz - self.ptrsize - - @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]: - 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.get_chunk_size() - - # if free-ed functions - def get_fwd_ptr(self, sll: bool) -> int: - # Not a single-linked-list (sll) or no Safe-Linking support yet - if not sll or get_libc_version() < (2, 32): - return gef.memory.read_integer(self.data_address) - # Unmask ("reveal") the Safe-Linking pointer - else: - return gef.memory.read_integer(self.data_address) ^ (self.data_address >> 12) - - @property - def fwd(self) -> int: - return self.get_fwd_ptr(False) - - fd = fwd # for compat - - def get_bkw_ptr(self) -> int: - return gef.memory.read_integer(self.data_address + self.ptrsize) - - @property - def bck(self) -> int: - return self.get_bkw_ptr() - - bk = bck # for compat - # endif free-ed functions - - def has_p_bit(self) -> bool: - return bool(gef.memory.read_integer(self.size_addr) & 0x01) - - def has_m_bit(self) -> bool: - return bool(gef.memory.read_integer(self.size_addr) & 0x02) - - def has_n_bit(self) -> bool: - return bool(gef.memory.read_integer(self.size_addr) & 0x04) - - 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_chunk_size_flag(self) -> str: - msg = [] - msg.append(f"PREV_INUSE flag: {Color.greenify('On') if self.has_p_bit() else Color.redify('Off')}") - msg.append(f"IS_MMAPPED flag: {Color.greenify('On') if self.has_m_bit() else Color.redify('Off')}") - msg.append(f"NON_MAIN_ARENA flag: {Color.greenify('On') if self.has_n_bit() else Color.redify('Off')}") - return "\n".join(msg) - - def _str_sizes(self) -> str: - msg = [] - failed = False - - try: - msg.append("Chunk size: {0:d} ({0:#x})".format(self.get_chunk_size())) - msg.append("Usable size: {0:d} ({0:#x})".format(self.get_usable_size())) - failed = True - except gdb.MemoryError: - msg.append(f"Chunk size: Cannot read at {self.size_addr:#x} (corrupted?)") - - try: - msg.append("Previous chunk size: {0:d} ({0:#x})".format(self.get_prev_chunk_size())) - failed = True - except gdb.MemoryError: - msg.append(f"Previous chunk size: Cannot read at {self.base_address:#x} (corrupted?)") - - if failed: - msg.append(self.str_chunk_size_flag()) - - return "\n".join(msg) - - def _str_pointers(self) -> str: - fwd = self.data_address - bkw = self.data_address + self.ptrsize - - msg = [] - try: - msg.append(f"Forward pointer: {self.get_fwd_ptr(False):#x}") - except gdb.MemoryError: - msg.append(f"Forward pointer: {fwd:#x} (corrupted?)") - - try: - msg.append(f"Backward pointer: {self.get_bkw_ptr():#x}") - except gdb.MemoryError: - msg.append(f"Backward pointer: {bkw:#x} (corrupted?)") - - return "\n".join(msg) - - def str_as_alloced(self) -> str: - return self._str_sizes() - - def str_as_freed(self) -> str: - return f"{self._str_sizes()}\n\n{self._str_pointers()}" - - def flags_as_string(self) -> str: - flags = [] - if self.has_p_bit(): - flags.append(Color.colorify("PREV_INUSE", "red bold")) - else: - flags.append(Color.colorify("! PREV_INUSE", "green bold")) - if self.has_m_bit(): - flags.append(Color.colorify("IS_MMAPPED", "red bold")) - if self.has_n_bit(): - flags.append(Color.colorify("NON_MAIN_ARENA", "red bold")) - return "|".join(flags) - - def __str__(self) -> str: - return (f"{Color.colorify('Chunk', 'yellow bold underline')}(addr={self.data_address:#x}, " - f"size={self.get_chunk_size():#x}, flags={self.flags_as_string()})") - - def psprint(self) -> str: - msg = [] - msg.append(str(self)) - if self.is_used(): - msg.append(self.str_as_alloced()) - else: - msg.append(self.str_as_freed()) - - return "\n".join(msg) + "\n" - - -@lru_cache() -def get_libc_version() -> Tuple[int, ...]: - sections = gef.memory.maps - for section in sections: - match = re.search(r"libc6?[-_](\d+)\.(\d+)\.so", section.path) - if match: - return tuple(int(_) for _ in match.groups()) - if "libc" in section.path: - try: - with open(section.path, "rb") as f: - data = f.read() - except OSError: - continue - match = re.search(PATTERN_LIBC_VERSION, data) - if match: - return tuple(int(_) for _ in match.groups()) - return 0, 0 - - -def titlify(text: str, color: Optional[str] = None, msg_color: Optional[str] = None) -> str: - """Print a centered title.""" - cols = get_terminal_size()[1] - nb = (cols - len(text) - 2) // 2 - if color is None: - color = gef.config["theme.default_title_line"] - if msg_color is None: - msg_color = gef.config["theme.default_title_message"] - - msg = [Color.colorify(f"{HORIZONTAL_LINE * nb} ", color), - Color.colorify(text, msg_color), - Color.colorify(f" {HORIZONTAL_LINE * nb}", color)] - return "".join(msg) - - -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: - fname = os.path.expanduser(os.path.expandvars(fname)) - with open(fname, "r") as f: - __data = f.readlines() - return __data[idx - 1] if 0 < idx < len(__data) else "" - - gef_print("") - exc_type, exc_value, exc_traceback = sys.exc_info() - - gef_print(" Exception raised ".center(80, HORIZONTAL_LINE)) - gef_print(f"{Color.colorify(exc_type.__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: - gef_print("lsb_release is missing, cannot collect additional debug information") - - 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) -> Optional[pathlib.Path]: - """Locate a command on the filesystem.""" - for path in os.environ["PATH"].split(os.pathsep): - dirname = pathlib.Path(path) - fpath = dirname / program - if os.access(fpath, os.X_OK): - return fpath - - raise FileNotFoundError(f"Missing file `{program}`") - - -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 = "<{:s}+{:04x}>".format(*sym) 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 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 RedirectOutputContext(): - def __init__(self, to: str = "/dev/null") -> None: - self.redirection_target_file = to - 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") - gdb.execute("set logging on") - return - - def __exit__(self, *exc: Any) -> None: - """Disable the output redirection, if any.""" - 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`.""" - gdb.execute("set logging overwrite") - gdb.execute(f"set logging file {to_file}") - gdb.execute("set logging redirect on") - gdb.execute("set logging on") - return - - -def disable_redirect_output() -> None: - """Disable the output redirection, if any.""" - gdb.execute("set logging off") - gdb.execute("set logging redirect off") - return - - -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) -> Optional[Tuple[Optional[str], Optional[Tuple[gdb.Symtab_and_line, ...]]]]: - """Fetch the proper symbol or None if not defined.""" - try: - return gdb.decode_line(sym)[1] - except gdb.error: - return None - - -@lru_cache(maxsize=512) -def gdb_get_location_from_symbol(address: int) -> Optional[Tuple[str, int]]: - """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 = gdb.execute(f"info symbol {address:#x}", to_string=True) - if sym.startswith("No symbol matches"): - return None - - i = sym.find(" in section ") - sym = sym[:i].split() - name, offset = sym[0], 0 - if len(sym) == 3 and sym[2].isdigit(): - offset = int(sym[2]) - 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): - 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 = "<{}+{}>".format(*loc) 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) -> Optional[int]: - """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 - - -def gdb_get_nth_next_instruction_address(addr: int, n: int) -> int: - """Return the address (Integer) of the `n`-th instruction after `addr`.""" - # fixed-length ABI - if gef.arch.instruction_length: - return addr + n * gef.arch.instruction_length - - # variable-length ABI - insn = list(gdb_disassemble(addr, count=n))[-1] - return insn.address - - -def gef_instruction_n(addr: int, n: int) -> Instruction: - """Return the `n`-th instruction after `addr` as an Instruction object.""" - 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: - 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 - - for insn in gdb_disassemble(addr, count=nb_insn): - yield insn - - -def capstone_disassemble(location: int, nb_insn: int, **kwargs: Any) -> Generator[Instruction, None, None]: - """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before - `addr` using the Capstone-Engine disassembler, if available. - Return an iterator of Instruction objects.""" - - def cs_insn_to_gef_insn(cs_insn: "capstone.CsInsn") -> Instruction: - sym_info = gdb_get_location_from_symbol(cs_insn.address) - loc = "<{}+{}>".format(*sym_info) if sym_info else "" - ops = [] + cs_insn.op_str.split(", ") - return Instruction(cs_insn.address, loc, cs_insn.mnemonic, ops, cs_insn.bytes) - - capstone = sys.modules["capstone"] - arch, mode = get_capstone_arch(arch=kwargs.get("arch"), mode=kwargs.get("mode"), endian=kwargs.get("endian")) - cs = capstone.Cs(arch, mode) - cs.detail = True - - page_start = align_address_to_page(location) - offset = location - page_start - pc = gef.arch.pc - - skip = int(kwargs.get("skip", 0)) - nb_prev = int(kwargs.get("nb_prev", 0)) - if nb_prev > 0: - location = gdb_get_nth_previous_instruction_address(pc, nb_prev) - nb_insn += nb_prev - - code = kwargs.get("code", gef.memory.read(location, gef.session.pagesize - offset - 1)) - for insn in cs.disasm(code, location): - if skip: - skip -= 1 - continue - nb_insn -= 1 - yield cs_insn_to_gef_insn(insn) - if nb_insn == 0: - break - return - - -def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[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 - - -@lru_cache(32) -def checksec(filename: str) -> 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.""" - readelf = gef.session.constants["readelf"] - - 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 - - results = collections.OrderedDict() - results["Canary"] = __check_security_property("-s", filename, r"__stack_chk_fail") is True - has_gnu_stack = __check_security_property("-W -l", filename, r"GNU_STACK") is True - if has_gnu_stack: - results["NX"] = __check_security_property("-W -l", filename, r"GNU_STACK.*RWE") is False - else: - results["NX"] = False - results["PIE"] = __check_security_property("-h", filename, r":.*EXEC") is False - results["Fortify"] = __check_security_property("-s", filename, r"_chk@GLIBC") is True - results["Partial RelRO"] = __check_security_property("-l", filename, r"GNU_RELRO") is True - results["Full RelRO"] = results["Partial RelRO"] and __check_security_property("-d", filename, r"BIND_NOW") is True - return results - - -@lru_cache() -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).strip() - if "The target architecture is set automatically (currently " in arch_str: - arch_str = arch_str.split("(currently ", 1)[1] - arch_str = arch_str.split(")", 1)[0] - elif "The target architecture is assumed to be " in arch_str: - arch_str = arch_str.replace("The target architecture is assumed to be ", "") - elif "The target architecture is set to " in arch_str: - # GDB version >= 10.1 - arch_str = re.findall(r"\"(.+)\"", arch_str)[0] - else: - # Unknown, we throw an exception to be safe - raise RuntimeError(f"Unknown architecture: {arch_str}") - return arch_str - - -@deprecated("Use `gef.binary.entry_point` instead") -def get_entry_point() -> Optional[int]: - """Return the binary entry point.""" - return gef.binary.entry_point if gef.binary else None - - -def is_pie(fpath: str) -> bool: - return checksec(fpath)["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 i in value_table: - flag_str = Color.boldify(value_table[i].upper()) if reg_value & (1< Optional[int]: - section = process_lookup_path(name) - return section.page_start if section else None - - -@lru_cache() -def get_zone_base_address(name: str) -> Optional[int]: - zone = file_lookup_name_path(name, get_filepath()) - return zone.zone_start if zone else None - - -# -# Architecture classes -# -def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: - """Class decorator for declaring an architecture to GEF.""" - global __registered_architectures__ - for key in cls.aliases: - __registered_architectures__[key] = cls - return cls - - -class Architecture(metaclass=abc.ABCMeta): - """Generic metaclass for the architecture supported by GEF.""" - - @abc.abstractproperty - def all_registers(self) -> List[str]: pass - @abc.abstractproperty - def instruction_length(self) -> Optional[int]: pass - @abc.abstractproperty - def nop_insn(self) -> bytes: pass - @abc.abstractproperty - def return_register(self) -> str: pass - @abc.abstractproperty - def flag_register(self) -> Optional[str]: pass - @abc.abstractproperty - def flags_table(self) -> Optional[Dict[int, str]]: pass - @abc.abstractproperty - def function_parameters(self) -> List[str]: pass - @abc.abstractmethod - def flag_register_to_human(self, val: Optional[int] = None) -> str: pass - @abc.abstractmethod - def is_call(self, insn: Instruction) -> bool: pass - @abc.abstractmethod - def is_ret(self, insn: Instruction) -> bool: pass - @abc.abstractmethod - def is_conditional_branch(self, insn: Instruction) -> bool: pass - @abc.abstractmethod - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: pass - @abc.abstractmethod - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: pass - @classmethod - @abc.abstractmethod - def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: pass - - arch = "" - mode = "" - aliases: Tuple[Union[str, int], ...] = [] - special_registers: List[str] = [] - - def reset_caches(self) -> None: - self.__get_register_for_selected_frame.cache_clear() - return - - def __get_register(self, regname: str) -> Optional[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, key) - - @lru_cache() - def __get_register_for_selected_frame(self, regname: str, hash_key: int) -> Optional[int]: - # 1st chance - try: - return parse_address(regname) - except gdb.error: - pass - - # 2nd chance - try: - regname = regname.lstrip("$") - value = gdb.selected_frame().read_register(regname) - return int(value) - except (ValueError, gdb.error): - pass - return None - - def register(self, name: str) -> Optional[int]: - return self.__get_register(name) - - @property - def registers(self) -> Generator[str, None, None]: - yield from self.all_registers - - @property - def pc(self) -> Optional[int]: - return self.register("$pc") - - @property - def sp(self) -> Optional[int]: - return self.register("$sp") - - @property - def fp(self) -> Optional[int]: - return self.register("$fp") - - _ptrsize = None - @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 - - _endianness = None - @property - def endianness(self) -> Endianness: - if not self._endianness: - output = gdb.execute("show endian", to_string=True).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 endianess found in '{output}'") - return self._endianness - - def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: - """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 = "" - all_registers = () - instruction_length = 0 - ptrsize = 0 - return_register = "" - function_parameters = () - syscall_register = "" - syscall_instructions = () - nop_insn = b"" - flag_register = None - flags_table = None - def flag_register_to_human(self, val: Optional[int] = None) -> str: return "" - def is_call(self, insn: Instruction) -> bool: return False - def is_ret(self, insn: Instruction) -> bool: return False - def is_conditional_branch(self, insn: Instruction) -> bool: return False - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: return False, "" - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: return 0 - - @classmethod - def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: - raise OSError(f"Architecture {cls.arch} not supported") - - -@register_architecture -class RISCV(Architecture): - arch = "RISCV" - mode = "RISCV" - aliases = ("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 - flag_register_to_human = None - flags_table = None - - @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") - - 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 == "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: - ra = None - if self.is_ret(insn): - ra = gef.arch.register("$ra") - elif frame.older(): - ra = frame.older().pc() - return ra - - -@register_architecture -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",] - - # https://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0041c/Caccegih.html - nop_insn = b"\x01\x10\xa0\xe1" # mov r1, r1 - return_register = "$r0" - flag_register = "$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 gef.arch.register(self.flag_register) & (1 << 5) - - @property - def pc(self) -> Optional[int]: - pc = gef.arch.register("$pc") - if self.is_thumb(): - pc += 1 - return pc - - @property - def mode(self) -> str: - return "THUMB" if self.is_thumb() else "ARM" - - @property - def instruction_length(self) -> Optional[int]: - # Thumb instructions have variable-length (2 or 4-byte) - return None if self.is_thumb() else 4 - - @property - def ptrsize(self) -> int: - return 2 if self.is_thumb() else 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: Optional[int] = 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: - ra = None - if self.is_ret(insn): - # 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 - ra = to_unsigned_long(dereference(ra_addr)) - elif insn.mnemonic == "ldr": - return to_unsigned_long(dereference(gef.arch.sp)) - else: # 'bx lr' or 'add pc, lr, #0' - return gef.arch.register("$lr") - elif frame.older(): - ra = frame.older().pc() - return ra - - @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) - - -@register_architecture -class AARCH64(ARM): - aliases = ("ARM64", "AARCH64", Elf.Abi.AARCH64) - arch = "ARM64" - mode = "" - - 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", - 6: "fast", - } - function_parameters = ["$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7"] - syscall_register = "$x8" - syscall_instructions = ["svc $x0"] - ptrsize = 8 - - 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: Optional[int] = 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) - - @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 not val: - 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< Optional[int]: - ra = None - if self.is_ret(insn): - ra = to_unsigned_long(dereference(gef.arch.sp)) - if frame.older(): - ra = frame.older().pc() - - return ra - - @classmethod - def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: - _NR_mprotect = 125 - insns = [ - "pushad", - f"mov eax, {_NR_mprotect:d}", - f"mov ebx, {addr:d}", - f"mov ecx, {size:d}", - f"mov edx, {perm.value:d}", - "int 0x80", - "popad", - ] - return "; ".join(insns) - - def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: - 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 - - -@register_architecture -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 = [ - "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", - ] - return "; ".join(insns) - - -@register_architecture -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 = "$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"] - - def flag_register_to_human(self, val: Optional[int] = None) -> str: - # https://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3) - if not val: - 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 = val&(1< Optional[int]: - ra = None - if self.is_ret(insn): - ra = gef.arch.register("$lr") - elif frame.older(): - ra = frame.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) - - -@register_architecture -class PowerPC64(PowerPC): - aliases = ("PowerPC64", Elf.Abi.POWERPC64, "PPC64") - arch = "PPC" - mode = "PPC64" - - -@register_architecture -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 = "$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: Optional[int] = None) -> str: - # https://www.gaisler.com/doc/sparcv8.pdf - reg = self.flag_register - if not val: - 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 = val&(1< Optional[int]: - ra = None - if self.is_ret(insn): - ra = gef.arch.register("$o7") - elif frame.older(): - ra = frame.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) - - -@register_architecture -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) - - -@register_architecture -class MIPS(Architecture): - aliases: Tuple[Union[str, Elf.Abi], ...] = ("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: Optional[int] = 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]), "{0[0]} == {0[1]}".format(ops) - elif mnemo == "bne": - taken, reason = gef.arch.register(ops[0]) != gef.arch.register(ops[1]), "{0[0]} != {0[1]}".format(ops) - elif mnemo == "beqz": - taken, reason = gef.arch.register(ops[0]) == 0, "{0[0]} == 0".format(ops) - elif mnemo == "bnez": - taken, reason = gef.arch.register(ops[0]) != 0, "{0[0]} != 0".format(ops) - elif mnemo == "bgtz": - taken, reason = gef.arch.register(ops[0]) > 0, "{0[0]} > 0".format(ops) - elif mnemo == "bgez": - taken, reason = gef.arch.register(ops[0]) >= 0, "{0[0]} >= 0".format(ops) - elif mnemo == "bltz": - taken, reason = gef.arch.register(ops[0]) < 0, "{0[0]} < 0".format(ops) - elif mnemo == "blez": - taken, reason = gef.arch.register(ops[0]) <= 0, "{0[0]} <= 0".format(ops) - return taken, reason - - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: - ra = None - if self.is_ret(insn): - ra = gef.arch.register("$ra") - elif frame.older(): - ra = frame.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) - - -@register_architecture -class MIPS64(MIPS): - aliases = ("MIPS64",) - arch = "MIPS" - mode = "MIPS64" - ptrsize = 8 - - -def copy_to_clipboard(data: str) -> 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: - 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 << 64) - 1 - return int(v.cast(gdb.Value(mask).type)) & mask - - -def get_path_from_info_proc() -> Optional[str]: - for x in gdb.execute("info proc", to_string=True).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) - 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) - 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) - return 'received: ""' in response - - -@lru_cache() -def get_filepath() -> Optional[str]: - """Return the local absolute path of the file currently debugged.""" - filename = gdb.current_progspace().filename - - if is_remote_debug(): - # if no filename specified, try downloading target from /proc - if filename is None: - pid = gef.session.pid - if pid > 0: - return download_file(f"/proc/{pid:d}/exe", use_cache=True) - return None - - # if target is remote file, download - elif filename.startswith("target:"): - fname = filename[len("target:") :] - return download_file(fname, use_cache=True, local_name=fname) - - elif filename.startswith(".gnu_debugdata for target:"): - fname = filename[len(".gnu_debugdata for target:") :] - return download_file(fname, use_cache=True, local_name=fname) - - elif gef.session.remote is not None: - return f"/tmp/gef/{gef.session.remote:d}/{get_path_from_info_proc()}" - return filename - else: - if filename is not None: - return filename - # inferior probably did not have name, extract cmdline from info proc - return get_path_from_info_proc() - - -def download_file(remote_path: str, use_cache: bool = False, local_name: Optional[str] = None) -> Optional[str]: - """Download filename `remote_path` inside the mirror tree inside the `gef.config["gef.tempdir"]`. - The tree architecture must be `gef.config["gef.tempdir"]/gef//`. - This allow a "chroot-like" tree format.""" - - local_root = pathlib.Path(gef.config["gef.tempdir"]) / str(gef.session.pid) - if local_name is None: - local_path = local_root / remote_path.strip(os.sep) - else: - local_path = local_root / local_name.strip(os.sep) - - if use_cache and local_path.exists(): - return str(local_path.absolute()) - - try: - local_path.parent.mkdir(parents=True, exist_ok=True) - gdb.execute(f"remote get {remote_path} {local_path.absolute()}") - local_path = str(local_path.absolute()) - except gdb.error: - # fallback memory view - with open(local_path, "w") as f: - if is_32bit(): - f.write(f"00000000-ffffffff rwxp 00000000 00:00 0 {get_filepath()}\n") - else: - f.write(f"0000000000000000-ffffffffffffffff rwxp 00000000 00:00 0 {get_filepath()}\n") - - except Exception as e: - err(f"download_file() failed: {e}") - local_path = None - - return local_path - - -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).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).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) -> Optional[Section]: - """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) -> Optional[Section]: - """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 - - for sect in gef.memory.maps: - if name in sect.path and sect.permission & perm: - return sect - - return None - - -@lru_cache() -def file_lookup_name_path(name: str, path: str) -> Optional[Zone]: - """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) -> Optional[Zone]: - """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 ida_synchronize_handler(_: "gdb.Event") -> None: - gdb.execute("ida-interact sync", from_tty=True) - return - - -def continue_handler(_: "gdb.Event") -> None: - """GDB event handler for new object continue cases.""" - return - - -def hook_stop_handler(_: "gdb.Event") -> None: - """GDB event handler for stop cases.""" - reset_all_caches() - gdb.execute("context") - return - - -def new_objfile_handler(_: "gdb.Event") -> None: - """GDB event handler for new object file cases.""" - reset_all_caches() - reset_architecture() - load_libc_args() - return - - -def exit_handler(_: "gdb.Event") -> None: - """GDB event handler for exit cases.""" - reset_all_caches() - gef.session.qemu_mode = False - if gef.session.remote and gef.config["gef-remote.clean_on_exit"] is True: - shutil.rmtree(f"/tmp/gef/{gef.session.remote:d}") - gef.session.remote = None - return - - -def memchanged_handler(_: "gdb.Event") -> None: - """GDB event handler for mem changes cases.""" - reset_all_caches() - return - - -def regchanged_handler(_: "gdb.Event") -> None: - """GDB event handler for reg changes cases.""" - reset_all_caches() - return - - -def load_libc_args() -> bool: - """Load the LIBC function arguments. Returns `True` on success, `False` or an Exception otherwise.""" - global gef - # load libc function arguments' definitions - if not gef.config["context.libc_args"]: - return False - - path = gef.config["context.libc_args_path"] - if not path: - return False - - path = pathlib.Path(path).expanduser().absolute() - if not path.exists(): - raise RuntimeError("Config `context.libc_args_path` set but it's not a directory") - - _arch_mode = f"{gef.arch.arch.lower()}_{gef.arch.mode}" - _libc_args_file = path / f"{_arch_mode}.json" - - # current arch and mode already loaded - if _arch_mode in gef.ui.highlight_table: - return True - - gef.ui.highlight_table[_arch_mode] = {} - try: - with _libc_args_file.open() as _libc_args: - gef.ui.highlight_table[_arch_mode] = json.load(_libc_args) - return True - except FileNotFoundError: - del gef.ui.highlight_table[_arch_mode] - warn(f"Config context.libc_args is set but definition cannot be loaded: file {_libc_args_file} not found") - except json.decoder.JSONDecodeError as e: - del gef.ui.highlight_table[_arch_mode] - warn(f"Config context.libc_args is set but definition cannot be loaded from file {_libc_args_file}: {e}") - return False - - -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 windll, create_string_buffer - 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("hh", fcntl.ioctl(1, termios.TIOCGWINSZ, "1234")) - return tty_rows, tty_columns - except OSError: - return 600, 100 - - -def get_generic_arch(module: ModuleType, prefix: str, arch: str, mode: Optional[str], big_endian: Optional[bool], to_string: bool = False) -> Tuple[str, Union[int, str]]: - """ - Retrieves architecture and mode from the arguments for use for the holy - {cap,key}stone/unicorn trinity. - """ - if to_string: - arch = f"{module.__name__}.{prefix}_ARCH_{arch}" - if mode: - mode = f"{module.__name__}.{prefix}_MODE_{mode}" - else: - mode = "" - if gef.arch.endianness == Endianness.BIG_ENDIAN: - mode += f" + {module.__name__}.{prefix}_MODE_BIG_ENDIAN" - else: - mode += f" + {module.__name__}.{prefix}_MODE_LITTLE_ENDIAN" - - else: - arch = getattr(module, f"{prefix}_ARCH_{arch}") - if mode: - mode = getattr(module, f"{prefix}_MODE_{mode}") - else: - mode = 0 - if big_endian: - mode |= getattr(module, f"{prefix}_MODE_BIG_ENDIAN") - else: - mode |= getattr(module, f"{prefix}_MODE_LITTLE_ENDIAN") - - return arch, mode - - -def get_generic_running_arch(module: ModuleType, prefix: str, to_string: bool = False) -> Union[Tuple[None, None], Tuple[str, Union[int, str]]]: - """ - Retrieves architecture and mode from the current context. - """ - - if not is_alive(): - return None, None - - if gef.arch is not None: - arch, mode = gef.arch.arch, gef.arch.mode - else: - raise OSError("Emulation not supported for your OS") - - return get_generic_arch(module, prefix, arch, mode, gef.arch.endianness == Endianness.BIG_ENDIAN, to_string) - - -def get_unicorn_arch(arch: Optional[str] = None, mode: Optional[str] = None, endian: Optional[bool] = None, to_string: bool = False) -> Union[Tuple[None, None], Tuple[str, Union[int, str]]]: - unicorn = sys.modules["unicorn"] - if (arch, mode, endian) == (None, None, None): - return get_generic_running_arch(unicorn, "UC", to_string) - return get_generic_arch(unicorn, "UC", arch, mode, endian, to_string) - - -def get_capstone_arch(arch: Optional[str] = None, mode: Optional[str] = None, endian: Optional[bool] = None, to_string: bool = False) -> Union[Tuple[None, None], Tuple[str, Union[int, str]]]: - capstone = sys.modules["capstone"] - - # hacky patch to unify capstone/ppc syntax with keystone & unicorn: - # CS_MODE_PPC32 does not exist (but UC_MODE_32 & KS_MODE_32 do) - if is_arch(Elf.Abi.POWERPC64): - raise OSError("Capstone not supported for PPC64 yet.") - - if is_alive() and is_arch(Elf.Abi.POWERPC): - - arch = "PPC" - mode = "32" - endian = (gef.arch.endianness == Endianness.BIG_ENDIAN) - return get_generic_arch(capstone, "CS", - arch or gef.arch.arch, - mode or gef.arch.mode, - endian, - to_string) - - if (arch, mode, endian) == (None, None, None): - return get_generic_running_arch(capstone, "CS", to_string) - return get_generic_arch(capstone, "CS", - arch or gef.arch.arch, - mode or gef.arch.mode, - endian or gef.arch.endianness == Endianness.BIG_ENDIAN, - to_string) - - -def get_keystone_arch(arch: Optional[str] = None, mode: Optional[str] = None, endian: Optional[bool] = None, to_string: bool = False) -> Union[Tuple[None, None], Tuple[str, Union[int, str]]]: - keystone = sys.modules["keystone"] - if (arch, mode, endian) == (None, None, None): - return get_generic_running_arch(keystone, "KS", to_string) - - if arch in ["ARM64", "SYSTEMZ"]: - modes = [None] - elif arch == "ARM" and mode == "ARMV8": - modes = ["ARM", "V8"] - elif arch == "ARM" and mode == "THUMBV8": - modes = ["THUMB", "V8"] - else: - modes = [mode] - a = arch - if not to_string: - mode = 0 - for m in modes: - arch, _mode = get_generic_arch(keystone, "KS", a, m, endian, to_string) - mode |= _mode - else: - mode = "" - for m in modes: - arch, _mode = get_generic_arch(keystone, "KS", a, m, endian, to_string) - mode += f"|{_mode}" - mode = mode[1:] - return arch, mode - - -def get_unicorn_registers(to_string: bool = False) -> Union[Dict[str, int], Dict[str, str]]: - "Return a dict matching the Unicorn identifier for a specific register." - unicorn = sys.modules["unicorn"] - regs = {} - - if gef.arch is not None: - arch = gef.arch.arch.lower() - else: - raise OSError("Oops") - - const = getattr(unicorn, f"{arch}_const") - for reg in gef.arch.all_registers: - regname = f"UC_{arch.upper()}_REG_{reg[1:].upper()}" - if to_string: - regs[reg] = f"{const.__name__}.{regname}" - else: - regs[reg] = getattr(const, regname) - return regs - - -def keystone_assemble(code: str, arch: int, mode: int, **kwargs: Any) -> Optional[Union[str, bytearray]]: - """Assembly encoding function based on keystone.""" - keystone = sys.modules["keystone"] - code = gef_pybytes(code) - addr = kwargs.get("addr", 0x1000) - - try: - ks = keystone.Ks(arch, mode) - enc, cnt = ks.asm(code, addr) - except keystone.KsError as e: - err(f"Keystone assembler error: {e}") - return None - - if cnt == 0: - return "" - - enc = bytearray(enc) - if "raw" not in kwargs: - s = binascii.hexlify(enc) - enc = b"\\x" + b"\\x".join([s[i : i + 2] for i in range(0, len(s), 2)]) - enc = enc.decode("utf-8") - - return enc - - -@lru_cache() -def get_elf_headers(filename: Optional[str] = None) -> Optional[Elf]: - """Return an Elf object with info from `filename`. If not provided, will return - the currently debugged file.""" - if not filename: - filename = get_filepath() - if not filename: - raise Exception("No file provided") - - if filename.startswith("target:"): - warn("Your file is remote, you should try using `gef-remote` instead") - return - - return Elf(filename) - - -@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: Optional[str] = None, default: Optional[str] = None) -> None: - """Sets the current architecture. - If an arch is explicitly specified, use that one, otherwise try to parse it - out of the current target. If that fails, and default is specified, select and - set that arch. - Raise an exception if the architecture cannot be set. - Does not return a value. - """ - global gef - arches = __registered_architectures__ - - if arch: - try: - gef.arch = arches[arch.upper()]() - return - except KeyError: - raise OSError(f"Specified arch {arch.upper()} is not supported") - - if not gef.binary: - gef.binary = get_elf_headers() - - arch_name = gef.binary.e_machine if gef.binary else get_arch() - - if ((arch_name == "MIPS" or arch_name == Elf.Abi.MIPS) - and (gef.binary is not None and gef.binary.e_class == Elf.Class.ELF_64_BITS)): - # MIPS64 = arch(MIPS) + 64b flag - arch_name = "MIPS64" - - try: - gef.arch = arches[arch_name]() - except KeyError: - if default: - try: - gef.arch = arches[default.upper()]() - except KeyError: - raise OSError(f"CPU not supported, neither is default {default.upper()}") - else: - raise OSError(f"CPU type is currently not supported: {get_arch()}") - return - - -@lru_cache() -def cached_lookup_type(_type: str) -> Optional[gdb.Type]: - 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: - pass - - raise OSError("GEF is running under an unsupported mode") - - -def clear_screen(tty: str = "") -> None: - """Clear the screen.""" - global gef - if not tty: - gdb.execute("shell clear -x") - return - - # Since the tty can be closed at any time, a PermissionError exception can - # occur when `clear_screen` is called. We handle this scenario properly - try: - with open(tty, "wt") as f: - f.write("\x1b[H\x1b[J") - except PermissionError: - gef.ui.redirect_fd = None - gef.config["context.redirect"] = "" - return - - -def format_address(addr: int) -> str: - """Format the address according to its size.""" - memalign_size = gef.arch.ptrsize - addr = align_address(addr) - - if memalign_size == 4: - return f"{addr:#08x}" - - return f"{addr:#016x}" - - -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.""" - if gef.arch.ptrsize == 4: - return address & 0xFFFFFFFF - - return address & 0xFFFFFFFFFFFFFFFF - - -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 malloc_align_address(address: int) -> int: - """Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github""" - __default_malloc_alignment = 0x10 - if is_x86_32() and get_libc_version() >= (2, 26): - # Special case introduced in Glibc 2.26: - # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/i386/malloc-alignment.h#L22 - malloc_alignment = __default_malloc_alignment - else: - # Generic case: - # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/generic/malloc-alignment.h#L22 - __alignof__long_double = int(safe_parse_and_eval("_Alignof(long double)") or __default_malloc_alignment) # fallback to default if the expression fails to evaluate - malloc_alignment = max(__alignof__long_double, 2 * gef.arch.ptrsize) - - ceil = lambda n: int(-1 * n // 1 * -1) - # align address to nearest next multiple of malloc_alignment - return malloc_alignment * ceil((address / malloc_alignment)) - - -def parse_address(address: str) -> int: - """Parse an address and return it as an Integer.""" - if is_hex(address): - return int(address, 16) - return to_unsigned_long(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 - - -@lru_cache() -def is_remote_debug() -> bool: - """"Return True is the current debugging session is running through GDB remote session.""" - return gef.session.remote is not None or "remote" in gdb.execute("maintenance print target-stack", to_string=True) - - -def de_bruijn(alphabet: bytes, n: int) -> Generator[str, 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[str, 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) -> Optional["gdb.Value"]: - """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: - pass - return None - - -@lru_cache() -def dereference(addr: int) -> Optional["gdb.Value"]: - """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()) - 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: - pass - return None - - -def gef_convenience(value: str) -> str: - """Defines a new convenience value.""" - global gef - var_name = f"$_gef{gef.session.convenience_vars_index:d}" - gef.session.convenience_vars_index += 1 - gdb.execute(f"""set {var_name} = "{value}" """) - 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) - - -@deprecated("Use `gef.session.pie_breakpoints[num]`") -def gef_get_pie_breakpoint(num: int) -> "PieVirtualBreakpoint": - return gef.session.pie_breakpoints[num] - - -# -# Deprecated API -# -@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() -> Optional[Tuple[int, int]]: - 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: - return gef.session.file.name - - -@deprecated("Use `gef.heap.main_arena`") -def get_glibc_arena() -> Optional[GlibcArena]: - return gef.heap.main_arena - - -@deprecated("Use `gef.arch.register(regname)`") -def get_register(regname) -> Optional[int]: - 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: Optional[str] = None, default: Optional[str] = None) -> None: - return reset_architecture(arch, default) - -# -# GDB event hooking -# - -@only_if_events_supported("cont") -def gef_on_continue_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.cont.connect(func) - - -@only_if_events_supported("cont") -def gef_on_continue_unhook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.cont.disconnect(func) - - -@only_if_events_supported("stop") -def gef_on_stop_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.stop.connect(func) - - -@only_if_events_supported("stop") -def gef_on_stop_unhook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.stop.disconnect(func) - - -@only_if_events_supported("exited") -def gef_on_exit_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.exited.connect(func) - - -@only_if_events_supported("exited") -def gef_on_exit_unhook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.exited.disconnect(func) - - -@only_if_events_supported("new_objfile") -def gef_on_new_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.new_objfile.connect(func) - - -@only_if_events_supported("new_objfile") -def gef_on_new_unhook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.new_objfile.disconnect(func) - - -@only_if_events_supported("memory_changed") -def gef_on_memchanged_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.memory_changed.connect(func) - - -@only_if_events_supported("memory_changed") -def gef_on_memchanged_unhook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.memory_changed.disconnect(func) - - -@only_if_events_supported("register_changed") -def gef_on_regchanged_hook(func: Callable[["gdb.Event"], None]) -> None: - gdb.events.register_changed.connect(func) - - -@only_if_events_supported("register_changed") -def gef_on_regchanged_unhook(func: Callable[["gdb.Event"], 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: Union[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) - except gdb.error as e: - err(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: Optional[int]) -> 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: - gdb.execute(f"return (unsigned int){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) - 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.get_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(" data = 'A'*{0:d} + 'B'*{1:d} + 'C'*{1:d}".format(offset, align)) - 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) - 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) - - if newloc != self: - ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", "yellow bold"), - self.ptr, self.size, - Color.colorify(f"{newloc:#x}", "green"),)) - else: - ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", "yellow bold"), - self.ptr, self.size, - Color.colorify(f"{newloc:#x}", "red"),)) - - 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 addr == 0: - 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, y) 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) - 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 - - -# -# Context Panes -# - -def register_external_context_pane(pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], Optional[str]]) -> 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. - - Example Usage: - 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) - """ - gef.gdb.add_context_pane(pane_name, display_pane_function, pane_title_function) - return - - -# -# Commands -# - -def register_external_command(obj: "GenericCommand") -> Type["GenericCommand"]: - """Registering function for new GEF (sub-)command to GDB.""" - cls = obj.__class__ - __registered_commands__.append(cls) - gef.gdb.load(initial=False) - gef.gdb.doc.add_command_to_doc((cls._cmdline_, cls, None)) - gef.gdb.doc.refresh() - return cls - - -def register_command(cls: Type["GenericCommand"]) -> Type["GenericCommand"]: - """Decorator for registering new GEF (sub-)command to GDB.""" - __registered_commands__.append(cls) - return cls - - -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.""" - __registered_commands__.insert(0, cls) - return cls - - -def register_function(cls: Type["GenericFunction"]) -> Type["GenericFunction"]: - """Decorator for registering a new convenience function to GDB.""" - __registered_functions__.append(cls) - return cls - - -class GenericCommand(gdb.Command, metaclass=abc.ABCMeta): - """This is an abstract class for invoking commands, should not be instantiated.""" - - def __init__(self, *args: Any, **kwargs: Any) -> None: - self.pre_load() - syntax = Color.yellowify("\nSyntax: ") + self._syntax_ - example = Color.yellowify("\nExample: ") + self._example_ if self._example_ else "" - self.__doc__ = self.__doc__.replace(" "*4, "") + syntax + example - self.repeat = False - self.repeat_count = 0 - self.__last_command = None - command_type = kwargs.setdefault("command", gdb.COMMAND_OBSCURE) - complete_type = kwargs.setdefault("complete", gdb.COMPLETE_NONE) - prefix = kwargs.setdefault("prefix", False) - super().__init__(self._cmdline_, command_type, complete_type, 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() - else: - err(f"Command '{self._cmdline_}' failed to execute properly, reason: {e}") - return - - def usage(self) -> None: - err(f"Syntax\n{self._syntax_}") - return - - @abc.abstractproperty - def _cmdline_(self) -> Optional[str]: pass - - @abc.abstractproperty - def _syntax_(self) -> Optional[str]: pass - - @abc.abstractproperty - def _example_(self) -> str: return "" - - @abc.abstractmethod - def do_invoke(self, argv: List[str]) -> None: pass - - def pre_load(self) -> None: pass - - def post_load(self) -> None: pass - - 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("") - 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("") - 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("") - def add_setting(self, name: str, value: Tuple[Any, type, str], description: str = "") -> None: - return self.__setitem__(name, (value, type(value), description)) - - def __setitem__(self, name: str, value: Union[Any, 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: - setting = gef.config.raw_entry(key) - setting.value = 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("") - 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).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_command -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 - with gef_fpath.open("rb") as f: - gef_hash = hashlib.sha256(f.read()).hexdigest() - - if os.access(f"{gef_dir}/.git", os.X_OK): - 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_command -class PrintFormatCommand(GenericCommand): - """Print bytes format in commonly used formats, such as literals in high level languages.""" - - valid_formats = ("py", "c", "js", "asm", "hex") - 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) - 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": True,}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - """Default value for print-format command.""" - args = 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 = [] - - 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)) - - if 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 = "buf {0} {1}".format(asm_type, sdata) - elif args.lang == "hex": - out = binascii.hexlify(gef.memory.read(start_addr, end_addr-start_addr)).decode() - - 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_command -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_command -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 = 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_command -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 = kwargs["arguments"] - if args.breakpoints[0] == -1: - # No breakpoint info needed - bps = [gef.session.pie_breakpoints[x] for x in gef.session.pie_breakpoints] - else: - bps = [gef.session.pie_breakpoints[x] for x in args.breakpoints] - - lines = [] - lines.append("VNum\tNum\tAddr") - lines += [ - f"{x.vbp_num}\t{x.bp_num if x.bp_num else 'N/A'}\t{x.addr}" for x in bps - ] - gef_print("\n".join(lines)) - return - - -@register_command -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 = kwargs["arguments"] - if args.breakpoints[0] == -1: - # no arg, delete all - to_delete = [gef.session.pie_breakpoints[x] for x in gef.session.pie_breakpoints] - self.delete_bp(to_delete) - else: - self.delete_bp([gef.session.pie_breakpoints[x] for x in args.breakpoints]) - return - - - @staticmethod - def delete_bp(breakpoints: List) -> 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_command -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 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(): - 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(e) - gdb.execute("kill") - return - - -@register_command -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(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_command -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(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_command -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: - 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: Tuple[str, 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_command -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() - - has_canary = checksec(get_filepath())["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_command -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 = state["Pid"] - gef_print(f"\tPID {RIGHT_ARROW} {pid} (Name: '{self.get_process_path_of(pid)}', 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_priority_command -class GefThemeCommand(GenericCommand): - """Customize GEF appearance.""" - - _cmdline_ = "theme" - _syntax_ = f"{_cmdline_} [KEY [VALUE]]" - - 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 not setting_name 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) - return - - -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() - return - - def __str__(self) -> str: - return self.name - - def pprint(self) -> None: - res = [] - for _name, _type in self.class_type._fields_: - 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: - """Returns a tuple of (class, instance) if modname!classname exists""" - fpath = self.module_path - spec = importlib.util.spec_from_file_location(fpath.stem, fpath) - 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 - _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_: - _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 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) - 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"} - _structs = {x for x in dir(self.raw) \ - if inspect.isclass(getattr(self.raw, x)) \ - and issubclass(getattr(self.raw, x), ctypes.Structure)} - for entry in (_structs - _invalid): - yield entry - 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 = pathlib.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) -> Optional[Tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"]]: - """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_command -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: - super().__init__(prefix=True) - self["struct_path"] = (str(pathlib.Path(gef.config["gef.tempdir"]) / "structs"), "Path to store/load the structure ctypes files") - 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") - return - - @parse_arguments({"type": "", "address": ""}, {}) - def do_invoke(self, *_: Any, **kwargs: Dict[str, Any]) -> None: - args = kwargs["arguments"] - if not args.type: - gdb.execute("pcustom list") - return - - _, structname = self.explode_type(args.type) - - 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 - 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_command -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) -> 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(structure_name, struct_color) for structure_name in module.values()]) - __filename = Color.colorify(str(module.path), filename_color) - gef_print(f"{RIGHT_ARROW} {__filename} ({__modules})") - return - - -@register_command -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_command -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 = pathlib.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_command -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) - 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 = int(res.split()[2], 0) - res = gdb.execute(f"""p/d {res}""", to_string=True) - res = int(res.split()[2], 0) - return res - -@register_command -class IdaInteractCommand(GenericCommand): - """IDA Interact: set of commands to interact with IDA via a XML RPC service - deployed via the IDA script `ida_gef.py`. It should be noted that this command - can also be used to interact with Binary Ninja (using the script `binja_gef.py`) - using the same interface.""" - - _cmdline_ = "ida-interact" - _syntax_ = f"{_cmdline_} METHOD [ARGS]" - _aliases_ = ["binaryninja-interact", "bn", "binja"] - _example_ = f"\n{_cmdline_} Jump $pc\n{_cmdline_} SetColor $pc ff00ff" - - def __init__(self) -> None: - super().__init__(prefix=False) - self["host"] = ("127.0.0.1", "IP address to use connect to IDA/Binary Ninja script") - self["port"] = (1337, "Port to use connect to IDA/Binary Ninja script") - self["sync_cursor"] = (False, "Enable real-time $pc synchronization") - - self.sock = None - self.version = ("", "") - self.old_bps = set() - return - - def is_target_alive(self, host: str, port: int) -> bool: - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(1) - s.connect((host, port)) - s.close() - except OSError: - return False - return True - - def connect(self, host: Optional[str] = None, port: Optional[int] = None) -> None: - """Connect to the XML-RPC service.""" - host = host or self["host"] - port = port or self["port"] - - try: - sock = xmlrpclib.ServerProxy(f"http://{host}:{port:d}") - gef_on_stop_hook(ida_synchronize_handler) - gef_on_continue_hook(ida_synchronize_handler) - self.version = sock.version() - except ConnectionRefusedError: - err(f"Failed to connect to '{host}:{port:d}'") - sock = None - self.sock = sock - return - - def disconnect(self) -> None: - gef_on_stop_unhook(ida_synchronize_handler) - gef_on_continue_unhook(ida_synchronize_handler) - self.sock = None - return - - @deprecated("") - def do_invoke(self, argv: List[str]) -> None: - def parsed_arglist(arglist: List[str]) -> List[str]: - args = [] - for arg in arglist: - try: - # try to solve the argument using gdb - argval = gdb.parse_and_eval(arg) - argval.fetch_lazy() - # check if value is addressable - argval = int(argval) if argval.address is None else int(argval.address) - # if the bin is PIE, we need to subtract the base address - if is_pie(get_filepath()) and main_base_address <= argval < main_end_address: - argval -= main_base_address - args.append(f"{argval:#x}") - except Exception: - # if gdb can't parse the value, let ida deal with it - args.append(arg) - return args - - if self.sock is None: - # trying to reconnect - self.connect() - if self.sock is None: - self.disconnect() - return - - if len(argv) == 0 or argv[0] in ("-h", "--help"): - method_name = argv[1] if len(argv) > 1 else None - self.usage(method_name) - return - - method_name = argv[0].lower() - if method_name == "version": - self.version = self.sock.version() - info(f"Enhancing {Color.greenify('gef')} with {Color.redify(self.version[0])} " - f"(SDK {Color.yellowify(self.version[1])})") - return - - if not is_alive(): - main_base_address = main_end_address = 0 - else: - vmmap = gef.memory.maps - main_base_address = min([x.page_start for x in vmmap if x.realpath == get_filepath()]) - main_end_address = max([x.page_end for x in vmmap if x.realpath == get_filepath()]) - - try: - if method_name == "sync": - self.synchronize() - else: - method = getattr(self.sock, method_name) - if len(argv) > 1: - args = parsed_arglist(argv[1:]) - res = method(*args) - else: - res = method() - - if method_name == "importstruct": - self.import_structures(res) - else: - gef_print(str(res)) - - if self["sync_cursor"] is True: - jump = getattr(self.sock, "jump") - jump(hex(gef.arch.pc-main_base_address),) - - except OSError: - self.disconnect() - return - - def synchronize(self) -> None: - """Submit all active breakpoint addresses to IDA/BN.""" - pc = gef.arch.pc - vmmap = gef.memory.maps - base_address = min([x.page_start for x in vmmap if x.path == get_filepath()]) - end_address = max([x.page_end for x in vmmap if x.path == get_filepath()]) - if not (base_address <= pc < end_address): - # do not sync in library - return - - breakpoints = gdb.breakpoints() or [] - gdb_bps = set() - for bp in breakpoints: - if bp.enabled and not bp.temporary: - if bp.location[0] == "*": # if it's an address i.e. location starts with "*" - addr = parse_address(bp.location[1:]) - else: # it is a symbol - addr = int(gdb.parse_and_eval(bp.location).address) - if not (base_address <= addr < end_address): - continue - gdb_bps.add(addr - base_address) - - added = gdb_bps - self.old_bps - removed = self.old_bps - gdb_bps - self.old_bps = gdb_bps - - try: - # it is possible that the server was stopped between now and the last sync - rc = self.sock.sync(f"{pc-base_address:#x}", list(added), list(removed)) - except ConnectionRefusedError: - self.disconnect() - return - - ida_added, ida_removed = rc - - # add new bp from IDA - for new_bp in ida_added: - location = base_address + new_bp - gdb.Breakpoint(f"*{location:#x}", type=gdb.BP_BREAKPOINT) - self.old_bps.add(location) - - # and remove the old ones - breakpoints = gdb.breakpoints() or [] - for bp in breakpoints: - if bp.enabled and not bp.temporary: - if bp.location[0] == "*": # if it's an address i.e. location starts with "*" - addr = parse_address(bp.location[1:]) - else: # it is a symbol - addr = int(gdb.parse_and_eval(bp.location).address) - - if not (base_address <= addr < end_address): - continue - - if (addr - base_address) in ida_removed: - if (addr - base_address) in self.old_bps: - self.old_bps.remove((addr - base_address)) - bp.delete() - return - - def usage(self, meth: Optional[str] = None) -> None: - if self.sock is None: - return - - if meth is not None: - gef_print(titlify(meth)) - gef_print(self.sock.system.methodHelp(meth)) - return - - info("Listing available methods and syntax examples: ") - for m in self.sock.system.listMethods(): - if m.startswith("system."): continue - gef_print(titlify(m)) - gef_print(self.sock.system.methodHelp(m)) - return - - def import_structures(self, structs: Dict[str, List[Tuple[int, str, int]]]) -> None: - if self.version[0] != "IDA Pro": - return - - path = gef.config["pcustom.struct_path"] - if path is None: - return - - if not os.path.isdir(path): - gef_makedirs(path) - - for struct_name in structs: - fullpath = pathlib.Path(path) / f"{struct_name}.py" - with fullpath.open("w") as f: - f.write("from ctypes import *\n\n") - f.write("class ") - f.write(struct_name) - f.write("(Structure):\n") - f.write(" _fields_ = [\n") - for _, name, size in structs[struct_name]: - name = bytes(name, encoding="utf-8") - if size == 1: csize = "c_uint8" - elif size == 2: csize = "c_uint16" - elif size == 4: csize = "c_uint32" - elif size == 8: csize = "c_uint64" - else: csize = f"c_byte * {size}" - m = f' (\"{name}\", {csize}),\n' - f.write(m) - f.write("]\n") - ok(f"Success, {len(structs):d} structure{'s' if len(structs) > 1 else ''} imported") - return - - -@register_command -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)}'") - - if haystack == "binary": - haystack = get_filepath() - - if needle == "binary": - needle = get_filepath() - - 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 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 - - 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 = DereferenceCommand.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_command -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"\n{_cmdline_} AAAAAAAA" - f"\n{_cmdline_} 0x555555554000 little stack" - f"\n{_cmdline_} AAAA 0x600000-0x601000") - - 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, Optional[str]]]: - """Search a pattern within a range defined by arguments.""" - _pattern = gef_pybytes(pattern) - step = 0x400 * 0x1000 - 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.error as e: - estr = str(e) - if estr.startswith("Cannot access memory "): - # - # This is a special case where /proc/$pid/maps - # shows virtual memory address with a read bit, - # but it cannot be read directly from userspace. - # - # See: https://github.com/hugsy/gef/issues/674 - # - err(estr) - return [] - else: - raise e - - for match in re.finditer(_pattern, mem): - start = chunk_addr + match.start() - if is_ascii_string(start): - ustr = gef.memory.read_ascii_string(start) - end = start + len(ustr) - else: - ustr = gef_pystring(_pattern) + "[...]" - end = start + len(_pattern) - locations.append((start, end, ustr)) - - 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 not section_name 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 - - 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() - - self.search_pattern(pattern, section_name) - else: - info(f"Searching '{Color.yellowify(pattern)}' in memory") - self.search_pattern(pattern, "") - return - - -@register_command -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: - 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: - 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_command -class ChangePermissionCommand(GenericCommand): - """Change a page permission. By default, it will change it to 7 (RWX).""" - - _cmdline_ = "set-permission" - _syntax_ = (f"{_cmdline_} address [permission]\n" - "\taddress\t\tan address within the memory page for which the permissions should be changed\n" - "\tpermission\ta 3-bit bitmask with read=1, write=2 and execute=4 as integer") - _aliases_ = ["mprotect"] - _example_ = f"{_cmdline_} $sp 7" - - def __init__(self) -> None: - super().__init__(complete=gdb.COMPLETE_LOCATION) - return - - def pre_load(self) -> None: - try: - __import__("keystone") - except ImportError: - msg = "Missing `keystone-engine` package, install with: `pip install keystone-engine`." - raise ImportWarning(msg) - return - - @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: - if len(argv) not in (1, 2): - err("Incorrect syntax") - self.usage() - return - - if len(argv) == 2: - perm = Permission(int(argv[1])) - else: - perm = Permission.ALL - - loc = safe_parse_and_eval(argv[0]) - if loc is None: - err("Invalid address") - return - - loc = int(loc) - sect = process_lookup_address(loc) - if sect is None: - err("Unmapped address") - return - - size = sect.page_end - sect.page_start - original_pc = gef.arch.pc - - info(f"Generating sys_mprotect({sect.page_start:#x}, {size:#x}, " - f"'{perm!s}') stub for arch {get_arch()}") - stub = self.get_stub_by_arch(sect.page_start, size, perm) - if stub is None: - err("Failed to generate mprotect opcodes") - return - - info("Saving original code") - original_code = gef.memory.read(original_pc, len(stub)) - - bp_loc = f"*{original_pc + len(stub):#x}" - info(f"Setting a restore breakpoint at {bp_loc}") - ChangePermissionBreakpoint(bp_loc, original_code, original_pc) - - info(f"Overwriting current memory at {loc:#x} ({len(stub)} bytes)") - gef.memory.write(original_pc, stub, len(stub)) - - info("Resuming execution") - gdb.execute("continue") - return - - def get_stub_by_arch(self, addr: int, size: int, perm: Permission) -> Union[str, bytearray, None]: - code = gef.arch.mprotect_asm(addr, size, perm) - arch, mode = get_keystone_arch() - raw_insns = keystone_assemble(code, arch, mode, raw=True) - return raw_insns - - -@register_command -class UnicornEmulateCommand(GenericCommand): - """Use Unicorn-Engine to emulate the behavior of the binary, without affecting the GDB runtime. - By default the command will emulate only the next instruction, but location and number of - instruction can be changed via arguments to the command line. By default, it will emulate - the next instruction from current PC.""" - - _cmdline_ = "unicorn-emulate" - _syntax_ = (f"{_cmdline_} [--start LOCATION] [--until LOCATION] [--skip-emulation] [--output-file PATH] [NB_INSTRUCTION]" - "\n\t--start LOCATION specifies the start address of the emulated run (default $pc)." - "\t--until LOCATION specifies the end address of the emulated run." - "\t--skip-emulation\t do not execute the script once generated." - "\t--output-file /PATH/TO/SCRIPT.py writes the persistent Unicorn script into this file." - "\tNB_INSTRUCTION indicates the number of instructions to execute" - "\nAdditional options can be setup via `gef config unicorn-emulate`") - _aliases_ = ["emulate",] - _example_ = f"{_cmdline_} --start $pc 10 --output-file /tmp/my-gef-emulation.py" - - def __init__(self) -> None: - super().__init__(complete=gdb.COMPLETE_LOCATION) - self["verbose"] = (False, "Set unicorn-engine in verbose mode") - self["show_disassembly"] = (False, "Show every instruction executed") - return - - def pre_load(self) -> None: - try: - __import__("unicorn") - except ImportError: - msg = "Missing `unicorn` package for Python. Install with `pip install unicorn`." - raise ImportWarning(msg) - - try: - __import__("capstone") - except ImportError: - msg = "Missing `capstone` package for Python. Install with `pip install capstone`." - raise ImportWarning(msg) - return - - @only_if_gdb_running - @parse_arguments({"nb": 1}, {"--start": "", "--until": "", "--skip-emulation": True, "--output-file": ""}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = kwargs["arguments"] - start_address = parse_address(str(args.start or gef.arch.pc)) - end_address = parse_address(str(args.until or self.get_unicorn_end_addr(start_address, args.nb))) - self.run_unicorn(start_address, end_address, skip_emulation=args.skip_emulation, to_file=args.output_file) - return - - def get_unicorn_end_addr(self, start_addr: int, nb: int) -> int: - dis = list(gef_disassemble(start_addr, nb + 1)) - last_insn = dis[-1] - return last_insn.address - - def run_unicorn(self, start_insn_addr: int, end_insn_addr: int, **kwargs: Any) -> None: - verbose = self["verbose"] or False - skip_emulation = kwargs.get("skip_emulation", False) - arch, mode = get_unicorn_arch(to_string=True) - unicorn_registers = get_unicorn_registers(to_string=True) - cs_arch, cs_mode = get_capstone_arch(to_string=True) - fname = gef.session.file.name - to_file = kwargs.get("to_file", None) - emulate_segmentation_block = "" - context_segmentation_block = "" - - if to_file: - tmp_filename = to_file - to_file = open(to_file, "w") - tmp_fd = to_file.fileno() - else: - tmp_fd, tmp_filename = tempfile.mkstemp(suffix=".py", prefix="gef-uc-") - - if is_x86(): - # need to handle segmentation (and pagination) via MSR - emulate_segmentation_block = """ -# from https://github.com/unicorn-engine/unicorn/blob/master/tests/regress/x86_64_msr.py -SCRATCH_ADDR = 0xf000 -SEGMENT_FS_ADDR = 0x5000 -SEGMENT_GS_ADDR = 0x6000 -FSMSR = 0xC0000100 -GSMSR = 0xC0000101 - -def set_msr(uc, msr, value, scratch=SCRATCH_ADDR): - buf = b"\\x0f\\x30" # x86: wrmsr - uc.mem_map(scratch, 0x1000) - uc.mem_write(scratch, buf) - uc.reg_write(unicorn.x86_const.UC_X86_REG_RAX, value & 0xFFFFFFFF) - uc.reg_write(unicorn.x86_const.UC_X86_REG_RDX, (value >> 32) & 0xFFFFFFFF) - uc.reg_write(unicorn.x86_const.UC_X86_REG_RCX, msr & 0xFFFFFFFF) - uc.emu_start(scratch, scratch+len(buf), count=1) - uc.mem_unmap(scratch, 0x1000) - return - -def set_gs(uc, addr): return set_msr(uc, GSMSR, addr) -def set_fs(uc, addr): return set_msr(uc, FSMSR, addr) - -""" - - context_segmentation_block = """ - emu.mem_map(SEGMENT_FS_ADDR-0x1000, 0x3000) - set_fs(emu, SEGMENT_FS_ADDR) - set_gs(emu, SEGMENT_GS_ADDR) -""" - - content = """#!{pythonbin} -i -# -# Emulation script for "{fname}" from {start:#x} to {end:#x} -# -# Powered by gef, unicorn-engine, and capstone-engine -# -# @_hugsy_ -# -import collections -import capstone, unicorn - -registers = collections.OrderedDict(sorted({{{regs}}}.items(), key=lambda t: t[0])) -uc = None -verbose = {verbose} -syscall_register = "{syscall_reg}" - -def disassemble(code, addr): - cs = capstone.Cs({cs_arch}, {cs_mode}) - for i in cs.disasm(code, addr): - return i - -def hook_code(emu, address, size, user_data): - code = emu.mem_read(address, size) - insn = disassemble(code, address) - print(">>> {{:#x}}: {{:s}} {{:s}}".format(insn.address, insn.mnemonic, insn.op_str)) - return - -def code_hook(emu, address, size, user_data): - code = emu.mem_read(address, size) - insn = disassemble(code, address) - print(">>> {{:#x}}: {{:s}} {{:s}}".format(insn.address, insn.mnemonic, insn.op_str)) - return - -def intr_hook(emu, intno, data): - print(" \\-> interrupt={{:d}}".format(intno)) - return - -def syscall_hook(emu, user_data): - sysno = emu.reg_read(registers[syscall_register]) - print(" \\-> syscall={{:d}}".format(sysno)) - return - -def print_regs(emu, regs): - for i, r in enumerate(regs): - print("{{:7s}} = {{:#0{ptrsize}x}} ".format(r, emu.reg_read(regs[r])), end="") - if (i % 4 == 3) or (i == len(regs)-1): print("") - return - -{emu_block} - -def reset(): - emu = unicorn.Uc({arch}, {mode}) - -{context_block} -""".format(pythonbin=PYTHONBIN, fname=fname, start=start_insn_addr, end=end_insn_addr, - regs=",".join([f"'{k.strip()}': {unicorn_registers[k]}" for k in unicorn_registers]), - verbose="True" if verbose else "False", - syscall_reg=gef.arch.syscall_register, - cs_arch=cs_arch, cs_mode=cs_mode, - ptrsize=gef.arch.ptrsize * 2 + 2, # two hex chars per byte plus "0x" prefix - emu_block=emulate_segmentation_block if is_x86() else "", - arch=arch, mode=mode, - context_block=context_segmentation_block if is_x86() else "") - - if verbose: - info("Duplicating registers") - - for r in gef.arch.all_registers: - gregval = gef.arch.register(r) - content += f" emu.reg_write({unicorn_registers[r]}, {gregval:#x})\n" - - vmmap = gef.memory.maps - if not vmmap: - warn("An error occurred when reading memory map.") - return - - if verbose: - info("Duplicating memory map") - - for sect in vmmap: - if sect.path == "[vvar]": - # this section is for GDB only, skip it - continue - - page_start = sect.page_start - page_end = sect.page_end - size = sect.size - perm = sect.permission - - content += f" # Mapping {sect.path}: {page_start:#x}-{page_end:#x}\n" - content += f" emu.mem_map({page_start:#x}, {size:#x}, {perm.value:#o})\n" - - if perm & Permission.READ: - code = gef.memory.read(page_start, size) - loc = f"/tmp/gef-{fname}-{page_start:#x}.raw" - with open(loc, "wb") as f: - f.write(bytes(code)) - - content += f" emu.mem_write({page_start:#x}, open('{loc}', 'rb').read())\n" - content += "\n" - - content += " emu.hook_add(unicorn.UC_HOOK_CODE, code_hook)\n" - content += " emu.hook_add(unicorn.UC_HOOK_INTR, intr_hook)\n" - if is_x86_64(): - content += " emu.hook_add(unicorn.UC_HOOK_INSN, syscall_hook, None, 1, 0, unicorn.x86_const.UC_X86_INS_SYSCALL)\n" - content += " return emu\n" - - content += """ -def emulate(emu, start_addr, end_addr): - print("========================= Initial registers =========================") - print_regs(emu, registers) - - try: - print("========================= Starting emulation =========================") - emu.emu_start(start_addr, end_addr) - except Exception as e: - emu.emu_stop() - print("========================= Emulation failed =========================") - print("[!] Error: {{}}".format(e)) - - print("========================= Final registers =========================") - print_regs(emu, registers) - return - - -uc = reset() -emulate(uc, {start:#x}, {end:#x}) - -# unicorn-engine script generated by gef -""".format(start=start_insn_addr, end=end_insn_addr) - - os.write(tmp_fd, gef_pybytes(content)) - os.close(tmp_fd) - - if kwargs.get("to_file", None): - info(f"Unicorn script generated as '{tmp_filename}'") - os.chmod(tmp_filename, 0o700) - - if skip_emulation: - return - - ok(f"Starting emulation: {start_insn_addr:#x} {RIGHT_ARROW} {end_insn_addr:#x}") - - res = gef_execute_external([PYTHONBIN, tmp_filename], as_list=True) - gef_print("\n".join(res)) - - if not kwargs.get("to_file", None): - os.unlink(tmp_filename) - return - - -@register_command -class RemoteCommand(GenericCommand): - """gef wrapper for the `target remote` command. This command will automatically - download the target binary in the local temporary directory (defaut /tmp) and then - source it. Additionally, it will fetch all the /proc/PID/maps and loads all its - information.""" - - _cmdline_ = "gef-remote" - _syntax_ = f"{_cmdline_} [OPTIONS] TARGET" - _example_ = (f"\n{_cmdline_} --pid 6789 localhost:1234" - f"\n{_cmdline_} --qemu-mode localhost:4444 # when using qemu-user") - - def __init__(self) -> None: - super().__init__(prefix=False) - self.handler_connected = False - self["clean_on_exit"] = (False, "Clean the temporary data downloaded when the session exits.") - return - - @parse_arguments( - {"target": ""}, - {"--update-solib": True, - "--download-everything": True, - "--download-lib": "", - "--is-extended-remote": True, - "--pid": 0, - "--qemu-mode": True}) - 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 = kwargs["arguments"] - if not args.target or ":" not in args.target: - err("A target (HOST:PORT) must always be provided.") - return - - if args.is_extended_remote and not args.pid: - err("A PID (--pid) is required for extended remote debugging") - return - - target = args.target - self.download_all_libs = args.download_everything - - if args.qemu_mode: - # compat layer for qemu-user - self.prepare_qemu_stub(target) - return - - # lazily install handler on first use - if not self.handler_connected: - gef_on_new_hook(self.new_objfile_handler) - self.handler_connected = True - - if not self.connect_target(target, args.is_extended_remote): - return - - pid = args.pid if args.is_extended_remote and args.pid else gef.session.pid - if args.is_extended_remote: - ok(f"Attaching to {pid:d}") - hide_context() - gdb.execute(f"attach {pid:d}") - unhide_context() - - self.setup_remote_environment(pid, args.update_solib) - - if not is_remote_debug(): - err("Failed to establish remote target environment.") - return - - if self.download_all_libs: - vmmap = gef.memory.maps - success = 0 - for sect in vmmap: - if sect.path.startswith("/"): - _file = download_file(sect.path) - if _file is None: - err(f"Failed to download {sect.path}") - else: - success += 1 - - ok(f"Downloaded {success:d} files") - - elif args.download_lib: - _file = download_file(args.download_lib) - if _file is None: - err("Failed to download remote file") - return - - ok(f"Download success: {args.download_lib} {RIGHT_ARROW} {_file}") - - if args.update_solib: - self.refresh_shared_library_path() - - - # refresh the architecture setting - reset_architecture() - gef.session.remote = pid - return - - def new_objfile_handler(self, event: "gdb.Event") -> None: - """Hook that handles new_objfile events, will update remote environment accordingly.""" - if not is_remote_debug(): - return - - if self.download_all_libs and event.new_objfile.filename.startswith("target:"): - remote_lib = event.new_objfile.filename[len("target:"):] - local_lib = download_file(remote_lib, use_cache=True) - if local_lib: - ok(f"Download success: {remote_lib} {RIGHT_ARROW} {local_lib}") - return - - def setup_remote_environment(self, pid: int, update_solib: bool = False) -> None: - """Clone the remote environment locally in the temporary directory. - The command will duplicate the entries in the /proc/ locally and then - source those information into the current gdb context to allow gef to use - all the extra commands as it was local debugging.""" - gdb.execute("reset-cache") - - infos = {} - for i in ("maps", "environ", "cmdline",): - infos[i] = self.load_from_remote_proc(pid, i) - if infos[i] is None: - err(f"Failed to load memory map of '{i}'") - return - - exepath = get_path_from_info_proc() - infos["exe"] = download_file(f"/proc/{pid:d}/exe", use_cache=False, local_name=exepath) - if not os.access(infos["exe"], os.R_OK): - err("Source binary is not readable") - return - - directory = os.path.sep.join([gef.config["gef.tempdir"], str(gef.session.pid)]) - # gdb.execute(f"file {infos['exe']}") - self["root"] = (directory, "Path to store the remote data") - ok(f"Remote information loaded to temporary path '{directory}'") - return - - def connect_target(self, target: str, is_extended_remote: bool) -> bool: - """Connect to remote target and get symbols. To prevent `gef` from requesting information - not fetched just yet, we disable the context disable when connection was successful.""" - hide_context() - try: - cmd = f"target {'extended-remote' if is_extended_remote else 'remote'} {target}" - gdb.execute(cmd) - ok(f"Connected to '{target}'") - ret = True - except Exception as e: - err(f"Failed to connect to {target}: {e}") - ret = False - unhide_context() - return ret - - def load_from_remote_proc(self, pid: int, info: str) -> Optional[str]: - """Download one item from /proc/pid.""" - remote_name = f"/proc/{pid:d}/{info}" - return download_file(remote_name, use_cache=False) - - def refresh_shared_library_path(self) -> None: - dirs = [r for r, d, f in os.walk(self["root"])] - path = ":".join(dirs) - gdb.execute(f"set solib-search-path {path}") - return - - def usage(self) -> None: - h = self._syntax_ - h += "\n\t TARGET (mandatory) specifies the host:port, serial port or tty to connect to.\n" - h += "\t-U will update gdb `solib-search-path` attribute to include the files downloaded from server (default: False).\n" - h += "\t-A will download *ALL* the remote shared libraries and store them in the new environment. " \ - "This command can take a few minutes to complete (default: False).\n" - h += "\t-D LIB will download the remote library called LIB.\n" - h += "\t-E Use 'extended-remote' to connect to the target.\n" - h += "\t-p PID (mandatory if -E is used) specifies PID of the debugged process on gdbserver's end.\n" - h += "\t-q Uses this option when connecting to a Qemu GDBserver.\n" - info(h) - return - - def prepare_qemu_stub(self, target: str) -> None: - global gef - - reset_all_caches() - arch = get_arch() - gef.binary = Elf(minimalist=True) - if arch.startswith("arm"): - gef.binary.e_machine = Elf.Abi.ARM - gef.arch = ARM() - elif arch.startswith("aarch64"): - gef.binary.e_machine = Elf.Abi.AARCH64 - gef.arch = AARCH64() - elif arch.startswith("i386:intel"): - gef.binary.e_machine = Elf.Abi.X86_32 - gef.arch = X86() - elif arch.startswith("i386:x86-64"): - gef.binary.e_machine = Elf.Abi.X86_64 - gef.binary.e_class = Elf.Class.ELF_64_BITS - gef.arch = X86_64() - elif arch.startswith("mips"): - gef.binary.e_machine = Elf.Abi.MIPS - gef.arch = MIPS() - elif arch.startswith("powerpc"): - gef.binary.e_machine = Elf.Abi.POWERPC - gef.arch = PowerPC() - elif arch.startswith("sparc"): - gef.binary.e_machine = Elf.Abi.SPARC - gef.arch = SPARC() - else: - raise RuntimeError(f"unsupported architecture: {arch}") - - ok(f"Setting Qemu-user stub for '{gef.arch.arch}' (memory mapping may be wrong)") - hide_context() - gdb.execute(f"target remote {target}") - unhide_context() - - if gef.session.pid == 1 and "ENABLE=1" in gdb.execute("maintenance packet Qqemu.sstepbits", to_string=True, from_tty=False): - gef.session.qemu_mode = True - reset_all_caches() - info("Note: By using Qemu mode, GEF will display the memory mapping of the Qemu process where the emulated binary resides") - gef.memory.maps - gdb.execute("context") - return - - -@register_command -class NopCommand(GenericCommand): - """Patch the instruction(s) pointed by parameters with NOP. Note: this command is architecture - aware.""" - - _cmdline_ = "nop" - _syntax_ = ("{_cmdline_} [LOCATION] [--nb NUM_BYTES]" - "\n\tLOCATION\taddress/symbol to patch" - "\t--nb NUM_BYTES\tInstead of writing one instruction, patch the specified number of bytes") - _example_ = f"{_cmdline_} $pc" - - def __init__(self) -> None: - super().__init__(complete=gdb.COMPLETE_LOCATION) - return - - def get_insn_size(self, addr: int) -> int: - cur_insn = gef_current_instruction(addr) - next_insn = gef_instruction_n(addr, 2) - return next_insn.address - cur_insn.address - - @parse_arguments({"address": "$pc"}, {"--nb": 0, }) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = kwargs["arguments"] - address = parse_address(args.address) if args.address else gef.arch.pc - number_of_bytes = args.nb or 1 - self.nop_bytes(address, number_of_bytes) - return - - @only_if_gdb_running - def nop_bytes(self, loc: int, num_bytes: int) -> None: - size = self.get_insn_size(loc) if num_bytes == 0 else num_bytes - nops = gef.arch.nop_insn - - if len(nops) > size: - err(f"Cannot patch instruction at {loc:#x} " - f"(nop_size is:{len(nops)}, insn_size is:{size})") - return - - while len(nops) < size: - nops += gef.arch.nop_insn - - if len(nops) != size: - err(f"Cannot patch instruction at {loc:#x} " - "(nop instruction does not evenly fit in requested size)") - return - - ok(f"Patching {size:d} bytes from {format_address(loc)}") - gef.memory.write(loc, nops, size) - - return - - -@register_command -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 = kwargs["arguments"] - loc = args.address if args.address else f"*{gef.arch.pc:#x}" - StubBreakpoint(loc, args.retval) - return - - -@register_command -class CapstoneDisassembleCommand(GenericCommand): - """Use capstone disassembly framework to disassemble code.""" - - _cmdline_ = "capstone-disassemble" - _syntax_ = f"{_cmdline_} [-h] [--show-opcodes] [--length LENGTH] [LOCATION]" - _aliases_ = ["cs-dis"] - _example_ = f"{_cmdline_} --length 50 $pc" - - def pre_load(self) -> None: - try: - __import__("capstone") - except ImportError: - msg = "Missing `capstone` package for Python. Install with `pip install capstone`." - raise ImportWarning(msg) - return - - def __init__(self) -> None: - super().__init__(complete=gdb.COMPLETE_LOCATION) - return - - @only_if_gdb_running - @parse_arguments({("location"): "$pc"}, {("--show-opcodes", "-s"): True, "--length": 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = kwargs["arguments"] - show_opcodes = args.show_opcodes - length = args.length or gef.config["context.nb_lines_code"] - location = parse_address(args.location) - if not location: - info(f"Can't find address for {args.location}") - return - - insns = [] - opcodes_len = 0 - for insn in capstone_disassemble(location, length, skip=length * self.repeat_count, **kwargs): - insns.append(insn) - opcodes_len = max(opcodes_len, len(insn.opcodes)) - - for insn in insns: - insn_fmt = f"{{:{opcodes_len}o}}" if show_opcodes else "{}" - text_insn = insn_fmt.format(insn) - msg = "" - - if insn.address == gef.arch.pc: - msg = Color.colorify(f"{RIGHT_ARROW} {text_insn}", "bold red") - reason = self.capstone_analyze_pc(insn, length)[0] - if reason: - gef_print(msg) - gef_print(reason) - break - else: - msg = f" {text_insn}" - - gef_print(msg) - return - - def capstone_analyze_pc(self, insn: Instruction, nb_insn: int) -> Tuple[bool, str]: - if gef.arch.is_conditional_branch(insn): - is_taken, reason = gef.arch.is_branch_taken(insn) - if is_taken: - reason = f"[Reason: {reason}]" if reason else "" - msg = Color.colorify(f"\tTAKEN {reason}", "bold green") - else: - reason = f"[Reason: !({reason})]" if reason else "" - msg = Color.colorify(f"\tNOT taken {reason}", "bold red") - return (is_taken, msg) - - if gef.arch.is_call(insn): - target_address = int(insn.operands[-1].split()[0], 16) - msg = [] - for i, new_insn in enumerate(capstone_disassemble(target_address, nb_insn)): - msg.append(f" {DOWN_ARROW if i == 0 else ' '} {new_insn!s}") - return (True, "\n".join(msg)) - - return (False, "") - - -@register_command -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_command -class GlibcHeapSetArenaCommand(GenericCommand): - """Display information on a heap chunk.""" - - _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 - def do_invoke(self, argv: List[str]) -> None: - global gef - - if not argv: - ok(f"Current arena set to: '{gef.heap.selected_arena}'") - return - - if is_hex(argv[0]): - new_arena_address = int(argv[0], 16) - else: - new_arena_symbol = safe_parse_and_eval(argv[0]) - if not new_arena_symbol: - err("Invalid symbol for arena") - return - new_arena_address = to_unsigned_long(new_arena_symbol) - - new_arena = GlibcArena( f"*{new_arena_address:#x}") - if new_arena not in gef.heap.arenas: - err("Invalid arena") - return - - gef.heap.selected_arena = new_arena - return - - -@register_command -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_command -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": True, "--number": 1}) - @only_if_gdb_running - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = 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 _ 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 - - -@register_command -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] [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"): True, "--allow-unaligned": True}) - @only_if_gdb_running - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = kwargs["arguments"] - arenas = gef.heap.arenas - for arena in arenas: - self.dump_chunks_arena(arena, print_arena=args.all, allow_unaligned=args.allow_unaligned) - if not args.all: - break - - def dump_chunks_arena(self, arena: GlibcArena, print_arena: bool = False, allow_unaligned: bool = False) -> None: - top_chunk_addr = arena.top - heap_addr = arena.heap_addr(allow_unaligned=allow_unaligned) - if heap_addr is None: - err("Could not find heap for arena") - return - if print_arena: - gef_print(str(arena)) - if arena.is_main_arena(): - self.dump_chunks_heap(heap_addr, top=top_chunk_addr, allow_unaligned=allow_unaligned) - else: - heap_info_structs = arena.get_heap_info_list() - first_heap_info = heap_info_structs.pop(0) - heap_info_t_size = int(arena) - first_heap_info.addr - until = first_heap_info.addr + first_heap_info.size - self.dump_chunks_heap(heap_addr, until=until, top=top_chunk_addr, allow_unaligned=allow_unaligned) - for heap_info in heap_info_structs: - start = heap_info.addr + heap_info_t_size - until = heap_info.addr + heap_info.size - self.dump_chunks_heap(start, until=until, top=top_chunk_addr, allow_unaligned=allow_unaligned) - return - - def dump_chunks_heap(self, start: int, until: Optional[int] = None, top: Optional[int] = None, allow_unaligned: bool = False) -> None: - nb = self["peek_nb_byte"] - chunk_iterator = GlibcChunk(start, from_base=True, allow_unaligned=allow_unaligned) - for chunk in chunk_iterator: - line = str(chunk) - if nb: - line += f"\n [{hexdump(gef.memory.read(chunk.data_address, nb), nb, base=chunk.data_address)}]" - gef_print(line) - - next_chunk_addr = chunk.get_next_chunk_addr() - if until and next_chunk_addr >= until: - break - - if chunk.base_address == top: - gef_print(f"{chunk!s} {LEFT_ARROW} {Color.greenify('top chunk')}") - break - return - - -@register_command -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 GlibcHeapBinsCommand._bin_types_: - gdb.execute(f"heap bins {bin_t}") - return - - bin_t = argv[0] - if bin_t not in GlibcHeapBinsCommand._bin_types_: - self.usage() - return - - gdb.execute(f"heap bins {bin_t}") - return - - @staticmethod - def pprint_bin(arena_addr: str, index: int, _type: str = "") -> int: - arena = GlibcArena(arena_addr) - fw, bk = arena.bin(index) - - if bk == 0x00 and fw == 0x00: - warn("Invalid backward and forward bin pointers(fw==bk==NULL)") - return -1 - - nb_chunk = 0 - head = GlibcChunk(bk, from_base=True).fwd - if fw == head: - return nb_chunk - - ok(f"{_type}bins[{index:d}]: fw={fw:#x}, bk={bk:#x}") - - m = [] - while fw != head: - chunk = GlibcChunk(fw, from_base=True) - m.append(f"{RIGHT_ARROW} {chunk!s}") - fw = chunk.fwd - nb_chunk += 1 - - if m: - gef_print(" ".join(m)) - return nb_chunk - - -@register_command -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 get_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(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 = [] - - # 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 chunk.data_address in chunks: - msg.append(f"{RIGHT_ARROW} [loop detected]") - break - - chunks.add(chunk.data_address) - - next_chunk = chunk.get_fwd_ptr(True) - if next_chunk == 0: - break - - chunk = GlibcChunk(next_chunk) - except gdb.MemoryError: - msg.append(f"{LEFT_ARROW} [Corrupted chunk at {chunk.data_address:#x}]") - break - - if msg: - tcache_empty = False - gef_print(f"Tcachebins[idx={i:d}, size={(i+2)*(gef.arch.ptrsize)*2:#x}] count={count:d} ", end="") - gef_print("".join(msg)) - - if tcache_empty: - gef_print("All tcachebins are empty") - - current_thread.switch() - return - - @staticmethod - def find_tcache() -> 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 - - @staticmethod - def check_thread_ids(tids: List[int]) -> List[int]: - """Check the validity, dedup, and return all valid tids.""" - existing_tids = [t.num for t in gdb.selected_inferior().threads()] - valid_tids = set() - for tid in tids: - try: - tid = int(tid) - except ValueError: - err(f"Invalid thread id {tid:d}") - continue - if tid in existing_tids: - valid_tids.add(tid) - else: - err(f"Unknown thread {tid}") - - return list(valid_tids) - - @staticmethod - def tcachebin(tcache_base: int, i: int) -> Tuple[Optional[GlibcChunk], int]: - """Return the head chunk in tcache[i] and the number of chunks in the bin.""" - if i >= GlibcHeapTcachebinsCommand.TCACHE_MAX_BINS: - err("Incorrect index value, index value must be between 0 and {}-1, given {}".format(GlibcHeapTcachebinsCommand.TCACHE_MAX_BINS, i)) - return None, 0 - - tcache_chunk = GlibcChunk(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 = ( - GlibcHeapTcachebinsCommand.TCACHE_MAX_BINS * 2 + - GlibcHeapTcachebinsCommand.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*GlibcHeapTcachebinsCommand.TCACHE_MAX_BINS + i*gef.arch.ptrsize) - chunk = GlibcChunk(int(chunk)) if chunk else None - return chunk, count - - -@register_command -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 = 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.get_chunk_size()) != i: - gef_print("[incorrect fastbin_index] ", end="") - - chunks.add(chunk.data_address) - - next_chunk = chunk.get_fwd_ptr(True) - if next_chunk == 0: - break - - chunk = GlibcChunk(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_command -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 = kwargs["arguments"] - if gef.heap.main_arena is None: - 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}")) - nb_chunk = GlibcHeapBinsCommand.pprint_bin(f"*{arena_addr}", 0, "unsorted_") - if nb_chunk >= 0: - info(f"Found {nb_chunk:d} chunks in unsorted bin.") - return - - -@register_command -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 = kwargs["arguments"] - if not gef.heap.main_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"Small Bins for arena at {arena_addr}")) - bins = {} - for i in range(1, 63): - nb_chunk = GlibcHeapBinsCommand.pprint_bin(f"*{arena_addr}", i, "small_") - 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} small non-empty bins.") - return - - -@register_command -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 = kwargs["arguments"] - if gef.heap.main_arena is None: - 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 = {} - for i in range(63, 126): - nb_chunk = GlibcHeapBinsCommand.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_command -class SolveKernelSymbolCommand(GenericCommand): - """Solve kernel symbols from kallsyms table.""" - - _cmdline_ = "ksymaddr" - _syntax_ = f"{_cmdline_} SymbolToSearch" - _example_ = f"{_cmdline_} prepare_creds" - - @parse_arguments({"symbol": ""}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - def hex_to_int(num): - try: - return int(num, 16) - except ValueError: - return 0 - args = kwargs["arguments"] - if not args.symbol: - self.usage() - return - sym = args.symbol - with open("/proc/kallsyms", "r") as f: - syms = [line.strip().split(" ", 2) for line in f] - matches = [(hex_to_int(addr), sym_t, " ".join(name.split())) for addr, sym_t, name in syms if sym in name] - for addr, sym_t, name in matches: - if sym == name.split()[0]: - ok(f"Found matching symbol for '{name}' at {addr:#x} (type={sym_t})") - else: - warn(f"Found partial match for '{sym}' at {addr:#x} (type={sym_t}): {name}") - if not matches: - err(f"No match for '{sym}'") - elif matches[0][0] == 0: - err("Check that you have the correct permissions to view kernel symbol addresses") - return - - -@register_command -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 = kwargs["arguments"] - if args.registers and args.registers[0]: - required_regs = set(args.registers) - valid_regs = [reg for reg in gef.arch.all_registers if reg in required_regs] - if valid_regs: - regs = valid_regs - invalid_regs = [reg for reg in required_regs if reg not in valid_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)) - old_value = ContextCommand.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_command -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_command -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 - - self.search_shellcode(argv) - return - - def search_shellcode(self, search_options: List) -> None: - # API : http://shell-storm.org/shellcode/ - args = "*".join(search_options) - - 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_command -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}}.php" - - 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...") - tempdir = gef.config["gef.tempdir"] - fd, fname = tempfile.mkstemp(suffix=".txt", prefix="sc-", text=True, dir=tempdir) - shellcode = res.splitlines()[7:-11] - shellcode = b"\n".join(shellcode).replace(b""", b'"') - os.write(fd, shellcode) - os.close(fd) - ok(f"Shellcode written to '{fname}'") - return - - -@register_command -class RopperCommand(GenericCommand): - """Ropper (https://scoding.de/ropper/) plugin.""" - - _cmdline_ = "ropper" - _syntax_ = f"{_cmdline_} [ROPPER_OPTIONS]" - - def __init__(self) -> None: - super().__init__(complete=gdb.COMPLETE_NONE) - return - - def pre_load(self) -> None: - try: - __import__("ropper") - except ImportError: - msg = "Missing `ropper` package for Python, install with: `pip install ropper`." - raise ImportWarning(msg) - return - - @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: - ropper = sys.modules["ropper"] - if "--file" not in argv: - path = get_filepath() - sect = next(filter(lambda x: x.path == path, gef.memory.maps)) - argv.append("--file") - argv.append(path) - argv.append("-I") - argv.append(f"{sect.page_start:#x}") - - import readline - # ropper set up own autocompleter after which gdb/gef autocomplete don't work - old_completer_delims = readline.get_completer_delims() - old_completer = readline.get_completer() - try: - ropper.start(argv) - except RuntimeWarning: - return - readline.set_completer(old_completer) - readline.set_completer_delims(old_completer_delims) - return - - -@register_command -class AssembleCommand(GenericCommand): - """Inline code assemble. Architecture can be set in GEF runtime config. """ - - _cmdline_ = "assemble" - _syntax_ = f"{_cmdline_} [-h] [--list-archs] [--mode MODE] [--arch ARCH] [--overwrite-location LOCATION] [--endian ENDIAN] [--as-shellcode] instruction;[instruction;...instruction;])" - _aliases_ = ["asm",] - _example_ = (f"\n{_cmdline_} -a x86 -m 32 nop ; nop ; inc eax ; int3" - f"\n{_cmdline_} -a arm -m arm add r0, r0, 1") - - valid_arch_modes = { - # Format: ARCH = [MODES] with MODE = (NAME, HAS_LITTLE_ENDIAN, HAS_BIG_ENDIAN) - "ARM": [("ARM", True, True), ("THUMB", True, True), - ("ARMV8", True, True), ("THUMBV8", True, True)], - "ARM64": [("0", True, False)], - "MIPS": [("MIPS32", True, True), ("MIPS64", True, True)], - "PPC": [("PPC32", False, True), ("PPC64", True, True)], - "SPARC": [("SPARC32", True, True), ("SPARC64", False, True)], - "SYSTEMZ": [("SYSTEMZ", True, True)], - "X86": [("16", True, False), ("32", True, False), - ("64", True, False)] - } - valid_archs = valid_arch_modes.keys() - valid_modes = [_ for sublist in valid_arch_modes.values() for _ in sublist] - - def __init__(self) -> None: - super().__init__() - self["default_architecture"] = ("X86", "Specify the default architecture to use when assembling") - self["default_mode"] = ("64", "Specify the default architecture to use when assembling") - return - - def pre_load(self) -> None: - try: - __import__("keystone") - except ImportError: - msg = "Missing `keystone-engine` package for Python, install with: `pip install keystone-engine`." - raise ImportWarning(msg) - return - - def usage(self) -> None: - super().usage() - gef_print("") - self.list_archs() - return - - def list_archs(self) -> None: - gef_print("Available architectures/modes (with endianness):") - # for updates, see https://github.com/keystone-engine/keystone/blob/master/include/keystone/keystone.h - for arch in self.valid_arch_modes: - gef_print(f"- {arch}") - for mode, le, be in self.valid_arch_modes[arch]: - if le and be: - endianness = "little, big" - elif le: - endianness = "little" - elif be: - endianness = "big" - gef_print(f" * {mode:<7} ({endianness})") - return - - @parse_arguments({"instructions": [""]}, {"--mode": "", "--arch": "", "--overwrite-location": 0, "--endian": "little", "--list-archs": True, "--as-shellcode": True}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - arch_s, mode_s, endian_s = self["default_architecture"], self["default_mode"], "" - - args = kwargs["arguments"] - if args.list_archs: - self.list_archs() - return - - if not args.instructions: - err("No instruction given.") - return - - if is_alive(): - arch_s, mode_s = gef.arch.arch, gef.arch.mode - endian_s = "big" if gef.arch.endianness == Endianness.BIG_ENDIAN else "" - - if args.arch: - arch_s = args.arch - arch_s = arch_s.upper() - - if args.mode: - mode_s = args.mode - mode_s = mode_s.upper() - - if args.endian == "big": - endian_s = "big" - endian_s = endian_s.upper() - - if arch_s not in self.valid_arch_modes: - raise AttributeError(f"invalid arch '{arch_s}'") - - valid_modes = self.valid_arch_modes[arch_s] - try: - mode_idx = [m[0] for m in valid_modes].index(mode_s) - except ValueError: - raise AttributeError(f"invalid mode '{mode_s}' for arch '{arch_s}'") - - if endian_s == "little" and not valid_modes[mode_idx][1] or endian_s == "big" and not valid_modes[mode_idx][2]: - raise AttributeError(f"invalid endianness '{endian_s}' for arch/mode '{arch_s}:{mode_s}'") - - arch, mode = get_keystone_arch(arch=arch_s, mode=mode_s, endian=endian_s) - insns = [x.strip() for x in " ".join(args.instructions).split(";") if x] - info(f"Assembling {len(insns)} instruction(s) for {arch_s}:{mode_s}") - - if args.as_shellcode: - gef_print("""sc="" """) - - raw = b"" - for insn in insns: - res = keystone_assemble(insn, arch, mode, raw=True) - if res is None: - gef_print("(Invalid)") - continue - - if args.overwrite_location: - raw += res - continue - - s = binascii.hexlify(res) - res = b"\\x" + b"\\x".join([s[i:i + 2] for i in range(0, len(s), 2)]) - res = res.decode("utf-8") - - if args.as_shellcode: - res = f"""sc+="{res}" """ - - gef_print(f"{res!s:60s} # {insn}") - - if args.overwrite_location: - l = len(raw) - info(f"Overwriting {l:d} bytes at {format_address(args.overwrite_location)}") - gef.memory.write(args.overwrite_location, raw, l) - return - - -@register_command -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": True, "--smart-scan": True}) - def do_invoke(self, _: List, **kwargs: Any) -> None: - args = 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_command -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 = kwargs["arguments"] - - if is_qemu_system(): - err("Unsupported") - return - - filename = args.filename or get_filepath() - if filename is None: - return - - elf = get_elf_headers(filename) - if elf is None: - 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", "{0} ({0:#x})".format(elf.e_ehsize)), - ("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(" [{:>2s}] {:12s} {:>8s} {:>10s} {:>10s} {:>8s} {:>8s} {:5s} {:>8s}".format( - "#", "Type", "Offset", "Virtaddr", "Physaddr", "FileSiz", "MemSiz", "Flags", "Align")) - - 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(" [{:2d}] {:12s} {:#8x} {:#10x} {:#10x} {:#8x} {:#8x} {:5s} {:#8x}".format( - i, p_type, p.p_offset, p.p_vaddr, p.p_paddr, p.p_filesz, p.p_memsz, p_flags, p.p_align)) - - gef_print("") - gef_print(titlify("Section Header")) - gef_print(" [{:>2s}] {:20s} {:>15s} {:>10s} {:>8s} {:>8s} {:>8s} {:5s} {:4s} {:4s} {:>8s}".format( - "#", "Name", "Type", "Address", "Offset", "Size", "EntSiz", "Flags", "Link", "Info", "Align")) - - 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_command -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() - - # 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_command -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 = kwargs["arguments"] - if not args.name: - err("Missing name for breakpoint") - self.usage() - return - - NamedBreakpoint(args.address, args.name) - return - - -@register_command -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, Optional[int]] = {} - - 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_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, "Show libc function call args description") - self["libc_args_path"] = ("", "Path to libc function call args json files, provided via gef-extras") - - if "capstone" in list(sys.modules.keys()): - self["use_capstone"] = (False, "Use capstone as disassembler in the code pane (instead of GDB)") - - self.layout_mapping = { - "legend": (self.show_legend, None), - "regs": (self.context_regs, None), - "stack": (self.context_stack, None), - "code": (self.context_code, None), - "args": (self.context_args, None), - "memory": (self.context_memory, None), - "source": (self.context_source, None), - "trace": (self.context_trace, None), - "threads": (self.context_threads, None), - "extra": (self.context_additional_information, None), - } - 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 - str_color = gef.config["theme.dereference_string"] - code_addr_color = gef.config["theme.address_code"] - stack_addr_color = gef.config["theme.address_stack"] - heap_addr_color = gef.config["theme.address_heap"] - changed_register_color = gef.config["theme.registers_value_changed"] - - gef_print("[ Legend: {} | {} | {} | {} | {} ]".format(Color.colorify("Modified register", changed_register_color), - Color.colorify("Code", code_addr_color), - Color.colorify("Heap", heap_addr_color), - Color.colorify("Stack", stack_addr_color), - Color.colorify("String", str_color))) - 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) - - for section in current_layout: - if section[0] == "-": - continue - - try: - display_pane_function, pane_title_function = self.layout_mapping[section] - 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)) - - self.context_title("") - - if self["clear_screen"] and len(argv) == 0: - clear_screen(redirect) - - if redirect and os.access(redirect, os.W_OK): - disable_redirect_output() - return - - def context_title(self, m: Optional[str]) -> 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 = "" - title += Color.colorify("{:{padd}<{width}} ".format("", - width=max(self.tty_columns - trail_len, 0), - padd=HORIZONTAL_LINE), - line_color) - title += Color.colorify(m, msg_color) - title += Color.colorify(" {:{padd}<4}".format("", padd=HORIZONTAL_LINE), - line_color) - gef_print(title) - return - - def context_regs(self) -> None: - self.context_title("registers") - ignored_registers = set(self["ignore_registers"].split()) - - if self["show_registers_raw"] is False: - regs = set(gef.arch.all_registers) - printable_registers = " ".join(list(regs - ignored_registers)) - gdb.execute(f"registers {printable_registers}") - return - - widest = l = max(map(len, gef.arch.all_registers)) - l += 5 - l += gef.arch.ptrsize * 2 - nb = get_terminal_size()[1] // l - 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) - 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"] - use_capstone = "use_capstone" in self and self["use_capstone"] - 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 [] - 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: - instruction_iterator = capstone_disassemble if use_capstone else gef_disassemble - - for insn in 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: - target = int(target, 0) - except TypeError: # Already an int - pass - except ValueError: - # If the operand isn't an address right now we can't parse it - continue - for i, tinsn in enumerate(instruction_iterator(target, 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.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 = [] - - for i, f in enumerate(symbol.type.fields()): - _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() -> Optional[int]: - pc = gef.arch.pc - try: - block = gdb.block_for_pc(pc) - block_start = block.start if block else gdb_get_nth_previous_instruction_address(pc, 5) - except RuntimeError: - block_start = gdb_get_nth_previous_instruction_address(pc, 5) - return block_start - - parameter_set = set() - pc = gef.arch.pc - block_start = __get_current_block_start_address() - if not block_start: - return - use_capstone = "use_capstone" in self and self["use_capstone"] - instruction_iterator = capstone_disassemble if use_capstone else gef_disassemble - function_parameters = gef.arch.function_parameters - arg_key_color = gef.config["theme.registers_register_name"] - - for insn in 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) - - nb_argument = None - _arch_mode = f"{gef.arch.arch.lower()}_{gef.arch.mode}" - _function_name = None - if function_name.endswith("@plt"): - _function_name = function_name.split("@")[0] - try: - nb_argument = len(gef.ui.highlight_table[_arch_mode][_function_name]) - except KeyError: - pass - - if not nb_argument: - 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)) - try: - args.append("{} = {} (def: {})".format(Color.colorify(_key, arg_key_color), _values, - gef.ui.highlight_table[_arch_mode][_function_name][_key])) - except KeyError: - 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 = symtab.fullname() - with open(fpath, "r") as f: - lines = [l.rstrip() for l in f.readlines()] - - 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"] - - nb_line = self["nb_lines_code"] - fn = symtab.filename - if len(fn) > 20: - fn = f"{fn[:15]}[...]{os.path.splitext(fn)[1]}" - 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 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.selected_frame() - current_frame = gdb.newest_frame() - frames = [current_frame] - while current_frame != orig_frame: - current_frame = current_frame.older() - frames.append(current_frame) - - nb_backtrace_before = self["nb_lines_backtrace_before"] - level = max(len(frames) - nb_backtrace_before - 1, 0) - current_frame = frames[level] - - while current_frame: - current_frame.select() - if not current_frame.is_valid(): - continue - - pc = 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 [] - m = "{}({})".format(Color.greenify(name), - ", ".join(["{}={!s}".format(Color.yellowify(x.sym), - x.sym.value(current_frame)) for x in frame_args])) - items.append(m) - else: - try: - insn = next(gef_disassemble(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)}")) - - gef_print("[{}] {}".format(Color.colorify(f"#{level}", "bold green" if current_frame == orig_frame else "bold pink"), - RIGHT_ARROW.join(items))) - current_frame = current_frame.older() - level += 1 - nb_backtrace -= 1 - if nb_backtrace == 0: - break - - orig_frame.select() - return - - def context_threads(self) -> None: - def reason() -> str: - res = gdb.execute("info program", to_string=True).splitlines() - if not res: - return "NOT RUNNING" - - for line in res: - 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(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_command -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_command -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_command -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_command -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_command -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_command -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: Optional[str] = None - self.__last_target = "$sp" - return - - @only_if_gdb_running - @parse_arguments({"address": "",}, {("--reverse", "-r"): True, ("--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 = 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), - } - - r, l = formats[arrange_as] - fmt_str = f"{{base}}{VERTICAL_LINE}+{{offset:#06x}} {{sym}}{{val:#0{l*2+2}x}} {{text}}" - fmt_pack = f"{endianness!s}{r}" - lines = [] - - i = 0 - text = "" - while i < length: - cur_addr = start_addr + (i + offset) * l - sym = gdb_get_location_from_symbol(cur_addr) - sym = "<{:s}+{:04x}> ".format(*sym) if sym else "" - mem = gef.memory.read(cur_addr, l) - 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) * l, sym=sym, val=val, text=text)) - i += 1 - - return lines - - -@register_command -class HexdumpQwordCommand(HexdumpCommand): - """Display SIZE lines of hexdump as QWORD from the memory location pointed by ADDRESS.""" - - _cmdline_ = "hexdump qword" - _syntax_ = f"{_cmdline_} [ADDRESS] [[L][SIZE]] [REVERSE]" - _example_ = f"{_cmdline_} qword $rsp L16 REVERSE" - - def __init__(self) -> None: - super().__init__() - self.format = "qword" - return - - -@register_command -class HexdumpDwordCommand(HexdumpCommand): - """Display SIZE lines of hexdump as DWORD from the memory location pointed by ADDRESS.""" - - _cmdline_ = "hexdump dword" - _syntax_ = f"{_cmdline_} [ADDRESS] [[L][SIZE]] [REVERSE]" - _example_ = f"{_cmdline_} $esp L16 REVERSE" - - def __init__(self) -> None: - super().__init__() - self.format = "dword" - return - - -@register_command -class HexdumpWordCommand(HexdumpCommand): - """Display SIZE lines of hexdump as WORD from the memory location pointed by ADDRESS.""" - - _cmdline_ = "hexdump word" - _syntax_ = f"{_cmdline_} [ADDRESS] [[L][SIZE]] [REVERSE]" - _example_ = f"{_cmdline_} $esp L16 REVERSE" - - def __init__(self) -> None: - super().__init__() - self.format = "word" - return - - -@register_command -class HexdumpByteCommand(HexdumpCommand): - """Display SIZE lines of hexdump as BYTE from the memory location pointed by ADDRESS.""" - - _cmdline_ = "hexdump byte" - _syntax_ = f"{_cmdline_} [ADDRESS] [[L][SIZE]] [REVERSE]" - _example_ = f"{_cmdline_} $rsp L16" - - def __init__(self) -> None: - super().__init__() - self.format = "byte" - return - - -@register_command -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: Optional[str] = None - return - - @only_if_gdb_running - @parse_arguments({"location": "", "values": ["", ]}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = 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] - - d = str(gef.arch.endianness) - for value in args.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_command -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_command -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_command -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_command -class PatchByteCommand(PatchCommand): - """Write specified WORD 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_command -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\"" - - @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: - argc = len(argv) - if argc != 2: - self.usage() - return - - location, s = argv[0:2] - addr = align_address(parse_address(location)) - - try: - s = codecs.escape_decode(s)[0] - except binascii.Error: - gef_print(f"Could not decode '\\xXX' encoded string \"{s}\"") - return - - gef.memory.write(addr, s, len(s)) - return - - -@lru_cache() -def dereference_from(addr: int) -> List[str]: - if not is_alive(): - return [format_address(addr),] - - 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(int(addr))) - 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) > 50: - txt = Color.colorify(f'"{s[:50]}[...]"', string_color) - else: - txt = Color.colorify(f'"{s}"', string_color) - - msg.append(txt) - break - - # if not able to parse cleanly, simply display and break - val = "{:#0{ma}x}".format(int(deref & 0xFFFFFFFFFFFFFFFF), ma=(gef.arch.ptrsize * 2 + 2)) - msg.append(val) - break - - return msg - - -@register_command -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) - l = "" - addr_l = format_address(int(addrs[0], 16)) - l += "{}{}{:+#07x}: {:{ma}s}".format(Color.colorify(addr_l, base_address_color), - VERTICAL_LINE, base_offset+offset, - sep.join(addrs[1:]), ma=(memalign*2 + 2)) - - 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))}" - l += Color.colorify(m, registers_color) - - offset += memalign - return l - - @only_if_gdb_running - @parse_arguments({"address": "$sp"}, {("-r", "--reference"): "", ("-l", "--length"): 10}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = 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: - from_insnum = nb * (self.repeat_count + 1) - 1 - to_insnum = self.repeat_count * nb - 1 - insnum_step = -1 - else: - from_insnum = 0 + self.repeat_count * nb - to_insnum = nb * (self.repeat_count + 1) - insnum_step = 1 - - start_address = align_address(target_addr) - base_offset = start_address - align_address(ref_addr) - - for i in range(from_insnum, to_insnum, insnum_step): - gef_print(DereferenceCommand.pprint_dereferenced(start_address, i, base_offset)) - - return - - -@register_command -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) - 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_command -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_command -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 - def do_invoke(self, argv: List[str]) -> None: - vmmap = gef.memory.maps - if not vmmap: - err("No address mapping information found") - return - - 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)) - - for entry in vmmap: - if not argv: - self.print_entry(entry) - continue - if argv[0] in entry.path: - self.print_entry(entry) - elif self.is_integer(argv[0]): - addr = int(argv[0], 0) - if addr >= entry.page_start and addr < entry.page_end: - self.print_entry(entry) - return - - 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"] - - l = [ - 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: - l.append(Color.colorify(str(entry.permission), "underline " + line_color)) - else: - l.append(Color.colorify(str(entry.permission), line_color)) - - l.append(Color.colorify(entry.path, line_color)) - line = " ".join(l) - - gef_print(line) - return - - def show_legend(self) -> None: - code_addr_color = gef.config["theme.address_code"] - stack_addr_color = gef.config["theme.address_stack"] - heap_addr_color = gef.config["theme.address_heap"] - - gef_print("[ Legend: {} | {} | {} ]".format(Color.colorify("Code", code_addr_color), - Color.colorify("Heap", heap_addr_color), - Color.colorify("Stack", stack_addr_color) - )) - return - - def is_integer(self, n: str) -> bool: - try: - int(n, 0) - except ValueError: - return False - return True - - -@register_command -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 - - l = [ - format_address(xfile.zone_start), - format_address(xfile.zone_end), - f"{xfile.name:<21s}", - xfile.filename, - ] - gef_print(" ".join(l)) - return - - -@register_command -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_command -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_command -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_command -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_command -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=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_command -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_command -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" - - @parse_arguments({"length": 0}, {("-n", "--n"): 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = 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_command -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"\n{_cmdline_} $pc" - f"\n{_cmdline_} 0x61616164" - f"\n{_cmdline_} aaab") - _aliases_ = ["pattern offset"] - - @only_if_gdb_running - @parse_arguments({"pattern": ""}, {("-n", "--n"): 0, ("-l", "--max-length"): 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: - args = kwargs["arguments"] - max_length = args.max_length or gef.config["pattern.length"] - n = args.n or gef.arch.ptrsize - info(f"Searching for '{args.pattern}'") - 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(symbol) - dereferenced_value = dereference(addr) - # 1-bis. try to dereference - if dereferenced_value: - addr = int(dereferenced_value) - struct_packsize = { - 2: "H", - 4: "I", - 8: "Q", - } - pattern_be = struct.pack(f">{struct_packsize[gef.arch.ptrsize]}", addr) - pattern_le = struct.pack(f"<{struct_packsize[gef.arch.ptrsize]}", addr) - else: - # 2. assume it's a plain string - pattern_be = gef_pybytes(pattern) - pattern_le = gef_pybytes(pattern[::-1]) - - cyclic_pattern = generate_cyclic_pattern(size, period) - found = False - 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 ''}") - found = True - - 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 ''}") - found = True - - if not found: - err(f"Pattern '{pattern}' not found") - return - - -@register_command -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 = checksec(filename) - 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_command -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 get_jmp_slots(self, readelf: str, filename: str) -> List[str]: - cmd = [readelf, "--relocs", filename] - lines = gef_execute_external(cmd, as_list=True) - return [line for line in lines if "JUMP" in line] - - @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: - try: - readelf = gef.session.constants["readelf"] - except OSError: - err("Missing `readelf`") - return - - # get the filtering parameter. - func_names_filter = [] - if argv: - func_names_filter = argv - - # getting vmmap to understand the boundaries of the main binary - # we will use this info to understand if a function has been resolved or not. - vmmap = gef.memory.maps - base_address = min(x.page_start for x in vmmap if x.path == get_filepath()) - end_address = max(x.page_end for x in vmmap if x.path == get_filepath()) - - # get the checksec output. - checksec_status = checksec(get_filepath()) - 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 - jmpslots = self.get_jmp_slots(readelf, get_filepath()) - - gef_print(f"\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: - 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 = f"[{hex(address_val)}] " - line += Color.colorify(f"{name} {RIGHT_ARROW} {hex(got_address)}", color) - gef_print(line) - - return - - -@register_command -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_command -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_command -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_command -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_command -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_command -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="/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_command -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 vulnerabity 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.Event") -> 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 - - -@register_command -class IsSyscallCommand(GenericCommand): - """Tells whether the next instruction is a system call.""" - _cmdline_ = "is-syscall" - _syntax_ = _cmdline_ - - def do_invoke(self, _: List[str]) -> None: - insn = gef_current_instruction(gef.arch.pc) - ok(f"Current instruction is{' ' if self.is_syscall(gef.arch, insn) else ' not '}a syscall") - - return - - def is_syscall(self, arch: Architecture, instruction: Instruction) -> bool: - insn_str = instruction.mnemonic + " " + ", ".join(instruction.operands) - return insn_str.strip() in arch.syscall_instructions - - -@register_command -class SyscallArgsCommand(GenericCommand): - """Gets the syscall name and arguments based on the register values in the current state.""" - _cmdline_ = "syscall-args" - _syntax_ = _cmdline_ - - def __init__(self) -> None: - super().__init__() - path = pathlib.Path(gef.config["gef.tempdir"]) / "syscall-tables" - self["path"] = (str(path.absolute()), "Path to store/load the syscall tables files") - return - - def do_invoke(self, _: List[str]) -> None: - path = self.get_settings_path() - if not path: - err(f"Cannot open '{self['path']}': check directory and/or " - "`gef config syscall-args.path` setting.") - return - - color = gef.config["theme.table_heading"] - arch = gef.arch.__class__.__name__ - syscall_table = self.get_syscall_table(arch) - - reg_value = gef.arch.register(gef.arch.syscall_register) - if reg_value not in syscall_table: - warn(f"There is no system call for {reg_value:#x}") - return - syscall_entry = syscall_table[reg_value] - - values = [] - for param in syscall_entry.params: - values.append(gef.arch.register(param.reg)) - - parameters = [s.param for s in syscall_entry.params] - registers = [s.reg for s in syscall_entry.params] - - info(f"Detected syscall {Color.colorify(syscall_entry.name, color)}") - gef_print(f" {syscall_entry.name}({', '.join(parameters)})") - - headers = ["Parameter", "Register", "Value"] - param_names = [re.split(r" |\*", p)[-1] for p in parameters] - info(Color.colorify("{:<20} {:<20} {}".format(*headers), color)) - for name, register, value in zip(param_names, registers, values): - line = f" {name:<20} {register:<20} {value:#x}" - - addrs = dereference_from(value) - - if len(addrs) > 1: - sep = f" {RIGHT_ARROW} " - line += sep - line += sep.join(addrs[1:]) - - gef_print(line) - - return - - def get_syscall_table(self, modname: str) -> Dict[str, Any]: - _mod = self.get_module(modname) - return getattr(_mod, "syscall_table") - - def get_module(self, modname: str) -> Any: - _fullname = self.get_filepath(modname).absolute() - return importlib.machinery.SourceFileLoader(modname, _fullname).load_module(None) - - def get_filepath(self, x: str) -> Optional[pathlib.Path]: - p = self.get_settings_path() - if not p: return None - return p / f"{x}.py" - - def get_settings_path(self) -> Optional[pathlib.Path]: - path = pathlib.Path(self["path"]).expanduser() - return path if path.is_dir() else None - - -# -# GDB Function declaration -# -class GenericFunction(gdb.Function, metaclass=abc.ABCMeta): - """This is an abstract class for invoking convenience functions, should not be instantiated.""" - - _example_ = "" - - @abc.abstractproperty - def _function_(self) -> str: pass - - @property - def _syntax_(self) -> str: - return f"${self._function_}([offset])" - - 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: List, 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 - - @abc.abstractmethod - def do_invoke(self, args: List) -> int: pass - - -@register_function -class StackOffsetFunction(GenericFunction): - """Return the current stack base address plus an optional offset.""" - _function_ = "_stack" - - 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_function -class HeapBaseFunction(GenericFunction): - """Return the current heap base address plus an optional offset.""" - _function_ = "_heap" - - def do_invoke(self, args: List) -> 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_function -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: - try: - name = args[0].string() - except IndexError: - name = gef.session.file.name - except gdb.error: - err(f"Invalid arg: {args[0]}") - return 0 - - try: - addr = int(get_section_base_address(name)) - except TypeError: - err(f"Cannot find section {name}") - return 0 - return addr - - -@register_function -class BssBaseFunction(GenericFunction): - """Return the current bss base address plus the given offset.""" - _function_ = "_bss" - _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_function -class GotBaseFunction(GenericFunction): - """Return the current GOT base address plus the given offset.""" - _function_ = "_got" - _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_command -class GefFunctionsCommand(GenericCommand): - """List the convenience functions provided by GEF.""" - _cmdline_ = "functions" - _syntax_ = _cmdline_ - - def __init__(self) -> None: - super().__init__() - self.docs = [] - self.setup() - return - - def setup(self) -> None: - global gef - for function in gef.gdb.loaded_functions: - self.add_function_to_doc(function) - self.__doc__ = "\n".join(sorted(self.docs)) - return - - def add_function_to_doc(self, function) -> None: - """Add function to documentation.""" - doc = getattr(function, "__doc__", "").lstrip() - doc = "\n ".join(doc.split("\n")) - syntax = getattr(function, "_syntax_", "").lstrip() - msg = f"{syntax:<25s} -- {Color.greenify(doc)}" - example = getattr(function, "_example_", "").strip() - if example: - msg += f"\n {'':27s} example: {Color.yellowify(example)}" - self.docs.append(msg) - 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(self.__doc__) - 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.extra_plugins_dir"] = GefSetting("", str, "Autoload additional GEF commands from external directory") - gef.config["gef.disable_color"] = GefSetting(False, bool, "Disable all colors in GEF") - gef.config["gef.tempdir"] = GefSetting(GEF_TEMP_DIR, str, "Directory to use for temporary/cache content") - gef.config["gef.show_deprecation_warnings"] = GefSetting(True, bool, "Toggle the display of the `deprecated` warnings") - self.loaded_commands: List[Tuple[str, Type[GenericCommand], Any]] = [] - self.loaded_functions: List[Type[GenericFunction]] = [] - self.missing_commands: Dict[str, Exception] = {} - return - - def setup(self) -> None: - self.load(initial=True) - # loading GEF sub-commands - self.doc = GefHelpCommand(self.loaded_commands) - self.cfg = GefConfigCommand(self.loaded_command_names) - GefSaveCommand() - GefRestoreCommand() - GefMissingCommand() - GefSetCommand() - GefRunCommand() - - # load the saved settings - gdb.execute("gef restore") - - # restore the autosave/autoreload breakpoints policy (if any) - self.__reload_auto_breakpoints() - - # load plugins from `extra_plugins_dir` - if self.__load_extra_plugins() > 0: - # if here, at least one extra plugin was loaded, so we need to restore - # the settings once more - gdb.execute("gef restore quiet") - return - - def __reload_auto_breakpoints(self) -> None: - bkp_fname = gef.config["gef.autosave_breakpoints_file"] - bkp_fname = bkp_fname[0] if bkp_fname else None - if bkp_fname: - # restore if existing - if os.access(bkp_fname, os.R_OK): - gdb.execute(f"source {bkp_fname}") - - # add hook for autosave breakpoints on quit command - source = [ - "define hook-quit", - f" save breakpoints {bkp_fname}", - "end", - ] - gef_execute_gdb_script("\n".join(source) + "\n") - return - - def __load_extra_plugins(self) -> int: - nb_added = -1 - try: - nb_inital = len(self.loaded_commands) - directories = gef.config["gef.extra_plugins_dir"] - if directories: - for directory in directories.split(";"): - directory = os.path.realpath(os.path.expanduser(directory)) - if os.path.isdir(directory): - sys.path.append(directory) - for fname in os.listdir(directory): - if not fname.endswith(".py"): continue - fpath = f"{directory}/{fname}" - if os.path.isfile(fpath): - gdb.execute(f"source {fpath}") - nb_added = len(self.loaded_commands) - nb_inital - if nb_added > 0: - ok(f"{Color.colorify(nb_added, 'bold green')} extra commands added from " - f"'{Color.colorify(directories, 'bold blue')}'") - except gdb.error as e: - err(f"failed: {e}") - return nb_added - - @property - def loaded_command_names(self) -> List[str]: - return [x[0] for x in self.loaded_commands] - - def invoke(self, args: Any, from_tty: bool) -> None: - self.dont_repeat() - gdb.execute("gef help") - return - - def add_context_pane(self, pane_name: str, display_pane_function: Callable, pane_title_function: Callable) -> None: - """Add a new context pane to ContextCommand.""" - for _, _, class_instance in self.loaded_commands: - if isinstance(class_instance, ContextCommand): - context = class_instance - break - else: - err("Cannot find ContextCommand") - return - - # assure users can toggle the new context - corrected_settings_name = pane_name.replace(" ", "_") - gef.config["context.layout"] += f" {corrected_settings_name}" - - # overload the printing of pane title - context.layout_mapping[corrected_settings_name] = (display_pane_function, pane_title_function) - - def load(self, initial: bool = False) -> None: - """Load all the commands and functions defined by GEF into GDB.""" - nb_missing = 0 - self.commands = [(x._cmdline_, x) for x in __registered_commands__] - - # load all of the functions - for function_class_name in __registered_functions__: - self.loaded_functions.append(function_class_name()) - - def is_loaded(x: str) -> bool: - return any(u for u in self.loaded_commands if x == u[0]) - - for cmd, class_obj in self.commands: - if is_loaded(cmd): - continue - - try: - self.loaded_commands.append((cmd, class_obj, class_obj())) - - if hasattr(class_obj, "_aliases_"): - aliases = getattr(class_obj, "_aliases_") - for alias in aliases: - GefAlias(alias, cmd) - - except Exception as reason: - self.missing_commands[cmd] = reason - nb_missing += 1 - - # sort by command name - self.loaded_commands = sorted(self.loaded_commands, key=lambda x: x[1]._cmdline_) - - if initial: - 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}" - nb_cmds = len(self.loaded_commands) - gef_print(f"{Color.colorify(nb_cmds, 'bold green')} commands loaded for " - f"GDB {Color.colorify(gdb.VERSION, 'bold yellow')} " - f"using Python engine {Color.colorify(ver, 'bold red')}") - - if nb_missing: - warn(f"{Color.colorify(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, commands: List[Tuple[str, Any, Any]]) -> None: - super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False) - self.docs = [] - self.generate_help(commands) - self.refresh() - return - - def invoke(self, args: Any, from_tty: bool) -> None: - self.dont_repeat() - gef_print(titlify("GEF - GDB Enhanced Features")) - gef_print(self.__doc__ or "") - return - - def generate_help(self, commands: List[Tuple[str, Type[GenericCommand], Any]]) -> None: - """Generate builtin commands documentation.""" - for command in commands: - self.add_command_to_doc(command) - return - - def add_command_to_doc(self, command: Tuple[str, Type[GenericCommand], Any]) -> None: - """Add command to GEF documentation.""" - cmd, class_obj, _ = command - if " " in cmd: - # do not print subcommands in gef help - return - doc = getattr(class_obj, "__doc__", "").lstrip() - doc = "\n ".join(doc.split("\n")) - aliases = f" (alias: {', '.join(class_obj._aliases_)})" if hasattr(class_obj, "_aliases_") else "" - msg = f"{cmd:<25s} -- {doc}{aliases}" - self.docs.append(msg) - return - - def refresh(self) -> None: - """Refresh the documentation.""" - self.__doc__ = "\n".join(sorted(self.docs)) - return - - -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, loaded_commands: List[str]) -> None: - super().__init__(self._cmdline_, gdb.COMMAND_NONE, prefix=False) - self.loaded_commands = loaded_commands - 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 - - 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: Tuple[str, Any]) -> None: - global gef - key, new_value = argv - - if "." not in key: - err("Invalid command format") - return - - loaded_commands = [ x[0] for x in gef.gdb.loaded_commands ] + ["gef"] - plugin_name = key.split(".", 1)[0] - if plugin_name not in loaded_commands: - err(f"Unknown plugin '{plugin_name}'") - return - - if key not in gef.config: - err(f"'{key}' is not a valid configuration setting") - return - - _type = gef.config.raw_entry(key).type - try: - if _type == bool: - _newval = True if new_value.upper() in ("TRUE", "T", "1") else False - else: - _newval = new_value - - gef.config[key] = _newval - except Exception: - err(f"{key} expects type '{_type.__name__}'") - return - - reset_all_caches() - return - - 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) - return - - def invoke(self, args: str, from_tty: bool) -> None: - self.dont_repeat() - if not os.access(GEF_RC, os.R_OK): - return - - quiet = args.lower() == "quiet" - 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: - pass - continue - - # load the other options - for optname in cfg.options(section): - key = f"{section}.{optname}" - try: - setting = gef.config.raw_entry(key) - except Exception as e: - warn(f"Invalid setting '{key}': {e}") - continue - new_value = cfg.get(section, optname) - if setting.type == bool: - new_value = True if new_value.upper() in ("TRUE", "T", "1") else False - setting.value = setting.type(new_value) - - # ensure that the temporary directory always exists - gef_makedirs(gef.config["gef.tempdir"]) - - 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 = gef.gdb.missing_commands.keys() - if not missing_commands: - ok("No missing command") - return - - for missing_command in missing_commands: - reason = gef.gdb.missing_commands[missing_command] - warn(f"Command `{missing_command}` is missing, reason {RIGHT_ARROW} {reason}") - 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[2] - 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 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) -> Optional[Tuple[str, Type, Any]]: - global gef - for _name, _class, _instance in gef.gdb.loaded_commands: - if cmd == _name: - return _name, _class, _instance - - return None - - -@register_command -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_command -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_command -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_command -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 confortable 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...") - old_ptses = set(os.listdir("/dev/pts")) - gdb.execute(f"! {tmux} split-window -h 'clear ; cat'") - gdb.execute(f"! {tmux} select-pane -L") - new_ptses = set(os.listdir("/dev/pts")) - pty = list(new_ptses - old_ptses)[0] - pty = f"/dev/pts/{pty}" - 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 - - -# -# GEF internal classes -# - -def __gef_prompt__(current_prompt: Any) -> 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 - if is_alive(): return GEF_PROMPT_ON - return GEF_PROMPT_OFF - - -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: # we're reseting 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 = None - return - - def write(self, address: int, buffer: ByteString, length: int = 0x10) -> None: - """Write `buffer` at address `address`.""" - 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: Optional[str] = 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: - err(f"Can't read memory at '{address}'") - return "" - 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) -> Optional[str]: - """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: - self.__maps = self.__parse_maps() - return self.__maps - - def __parse_maps(self) -> List[Section]: - """Return the mapped memory sections""" - try: - return list(self.__parse_procfs_maps()) - except FileNotFoundError: - return list(self.__parse_gdb_info_sections()) - - def __parse_procfs_maps(self) -> Generator[Section, None, None]: - """Get the memory mapping from procfs.""" - def open_file(path: str, use_cache: bool = False) -> IO: - """Attempt to open the given file, if remote debugging is active, download - it first to the mirror in /tmp/.""" - if is_remote_debug() and not gef.session.qemu_mode: - lpath = download_file(path, use_cache) - if not lpath: - raise IOError(f"cannot open remote path {path}") - path = lpath - return open(path, "r") - - __process_map_file = f"/proc/{gef.session.pid}/maps" - with open_file(__process_map_file, use_cache=False) 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 = [int(x, 16) for x in addr.split("-")] - 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 - - def __parse_gdb_info_sections(self) -> Generator[Section, None, None]: - """Get the memory mapping from GDB's command `maintenance info sections` (limited info).""" - stream = StringIO(gdb.execute("maintenance info sections", to_string=True)) - - for line in stream: - if not line: - break - - try: - parts = [x for x in 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.from_info_sections(parts[5:]) - yield Section( - page_start=addr_start, - page_end=addr_end, - offset=off, - permission=perm, - inode="", - path=path - ) - - except IndexError: - continue - except ValueError: - continue - return - - -class GefHeapManager(GefManager): - """Class managing session heap.""" - def __init__(self) -> None: - self.reset_caches() - return - - def reset_caches(self) -> None: - self.__libc_main_arena: Optional[GlibcArena] = None - self.__libc_selected_arena: Optional[GlibcArena] = None - self.__heap_base = None - return - - @property - def main_arena(self) -> Optional[GlibcArena]: - if not self.__libc_main_arena: - try: - __main_arena_addr = search_for_main_arena() - 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: - # the search for arena can fail when the session is not started - pass - return self.__libc_main_arena - - @property - def selected_arena(self) -> Optional[GlibcArena]: - if not self.__libc_selected_arena: - # `selected_arena` must default to `main_arena` - self.__libc_selected_arena = self.__libc_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) -> Union[List, Iterator[GlibcArena]]: - if not self.main_arena: - return [] - return iter(self.main_arena) - - @property - def base_address(self) -> Optional[int]: - if not self.__heap_base: - base = 0 - try: - base = parse_address("mp_->sbrk_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) -> Union[List, Iterator]: - if not self.base_address: - return [] - return iter(GlibcChunk(self.base_address, from_base=True)) - - -class GefSetting: - """Basic class for storing gef settings as objects""" - def __init__(self, value: Any, cls: Optional[type] = None, description: Optional[str] = None) -> None: - self.value = value - self.type = cls or type(value) - self.description = description or "" - return - - -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: - return dict.__getitem__(self, name).value - - def __setitem__(self, name: str, value: Any) -> None: - # check if the key exists - if dict.__contains__(self, name): - # if so, update its value directly - setting = dict.__getitem__(self, name) - setting.value = setting.type(value) - dict.__setitem__(self, name, setting) - else: - # if not, `value` must be a GefSetting - if not isinstance(value, GefSetting): raise Exception("Invalid argument") - if not value.type: raise Exception("Invalid type") - if not value.description: raise Exception("Invalid description") - dict.__setitem__(self, name, value) - return - - def __delitem__(self, name: str) -> None: - dict.__delitem__(self, name) - return - - def raw_entry(self, name: str) -> GefSetting: - return dict.__getitem__(self, name) - - -class GefSessionManager(GefManager): - """Class managing the runtime properties of GEF. """ - def __init__(self) -> None: - self.reset_caches() - self.remote = None - self.qemu_mode = False - self.convenience_vars_index = 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 = 1 - self.aliases: List[GefAlias] = [] - self.constants = {} # a dict for runtime constants (like 3rd party file paths) - # add a few extra runtime constants to avoid lookups - # those must be found, otherwise IOError will be raised - for constant in ("python3", "readelf", "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.__canary = None - return - - @property - def auxiliary_vector(self) -> Optional[Dict[str, int]]: - if not is_alive(): - return None - - if not self.__auxiliary_vector: - auxiliary_vector = {} - auxv_info = gdb.execute("info auxv", to_string=True) - if "failed" in auxv_info: - err(auxv_info) # print GDB error - 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 # a valid entry should have at least 4 columns - __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 gef.session.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: - """Return a Path object of the target process.""" - if not self.__file: - self.__file = pathlib.Path(gdb.current_progspace().filename) - return self.__file - - @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) -> Optional[Tuple[int, int]]: - """Returns a tuple of the 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 - self.__canary = (canary, canary_location) - return self.__canary - - -class GefUiManager(GefManager): - """Class managing UI settings.""" - def __init__(self) -> None: - self.redirect_fd : Optional[TextIOWrapper] = None - self.context_hidden = False - self.stream_buffer : Optional[StringIO] = None - self.highlight_table: Dict[str, str] = {} - self.watches: Dict[int, Tuple[int, str]] = {} - self.context_messages: List[str] = [] - return - - -class Gef: - """The GEF root class, which serves as a entrypoint for all the debugging session attributes (architecture, - memory, settings, etc.).""" - def __init__(self) -> None: - self.binary: Optional[Elf] = None - self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` - self.config = GefSettingsManager() - self.ui = GefUiManager() - return - - 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() - tempdir = self.config["gef.tempdir"] - gef_makedirs(tempdir) - gdb.execute(f"save gdb-index {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 - - -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) - - try: - pyenv = which("pyenv") - PYENV_ROOT = gef_pystring(subprocess.check_output([pyenv, "root"]).strip()) - PYENV_VERSION = gef_pystring(subprocess.check_output([pyenv, "version-name"]).strip()) - site_packages_dir = os.path.join(PYENV_ROOT, "versions", PYENV_VERSION, "lib", - f"python{PYENV_VERSION[:3]}", "site-packages") - site.addsitedir(site_packages_dir) - except FileNotFoundError: - pass - - # When using a Python virtual environment, GDB still loads the system-installed Python - # so GEF doesn't load site-packages dir from environment - # In order to fix it, from the shell with venv activated we run the python binary, - # take and parse its path, add the path to the current python process using sys.path.extend - PYTHONBIN = which("python3") - PREFIX = gef_pystring(subprocess.check_output([PYTHONBIN, '-c', 'import os, sys;print((sys.prefix))'])).strip("\\n") - if PREFIX != sys.base_prefix: - SITE_PACKAGES_DIRS = subprocess.check_output( - [PYTHONBIN, "-c", "import os, sys;print(os.linesep.join(sys.path).strip())"]).decode("utf-8").split() - sys.path.extend(SITE_PACKAGES_DIRS) - - # setup prompt - gdb.prompt_hook = __gef_prompt__ - - # setup config - gdb_initial_settings = ( - "set confirm off", - "set verbose off", - "set pagination off", - "set print elements 0", - "set history save on", - "set history filename ~/.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 - - # load GEF - reset() - - # 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) - - if gdb.current_progspace().filename is not None: - # if here, we are sourcing gef from a gdb session already attached - # we must force a call to the new_objfile handler (see issue #278) - new_objfile_handler(None) - - GefTmuxSetup() diff --git a/gdb/.config/gdb/init b/gdb/.config/gdb/init deleted file mode 100644 index dc69f24..0000000 --- a/gdb/.config/gdb/init +++ /dev/null @@ -1 +0,0 @@ -source ~/.config/gdb/.gef-283690ae9bfcecbb3deb80cd275d327c46b276b5.py diff --git a/lvim/.config/lvim/config.lua b/lvim/.config/lvim/config.lua deleted file mode 100644 index 1a8d8f6..0000000 --- a/lvim/.config/lvim/config.lua +++ /dev/null @@ -1,197 +0,0 @@ ---[[ -lvim is the global options object - -Linters should be -filled in as strings with either -a global executable or a path to -an executable -]] --- THESE ARE EXAMPLE CONFIGS FEEL FREE TO CHANGE TO WHATEVER YOU WANT - --- general -lvim.log.level = "warn" -lvim.format_on_save = true -lvim.colorscheme = "nord" --- to disable icons and use a minimalist setup, uncomment the following --- lvim.use_icons = false - --- keymappings [view all the defaults by pressing Lk] -lvim.leader = "space" --- add your own keymapping -lvim.keys.normal_mode[""] = ":w" --- lvim.keys.normal_mode[""] = ":BufferLineCycleNext" --- lvim.keys.normal_mode[""] = ":BufferLineCyclePrev" --- unmap a default keymapping --- vim.keymap.del("n", "") --- override a default keymapping --- lvim.keys.normal_mode[""] = ":q" -- or vim.keymap.set("n", "", ":q" ) - --- Change Telescope navigation to use j and k for navigation and n and p for history in both input and normal mode. --- we use protected-mode (pcall) just in case the plugin wasn't loaded yet. --- local _, actions = pcall(require, "telescope.actions") --- lvim.builtin.telescope.defaults.mappings = { --- -- for input mode --- i = { --- [""] = actions.move_selection_next, --- [""] = actions.move_selection_previous, --- [""] = actions.cycle_history_next, --- [""] = actions.cycle_history_prev, --- }, --- -- for normal mode --- n = { --- [""] = actions.move_selection_next, --- [""] = actions.move_selection_previous, --- }, --- } - --- Change theme settings --- lvim.builtin.theme.options.dim_inactive = true --- lvim.builtin.theme.options.style = "storm" - --- Use which-key to add extra bindings with the leader-key prefix --- lvim.builtin.which_key.mappings["P"] = { "Telescope projects", "Projects" } --- lvim.builtin.which_key.mappings["t"] = { --- name = "+Trouble", --- r = { "Trouble lsp_references", "References" }, --- f = { "Trouble lsp_definitions", "Definitions" }, --- d = { "Trouble document_diagnostics", "Diagnostics" }, --- q = { "Trouble quickfix", "QuickFix" }, --- l = { "Trouble loclist", "LocationList" }, --- w = { "Trouble workspace_diagnostics", "Workspace Diagnostics" }, --- } - --- TODO: User Config for predefined plugins --- After changing plugin config exit and reopen LunarVim, Run :PackerInstall :PackerCompile -lvim.builtin.alpha.active = true -lvim.builtin.alpha.mode = "dashboard" -lvim.builtin.terminal.active = true -lvim.builtin.nvimtree.setup.view.side = "left" -lvim.builtin.nvimtree.setup.renderer.icons.show.git = false - --- if you don't want all the parsers change this to a table of the ones you want -lvim.builtin.treesitter.ensure_installed = { - "bash", - "c", - "javascript", - "json", - "lua", - "python", - "typescript", - "tsx", - "css", - "rust", - "java", - "yaml", -} - -lvim.builtin.treesitter.ignore_install = { "haskell" } -lvim.builtin.treesitter.highlight.enable = true - --- generic LSP settings - --- -- make sure server will always be installed even if the server is in skipped_servers list --- lvim.lsp.installer.setup.ensure_installed = { --- "sumneko_lua", --- "jsonls", --- } --- -- change UI setting of `LspInstallInfo` --- -- see --- lvim.lsp.installer.setup.ui.check_outdated_servers_on_open = false --- lvim.lsp.installer.setup.ui.border = "rounded" --- lvim.lsp.installer.setup.ui.keymaps = { --- uninstall_server = "d", --- toggle_server_expand = "o", --- } - --- ---@usage disable automatic installation of servers --- lvim.lsp.installer.setup.automatic_installation = false - --- ---configure a server manually. !!Requires `:LvimCacheReset` to take effect!! --- ---see the full default list `:lua print(vim.inspect(lvim.lsp.automatic_configuration.skipped_servers))` --- vim.list_extend(lvim.lsp.automatic_configuration.skipped_servers, { "pyright" }) --- local opts = {} -- check the lspconfig documentation for a list of all possible options --- require("lvim.lsp.manager").setup("pyright", opts) - --- ---remove a server from the skipped list, e.g. eslint, or emmet_ls. !!Requires `:LvimCacheReset` to take effect!! --- ---`:LvimInfo` lists which server(s) are skipped for the current filetype --- lvim.lsp.automatic_configuration.skipped_servers = vim.tbl_filter(function(server) --- return server ~= "emmet_ls" --- end, lvim.lsp.automatic_configuration.skipped_servers) - --- -- you can set a custom on_attach function that will be used for all the language servers --- -- See --- lvim.lsp.on_attach_callback = function(client, bufnr) --- local function buf_set_option(...) --- vim.api.nvim_buf_set_option(bufnr, ...) --- end --- --Enable completion triggered by --- buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc") --- end - --- -- set a formatter, this will override the language server formatting capabilities (if it exists) --- local formatters = require "lvim.lsp.null-ls.formatters" --- formatters.setup { --- { command = "black", filetypes = { "python" } }, --- { command = "isort", filetypes = { "python" } }, --- { --- -- each formatter accepts a list of options identical to https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#Configuration --- command = "prettier", --- ---@usage arguments to pass to the formatter --- -- these cannot contain whitespaces, options such as `--line-width 80` become either `{'--line-width', '80'}` or `{'--line-width=80'}` --- extra_args = { "--print-with", "100" }, --- ---@usage specify which filetypes to enable. By default a providers will attach to all the filetypes it supports. --- filetypes = { "typescript", "typescriptreact" }, --- }, --- } - --- -- set additional linters --- local linters = require "lvim.lsp.null-ls.linters" --- linters.setup { --- { command = "flake8", filetypes = { "python" } }, --- { --- -- each linter accepts a list of options identical to https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md#Configuration --- command = "shellcheck", --- ---@usage arguments to pass to the formatter --- -- these cannot contain whitespaces, options such as `--line-width 80` become either `{'--line-width', '80'}` or `{'--line-width=80'}` --- extra_args = { "--severity", "warning" }, --- }, --- { --- command = "codespell", --- ---@usage specify which filetypes to enable. By default a providers will attach to all the filetypes it supports. --- filetypes = { "javascript", "python" }, --- }, --- } - --- Additional Plugins -lvim.plugins = { - { - "editorconfig/editorconfig-vim", - "shaunsingh/nord.nvim", - "sheerun/vim-polyglot", - "edluffy/hologram.nvim", - "lepture/vim-jinja", - "google/vim-jsonnet", - "elixir-editors/vim-elixir", - "alaviss/nim.nvim" - } -} -vim.scrolloff = 99999 - -vim.g.nord_disable_background = true -vim.g.nord_borders = true -vim.g.nord_contrast = true -vim.g.nord_cursorline_transparent = true - --- Autocommands (https://neovim.io/doc/user/autocmd.html) --- vim.api.nvim_create_autocmd("BufEnter", { --- pattern = { "*.json", "*.jsonc" }, --- -- enable wrap mode for json files only --- command = "setlocal wrap", --- }) --- vim.api.nvim_create_autocmd("FileType", { --- pattern = "zsh", --- callback = function() --- -- let treesitter use bash highlight for zsh files as well --- require("nvim-treesitter.highlight").attach(0, "bash") --- end, --- }) diff --git a/sh/.config/shell/aliasrc b/sh/.config/shell/aliasrc deleted file mode 100644 index 4d0851d..0000000 --- a/sh/.config/shell/aliasrc +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -# Use neovim for vim if present. -[ -x "$(command -v nvim)" ] && alias vim="nvim" vimdiff="nvim -d" - -# Use $XINITRC variable if file exists. -[ -f "$XINITRC" ] && alias startx="startx $XINITRC" - -# sudo not required for some system commands -for x in mount umount sv emerge apt pacman updatedb su ; do - alias $x="sudo $x" -done - -if command -v doas &>/dev/null -then - alias sudo='doas' -else - alias sudo='sudo ' -fi - -# Verbosity and settings that you pretty much just always are going to want. -alias \ - cp="cp -iv" \ - mv="mv -iv" \ - rm="rm -vI" \ - bc="bc -ql" \ - info="pinfo"\ - mkdir="mkdir -pv" \ - yt="yt-dlp --add-metadata -i" \ - nvim="lvim" \ - yta="yt -x -f bestaudio/best" \ - ffmpeg="ffmpeg -hide_banner" \ - df="df -h" \ - du="dust -r" \ - free="free -m" \ - gdb='gdb -q -nh -x "$XDG_CONFIG_HOME/gdb/init"' \ - -# Colorize commands when possible. -alias \ - ls="exa -aF --group-directories-first" \ - ll="exa -alg --group-directories-first" \ - lt='exa -aTF --group-directories-first' \ - lr='exa -aRF' \ - grep="rg --color=auto" \ - diff="delta" \ - cat='bat --paging=never --style=plain' \ - -# These common commands are just too long! Abbreviate them. -alias \ - ka="killall" \ - g="git" \ - trem="transmission-remote" \ - YT="youtube-viewer" \ - sdn="sudo shutdown -h now" \ - e="$EDITOR" \ - v="$EDITOR" \ - em="sudo emerge" \ - p="sudo pacman" \ - xi="sudo xbps-install" \ - xr="sudo xbps-remove -R" \ - xq="xbps-query" \ - z="zathura" - diff --git a/tmux/.config/tmux/tmux.conf b/tmux/.config/tmux/tmux.conf deleted file mode 100644 index 5c017d8..0000000 --- a/tmux/.config/tmux/tmux.conf +++ /dev/null @@ -1,30 +0,0 @@ -set-option -g default-shell "/bin/zsh" - -# https://github.com/microsoft/WSL/issues/5931 -set -sg escape-time 50 - -set -g mouse on -set -ga terminal-overrides ",xterm-256color:Tc" -set -g default-terminal "tmux-256color" -set -g @plugin "arcticicestudio/nord-tmux" -set -g @plugin 'tmux-plugins/tmux-sensible' -set -g @plugin 'tmux-plugins/tmux-yank' -set -g default-command "${SHELL} -l" -set -g @plugin 'jaclu/tmux-menus' -set -g @plugin 'schasse/tmux-jump' -set -g @plugin 'MunifTanjim/tmux-mode-indicator' - -set-environment -g TMUX_PLUGIN_MANAGER_PATH "$XDG_DATA_HOME/tmux/plugins/" - -#ctrl-a for prefix -unbind C-b -set-option -g prefix C-a -bind-key C-a send-prefix - -bind -n M-Left select-pane -L -bind -n M-Right select-pane -R -bind -n M-Up select-pane -U -bind -n M-Down select-pane -D - -#Make sure you have tmux-plugin-manager installed! -run '/usr/share/tmux-plugin-manager/tpm' diff --git a/zsh/.config/zsh/.p10k.zsh b/zsh/.config/zsh/.p10k.zsh deleted file mode 100644 index 733b8e6..0000000 --- a/zsh/.config/zsh/.p10k.zsh +++ /dev/null @@ -1,1624 +0,0 @@ -# Generated by Powerlevel10k configuration wizard on 2022-08-31 at 16:10 CDT. -# Based on romkatv/powerlevel10k/config/p10k-lean.zsh. -# Wizard options: nerdfont-complete + powerline, small icons, unicode, lean, 24h time, -# 1 line, compact, few icons, concise, transient_prompt, instant_prompt=quiet. -# Type `p10k configure` to generate another config. -# -# Config for Powerlevel10k with lean prompt style. Type `p10k configure` to generate -# your own config based on it. -# -# Tip: Looking for a nice color? Here's a one-liner to print colormap. -# -# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done - -# Temporarily change options. -'builtin' 'local' '-a' 'p10k_config_opts' -[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') -[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') -[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') -'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' - -() { - emulate -L zsh -o extended_glob - - # Unset all configuration options. This allows you to apply configuration changes without - # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. - unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' - - # Zsh >= 5.1 is required. - autoload -Uz is-at-least && is-at-least 5.1 || return - - # The list of segments shown on the left. Fill it with the most important segments. - typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( - # os_icon # os identifier - dir # current directory - vcs # git status - prompt_char # prompt symbol - ) - - # The list of segments shown on the right. Fill it with less important segments. - # Right prompt on the last prompt line (where you are typing your commands) gets - # automatically hidden when the input line reaches it. Right prompt above the - # last prompt line gets hidden if it would overlap with left prompt. - typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( - status # exit code of the last command - command_execution_time # duration of the last command - background_jobs # presence of background jobs - direnv # direnv status (https://direnv.net/) - #asdf # asdf version manager (https://github.com/asdf-vm/asdf) - virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) - #anaconda # conda environment (https://conda.io/) - #pyenv # python environment (https://github.com/pyenv/pyenv) - goenv # go environment (https://github.com/syndbg/goenv) - #nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) - #nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) - #nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) - node_version # node.js version - go_version # go version (https://golang.org) - rust_version # rustc version (https://www.rust-lang.org) - dotnet_version # .NET version (https://dotnet.microsoft.com) - php_version # php version (https://www.php.net/) - laravel_version # laravel php framework version (https://laravel.com/) - java_version # java version (https://www.java.com/) - #package # name@version from package.json (https://docs.npmjs.com/files/package.json) - #rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) - #rvm # ruby version from rvm (https://rvm.io) - #fvm # flutter version management (https://github.com/leoafarias/fvm) - #luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) - #jenv # java version from jenv (https://github.com/jenv/jenv) - #plenv # perl version from plenv (https://github.com/tokuhirom/plenv) - #perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew) - #phpenv # php version from phpenv (https://github.com/phpenv/phpenv) - #scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) - #haskell_stack # haskell version from stack (https://haskellstack.org/) - kubecontext # current kubernetes context (https://kubernetes.io/) - terraform # terraform workspace (https://www.terraform.io) - # terraform_version # terraform version (https://www.terraform.io) - aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) - aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) - azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) - gcloud # google cloud cli account and project (https://cloud.google.com/) - google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) - toolbox # toolbox name (https://github.com/containers/toolbox) - context # user@hostname - #nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) - ranger # ranger shell (https://github.com/ranger/ranger) - nnn # nnn shell (https://github.com/jarun/nnn) - xplr # xplr shell (https://github.com/sayanarijit/xplr) - vim_shell # vim shell indicator (:sh) - midnight_commander # midnight commander shell (https://midnight-commander.org/) - nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) - # vpn_ip # virtual private network indicator - # load # CPU load - # disk_usage # disk usage - # ram # free RAM - # swap # used swap - todo # todo items (https://github.com/todotxt/todo.txt-cli) - #timewarrior # timewarrior tracking status (https://timewarrior.net/) - #taskwarrior # taskwarrior task count (https://taskwarrior.org/) - time # current time - # ip # ip address and bandwidth usage for a specified network interface - # public_ip # public IP address - # proxy # system-wide http/https/ftp proxy - # battery # internal battery - # wifi # wifi speed - # example # example user-defined segment (see prompt_example function below) - ) - - # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. - typeset -g POWERLEVEL9K_MODE=nerdfont-complete - # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid - # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. - typeset -g POWERLEVEL9K_ICON_PADDING=none - - # Basic style options that define the overall look of your prompt. You probably don't want to - # change them. - typeset -g POWERLEVEL9K_BACKGROUND= # transparent background - typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace - typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space - typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol - - # When set to true, icons appear before content on both sides of the prompt. When set - # to false, icons go after content. If empty or not set, icons go before content in the left - # prompt and after content in the right prompt. - # - # You can also override it for a specific segment: - # - # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false - # - # Or for a specific segment in specific state: - # - # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false - typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true - - # Add an empty line before each prompt. - typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false - - # Connect left prompt lines with these symbols. - typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX= - typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX= - typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX= - # Connect right prompt lines with these symbols. - typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX= - typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX= - typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX= - - # The left end of left prompt. - typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= - # The right end of right prompt. - typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL= - - # Ruler, a.k.a. the horizontal line before each prompt. If you set it to true, you'll - # probably want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false above and - # POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' below. - typeset -g POWERLEVEL9K_SHOW_RULER=false - typeset -g POWERLEVEL9K_RULER_CHAR='─' # reasonable alternative: '·' - typeset -g POWERLEVEL9K_RULER_FOREGROUND=242 - - # Filler between left and right prompt on the first prompt line. You can set it to '·' or '─' - # to make it easier to see the alignment between left and right prompt and to separate prompt - # from command output. It serves the same purpose as ruler (see above) without increasing - # the number of prompt lines. You'll probably want to set POWERLEVEL9K_SHOW_RULER=false - # if using this. You might also like POWERLEVEL9K_PROMPT_ADD_NEWLINE=false for more compact - # prompt. - typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' - if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then - # The color of the filler. - typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=242 - # Add a space between the end of left prompt and the filler. - typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=' ' - # Add a space between the filler and the start of right prompt. - typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL=' ' - # Start filler from the edge of the screen if there are no left segments on the first line. - typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' - # End filler on the edge of the screen if there are no right segments on the first line. - typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' - fi - - #################################[ os_icon: os identifier ]################################## - # OS identifier color. - typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND= - # Custom icon. - # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' - - ################################[ prompt_char: prompt symbol ]################################ - # Green prompt symbol if the last command succeeded. - typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 - # Red prompt symbol if the last command failed. - typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 - # Default prompt symbol. - typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' - # Prompt symbol in command vi mode. - typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' - # Prompt symbol in visual vi mode. - typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' - # Prompt symbol in overwrite vi mode. - typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' - typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true - # No line terminator if prompt_char is the last segment. - typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='' - # No line introducer if prompt_char is the first segment. - typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= - - ##################################[ dir: current directory ]################################## - # Default current directory color. - typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 - # If directory is too long, shorten some of its segments to the shortest possible unique - # prefix. The shortened directory can be tab-completed to the original. - typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique - # Replace removed segment suffixes with this symbol. - typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= - # Color of the shortened directory segments. - typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 - # Color of the anchor directory segments. Anchor segments are never shortened. The first - # segment is always an anchor. - typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 - # Display anchor directory segments in bold. - typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true - # Don't shorten directories that contain any of these files. They are anchors. - local anchor_files=( - .bzr - .citc - .git - .hg - .node-version - .python-version - .go-version - .ruby-version - .lua-version - .java-version - .perl-version - .php-version - .tool-version - .shorten_folder_marker - .svn - .terraform - CVS - Cargo.toml - composer.json - go.mod - package.json - stack.yaml - ) - typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" - # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains - # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is - # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) - # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers - # and other directories don't. - # - # Optionally, "first" and "last" can be followed by ":" where is an integer. - # This moves the truncation point to the right (positive offset) or to the left (negative offset) - # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" - # respectively. - typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false - # Don't shorten this many last directory segments. They are anchors. - typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 - # Shorten directory if it's longer than this even if there is space for it. The value can - # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, - # directory will be shortened only when prompt doesn't fit or when other parameters demand it - # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). - # If set to `0`, directory will always be shortened to its minimum length. - typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 - # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this - # many columns for typing commands. - typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 - # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least - # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. - typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 - # If set to true, embed a hyperlink into the directory. Useful for quickly - # opening a directory in the file manager simply by clicking the link. - # Can also be handy when the directory is shortened, as it allows you to see - # the full directory that was used in previous commands. - typeset -g POWERLEVEL9K_DIR_HYPERLINK=false - - # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON - # and POWERLEVEL9K_DIR_CLASSES below. - typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 - - # The default icon shown next to non-writable and non-existent directories when - # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. - # typeset -g POWERLEVEL9K_LOCK_ICON='⭐' - - # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different - # directories. It must be an array with 3 * N elements. Each triplet consists of: - # - # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with - # extended_glob option enabled. - # 2. Directory class for the purpose of styling. - # 3. An empty string. - # - # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. - # - # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories - # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. - # - # For example, given these settings: - # - # typeset -g POWERLEVEL9K_DIR_CLASSES=( - # '~/work(|/*)' WORK '' - # '~(|/*)' HOME '' - # '*' DEFAULT '') - # - # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one - # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or - # WORK_NON_EXISTENT. - # - # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an - # option to define custom colors and icons for different directory classes. - # - # # Styling for WORK. - # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 - # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 - # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 - # - # # Styling for WORK_NOT_WRITABLE. - # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 - # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 - # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 - # - # # Styling for WORK_NON_EXISTENT. - # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 - # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 - # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 - # - # If a styling parameter isn't explicitly defined for some class, it falls back to the classless - # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls - # back to POWERLEVEL9K_DIR_FOREGROUND. - # - typeset -g POWERLEVEL9K_DIR_CLASSES=() - - # Custom prefix. - # typeset -g POWERLEVEL9K_DIR_PREFIX='%fin ' - - #####################################[ vcs: git status ]###################################### - # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon. - typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= - - # Untracked files icon. It's really a question mark, your font isn't broken. - # Change the value of this parameter to show a different icon. - typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' - - # Formatter for Git status. - # - # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. - # - # You can edit the function to customize how Git status looks. - # - # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: - # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. - function my_git_formatter() { - emulate -L zsh - - if [[ -n $P9K_CONTENT ]]; then - # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from - # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. - typeset -g my_git_format=$P9K_CONTENT - return - fi - - if (( $1 )); then - # Styling for up-to-date Git status. - local meta='%f' # default foreground - local clean='%76F' # green foreground - local modified='%178F' # yellow foreground - local untracked='%39F' # blue foreground - local conflicted='%196F' # red foreground - else - # Styling for incomplete and stale Git status. - local meta='%244F' # grey foreground - local clean='%244F' # grey foreground - local modified='%244F' # grey foreground - local untracked='%244F' # grey foreground - local conflicted='%244F' # grey foreground - fi - - local res - - if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then - local branch=${(V)VCS_STATUS_LOCAL_BRANCH} - # If local branch name is at most 32 characters long, show it in full. - # Otherwise show the first 12 … the last 12. - # Tip: To always show local branch name in full without truncation, delete the next line. - (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line - res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" - fi - - if [[ -n $VCS_STATUS_TAG - # Show tag only if not on a branch. - # Tip: To always show tag, delete the next line. - && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line - ]]; then - local tag=${(V)VCS_STATUS_TAG} - # If tag name is at most 32 characters long, show it in full. - # Otherwise show the first 12 … the last 12. - # Tip: To always show tag name in full without truncation, delete the next line. - (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line - res+="${meta}#${clean}${tag//\%/%%}" - fi - - # Display the current Git commit if there is no branch and no tag. - # Tip: To always display the current Git commit, delete the next line. - [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line - res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" - - # Show tracking branch name if it differs from local branch. - if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then - res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" - fi - - # Display "wip" if the latest commit's summary contains "wip" or "WIP". - if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then - res+=" ${modified}wip" - fi - - # ⇣42 if behind the remote. - (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" - # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. - (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " - (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" - # ⇠42 if behind the push remote. - (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" - (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " - # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. - (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" - # *42 if have stashes. - (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" - # 'merge' if the repo is in an unusual state. - [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" - # ~42 if have merge conflicts. - (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" - # +42 if have staged changes. - (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" - # !42 if have unstaged changes. - (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" - # ?42 if have untracked files. It's really a question mark, your font isn't broken. - # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. - # Remove the next line if you don't want to see untracked files at all. - (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" - # "─" if the number of unstaged files is unknown. This can happen due to - # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower - # than the number of files in the Git index, or due to bash.showDirtyState being set to false - # in the repository config. The number of staged and untracked files may also be unknown - # in this case. - (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" - - typeset -g my_git_format=$res - } - functions -M my_git_formatter 2>/dev/null - - # Don't count the number of unstaged, untracked and conflicted files in Git repositories with - # more than this many files in the index. Negative value means infinity. - # - # If you are working in Git repositories with tens of millions of files and seeing performance - # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output - # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's - # config: `git config bash.showDirtyState false`. - typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 - - # Don't show Git status in prompt for repositories whose workdir matches this pattern. - # For example, if set to '~', the Git repository at $HOME/.git will be ignored. - # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. - typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' - - # Disable the default Git status formatting. - typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true - # Install our own Git status formatter. - typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' - typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' - # Enable counters for staged, unstaged, etc. - typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 - - # Icon color. - typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 - typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 - # Custom icon. - typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= - # Custom prefix. - # typeset -g POWERLEVEL9K_VCS_PREFIX='%fon ' - - # Show status of repositories of these types. You can add svn and/or hg if you are - # using them. If you do, your prompt may become slow even when your current directory - # isn't in an svn or hg repository. - typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) - - # These settings are used for repositories other than Git or when gitstatusd fails and - # Powerlevel10k has to fall back to using vcs_info. - typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 - typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 - typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 - - ##########################[ status: exit code of the last command ]########################### - # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and - # style them independently from the regular OK and ERROR state. - typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true - - # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as - # it will signify success by turning green. - typeset -g POWERLEVEL9K_STATUS_OK=false - typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 - typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' - - # Status when some part of a pipe command fails but the overall exit status is zero. It may look - # like this: 1|0. - typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true - typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 - typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' - - # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as - # it will signify error by turning red. - typeset -g POWERLEVEL9K_STATUS_ERROR=true - typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 - typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘' - - # Status when the last command was terminated by a signal. - typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true - typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 - # Use terse signal names: "INT" instead of "SIGINT(2)". - typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false - typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘' - - # Status when some part of a pipe command fails and the overall exit status is also non-zero. - # It may look like this: 1|0. - typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true - typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 - typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘' - - ###################[ command_execution_time: duration of the last command ]################### - # Show duration of the last command if takes at least this many seconds. - typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 - # Show this many fractional digits. Zero means round to seconds. - typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 - # Execution time color. - typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=101 - # Duration format: 1d 2h 3m 4s. - typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' - # Custom icon. - typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= - # Custom prefix. - # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%ftook ' - - #######################[ background_jobs: presence of background jobs ]####################### - # Don't show the number of background jobs. - typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false - # Background jobs color. - typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=70 - # Custom icon. - # typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #######################[ direnv: direnv status (https://direnv.net/) ]######################## - # Direnv color. - typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 - # Custom icon. - # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### - # Default asdf color. Only used to display tools for which there is no color override (see below). - # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. - typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 - - # There are four parameters that can be used to hide asdf tools. Each parameter describes - # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at - # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to - # hide a tool, it gets shown. - # - # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and - # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: - # - # asdf local python 3.8.1 - # asdf global python 3.8.1 - # - # After running both commands the current python version is 3.8.1 and its source is "local" as - # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, - # it'll hide python version in this case because 3.8.1 is the same as the global version. - # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't - # contain "local". - - # Hide tool versions that don't come from one of these sources. - # - # Available sources: - # - # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" - # - local `asdf current` says "set by /some/not/home/directory/file" - # - global `asdf current` says "set by /home/username/file" - # - # Note: If this parameter is set to (shell local global), it won't hide tools. - # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. - typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) - - # If set to false, hide tool versions that are the same as global. - # - # Note: The name of this parameter doesn't reflect its meaning at all. - # Note: If this parameter is set to true, it won't hide tools. - # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. - typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false - - # If set to false, hide tool versions that are equal to "system". - # - # Note: If this parameter is set to true, it won't hide tools. - # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. - typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true - - # If set to non-empty value, hide tools unless there is a file matching the specified file pattern - # in the current directory, or its parent directory, or its grandparent directory, and so on. - # - # Note: If this parameter is set to empty value, it won't hide tools. - # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. - # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. - # - # Example: Hide nodejs version when there is no package.json and no *.js files in the current - # directory, in `..`, in `../..` and so on. - # - # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' - typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= - - # Ruby version from asdf. - typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 - # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Python version from asdf. - typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 - # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Go version from asdf. - typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 - # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Node.js version from asdf. - typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 - # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Rust version from asdf. - typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 - # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' - - # .NET Core version from asdf. - typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 - # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_DOTNET_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Flutter version from asdf. - typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 - # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Lua version from asdf. - typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 - # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Java version from asdf. - typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 - # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Perl version from asdf. - typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 - # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Erlang version from asdf. - typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 - # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Elixir version from asdf. - typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 - # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Postgres version from asdf. - typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 - # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' - - # PHP version from asdf. - typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 - # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Haskell version from asdf. - typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 - # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' - - # Julia version from asdf. - typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 - # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' - - ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### - # NordVPN connection indicator color. - typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 - # Hide NordVPN connection indicator when not connected. - typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= - typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= - # Custom icon. - # typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## - # Ranger shell color. - typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 - # Custom icon. - # typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### - # Nnn shell color. - typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 - # Custom icon. - # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## - # xplr shell color. - typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 - # Custom icon. - # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########################[ vim_shell: vim shell indicator (:sh) ]########################### - # Vim shell indicator color. - typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 - # Custom icon. - # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### - # Midnight Commander shell color. - typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 - # Custom icon. - # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## - # Nix shell color. - typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 - - # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. - # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= - - # Custom icon. - # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##################################[ disk_usage: disk usage ]################################## - # Colors for different levels of disk usage. - typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 - typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 - typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 - # Thresholds for different levels of disk usage (percentage points). - typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 - typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 - # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. - typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false - # Custom icon. - # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ######################################[ ram: free RAM ]####################################### - # RAM color. - typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 - # Custom icon. - # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #####################################[ swap: used swap ]###################################### - # Swap color. - typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 - # Custom icon. - # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ######################################[ load: CPU load ]###################################### - # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. - typeset -g POWERLEVEL9K_LOAD_WHICH=5 - # Load color when load is under 50%. - typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 - # Load color when load is between 50% and 70%. - typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 - # Load color when load is over 70%. - typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 - # Custom icon. - # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ - # Todo color. - typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 - # Hide todo when the total number of tasks is zero. - typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true - # Hide todo when the number of tasks after filtering is zero. - typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false - - # Todo format. The following parameters are available within the expansion. - # - # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. - # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. - # - # These variables correspond to the last line of the output of `todo.sh -p ls`: - # - # TODO: 24 of 42 tasks shown - # - # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. - # - # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' - - # Custom icon. - # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ - # Timewarrior color. - typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 - # If the tracked task is longer than 24 characters, truncate and append "…". - # Tip: To always display tasks without truncation, delete the following parameter. - # Tip: To hide task names and display just the icon when time tracking is enabled, set the - # value of the following parameter to "". - typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' - - # Custom icon. - # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## - # Taskwarrior color. - typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 - - # Taskwarrior segment format. The following parameters are available within the expansion. - # - # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. - # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. - # - # Zero values are represented as empty parameters. - # - # The default format: - # - # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' - # - # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' - - # Custom icon. - # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##################################[ context: user@hostname ]################################## - # Context color when running with privileges. - typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 - # Context color in SSH without privileges. - typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 - # Default context color (no privileges, no SSH). - typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 - - # Context format when running with privileges: bold user@hostname. - typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' - # Context format when in SSH without privileges: user@hostname. - typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' - # Default context format (no privileges, no SSH): user@hostname. - typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' - - # Don't show context unless running with privileges or in SSH. - # Tip: Remove the next line to always show context. - typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= - - # Custom icon. - # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' - # Custom prefix. - # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%fwith ' - - ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### - # Python virtual environment color. - typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 - # Don't show Python version next to the virtual environment name. - typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false - # If set to "false", won't show virtualenv if pyenv is already shown. - # If set to "if-different", won't show virtualenv if it's the same as pyenv. - typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false - # Separate environment name from Python version only with a space. - typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= - # Custom icon. - # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #####################[ anaconda: conda environment (https://conda.io/) ]###################### - # Anaconda environment color. - typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 - - # Anaconda segment format. The following parameters are available within the expansion. - # - # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. - # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. - # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). - # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). - # - # CONDA_PROMPT_MODIFIER can be configured with the following command: - # - # conda config --set env_prompt '({default_env}) ' - # - # The last argument is a Python format string that can use the following variables: - # - # - prefix The same as CONDA_PREFIX. - # - default_env The same as CONDA_DEFAULT_ENV. - # - name The last segment of CONDA_PREFIX. - # - stacked_env Comma-separated list of names in the environment stack. The first element is - # always the same as default_env. - # - # Note: '({default_env}) ' is the default value of env_prompt. - # - # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER - # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former - # is empty. - typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' - - # Custom icon. - # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ - # Pyenv color. - typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 - # Hide python version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) - # If set to false, hide python version if it's the same as global: - # $(pyenv version-name) == $(pyenv global). - typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide python version if it's equal to "system". - typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true - - # Pyenv segment format. The following parameters are available within the expansion. - # - # - P9K_CONTENT Current pyenv environment (pyenv version-name). - # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). - # - # The default format has the following logic: - # - # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or - # starts with "$P9K_PYENV_PYTHON_VERSION/". - # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". - typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' - - # Custom icon. - # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ - # Goenv color. - typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 - # Hide go version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) - # If set to false, hide go version if it's the same as global: - # $(goenv version-name) == $(goenv global). - typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide go version if it's equal to "system". - typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## - # Nodenv color. - typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 - # Hide node version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) - # If set to false, hide node version if it's the same as global: - # $(nodenv version-name) == $(nodenv global). - typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide node version if it's equal to "system". - typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### - # Nvm color. - typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 - # Custom icon. - # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ - # Nodeenv color. - typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 - # Don't show Node version next to the environment name. - typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false - # Separate environment name from Node version only with a space. - typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= - # Custom icon. - # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##############################[ node_version: node.js version ]############################### - # Node version color. - typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 - # Show node version only when in a directory tree containing package.json. - typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true - # Custom icon. - # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #######################[ go_version: go version (https://golang.org) ]######################## - # Go version color. - typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 - # Show go version only when in a go project subdirectory. - typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true - # Custom icon. - # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## - # Rust version color. - typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 - # Show rust version only when in a rust project subdirectory. - typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true - # Custom icon. - # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ - # .NET version color. - typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 - # Show .NET version only when in a .NET project subdirectory. - typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true - # Custom icon. - # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #####################[ php_version: php version (https://www.php.net/) ]###################### - # PHP version color. - typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 - # Show PHP version only when in a PHP project subdirectory. - typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true - # Custom icon. - # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### - # Laravel version color. - typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 - # Custom icon. - # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ####################[ java_version: java version (https://www.java.com/) ]#################### - # Java version color. - typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 - # Show java version only when in a java project subdirectory. - typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true - # Show brief version. - typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false - # Custom icon. - # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### - # Package color. - typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 - # Package format. The following parameters are available within the expansion. - # - # - P9K_PACKAGE_NAME The value of `name` field in package.json. - # - P9K_PACKAGE_VERSION The value of `version` field in package.json. - # - # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' - # Custom icon. - # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## - # Rbenv color. - typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 - # Hide ruby version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) - # If set to false, hide ruby version if it's the same as global: - # $(rbenv version-name) == $(rbenv global). - typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide ruby version if it's equal to "system". - typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## - # Rvm color. - typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 - # Don't show @gemset at the end. - typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false - # Don't show ruby- at the front. - typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false - # Custom icon. - # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ - # Fvm color. - typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 - # Custom icon. - # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### - # Lua color. - typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 - # Hide lua version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) - # If set to false, hide lua version if it's the same as global: - # $(luaenv version-name) == $(luaenv global). - typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide lua version if it's equal to "system". - typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ - # Java color. - typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 - # Hide java version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) - # If set to false, hide java version if it's the same as global: - # $(jenv version-name) == $(jenv global). - typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide java version if it's equal to "system". - typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ - # Perl color. - typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 - # Hide perl version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) - # If set to false, hide perl version if it's the same as global: - # $(plenv version-name) == $(plenv global). - typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide perl version if it's equal to "system". - typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############ - # Perlbrew color. - typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67 - # Show perlbrew version only when in a perl project subdirectory. - typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true - # Don't show "perl-" at the front. - typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false - # Custom icon. - # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ - # PHP color. - typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 - # Hide php version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) - # If set to false, hide php version if it's the same as global: - # $(phpenv version-name) == $(phpenv global). - typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide php version if it's equal to "system". - typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### - # Scala color. - typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 - # Hide scala version if it doesn't come from one of these sources. - typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) - # If set to false, hide scala version if it's the same as global: - # $(scalaenv version-name) == $(scalaenv global). - typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false - # If set to false, hide scala version if it's equal to "system". - typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true - # Custom icon. - # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### - # Haskell color. - typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 - # Hide haskell version if it doesn't come from one of these sources. - # - # shell: version is set by STACK_YAML - # local: version is set by stack.yaml up the directory tree - # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) - typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) - # If set to false, hide haskell version if it's the same as in the implicit global project. - typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true - # Custom icon. - # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# - # Show kubecontext only when the command you are typing invokes one of these tools. - # Tip: Remove the next line to always show kubecontext. - typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold' - - # Kubernetes context classes for the purpose of using different colors, icons and expansions with - # different contexts. - # - # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element - # in each pair defines a pattern against which the current kubernetes context gets matched. - # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) - # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, - # you'll see this value in your prompt. The second element of each pair in - # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The - # first match wins. - # - # For example, given these settings: - # - # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( - # '*prod*' PROD - # '*test*' TEST - # '*' DEFAULT) - # - # If your current kubernetes context is "deathray-testing/default", its class is TEST - # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. - # - # You can define different colors, icons and content expansions for different classes: - # - # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 - # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' - typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( - # '*prod*' PROD # These values are examples that are unlikely - # '*test*' TEST # to match your needs. Customize them as needed. - '*' DEFAULT) - typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 - # typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' - - # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext - # segment. Parameter expansions are very flexible and fast, too. See reference: - # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. - # - # Within the expansion the following parameters are always available: - # - # - P9K_CONTENT The content that would've been displayed if there was no content - # expansion defined. - # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the - # output of `kubectl config get-contexts`. - # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the - # output of `kubectl config get-contexts`. - # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE - # in the output of `kubectl config get-contexts`. If there is no - # namespace, the parameter is set to "default". - # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the - # output of `kubectl config get-contexts`. - # - # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), - # the following extra parameters are available: - # - # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". - # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. - # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. - # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. - # - # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, - # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": - # - # - P9K_KUBECONTEXT_CLOUD_NAME=gke - # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account - # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a - # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 - # - # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": - # - # - P9K_KUBECONTEXT_CLOUD_NAME=eks - # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 - # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 - # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 - typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= - # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. - POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' - # Append the current context's namespace if it's not "default". - POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' - - # Custom prefix. - # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%fat ' - - ################[ terraform: terraform workspace (https://www.terraform.io) ]################# - # Don't show terraform workspace if it's literally "default". - typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false - # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element - # in each pair defines a pattern against which the current terraform workspace gets matched. - # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) - # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, - # you'll see this value in your prompt. The second element of each pair in - # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The - # first match wins. - # - # For example, given these settings: - # - # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( - # '*prod*' PROD - # '*test*' TEST - # '*' OTHER) - # - # If your current terraform workspace is "project_test", its class is TEST because "project_test" - # doesn't match the pattern '*prod*' but does match '*test*'. - # - # You can define different colors, icons and content expansions for different classes: - # - # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 - # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' - typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( - # '*prod*' PROD # These values are examples that are unlikely - # '*test*' TEST # to match your needs. Customize them as needed. - '*' OTHER) - typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 - # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #############[ terraform_version: terraform version (https://www.terraform.io) ]############## - # Terraform version color. - typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 - # Custom icon. - # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# - # Show aws only when the command you are typing invokes one of these tools. - # Tip: Remove the next line to always show aws. - typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|terraform|pulumi|terragrunt' - - # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element - # in each pair defines a pattern against which the current AWS profile gets matched. - # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) - # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, - # you'll see this value in your prompt. The second element of each pair in - # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The - # first match wins. - # - # For example, given these settings: - # - # typeset -g POWERLEVEL9K_AWS_CLASSES=( - # '*prod*' PROD - # '*test*' TEST - # '*' DEFAULT) - # - # If your current AWS profile is "company_test", its class is TEST - # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. - # - # You can define different colors, icons and content expansions for different classes: - # - # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 - # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' - typeset -g POWERLEVEL9K_AWS_CLASSES=( - # '*prod*' PROD # These values are examples that are unlikely - # '*test*' TEST # to match your needs. Customize them as needed. - '*' DEFAULT) - typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 - # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' - - # AWS segment format. The following parameters are available within the expansion. - # - # - P9K_AWS_PROFILE The name of the current AWS profile. - # - P9K_AWS_REGION The region associated with the current AWS profile. - typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' - - #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# - # AWS Elastic Beanstalk environment color. - typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 - # Custom icon. - # typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## - # Show azure only when the command you are typing invokes one of these tools. - # Tip: Remove the next line to always show azure. - typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' - # Azure account name color. - typeset -g POWERLEVEL9K_AZURE_FOREGROUND=32 - # Custom icon. - # typeset -g POWERLEVEL9K_AZURE_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### - # Show gcloud only when the command you are typing invokes one of these tools. - # Tip: Remove the next line to always show gcloud. - typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil' - # Google cloud color. - typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 - - # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or - # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative - # enough. You can use the following parameters in the expansions. Each of them corresponds to the - # output of `gcloud` tool. - # - # Parameter | Source - # -------------------------|-------------------------------------------------------------------- - # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' - # P9K_GCLOUD_ACCOUNT | gcloud config get-value account - # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project - # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' - # - # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. - # - # Obtaining project name requires sending a request to Google servers. This can take a long time - # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud - # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets - # set and gcloud prompt segment transitions to state COMPLETE. - # - # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL - # and COMPLETE. You can also hide gcloud in state PARTIAL by setting - # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and - # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. - typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' - typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' - - # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name - # this often. Negative value disables periodic polling. In this mode project name is retrieved - # only when the current configuration, account or project id changes. - typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 - - # Custom icon. - # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# - # Show google_app_cred only when the command you are typing invokes one of these tools. - # Tip: Remove the next line to always show google_app_cred. - typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' - - # Google application credentials classes for the purpose of using different colors, icons and - # expansions with different credentials. - # - # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first - # element in each pair defines a pattern against which the current kubernetes context gets - # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion - # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION - # parameters, you'll see this value in your prompt. The second element of each pair in - # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. - # The first match wins. - # - # For example, given these settings: - # - # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( - # '*:*prod*:*' PROD - # '*:*test*:*' TEST - # '*' DEFAULT) - # - # If your current Google application credentials is "service_account deathray-testing x@y.com", - # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. - # - # You can define different colors, icons and content expansions for different classes: - # - # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 - # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' - # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' - typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( - # '*:*prod*:*' PROD # These values are examples that are unlikely - # '*:*test*:*' TEST # to match your needs. Customize them as needed. - '*' DEFAULT) - typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 - # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' - - # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by - # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: - # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. - # - # You can use the following parameters in the expansion. Each of them corresponds to one of the - # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. - # - # Parameter | JSON key file field - # ---------------------------------+--------------- - # P9K_GOOGLE_APP_CRED_TYPE | type - # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id - # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email - # - # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. - typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' - - ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### - # Toolbox color. - typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 - # Don't display the name of the toolbox if it matches fedora-toolbox-*. - typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' - # Custom icon. - # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' - # Custom prefix. - # typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%fin ' - - ###############################[ public_ip: public IP address ]############################### - # Public IP color. - typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 - # Custom icon. - # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ########################[ vpn_ip: virtual private network indicator ]######################### - # VPN IP color. - typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 - # When on VPN, show just an icon without the IP address. - # Tip: To display the private IP address when on VPN, remove the next line. - typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= - # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN - # to see the name of the interface. - typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*' - # If set to true, show one segment per matching network interface. If set to false, show only - # one segment corresponding to the first matching network interface. - # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. - typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false - # Custom icon. - # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### - # IP color. - typeset -g POWERLEVEL9K_IP_FOREGROUND=38 - # The following parameters are accessible within the expansion: - # - # Parameter | Meaning - # ----------------------+------------------------------------------- - # P9K_IP_IP | IP address - # P9K_IP_INTERFACE | network interface - # P9K_IP_RX_BYTES | total number of bytes received - # P9K_IP_TX_BYTES | total number of bytes sent - # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt - # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt - # P9K_IP_RX_RATE | receive rate (since last prompt) - # P9K_IP_TX_RATE | send rate (since last prompt) - typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='$P9K_IP_IP${P9K_IP_RX_RATE:+ %70F⇣$P9K_IP_RX_RATE}${P9K_IP_TX_RATE:+ %215F⇡$P9K_IP_TX_RATE}' - # Show information for the first network interface whose name matches this regular expression. - # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. - typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' - # Custom icon. - # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' - - #########################[ proxy: system-wide http/https/ftp proxy ]########################## - # Proxy color. - typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 - # Custom icon. - # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' - - ################################[ battery: internal battery ]################################# - # Show battery in red when it's below this level and not connected to power supply. - typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 - typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 - # Show battery in green when it's charging or fully charged. - typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 - # Show battery in yellow when it's discharging. - typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 - # Battery pictograms going from low to high level of charge. - typeset -g POWERLEVEL9K_BATTERY_STAGES='\uf58d\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf578' - # Don't show the remaining time to charge/discharge. - typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false - - #####################################[ wifi: wifi speed ]##################################### - # WiFi color. - typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 - # Custom icon. - # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' - - # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). - # - # # Wifi colors and icons for different signal strength levels (low to high). - # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values - # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values - # - # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' - # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' - # - # The following parameters are accessible within the expansions: - # - # Parameter | Meaning - # ----------------------+--------------- - # P9K_WIFI_SSID | service set identifier, a.k.a. network name - # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown - # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second - # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 - # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 - # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) - - ####################################[ time: current time ]#################################### - # Current time color. - typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 - # Format for the current time: 09:51:02. See `man 3 strftime`. - typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}' - # If set to true, time will update when you hit enter. This way prompts for the past - # commands will contain the start times of their commands as opposed to the default - # behavior where they contain the end times of their preceding commands. - typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false - # Custom icon. - typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= - # Custom prefix. - # typeset -g POWERLEVEL9K_TIME_PREFIX='%fat ' - - # Example of a user-defined prompt segment. Function prompt_example will be called on every - # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or - # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. - # - # Type `p10k help segment` for documentation and a more sophisticated example. - function prompt_example() { - p10k segment -f 208 -i '⭐' -t 'hello, %n' - } - - # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job - # is to generate the prompt segment for display in instant prompt. See - # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. - # - # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function - # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k - # will replay these calls without actually calling instant_prompt_*. It is imperative that - # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this - # rule is not observed, the content of instant prompt will be incorrect. - # - # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If - # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. - function instant_prompt_example() { - # Since prompt_example always makes the same `p10k segment` calls, we can call it from - # instant_prompt_example. This will give us the same `example` prompt segment in the instant - # and regular prompts. - prompt_example - } - - # User-defined prompt segments can be customized the same way as built-in segments. - # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 - # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' - - # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt - # when accepting a command line. Supported values: - # - # - off: Don't change prompt when accepting a command line. - # - always: Trim down prompt when accepting a command line. - # - same-dir: Trim down prompt when accepting a command line unless this is the first command - # typed after changing current working directory. - typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always - - # Instant prompt mode. - # - # - off: Disable instant prompt. Choose this if you've tried instant prompt and found - # it incompatible with your zsh configuration files. - # - quiet: Enable instant prompt and don't print warnings when detecting console output - # during zsh initialization. Choose this if you've read and understood - # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. - # - verbose: Enable instant prompt and print a warning when detecting console output during - # zsh initialization. Choose this if you've never tried instant prompt, haven't - # seen the warning, or if you are unsure what this all means. - typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet - - # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. - # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload - # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you - # really need it. - typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true - - # If p10k is already loaded, reload configuration. - # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. - (( ! $+functions[p10k] )) || p10k reload -} - -typeset -g POWERLEVEL9K_TERM_SHELL_INTEGRATION=true - -# Tell `p10k configure` which file it should overwrite. -typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} - -(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} -'builtin' 'unset' 'p10k_config_opts' diff --git a/zsh/.config/zsh/.zshenv b/zsh/.config/zsh/.zshenv deleted file mode 100644 index 85815a6..0000000 --- a/zsh/.config/zsh/.zshenv +++ /dev/null @@ -1,167 +0,0 @@ -#!/bin/zsh -# profile file. Runs on login. Environmental variables are set here. - -# If you don't plan on reverting to bash, you can remove the link in ~/.profile -# to clean up. - -export FPATH="$FPATH:${XDG_DATA_HOME:-$HOME/.local/share/zsh/site-functions}" - -unsetopt PROMPT_SP - -# Default programs: -export EDITOR="lvim +star" -export BROWSER="chromium" - -# ~/ Clean-up: -[ -z "$XDG_CONFIG_HOME" ] && export XDG_CONFIG_HOME="$HOME/.config" -[ -z "$XDG_DATA_HOME" ] && export XDG_DATA_HOME="$HOME/.local/share" -[ -z "$XDG_CACHE_HOME" ] && export XDG_CACHE_HOME="$HOME/.cache" -[ -z "$XDG_STATE_HOME" ] && export XDG_STATE_HOME="$HOME/.local/state" -[ -z "$XDG_BIN_HOME" ] && export XDG_BIN_HOME="$HOME/.local/bin" - -export USERXSESSION="$XDG_CACHE_HOME/X11/xsession" -export USERXSESSIONRC="$XDG_CACHE_HOME/X11/xsessionrc" -export ALTUSERXSESSION="$XDG_CACHE_HOME/X11/Xsession" -export ERRFILE="$XDG_CACHE_HOME/X11/xsession-errors" -export GPG_TTY=$TTY -export XINITRC="${XDG_CONFIG_HOME:-$HOME/.config}/x11/xinitrc" -# this breaks X forwarding over SSH -#export XAUTHORITY="$XDG_RUNTIME_DIR/Xauthority" # This line will break some DMs. -export NOTMUCH_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/notmuch-config" -export GTK2_RC_FILES="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-2.0/gtkrc-2.0" -export LESSHISTFILE="-" -export WGETRC="${XDG_CONFIG_HOME:-$HOME/.config}/wget/wgetrc" -export INPUTRC="${XDG_CONFIG_HOME:-$HOME/.config}/shell/inputrc" -export ZDOTDIR="${XDG_CONFIG_HOME:-$HOME/.config}/zsh" -export NUGET_PACKAGES="$XDG_CACHE_HOME/NuGetPackages" -export AWS_SHARED_CREDENTIALS_FILE="$XDG_CONFIG_HOME/aws/credentials" -export AWS_CONFIG_FILE="$XDG_CONFIG_HOME/aws/config" -export ALSA_CONFIG_PATH="$XDG_CONFIG_HOME/alsa/asoundrc" -export AZURE_CONFIG_DIR="$XDG_DATA_HOME/azure" -export MIX_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/mix" -export GNUPGHOME="${XDG_DATA_HOME:-$HOME/.local/share}/gnupg" -export WINEPREFIX="${XDG_DATA_HOME:-$HOME/.local/share}/wineprefixes/default" -export KODI_DATA="${XDG_DATA_HOME:-$HOME/.local/share}/kodi" -export PASSWORD_STORE_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/password-store" -export TMUX_TMPDIR="$XDG_RUNTIME_DIR" -export ANDROID_SDK_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/android" -export CARGO_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/cargo" -export GOBIN="${XDG_BIN_HOME:-$HOME/.local/bin}" -export GOPATH="${XDG_DATA_HOME:-$HOME/.local/share}/go" -export GOMODCACHE="$XDG_CACHE_HOME"/go/mod -export VOLTA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/volta" -export PNPM_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/pnpm" -export DENO_INSTALL_ROOT="${XDG_BIN_HOME:-$HOME/.local/bin}" -export ANSIBLE_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/ansible/ansible.cfg" -export UNISON="${XDG_DATA_HOME:-$HOME/.local/share}/unison" -export HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/history" -export WEECHAT_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/weechat" -export MBSYNCRC="${XDG_CONFIG_HOME:-$HOME/.config}/mbsync/config" -export ELECTRUMDIR="${XDG_DATA_HOME:-$HOME/.local/share}/electrum" -export OCTAVE_SITE_INITFILE="$XDG_CONFIG_HOME/octave/octaverc" -export OCTAVE_HISTFILE="$XDG_CACHE_HOME/octave/octave_hist" -export ICEAUTHORITY="$XDG_CACHE_HOME/ICEauthority" -export HISTORY_IGNORE="(ls|cd|pwd|exit|sudo reboot|history|cd -|cd ..|clear|fg)" -export GDBHISTFILE="$XDG_DATA_HOME"/gdb/history -export SONARLINT_USER_HOME="$XDG_DATA_HOME/sonarlint" -export OMNISHARPHOME="$XDG_CONFIG_HOME"/omnisharp -export MIX_XDG=true - -# Other program settings: -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DICS="/usr/share/stardict/dic/" -export SUDO_ASKPASS="$HOME/.local/bin/dmenupass" -export FZF_DEFAULT_OPTS="--layout=reverse --height 40%" -export LESS=-R -export LESS_TERMCAP_mb="$(printf '%b' '')" -export LESS_TERMCAP_md="$(printf '%b' '')" -export LESS_TERMCAP_me="$(printf '%b' '')" -export LESS_TERMCAP_so="$(printf '%b' '')" -export LESS_TERMCAP_se="$(printf '%b' '')" -export LESS_TERMCAP_us="$(printf '%b' '')" -export LESS_TERMCAP_ue="$(printf '%b' '')" -export LESSOPEN="| /usr/bin/highlight -O ansi %s 2>/dev/null" -export QT_QPA_PLATFORMTHEME="qt5ct" # Have QT use gtk2 theme. -export MOZ_USE_XINPUT2="1" # Mozilla smooth scrolling/touchpads. -export AWT_TOOLKIT="MToolkit wmname LG3D" #May have to install wmname -export _JAVA_AWT_WM_NONREPARENTING=1 # Fix for Java applications in dwm -export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME"/java - -# This is the list for lf icons: -export LF_ICONS="di=📁:\ -fi=📃:\ -tw=🤝:\ -ow=📂:\ -ln=⛓:\ -or=❌:\ -ex=🎯:\ -*.txt=✍:\ -*.mom=✍:\ -*.me=✍:\ -*.ms=✍:\ -*.png=🖼:\ -*.webp=🖼:\ -*.ico=🖼:\ -*.jpg=📸:\ -*.jpe=📸:\ -*.jpeg=📸:\ -*.gif=🖼:\ -*.svg=🗺:\ -*.tif=🖼:\ -*.tiff=🖼:\ -*.xcf=🖌:\ -*.html=🌎:\ -*.xml=📰:\ -*.gpg=🔒:\ -*.css=🎨:\ -*.pdf=📚:\ -*.djvu=📚:\ -*.epub=📚:\ -*.csv=📓:\ -*.xlsx=📓:\ -*.tex=📜:\ -*.md=📘:\ -*.r=📊:\ -*.R=📊:\ -*.rmd=📊:\ -*.Rmd=📊:\ -*.m=📊:\ -*.mp3=🎵:\ -*.opus=🎵:\ -*.ogg=🎵:\ -*.m4a=🎵:\ -*.flac=🎼:\ -*.wav=🎼:\ -*.mkv=🎥:\ -*.mp4=🎥:\ -*.webm=🎥:\ -*.mpeg=🎥:\ -*.avi=🎥:\ -*.mov=🎥:\ -*.mpg=🎥:\ -*.wmv=🎥:\ -*.m4b=🎥:\ -*.flv=🎥:\ -*.zip=📦:\ -*.rar=📦:\ -*.7z=📦:\ -*.tar.gz=📦:\ -*.z64=🎮:\ -*.v64=🎮:\ -*.n64=🎮:\ -*.gba=🎮:\ -*.nes=🎮:\ -*.gdi=🎮:\ -*.1=ℹ:\ -*.nfo=ℹ:\ -*.info=ℹ:\ -*.log=📙:\ -*.iso=📀:\ -*.img=📀:\ -*.bib=🎓:\ -*.ged=👪:\ -*.part=💔:\ -*.torrent=🔽:\ -*.jar=♨:\ -*.java=♨:\ -" diff --git a/zsh/.config/zsh/.zshopts b/zsh/.config/zsh/.zshopts deleted file mode 100755 index 6756bfd..0000000 --- a/zsh/.config/zsh/.zshopts +++ /dev/null @@ -1,71 +0,0 @@ -#! /usr/bin/env zsh -# History settings -HISTSIZE=50000 -SAVEHIST=10000 - -setopt append_history # append the history -setopt inc_append_history # append to history in the current - # session and not just when the session ends -setopt share_history # share history between sessions -setopt extended_history # include statistics of when/how long/etc a - # command has run -setopt hist_ignore_dups # do not store dupes executed after eachother -setopt hist_ignore_all_dups # removes copies of the same line -setopt hist_expire_dups_first # removes copies when the histfile fills up -setopt hist_save_no_dups # don't save dupes from the same session -setopt hist_find_no_dups # if we find dupes in the history, don't show - # them in editor commands) -setopt hist_reduce_blanks # remove blank lines from the command which - # mean nothing to the shell - -# Disable this on boxes that are affected by bug -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924736 -# is-at-least 5.5 && unsetopt hist_reduce_blanks -# bugfix is incoming, lets see what it does -# is-at-least 5.7.2 && setopt hist_reduce_blanks - -setopt hist_ignore_space # lines starting with space don't go into the - # history -setopt no_hist_beep # silence..! -setopt hist_verify -setopt hist_no_store # don't store history/fc commands -#setopt hist_no_functions # don't show history of function definitions - -setopt bg_nice # nice bg commands -setopt notify # notify when a command returns exit code - -setopt no_beep # silence..! - -#unsetopt auto_cd # disable $ ./bin as cd ./bin -setopt extendedglob # ls ^bla.* will not show ^bla.txt for example - -setopt correct # correct incorrent cmd's -setopt correctall # correct everything, use - # `nocorrect mv foo bar` to negate this feature - # for a command - -setopt hash_list_all # fill the lookup table for tab completions - -unsetopt promptcr # prevent the prompt overwriting output when - # there is no newline - -unsetopt nomatch # -setopt prompt_subst # Enable prompt substition - -setopt glob_subst # global substitution -setopt globdots # Also look for . files - -setopt longlistjobs -setopt completeinword - -# Directories -setopt auto_pushd # cd foo == pushd foo -setopt pushd_ignore_dups # no duplicates in the list -setopt pushdminus -setopt auto_name_dirs # foo=/path/to/foo is the same as - # hash -d foo=/path/to/foo - -# Misc -setopt interactivecomments # $ # foo doesn't become an error when hitting - # enter - diff --git a/zsh/.config/zsh/.zshrc b/zsh/.config/zsh/.zshrc deleted file mode 100644 index bdd34e1..0000000 --- a/zsh/.config/zsh/.zshrc +++ /dev/null @@ -1,128 +0,0 @@ -# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.config/zsh/.zshrc. -# Initialization code that may require console input (password prompts, [y/n] -# confirmations, etc.) must go above this block; everything else may go below. -[[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]] && source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" -# add .local/bin to PATH -export PATH="${XDG_BIN_HOME:-$HOME/.local/bin}:$PATH" -#export PATH="${$(find ~/.local/bin -type d -printf %p:)%%:}:$PATH" - -eval "$(antidot init)" -#eval "$(lesspipe.sh)" - -# Luke's config for the Zoomer Shell - -if [ -f /etc/os-release ] -then - . /etc/os-release -else - ID=`uname -s` -fi -case "$ID" in - Darwin ) - source $HOME/.nix-profile/share/zsh-powerlevel10k/powerlevel10k.zsh-theme - ;; - * ) - [ -f /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme ] && source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme || source ${XDG_DATA_HOME:-$HOME/.local/share}/powerlevel10k/powerlevel10k.zsh-theme - ;; -esac - - -# Enable colors and change prompt: -autoload -U colors && colors # Load colors -setopt autocd # Automatically cd into typed directory. -#stty stop undef # Disable ctrl-s to freeze terminal. -setopt interactive_comments -setopt complete_aliases - -# History in cache directory: -HISTSIZE=100000000 -SAVEHIST=100000000 -HISTFILE="$XDG_STATE_HOME"/zsh/history -setopt BANG_HIST # Treat the '!' character specially during expansion. -setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format. -setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits. -setopt SHARE_HISTORY # Share history between all sessions. -setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history. -setopt HIST_IGNORE_DUPS # Don't record an entry that was just recorded again. -setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate. -setopt HIST_FIND_NO_DUPS # Do not display a line previously found. -setopt HIST_IGNORE_SPACE # Don't record an entry starting with a space. -setopt HIST_SAVE_NO_DUPS # Don't write duplicate entries in the history file. -setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry. -setopt HIST_VERIFY # Don't execute immediately upon history expansion. -setopt HIST_BEEP # Beep when accessing nonexistent history. - -# Load aliases and shortcuts if existent. -[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" -[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" -[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" -[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshopts" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshopts" - -# Basic auto/tab complete: -fpath=(~/.config/zsh/completions $fpath) -autoload -U compinit -zstyle ':completion:*' menu select -zmodload zsh/complist -#compinit -compinit -d ~/.cache/zsh/zcompdump-$ZSH_VERSION -_comp_options+=(globdots) # Include hidden files. - -# To customize prompt, run `p10k configure` or edit ~/.config/zsh/. p10k.zsh. -[[ ! -f ~/.config/zsh/.p10k.zsh ]] || source ~/.config/zsh/.p10k.zsh -[[ ! -f ~/.config/zsh/keybindings.zsh ]] || source ~/.config/zsh/keybindings.zsh - -# zsh parameter completion for the dotnet CLI -_dotnet_zsh_complete() -{ - local completions=("$(dotnet complete "$words")") - - # If the completion list is empty, just continue with filename selection - if [ -z "$completions" ] - then - _arguments '*::arguments: _normal' - return - fi - - # This is not a variable assignment, don't remove spaces! - _values = "${(ps:\n:)completions}" -} - -compdef _dotnet_zsh_complete dotnet - -[[ -f ~/.config/tabtab/zsh/__tabtab.zsh ]] && . ~/.config/tabtab/zsh/__tabtab.zsh || true - -if [ -n $WSL_DISTRO_NAME ] -then - command_not_found_handler() { - if [ -x "$(command -v $@.exe)" ]; then - $@.exe - else - echo >&2 "zsh: command not found: $@" - return 127 - fi -} -fi - - -case "$ID" in - debian | ubuntu ) - source /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh - source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh - ;; - arch | artix | msys2 ) - source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh - source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh - ;; - gentoo ) - source /usr/share/zsh/site-functions/zsh-syntax-highlighting.zsh - source /usr/share/zsh/site-functions/zsh-autosuggestions.zsh - ;; - Darwin ) - source $HOME/.nix-profile/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh - source $HOME/.nix-profile/share/zsh-autosuggestions/zsh-autosuggestions.zsh - ;; - * ) - echo "no highlight for you" - ;; -esac - diff --git a/zsh/.config/zsh/keybindings.zsh b/zsh/.config/zsh/keybindings.zsh deleted file mode 100644 index 33b8195..0000000 --- a/zsh/.config/zsh/keybindings.zsh +++ /dev/null @@ -1,138 +0,0 @@ -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Standard-Widgets - -# Make sure that the terminal is in application mode when zle is active, since -# only then values from $terminfo are valid -if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then - function zle-line-init() { - echoti smkx - } - function zle-line-finish() { - echoti rmkx - } - zle -N zle-line-init - zle -N zle-line-finish -fi - -# Use emacs key bindings -bindkey -e - -# [PageUp] - Up a line of history -if [[ -n "${terminfo[kpp]}" ]]; then - bindkey -M emacs "${terminfo[kpp]}" up-line-or-history - bindkey -M viins "${terminfo[kpp]}" up-line-or-history - bindkey -M vicmd "${terminfo[kpp]}" up-line-or-history -fi -# [PageDown] - Down a line of history -if [[ -n "${terminfo[knp]}" ]]; then - bindkey -M emacs "${terminfo[knp]}" down-line-or-history - bindkey -M viins "${terminfo[knp]}" down-line-or-history - bindkey -M vicmd "${terminfo[knp]}" down-line-or-history -fi - -# Start typing + [Up-Arrow] - fuzzy find history forward -if [[ -n "${terminfo[kcuu1]}" ]]; then - autoload -U up-line-or-beginning-search - zle -N up-line-or-beginning-search - - bindkey -M emacs "${terminfo[kcuu1]}" up-line-or-beginning-search - bindkey -M viins "${terminfo[kcuu1]}" up-line-or-beginning-search - bindkey -M vicmd "${terminfo[kcuu1]}" up-line-or-beginning-search -fi -# Start typing + [Down-Arrow] - fuzzy find history backward -if [[ -n "${terminfo[kcud1]}" ]]; then - autoload -U down-line-or-beginning-search - zle -N down-line-or-beginning-search - - bindkey -M emacs "${terminfo[kcud1]}" down-line-or-beginning-search - bindkey -M viins "${terminfo[kcud1]}" down-line-or-beginning-search - bindkey -M vicmd "${terminfo[kcud1]}" down-line-or-beginning-search -fi - -# [Home] - Go to beginning of line -if [[ -n "${terminfo[khome]}" ]]; then - bindkey -M emacs "${terminfo[khome]}" beginning-of-line - bindkey -M viins "${terminfo[khome]}" beginning-of-line - bindkey -M vicmd "${terminfo[khome]}" beginning-of-line -fi -# [End] - Go to end of line -if [[ -n "${terminfo[kend]}" ]]; then - bindkey -M emacs "${terminfo[kend]}" end-of-line - bindkey -M viins "${terminfo[kend]}" end-of-line - bindkey -M vicmd "${terminfo[kend]}" end-of-line -fi - -# [Shift-Tab] - move through the completion menu backwards -if [[ -n "${terminfo[kcbt]}" ]]; then - bindkey -M emacs "${terminfo[kcbt]}" reverse-menu-complete - bindkey -M viins "${terminfo[kcbt]}" reverse-menu-complete - bindkey -M vicmd "${terminfo[kcbt]}" reverse-menu-complete -fi - -# [Backspace] - delete backward -bindkey -M emacs '^?' backward-delete-char -bindkey -M viins '^?' backward-delete-char -bindkey -M vicmd '^?' backward-delete-char -# [Delete] - delete forward -if [[ -n "${terminfo[kdch1]}" ]]; then - bindkey -M emacs "${terminfo[kdch1]}" delete-char - bindkey -M viins "${terminfo[kdch1]}" delete-char - bindkey -M vicmd "${terminfo[kdch1]}" delete-char -else - bindkey -M emacs "^[[3~" delete-char - bindkey -M viins "^[[3~" delete-char - bindkey -M vicmd "^[[3~" delete-char - - bindkey -M emacs "^[3;5~" delete-char - bindkey -M viins "^[3;5~" delete-char - bindkey -M vicmd "^[3;5~" delete-char -fi - -# [Ctrl-Delete] - delete whole forward-word -bindkey -M emacs '^[[3;5~' kill-word -bindkey -M viins '^[[3;5~' kill-word -bindkey -M vicmd '^[[3;5~' kill-word - -# [Ctrl-RightArrow] - move forward one word -bindkey -M emacs '^[[1;5C' forward-word -bindkey -M viins '^[[1;5C' forward-word -bindkey -M vicmd '^[[1;5C' forward-word -# [Ctrl-LeftArrow] - move backward one word -bindkey -M emacs '^[[1;5D' backward-word -bindkey -M viins '^[[1;5D' backward-word -bindkey -M vicmd '^[[1;5D' backward-word - - -bindkey '\ew' kill-region # [Esc-w] - Kill from the cursor to the mark -bindkey -s '\el' 'ls\n' # [Esc-l] - run command: ls -bindkey '^r' history-incremental-search-backward # [Ctrl-r] - Search backward incrementally for a specified string. The string may begin with ^ to anchor the search to the beginning of the line. -bindkey ' ' magic-space # [Space] - don't do history expansion - - -# Edit the current command line in $EDITOR -autoload -U edit-command-line -zle -N edit-command-line -bindkey '\C-x\C-e' edit-command-line - -# file rename magick -bindkey "^[m" copy-prev-shell-word - -# consider emacs keybindings: - -#bindkey -e ## emacs key bindings -# -#bindkey '^[[A' up-line-or-search -#bindkey '^[[B' down-line-or-search -#bindkey '^[^[[C' emacs-forward-word -#bindkey '^[^[[D' emacs-backward-word -# -#bindkey -s '^X^Z' '%-^M' -#bindkey '^[e' expand-cmd-path -#bindkey '^[^I' reverse-menu-complete -#bindkey '^X^N' accept-and-infer-next-history -#bindkey '^W' kill-region -#bindkey '^I' complete-word -## Fix weird sequence that rxvt produces -#bindkey -s '^[[Z' '\t' -# \ No newline at end of file -- cgit v1.2.3