Module: PWN::Plugins::REPL::Mesh
- Defined in:
- lib/pwn/plugins/repl/mesh.rb
Overview
pwn-mesh REPL mode.
Constant Summary collapse
- PWN_MESH_TRANSPORTS =
%i[serial bluetooth tcp mqtt].freeze
Class Method Summary collapse
- .add_commands ⇒ Object
-
.authors ⇒ Object
- Author(s)
0day Inc.
-
.help ⇒ Object
Display usage for this module.
Class Method Details
.add_commands ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/pwn/plugins/repl/mesh.rb', line 16 def self.add_commands Pry::Commands.create_command 'pwn-mesh' do description 'Communicate with Meshtastic network within pwn REPL.' def process pi = pry_instance # meshtastic is a *setup-managed* gem (see pwn.gemspec / PWN::Setup): # its rubygems.org releases carry `required_ruby_version >= 4.0`, so # it cannot be a hard runtime dependency while pwn supports ruby 3.3+. begin require 'meshtastic' rescue LoadError => e output.puts "pwn-mesh unavailable: #{e.}" output.puts " meshtastic requires ruby >= 4.0 (running #{RUBY_VERSION})." if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('4.0.0') output.puts ' Run: `pwn setup --profile full` (or `gem install meshtastic`) on ruby >= 4.0.' return end pi.config.pwn_mesh = true pi.config.pwn_ai = false pi.config.pwn_asm = false meshtastic_env = PWN::Env[:plugins][:meshtastic] || {} PWN.send(:remove_const, :MeshTxEchoThread) if PWN.const_defined?(:MeshTxEchoThread) PWN.send(:remove_const, :MqttObj) if PWN.const_defined?(:MqttObj) PWN.send(:remove_const, :MeshObj) if PWN.const_defined?(:MeshObj) PWN.send(:remove_const, :MeshRxHeaderWin) if PWN.const_defined?(:MeshRxHeaderWin) PWN.send(:remove_const, :MeshRxBodyWin) if PWN.const_defined?(:MeshRxBodyWin) PWN.send(:remove_const, :MeshTxWin) if PWN.const_defined?(:MeshTxWin) PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex) PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread) PWN.send(:remove_const, :MeshSubThread) if PWN.const_defined?(:MeshSubThread) PWN.send(:remove_const, :MeshTransport) if PWN.const_defined?(:MeshTransport) PWN.send(:remove_const, :MeshTxPrompt) if PWN.const_defined?(:MeshTxPrompt) PWN.send(:remove_const, :MeshTxEpoch) if PWN.const_defined?(:MeshTxEpoch) PWN.send(:remove_const, :MeshTxBlank) if PWN.const_defined?(:MeshTxBlank) PWN.send(:remove_const, :MeshLastSubmit) if PWN.const_defined?(:MeshLastSubmit) PWN.send(:remove_const, :MeshRxState) if PWN.const_defined?(:MeshRxState) begin mesh_obj = PWN::Plugins::REPL.send(:mesh_connect, env: meshtastic_env) rescue StandardError => e output.puts "pwn-mesh unavailable: #{e.class}: #{e.}" pi.config.pwn_mesh = false return end PWN.const_set(:MeshObj, mesh_obj) PWN.const_set(:MqttObj, mesh_obj) active_channel = meshtastic_env[:channel][:active].to_s.to_sym channel_env = meshtastic_env[:channel][active_channel] psk = channel_env[:psk] region = channel_env[:region] topic = channel_env[:topic] channel_num = channel_env[:channel_num] link = PWN::Plugins::REPL.send(:mesh_link_label, env: meshtastic_env) # Init ncurses UI (idempotent) with separate RX (top) and TX (bottom) panes Curses.init_screen Curses.curs_set(0) Curses.noecho Curses.cbreak Curses.crmode Curses.ESCDELAY = 0 Curses.start_color Curses.use_default_colors Curses.init_pair(20, Curses::COLOR_CYAN, -1) Curses.init_pair(21, Curses::COLOR_YELLOW, -1) Curses.init_pair(22, Curses::COLOR_BLACK, Curses::COLOR_CYAN) Curses.init_pair(23, Curses::COLOR_GREEN, -1) Curses.init_pair(24, Curses::COLOR_WHITE, -1) PWN.const_set(:MeshColors, [20, 23, 21]) PWN.const_set(:MeshLastColor, 20) layout = PWN::Plugins::REPL.send(:mesh_layout, lines: Curses.lines, cols: Curses.cols) mesh_tx_rows = layout[:tx] mesh_header_rows = layout[:header] body_height = layout[:body] rx_header_win = Curses::Window.new(mesh_header_rows, Curses.cols, 0, 0) rx_header_win.scrollok(false) rx_header_win.nodelay = true PWN::Plugins::REPL.send(:mesh_box!, win: rx_header_win) rx_header_win.attron(Curses.color_pair(20) | Curses::A_BOLD) rx_header = " pwn.mesh #{link} #{region}/#{topic} ch #{channel_num} " inner_cols = [Curses.cols - 2, 1].max rx_header_win.setpos(1, 1) rx_header_win.addstr(rx_header[0, inner_cols].to_s.ljust(inner_cols)) rx_header_win.attroff(Curses.color_pair(20) | Curses::A_BOLD) rx_header_win.refresh PWN.const_set(:MeshRxHeaderWin, rx_header_win) body_start_row = mesh_header_rows frame = Curses::Window.new(body_height, Curses.cols, body_start_row, 0) PWN::Plugins::REPL.send(:mesh_box!, win: frame) frame.setpos(0, 2) frame.addstr(' CONVERSATION ') frame.refresh PWN.const_set(:MeshRxFrameWin, frame) rx_body_win = Curses::Window.new( [body_height - 2, 1].max, [Curses.cols - 4, 1].max, body_start_row + (body_height > 2 ? 1 : 0), Curses.cols > 4 ? 2 : 0 ) rx_body_win.scrollok(true) rx_body_win.nodelay = true rx_body_win.refresh PWN.const_set(:MeshRxBodyWin, rx_body_win) tx_win = Curses::Window.new(mesh_tx_rows, Curses.cols, layout[:tx_top], 0) tx_win.scrollok(false) tx_win.nodelay = true PWN::Plugins::REPL.send(:mesh_box!, win: tx_win) tx_win.refresh PWN.const_set(:MeshTxWin, tx_win) PWN.const_set(:MeshMutex, Mutex.new) # Curses owns input and rendering until this command returns to Pry. PWN.const_set(:MeshEvents, Queue.new) PWN::Plugins::REPL.send(:mesh_start_rx!, env: meshtastic_env, obj: mesh_obj) PWN::Plugins::REPL.send(:mesh_refresh_ui!, env: meshtastic_env) PWN::Plugins::REPL.send(:mesh_ui_puts, text: 'Ready. /menu opens settings. Incoming channel messages appear here.') PWN::Plugins::REPL.send(:mesh_console_loop, pry: pi) rescue StandardError => e raise e ensure PWN::Plugins::REPL.leave_special_mode!(pry: pi) if pi&.config&.pwn_mesh end end end |
.authors ⇒ Object
- Author(s)
0day Inc. [email protected]
1862 1863 1864 1865 1866 |
# File 'lib/pwn/plugins/repl/mesh.rb', line 1862 public_class_method def self. "AUTHOR(S): 0day Inc. <[email protected]> " end |
.help ⇒ Object
Display usage for this module.
1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 |
# File 'lib/pwn/plugins/repl/mesh.rb', line 1870 public_class_method def self.help puts "USAGE: # Register the pwn-mesh Pry command. #{self}.add_commands # Curses arrow-key picker for pwn-mesh channel, transport, and device lists. #{self}.mesh_menu_pick( title: 'optional - window title drawn on the boxed curses menu', items: 'required - Array of selectable strings such as channel names', current: 'optional - currently selected item to highlight', getch: 'optional - proc returning the next key so specs can drive the menu without a TTY' ) # Clear the pwn-mesh TX buffer after a slash command or sent mesh line. #{self}.mesh_reset_input!( pry: 'optional - Pry instance whose Reline/line_buffer should be emptied', submitted: 'optional - the line that was just accepted so the TX pane can hide it' ) # Curses overlay rows for the pwn-mesh slash menu while typing a leading slash. #{self}.pwn_mesh_menu_rows( line: 'optional - current TX buffer; leading slash lists matching mesh commands' ) # TAB hits for pwn-mesh slash menus (commands, named channels, transports, devices). #{self}.pwn_mesh_complete( target: 'required - token Reline is completing in pwn-mesh', line: 'optional - full line buffer so /channel P<TAB> can list named channels' ) # Install Reline dropdown for pwn-mesh slash commands. #{self}.install_pwn_mesh_completer!( pry: 'optional - Pry instance stored for mesh TAB completion' ) # Run a leading-slash pwn-mesh command locally instead of sending it as mesh text. #{self}.pwn_mesh_dispatch_slash!( request: 'optional - full line such as /channel list or /transport serial', pry: 'optional - Pry instance used by /back to leave pwn-mesh' ) # Write plugins.meshtastic from the live Env into the encrypted pwn.yaml vault. #{self}.persist_mesh_env( mesh: 'optional - meshtastic Hash to persist (defaults to PWN::Env plugins.meshtastic)' ) # Print the AUTHOR(S) string for this module. #{self}.authors " constants.sort end |