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

Gallopsled / pwntools / 5501444272

pending completion
5501444272

Pull #2221

github-actions

web-flow
Merge b6d25b636 into 9c83fa109
Pull Request #2221: Add shellcraft.sleep template wrapping SYS_nanosleep

3702 of 6659 branches covered (55.59%)

11579 of 16977 relevant lines covered (68.2%)

0.68 hits per line

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

8.64
/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✔
22
    import fcntl
×
23
    import termios
×
24
    env = dict(os.environ)
×
25
    env.pop("PWNLIB_NOTERM", None)
×
26
    env["TERM"] = "xterm-256color"
×
27
    def handleusr1(sig, frame):
×
28
        s = p.stderr.read()
×
29
        log.error("child process failed:\n%s", s.decode())
×
30
    signal.signal(signal.SIGUSR1, handleusr1)
×
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
42
    if "coverage" in sys.modules:
×
43
        cmd = "import coverage; coverage.process_startup()\n" + cmd
×
44
        env.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
×
45
    p = process([sys.executable, "-c", cmd], env=env, stderr=subprocess.PIPE)
×
46
    try:
×
47
        p.recvuntil(b"\33[6n")
×
48
    except EOFError:
×
49
        raise EOFError("process terminated with code: %r (%r)" % (p.poll(True), p.stderr.read()))
×
50
    # late initialization can lead to EINTR in many places
51
    fcntl.ioctl(p.stdout.fileno(), termios.TIOCSWINSZ, struct.pack("hh", 80, 80))
×
52
    p.stdout.write(b"\x1b[1;1R")
×
53
    time.sleep(0.5)
×
54
    return p
×
55

56
def yesno(prompt, default=None):
1✔
57
    r"""Presents the user with prompt (typically in the form of question)
58
    which the user must answer yes or no.
59

60
    Arguments:
61
      prompt (str): The prompt to show
62
      default: The default option;  `True` means "yes"
63

64
    Returns:
65
      `True` if the answer was "yes", `False` if "no"
66

67
    Examples:
68

69
    .. doctest::
70
       :skipif: branch_dev
71

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

89
    Tests:
90

91
    .. doctest::
92
       :skipif: branch_dev
93

94
        >>> p = testpwnproc("print(yesno('is it ok??'))")
95
        >>> b"is it ok" in p.recvuntil(b"??")
96
        True
97
        >>> p.sendline(b"x\nny")
98
        >>> b"True" in p.recvall()
99
        True
100
    """
101

102
    if default is not None and not isinstance(default, bool):
×
103
        raise ValueError('yesno(): default must be a boolean or None')
×
104

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

142
def options(prompt, opts, default = None):
1✔
143
    r"""Presents the user with a prompt (typically in the
144
    form of a question) and a number of options.
145

146
    Arguments:
147
      prompt (str): The prompt to show
148
      opts (list): The options to show to the user
149
      default: The default option to choose
150

151
    Returns:
152
      The users choice in the form of an integer.
153

154
    Examples:
155

156
    .. doctest::
157
       :skipif: branch_dev
158

159
        >>> options("Select a color", ("red", "green", "blue"), "green")
160
        Traceback (most recent call last):
161
        ...
162
        ValueError: options(): default must be a number or None
163

164
    Tests:
165

166
    .. doctest::
167
       :skipif: branch_dev
168

169
        >>> p = testpwnproc("print(options('select a color', ('red', 'green', 'blue')))")
170
        >>> p.sendline(b"\33[C\33[A\33[A\33[B\33[1;5A\33[1;5B 0310")
171
        >>> _ = p.recvall()
172
        >>> saved_stdin = sys.stdin
173
        >>> try:
174
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n4\n\n3\n"))
175
        ...     with context.local(log_level="INFO"):
176
        ...         options("select a color A", ("red", "green", "blue"), 0)
177
        ...         options("select a color B", ("red", "green", "blue"))
178
        ... finally:
179
        ...     sys.stdin = saved_stdin
180
         [?] select a color A
181
               1) red
182
               2) green
183
               3) blue
184
             Choice [1] 0
185
         [?] 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  [?] select a color B
194
               1) red
195
               2) green
196
               3) blue
197
             Choice 2
198
    """
199

200
    if default is not None and not isinstance(default, six.integer_types):
×
201
        raise ValueError('options(): default must be a number or None')
×
202

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

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

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

278
    Examples:
279

280
    .. doctest::
281
       :skipif: branch_dev
282

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

293
    Tests:
294

295
    .. doctest::
296
       :skipif: branch_dev
297

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

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

329
def more(text):
1✔
330
    r"""more(text)
331

332
    Shows text like the command line tool ``more``.
333

334
    It not in term_mode, just prints the data to the screen.
335

336
    Arguments:
337
      text(str):  The text to show.
338

339
    Returns:
340
      :const:`None`
341

342
    Tests:
343

344
    .. doctest::
345
       :skipif: branch_dev
346
       
347
        >>> more("text")
348
        text
349
        >>> p = testpwnproc("more('text\\n' * (term.height + 2))")
350
        >>> p.send(b"x")
351
        >>> data = p.recvall()
352
        >>> b"text" in data or data
353
        True
354
    """
355
    if term.term_mode:
×
356
        lines = text.split('\n')
×
357
        h = term.output(term.text.reverse('(more)'), float = True, frozen = False)
×
358
        step = term.height - 1
×
359
        for i in range(0, len(lines), step):
×
360
            for l in lines[i:i + step]:
×
361
                print(l)
×
362
            if i + step < len(lines):
×
363
                term.key.get()
×
364
        h.delete()
×
365
    else:
366
        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