• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 7250413177

18 Dec 2023 03:44PM UTC coverage: 71.866% (-2.7%) from 74.55%
7250413177

Pull #2297

github

web-flow
Merge fbc1d8e0b into c7649c95e
Pull Request #2297: add "retguard" property and display it with checksec

4328 of 7244 branches covered (0.0%)

5 of 6 new or added lines in 1 file covered. (83.33%)

464 existing lines in 9 files now uncovered.

12381 of 17228 relevant lines covered (71.87%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.8
/pwnlib/ui.py
1
from __future__ import absolute_import
1✔
2
from __future__ import division
1✔
3

4
import os
1✔
5
import signal
1✔
6
import six
1✔
7
import string
1✔
8
import struct
1✔
9
import subprocess
1✔
10
import sys
1✔
11
import time
1✔
12
import types
1✔
13

14
from pwnlib import term
1✔
15
from pwnlib.log import getLogger
1✔
16
from pwnlib.term.readline import raw_input
1✔
17
from pwnlib.tubes.process import process
1✔
18

19
log = getLogger(__name__)
1✔
20

21
def testpwnproc(cmd):
1✔
UNCOV
22
    import fcntl
×
UNCOV
23
    import termios
×
UNCOV
24
    env = dict(os.environ)
×
UNCOV
25
    env.pop("PWNLIB_NOTERM", None)
×
UNCOV
26
    env["TERM"] = "xterm-256color"
×
UNCOV
27
    def handleusr1(sig, frame):
×
28
        s = p.stderr.read()
×
29
        log.error("child process failed:\n%s", s.decode())
×
UNCOV
30
    signal.signal(signal.SIGUSR1, handleusr1)
×
UNCOV
31
    cmd = """\
×
32
import os
33
import signal
34
import sys
35
_ehook = sys.excepthook
36
def ehook(*args):
37
    _ehook(*args)
38
    os.kill(os.getppid(), signal.SIGUSR1)
39
sys.excepthook = ehook
40
from pwn import *
41
""" + cmd
UNCOV
42
    if "coverage" in sys.modules:
×
UNCOV
43
        cmd = "import coverage; coverage.process_startup()\n" + cmd
×
UNCOV
44
        env.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
×
UNCOV
45
    env['COLUMNS'] = '80'
×
UNCOV
46
    env['ROWS'] = '24'
×
UNCOV
47
    p = process([sys.executable, "-c", cmd], env=env, stderr=subprocess.PIPE)
×
48
    # late initialization can lead to EINTR in many places
UNCOV
49
    fcntl.ioctl(p.stdout.fileno(), termios.TIOCSWINSZ, struct.pack('HH', 24, 80))
×
UNCOV
50
    return p
×
51

52
def yesno(prompt, default=None):
1✔
53
    r"""Presents the user with prompt (typically in the form of question)
54
    which the user must answer yes or no.
55

56
    Arguments:
57
      prompt (str): The prompt to show
58
      default: The default option;  `True` means "yes"
59

60
    Returns:
61
      `True` if the answer was "yes", `False` if "no"
62

63
    Examples:
64

65
    .. doctest::
66
       :skipif: branch_dev
67

68
        >>> yesno("A number:", 20)
69
        Traceback (most recent call last):
70
        ...
71
        ValueError: yesno(): default must be a boolean or None
72
        >>> saved_stdin = sys.stdin
73
        >>> try:
74
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"x\nyes\nno\n\n"))
75
        ...     yesno("is it good 1")
76
        ...     yesno("is it good 2", True)
77
        ...     yesno("is it good 3", False)
78
        ... finally:
79
        ...     sys.stdin = saved_stdin
80
         [?] is it good 1 [yes/no] Please answer yes or no
81
         [?] is it good 1 [yes/no] True
82
         [?] is it good 2 [Yes/no] False
83
         [?] is it good 3 [yes/No] False
84

85
    Tests:
86

87
    .. doctest::
88
       :skipif: branch_dev
89

90
        >>> p = testpwnproc("print(yesno('is it ok??'))")
91
        >>> b"is it ok" in p.recvuntil(b"??")
92
        True
93
        >>> p.sendline(b"x\nny")
94
        >>> b"True" in p.recvall()
95
        True
96
    """
97

UNCOV
98
    if default is not None and not isinstance(default, bool):
×
UNCOV
99
        raise ValueError('yesno(): default must be a boolean or None')
×
100

UNCOV
101
    if term.term_mode:
×
UNCOV
102
        term.output(' [?] %s [' % prompt)
×
UNCOV
103
        yesfocus, yes = term.text.bold('Yes'), 'yes'
×
UNCOV
104
        nofocus, no = term.text.bold('No'), 'no'
×
UNCOV
105
        hy = term.output(yesfocus if default is True else yes)
×
UNCOV
106
        hs = term.output('/')
×
UNCOV
107
        hn = term.output(nofocus if default is False else no)
×
UNCOV
108
        he = term.output(']\n')
×
UNCOV
109
        cur = default
×
UNCOV
110
        while True:
×
UNCOV
111
            k = term.key.get()
×
UNCOV
112
            if   k in ('y', 'Y', '<left>') and cur is not True:
×
UNCOV
113
                cur = True
×
UNCOV
114
                hy.update(yesfocus)
×
UNCOV
115
                hn.update(no)
×
UNCOV
116
            elif k in ('n', 'N', '<right>') and cur is not False:
×
UNCOV
117
                cur = False
×
UNCOV
118
                hy.update(yes)
×
UNCOV
119
                hn.update(nofocus)
×
UNCOV
120
            elif k == '<enter>':
×
UNCOV
121
                if cur is not None:
×
UNCOV
122
                    return cur
×
123
    else:
UNCOV
124
        prompt = ' [?] %s [%s/%s] ' % (prompt,
×
125
                                       'Yes' if default is True else 'yes',
126
                                       'No' if default is False else 'no',
127
                                       )
UNCOV
128
        while True:
×
UNCOV
129
            opt = raw_input(prompt).strip().lower()
×
UNCOV
130
            if not opt and default is not None:
×
UNCOV
131
                return default
×
UNCOV
132
            elif opt in (b'y', b'yes'):
×
UNCOV
133
                return True
×
UNCOV
134
            elif opt in (b'n', b'no'):
×
UNCOV
135
                return False
×
UNCOV
136
            print('Please answer yes or no')
×
137

138
def options(prompt, opts, default = None):
1✔
139
    r"""Presents the user with a prompt (typically in the
140
    form of a question) and a number of options.
141

142
    Arguments:
143
      prompt (str): The prompt to show
144
      opts (list): The options to show to the user
145
      default: The default option to choose
146

147
    Returns:
148
      The users choice in the form of an integer.
149

150
    Examples:
151

152
    .. doctest::
153
       :skipif: branch_dev
154

155
        >>> options("Select a color", ("red", "green", "blue"), "green")
156
        Traceback (most recent call last):
157
        ...
158
        ValueError: options(): default must be a number or None
159

160
    Tests:
161

162
    .. doctest::
163
       :skipif: branch_dev
164

165
        >>> p = testpwnproc("print(options('select a color', ('red', 'green', 'blue')))")
166
        >>> p.sendline(b"\33[C\33[A\33[A\33[B\33[1;5A\33[1;5B 0310")
167
        >>> _ = p.recvall()
168
        >>> saved_stdin = sys.stdin
169
        >>> try:
170
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n4\n\n3\n"))
171
        ...     with context.local(log_level="INFO"):
172
        ...         options("select a color A", ("red", "green", "blue"), 0)
173
        ...         options("select a color B", ("red", "green", "blue"))
174
        ... finally:
175
        ...     sys.stdin = saved_stdin
176
         [?] select a color A
177
               1) red
178
               2) green
179
               3) blue
180
             Choice [1] 0
181
         [?] select a color B
182
               1) red
183
               2) green
184
               3) blue
185
             Choice  [?] select a color B
186
               1) red
187
               2) green
188
               3) blue
189
             Choice  [?] select a color B
190
               1) red
191
               2) green
192
               3) blue
193
             Choice 2
194
    """
195

UNCOV
196
    if default is not None and not isinstance(default, six.integer_types):
×
UNCOV
197
        raise ValueError('options(): default must be a number or None')
×
198

UNCOV
199
    if term.term_mode:
×
UNCOV
200
        numfmt = '%' + str(len(str(len(opts)))) + 'd) '
×
UNCOV
201
        print(' [?] ' + prompt)
×
UNCOV
202
        hs = []
×
UNCOV
203
        space = '       '
×
UNCOV
204
        arrow = term.text.bold_green('    => ')
×
UNCOV
205
        cur = default
×
UNCOV
206
        for i, opt in enumerate(opts):
×
UNCOV
207
            h = term.output(arrow if i == cur else space, frozen = False)
×
UNCOV
208
            num = numfmt % (i + 1)
×
UNCOV
209
            h1 = term.output(num)
×
UNCOV
210
            h2 = term.output(opt + '\n', indent = len(num) + len(space))
×
UNCOV
211
            hs.append((h, h1, h2))
×
UNCOV
212
        ds = ''
×
UNCOV
213
        while True:
×
UNCOV
214
            prev = cur
×
UNCOV
215
            was_digit = False
×
UNCOV
216
            k = term.key.get()
×
UNCOV
217
            if   k == '<up>':
×
UNCOV
218
                if cur is None:
×
UNCOV
219
                    cur = 0
×
220
                else:
UNCOV
221
                    cur = max(0, cur - 1)
×
UNCOV
222
            elif k == '<down>':
×
UNCOV
223
                if cur is None:
×
224
                    cur = 0
×
225
                else:
UNCOV
226
                    cur = min(len(opts) - 1, cur + 1)
×
UNCOV
227
            elif k == 'C-<up>':
×
UNCOV
228
                cur = 0
×
UNCOV
229
            elif k == 'C-<down>':
×
UNCOV
230
                cur = len(opts) - 1
×
UNCOV
231
            elif k in ('<enter>', '<right>'):
×
UNCOV
232
                if cur is not None:
×
UNCOV
233
                    return cur
×
UNCOV
234
            elif k in tuple(string.digits):
×
UNCOV
235
                was_digit = True
×
UNCOV
236
                d = str(k)
×
UNCOV
237
                n = int(ds + d)
×
UNCOV
238
                if 0 < n <= len(opts):
×
UNCOV
239
                    ds += d
×
UNCOV
240
                    cur = n - 1
×
UNCOV
241
                elif d != '0':
×
UNCOV
242
                    ds = d
×
UNCOV
243
                    n = int(ds)
×
UNCOV
244
                    cur = n - 1
×
245

UNCOV
246
            if prev != cur:
×
UNCOV
247
                if prev is not None:
×
UNCOV
248
                    hs[prev][0].update(space)
×
UNCOV
249
                if was_digit:
×
UNCOV
250
                    hs[cur][0].update(term.text.bold_green('%5s> ' % ds))
×
251
                else:
UNCOV
252
                    hs[cur][0].update(arrow)
×
253
    else:
UNCOV
254
        linefmt =       '       %' + str(len(str(len(opts)))) + 'd) %s'
×
UNCOV
255
        if default is not None:
×
UNCOV
256
            default += 1
×
UNCOV
257
        while True:
×
UNCOV
258
            print(' [?] ' + prompt)
×
UNCOV
259
            for i, opt in enumerate(opts):
×
UNCOV
260
                print(linefmt % (i + 1, opt))
×
UNCOV
261
            s = '     Choice '
×
UNCOV
262
            if default:
×
UNCOV
263
                s += '[%s] ' % str(default)
×
UNCOV
264
            try:
×
UNCOV
265
                x = int(raw_input(s) or default)
×
UNCOV
266
            except (ValueError, TypeError):
×
UNCOV
267
                continue
×
UNCOV
268
            if x >= 1 and x <= len(opts):
×
UNCOV
269
                return x - 1
×
270

271
def pause(n=None):
1✔
272
    r"""Waits for either user input or a specific number of seconds.
273

274
    Examples:
275

276
    .. doctest::
277
       :skipif: branch_dev
278

279
        >>> with context.local(log_level="INFO"):
280
        ...     pause(1)
281
        [x] Waiting
282
        [x] Waiting: 1...
283
        [+] Waiting: Done
284
        >>> pause("whatever")
285
        Traceback (most recent call last):
286
        ...
287
        ValueError: pause(): n must be a number or None
288

289
    Tests:
290

291
    .. doctest::
292
       :skipif: branch_dev
293

294
        >>> saved_stdin = sys.stdin
295
        >>> try:
296
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n"))
297
        ...     with context.local(log_level="INFO"):
298
        ...         pause()
299
        ... finally:
300
        ...     sys.stdin = saved_stdin
301
        [*] Paused (press enter to continue)
302
        >>> p = testpwnproc("pause()")
303
        >>> b"Paused" in p.recvuntil(b"press any")
304
        True
305
        >>> p.send(b"x")
306
        >>> _ = p.recvall()
307
    """
308

UNCOV
309
    if n is None:
×
UNCOV
310
        if term.term_mode:
×
UNCOV
311
            log.info('Paused (press any to continue)')
×
UNCOV
312
            term.getkey()
×
313
        else:
UNCOV
314
            log.info('Paused (press enter to continue)')
×
UNCOV
315
            raw_input('')
×
UNCOV
316
    elif isinstance(n, six.integer_types):
×
UNCOV
317
        with log.waitfor("Waiting") as l:
×
UNCOV
318
            for i in range(n, 0, -1):
×
UNCOV
319
                l.status('%d... ' % i)
×
UNCOV
320
                time.sleep(1)
×
UNCOV
321
            l.success()
×
322
    else:
UNCOV
323
        raise ValueError('pause(): n must be a number or None')
×
324

325
def more(text):
1✔
326
    r"""more(text)
327

328
    Shows text like the command line tool ``more``.
329

330
    It not in term_mode, just prints the data to the screen.
331

332
    Arguments:
333
      text(str):  The text to show.
334

335
    Returns:
336
      :const:`None`
337

338
    Tests:
339

340
    .. doctest::
341
       :skipif: branch_dev
342
       
343
        >>> more("text")
344
        text
345
        >>> p = testpwnproc("more('text\\n' * (term.height + 2))")
346
        >>> p.send(b"x")
347
        >>> data = p.recvall()
348
        >>> b"text" in data or data
349
        True
350
    """
UNCOV
351
    if term.term_mode:
×
UNCOV
352
        lines = text.split('\n')
×
UNCOV
353
        h = term.output(term.text.reverse('(more)'), float = True, frozen = False)
×
UNCOV
354
        step = term.height - 1
×
UNCOV
355
        for i in range(0, len(lines), step):
×
UNCOV
356
            for l in lines[i:i + step]:
×
UNCOV
357
                print(l)
×
UNCOV
358
            if i + step < len(lines):
×
UNCOV
359
                term.key.get()
×
360
    else:
UNCOV
361
        print(text)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc