• 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

22.99
/pwnlib/term/key.py
1
from __future__ import absolute_import
1✔
2
from __future__ import division
1✔
3

4
import errno
1✔
5
import os
1✔
6
import select
1✔
7
import six
1✔
8
import string
1✔
9
import sys
1✔
10

11
from pwnlib.term import keyconsts as kc
1✔
12
from pwnlib.term import termcap
1✔
13
from pwnlib.term import term
1✔
14

15
__all__ = ['getch', 'getraw', 'get', 'unget']
1✔
16

17
# When set, convert keypad codes into regular key presses, e.g. "+" instead of
18
# `<kp plus>`
19
FLAG_CONVERTKP = True
1✔
20

21
try:    _fd = sys.stdin.fileno()
1✔
22
except Exception: _fd = os.open(os.devnull, os.O_RDONLY)
×
23

24
def getch(timeout = 0):
1✔
UNCOV
25
    term.setupterm()
×
UNCOV
26
    while True:
×
UNCOV
27
        try:
×
UNCOV
28
            rfds, _wfds, _xfds = select.select([_fd], [], [], timeout)
×
UNCOV
29
            if rfds:
×
UNCOV
30
                with term.rlock:
×
UNCOV
31
                    rfds, _wfds, _xfds = select.select([_fd], [], [], 0)
×
UNCOV
32
                    if not rfds: continue
×
UNCOV
33
                    c = os.read(_fd, 1)
×
UNCOV
34
                return ord(c) if c else None
×
35
            else:
UNCOV
36
                return None
×
37
        except select.error as e:
×
38
            if e.args[0] == errno.EINTR:
×
39
                continue
×
40
            raise
×
41

42
def getraw(timeout = None):
1✔
43
    '''Get list of raw key codes corresponding to zero or more key presses'''
UNCOV
44
    cs = []
×
UNCOV
45
    c = getch(timeout)
×
UNCOV
46
    while c is not None: # timeout
×
UNCOV
47
        cs.append(c)
×
UNCOV
48
        if c is None: # EOF
×
49
            break
×
UNCOV
50
        c = getch()
×
UNCOV
51
    return cs
×
52

53
class Matcher:
1✔
54
    def __init__(self, desc):
1✔
55
        self._desc = desc
1✔
56
        desc = desc.split('-')
1✔
57
        mods = desc[:-1]
1✔
58
        k = desc[-1]
1✔
59
        if k == '<space>':
1!
60
            k = ' '
×
61
        m = kc.MOD_NONE
1✔
62
        if 'S' in mods:
1!
63
            m |= kc.MOD_SHIFT
×
64
        if 'M' in mods:
1✔
65
            m |= kc.MOD_ALT
1✔
66
        if 'C' in mods:
1✔
67
            m |= kc.MOD_CTRL
1✔
68
        if   len(k) == 1:
1✔
69
            t = kc.TYPE_UNICODE
1✔
70
            c = k
1✔
71
            h = ord(k)
1✔
72
        elif k[0] == '<' and k in kc.KEY_NAMES_REVERSE:
1!
73
            t = kc.TYPE_KEYSYM
1✔
74
            c = kc.KEY_NAMES_REVERSE[k]
1✔
75
            h = c
1✔
76
        elif k[:2] == '<f' and k[-1] == '>' and k[2:-1].isdigit():
×
77
            t = kc.TYPE_FUNCTION
×
78
            c = int(k[2:-1])
×
79
            h = c
×
80
        else:
81
            raise ValueError('bad key description "%s"' % k)
×
82
        self._type = t
1✔
83
        self._code = c
1✔
84
        self._mods = m
1✔
85
        self._hash = h | (m << 6) | (t << 7)
1✔
86

87
    def __call__(self, k):
1✔
UNCOV
88
        if isinstance(k, Key):
×
UNCOV
89
            return all([k.type == self._type,
×
90
                        k.code == self._code,
91
                        k.mods == self._mods,
92
                        ])
93

94
    def __eq__(self, other):
1✔
95
        if   isinstance(other, Matcher):
1!
96
            return all([other._type == self._type,
1✔
97
                        other._code == self._code,
98
                        other._mods == self._mods,
99
                        ])
100
        elif isinstance(other, Key):
×
101
            return self.__call__(other)
×
102
        else:
103
            return False
×
104

105
    def __neq__(self, other):
1✔
106
        return not self == other
×
107

108
    def __hash__(self):
1✔
109
        return self._hash
1✔
110

111
    def __str__(self):
1✔
112
        return self._desc
×
113

114
class Key:
1✔
115
    def __init__(self, type, code = None, mods = kc.MOD_NONE):
1✔
UNCOV
116
        self.type = type
×
UNCOV
117
        self.code = code
×
UNCOV
118
        self.mods = mods
×
UNCOV
119
        self._str = None
×
120

121
    def __str__(self):
1✔
UNCOV
122
        if self._str:
×
123
            return self._str
×
UNCOV
124
        if   self.type == kc.TYPE_UNICODE:
×
UNCOV
125
            if self.code == ' ':
×
126
                s = '<space>'
×
127
            else:
UNCOV
128
                s = self.code
×
129
        elif self.type == kc.TYPE_KEYSYM:
×
130
            s = kc.KEY_NAMES.get(self.code, '<SYMNAME-%d>' % self.code)
×
131
        elif self.type == kc.TYPE_FUNCTION:
×
132
            s = '<f%d>' % self.code
×
133
        elif self.type == kc.TYPE_POSITION:
×
134
            s = 'Position(%d, %d)' % self.code
×
135
        elif self.type == kc.TYPE_EOF:
×
136
            s = 'EOF'
×
137
        else:
138
            s = '<UNKNOWN>'
×
UNCOV
139
        if self.mods & kc.MOD_SHIFT:
×
140
            s = 'S-' + s
×
UNCOV
141
        if self.mods & kc.MOD_ALT:
×
142
            s = 'M-' + s
×
UNCOV
143
        if self.mods & kc.MOD_CTRL:
×
144
            s = 'C-' + s
×
UNCOV
145
        self._str = s
×
UNCOV
146
        return s
×
147

148
    def __repr__(self):
1✔
149
        return self.__str__()
×
150

151
    def __eq__(self, other):
1✔
UNCOV
152
        if   isinstance(other, (six.text_type, six.binary_type)):
×
UNCOV
153
            return Matcher(other)(self)
×
154
        elif isinstance(other, Matcher):
×
155
            return other(self)
×
156
        elif isinstance(other, Key):
×
157
            return all([self.type == other.type,
×
158
                        self.code == other.code,
159
                        self.mods == other.mods,
160
                        ])
161
        else:
162
            return False
×
163

164
_cbuf = []
1✔
165
_kbuf = []
1✔
166

167
def _read(timeout = 0):
1✔
UNCOV
168
    _cbuf.extend(getraw(timeout))
×
169

170
def _peek():
1✔
UNCOV
171
    if _cbuf:
×
UNCOV
172
        return _peek_ti() or _peek_csi() or _peek_simple()
×
173

174
def get(timeout = None):
1✔
UNCOV
175
    if _kbuf:
×
176
        return _kbuf.pop(0)
×
UNCOV
177
    k = _peek()
×
UNCOV
178
    if k:
×
UNCOV
179
        return k
×
UNCOV
180
    _read(timeout)
×
UNCOV
181
    return _peek()
×
182

183
def unget(k):
1✔
184
    _kbuf.append(k)
×
185

186
# terminfo
187
def _name_to_key(fname):
1✔
UNCOV
188
    if   fname in kc.FUNCSYMS:
×
UNCOV
189
        k = Key(kc.TYPE_KEYSYM, *kc.FUNCSYMS[fname])
×
UNCOV
190
    elif fname[0] == 'f' and fname[1:].isdigit():
×
UNCOV
191
        k = Key(kc.TYPE_FUNCTION, int(fname[1:]))
×
UNCOV
192
    elif fname[0] == 's':
×
UNCOV
193
        k = _name_to_key(fname[1:])
×
UNCOV
194
        if k:
×
UNCOV
195
            k.mods |= kc.MOD_SHIFT
×
196
    else:
UNCOV
197
        return None
×
UNCOV
198
    return k
×
199

200
_ti_table = None
1✔
201

202
def _peek_ti():
1✔
203
    global _cbuf
UNCOV
204
    if _ti_table is None:
×
UNCOV
205
        _init_ti_table()
×
206
    # XXX: Faster lookup, plox
UNCOV
207
    for seq, key in _ti_table:
×
UNCOV
208
        if _cbuf[:len(seq)] == seq:
×
209
            _cbuf = _cbuf[len(seq):]
×
210
            return key
×
211

212
def _init_ti_table():
1✔
213
    global _ti_table
UNCOV
214
    _ti_table = []
×
UNCOV
215
    for fname, name in zip(kc.STRFNAMES, kc.STRNAMES):
×
UNCOV
216
        seq = termcap.get(name)
×
UNCOV
217
        if not seq:
×
UNCOV
218
            continue
×
UNCOV
219
        k = _name_to_key(fname)
×
UNCOV
220
        if k:
×
UNCOV
221
            _ti_table.append((list(bytearray(seq)), k))
×
222

223
# csi
224
def _parse_csi(offset):
1✔
UNCOV
225
    i = offset
×
UNCOV
226
    while i < len(_cbuf):
×
UNCOV
227
        c = _cbuf[i]
×
UNCOV
228
        if c >= 0x40 and c < 0x80:
×
UNCOV
229
            break
×
UNCOV
230
        i += 1
×
UNCOV
231
    if i >= len(_cbuf):
×
232
        return
×
UNCOV
233
    end = i
×
UNCOV
234
    cmd = [c, None, None]
×
235

UNCOV
236
    i = offset
×
UNCOV
237
    in_num = False
×
UNCOV
238
    args = []
×
UNCOV
239
    if _cbuf[i] >= ord('<') and _cbuf[i] <= ord('?'):
×
240
        cmd[1] = _cbuf[i]
×
241
        i += 1
×
UNCOV
242
    while i < end:
×
UNCOV
243
        c = _cbuf[i]
×
UNCOV
244
        if   c >= ord('0') and c <= ord('9'):
×
UNCOV
245
            if not in_num:
×
UNCOV
246
                args.append(c - ord('0'))
×
UNCOV
247
                in_num = True
×
248
            else:
249
                args[-1] = args[-1] * 10 + c - ord('0')
×
UNCOV
250
        elif c == ord(';'):
×
UNCOV
251
            if not in_num:
×
252
                args.append(None)
×
UNCOV
253
            in_num = False
×
UNCOV
254
            if len(args) > 16:
×
255
                break
×
256
        elif c >= 0x20 and c <= 0x2f:
×
257
            cmd[2] = c
×
258
            break
×
259

UNCOV
260
        i += 1
×
261

UNCOV
262
    return cmd, args, end + 1
×
263

264
def _csi_func(cmd, args):
1✔
265
    k = Key(kc.TYPE_UNKNOWN)
×
266
    if len(args) > 1 and args[1]:
×
267
        k.mods |= args[1] - 1
×
268

269
    if   args[0] == 0x1b and len(args) == 3:
×
270
        k.type = kc.TYPE_KEYSYM
×
271
        k.code = args[2]
×
272
        return k
×
273
    elif args[0] in _csi_funcs:
×
274
        f = _csi_funcs[args[0]]
×
275
        k.type = f[0]
×
276
        k.code = f[1]
×
277
        return k
×
278

279
def _csi_ss3(cmd, args):
1✔
UNCOV
280
    t, c = _csi_ss3s[chr(cmd[0])]
×
UNCOV
281
    k = Key(t, c)
×
UNCOV
282
    if len(args) > 1 and args[1]:
×
UNCOV
283
        k.mods |= args[1] - 1
×
UNCOV
284
    return k
×
285

286
def _csi_u(cmd, args):
1✔
287
    k = Key(kc.TYPE_UNICODE, six.unichr(args[0]))
×
288
    if len(args) > 1 and args[1]:
×
289
        k.mods |= args[1] - 1
×
290
    return k
×
291

292
def _csi_R(cmd, args):
1✔
293
    if cmd[0] == ord('R') and cmd[1] == ord('?'):
×
294
        if len(args) < 2:
×
295
            return
×
296
        return Key(kc.TYPE_POSITION, (args[1], args[0]))
×
297
    else:
298
        return _csi_ss3(cmd, args)
×
299

300
_csi_handlers = {
1✔
301
    '~' : _csi_func,
302
    'u' : _csi_u,
303
    'R' : _csi_R,
304
    }
305

306
_csi_ss3s = {
1✔
307
    'A': (kc.TYPE_KEYSYM, kc.KEY_UP),
308
    'B': (kc.TYPE_KEYSYM, kc.KEY_DOWN),
309
    'C': (kc.TYPE_KEYSYM, kc.KEY_RIGHT),
310
    'D': (kc.TYPE_KEYSYM, kc.KEY_LEFT),
311
    'E': (kc.TYPE_KEYSYM, kc.KEY_BEGIN),
312
    'F': (kc.TYPE_KEYSYM, kc.KEY_END),
313
    'H': (kc.TYPE_KEYSYM, kc.KEY_HOME),
314
    'P': (kc.TYPE_FUNCTION, 1),
315
    'Q': (kc.TYPE_FUNCTION, 2),
316
    'R': (kc.TYPE_FUNCTION, 3),
317
    'S': (kc.TYPE_FUNCTION, 4),
318
    'Z': (kc.TYPE_KEYSYM, kc.KEY_TAB),
319
}
320

321
_csi_ss3kp = {
1✔
322
    'M': (kc.TYPE_KEYSYM, kc.KEY_KPENTER , None),
323
    'X': (kc.TYPE_KEYSYM, kc.KEY_KPEQUALS, '='),
324
    'j': (kc.TYPE_KEYSYM, kc.KEY_KPMULT  , '*'),
325
    'k': (kc.TYPE_KEYSYM, kc.KEY_KPPLUS  , '+'),
326
    'l': (kc.TYPE_KEYSYM, kc.KEY_KPCOMMA , ','),
327
    'm': (kc.TYPE_KEYSYM, kc.KEY_KPMINUS , '-'),
328
    'n': (kc.TYPE_KEYSYM, kc.KEY_KPPERIOD, '.'),
329
    'o': (kc.TYPE_KEYSYM, kc.KEY_KPDIV   , '/'),
330
    'p': (kc.TYPE_KEYSYM, kc.KEY_KP0     , '0'),
331
    'q': (kc.TYPE_KEYSYM, kc.KEY_KP1     , '1'),
332
    'r': (kc.TYPE_KEYSYM, kc.KEY_KP2     , '2'),
333
    's': (kc.TYPE_KEYSYM, kc.KEY_KP3     , '3'),
334
    't': (kc.TYPE_KEYSYM, kc.KEY_KP4     , '4'),
335
    'u': (kc.TYPE_KEYSYM, kc.KEY_KP5     , '5'),
336
    'v': (kc.TYPE_KEYSYM, kc.KEY_KP6     , '6'),
337
    'w': (kc.TYPE_KEYSYM, kc.KEY_KP7     , '7'),
338
    'x': (kc.TYPE_KEYSYM, kc.KEY_KP8     , '8'),
339
    'y': (kc.TYPE_KEYSYM, kc.KEY_KP9     , '9'),
340
}
341

342
_csi_funcs = {
1✔
343
    1 : (kc.TYPE_KEYSYM, kc.KEY_FIND),
344
    2 : (kc.TYPE_KEYSYM, kc.KEY_INSERT),
345
    3 : (kc.TYPE_KEYSYM, kc.KEY_DELETE),
346
    4 : (kc.TYPE_KEYSYM, kc.KEY_SELECT),
347
    5 : (kc.TYPE_KEYSYM, kc.KEY_PAGEUP),
348
    6 : (kc.TYPE_KEYSYM, kc.KEY_PAGEDOWN),
349
    7 : (kc.TYPE_KEYSYM, kc.KEY_HOME),
350
    8 : (kc.TYPE_KEYSYM, kc.KEY_END),
351
    11: (kc.TYPE_FUNCTION, 1),
352
    12: (kc.TYPE_FUNCTION, 2),
353
    13: (kc.TYPE_FUNCTION, 3),
354
    14: (kc.TYPE_FUNCTION, 4),
355
    15: (kc.TYPE_FUNCTION, 5),
356
    17: (kc.TYPE_FUNCTION, 6),
357
    18: (kc.TYPE_FUNCTION, 7),
358
    19: (kc.TYPE_FUNCTION, 8),
359
    20: (kc.TYPE_FUNCTION, 9),
360
    21: (kc.TYPE_FUNCTION, 10),
361
    23: (kc.TYPE_FUNCTION, 11),
362
    24: (kc.TYPE_FUNCTION, 12),
363
    25: (kc.TYPE_FUNCTION, 13),
364
    26: (kc.TYPE_FUNCTION, 14),
365
    28: (kc.TYPE_FUNCTION, 15),
366
    29: (kc.TYPE_FUNCTION, 16),
367
    31: (kc.TYPE_FUNCTION, 17),
368
    32: (kc.TYPE_FUNCTION, 18),
369
    33: (kc.TYPE_FUNCTION, 19),
370
    34: (kc.TYPE_FUNCTION, 20),
371
    }
372

373
def _peekkey_csi(offset):
1✔
374
    global _cbuf
UNCOV
375
    ret = _parse_csi(offset)
×
UNCOV
376
    if not ret:
×
377
        _cbuf = _cbuf[offset:]
×
378
        return Key(kc.TYPE_UNICODE, u'[', kc.MOD_ALT)
×
UNCOV
379
    cmd, args, numb = ret
×
380
    # print(cmd, args, '\r')
UNCOV
381
    _cbuf = _cbuf[numb:]
×
UNCOV
382
    k = None
×
UNCOV
383
    if   chr(cmd[0]) in _csi_handlers:
×
384
        k = _csi_handlers[chr(cmd[0])](cmd, args)
×
UNCOV
385
    elif chr(cmd[0]) in _csi_ss3s:
×
UNCOV
386
        k = _csi_ss3(cmd, args)
×
UNCOV
387
        if k and chr(cmd[0]) == 'Z':
×
388
            k.mods |= kc.MOD_SHIFT
×
389

UNCOV
390
    if k:
×
UNCOV
391
        return k
×
392
    else:
393
        return Key(kc.TYPE_UNKNOWN_CSI, (cmd, args))
×
394

395
def _peekkey_ss3(offset):
1✔
396
    global _cbuf
397
    if len(_cbuf) <= offset:
×
398
        return Key(kc.TYPE_UNICODE, u'O', kc.MOD_ALT)
×
399
    cmd = _cbuf[offset]
×
400
    if cmd < 0x40 or cmd >= 0x80:
×
401
        return
×
402
    _cbuf = _cbuf[offset:]
×
403

404
    if chr(cmd) in _csi_ss3s:
×
405
        return Key(*_csi_ss3s[chr(cmd)])
×
406

407
    if chr(cmd) in _csi_ss3kp:
×
408
        t, c, a = _csi_ss3kp[chr(cmd)]
×
409
        if FLAG_CONVERTKP and a:
×
410
            return Key(kc.TYPE_UNICODE, a)
×
411
        else:
412
            return Key(t, c)
×
413

414
def _peek_csi():
1✔
415
    global _cbuf
416
    # print('csi', _cbuf, '\r')
UNCOV
417
    c0 = _cbuf[0]
×
UNCOV
418
    if   c0 == 0x1b and len(_cbuf) >= 2:
×
UNCOV
419
        c1 = _cbuf[1]
×
UNCOV
420
        if   c1 == ord('['):
×
UNCOV
421
            return _peekkey_csi(2)
×
422
        elif c1 == ord('O'):
×
423
            return _peekkey_ss3(2)
×
UNCOV
424
    elif c0 == 0x8f:
×
425
        return _peekkey_ss3(1)
×
UNCOV
426
    elif c0 == 0x9b:
×
427
        return _peekkey_csi(1)
×
428

429
def _peek_simple():
1✔
430
    global _cbuf
431
    # print('simple', _cbuf, '\r')
UNCOV
432
    if not _cbuf:
×
433
        return
×
UNCOV
434
    c0 = _cbuf.pop(0)
×
UNCOV
435
    if   c0 is None:
×
436
        _cbuf = []
×
437
        return Key(kc.TYPE_EOF)
×
UNCOV
438
    elif c0 == 0x1b:
×
439
        if _cbuf:
×
440
            k = _peek()
×
441
            # print(k)
442
            if k:
×
443
                # need to deep copy or we risk modifying keys in ti table
444
                return Key(k.type, k.code, k.mods | kc.MOD_ALT)
×
445
        else:
446
            return Key(kc.TYPE_KEYSYM, kc.KEY_ESCAPE)
×
UNCOV
447
    elif c0 < 0xa0:
×
UNCOV
448
        if   c0 < 0x20:
×
UNCOV
449
            if   c0 == 8:
×
450
                k = Key(kc.TYPE_KEYSYM, kc.KEY_BACKSPACE)
×
UNCOV
451
            elif c0 == 9:
×
452
                k = Key(kc.TYPE_KEYSYM, kc.KEY_TAB)
×
UNCOV
453
            elif c0 in (10, 13):
×
UNCOV
454
                k = Key(kc.TYPE_KEYSYM, kc.KEY_ENTER)
×
455
            else:
456
                k = Key(kc.TYPE_UNICODE)
×
457
                if   c0 == 0:
×
458
                    k.code = u' '
×
459
                elif chr(c0 + 0x40) in string.ascii_uppercase:
×
460
                    k.code = six.unichr(c0 + 0x60)
×
461
                else:
462
                    k.code = six.unichr(c0 + 0x40)
×
463
                k.mods |= kc.MOD_CTRL
×
UNCOV
464
        elif c0 == 0x7f:
×
465
            # print('del\r')
466
            k = Key(kc.TYPE_KEYSYM, kc.KEY_DEL)
×
UNCOV
467
        elif c0 >= 0x20 and c0 < 0x80:
×
UNCOV
468
            k = Key(kc.TYPE_UNICODE, six.unichr(c0))
×
469
        else:
470
            k = Key(kc.TYPE_UNICODE, six.unichr(c0 - 0x40), kc.MOD_CTRL | kc.MOD_ALT)
×
471
    else: # utf8
472
        n = 0
×
473
        if   c0 & 0b11100000 == 0b11000000:
×
474
            n = 2
×
475
        elif c0 & 0b11110000 == 0b11100000:
×
476
            n = 3
×
477
        elif c0 & 0b11111000 == 0b11110000:
×
478
            n = 4
×
479
        elif c0 & 0b11111100 == 0b11111000:
×
480
            n = 5
×
481
        elif c0 & 0b11111110 == 0b11111100:
×
482
            n = 6
×
483
        if n:
×
484
            c = [c0] + _cbuf[:n - 1]
×
485
            k = Key(kc.TYPE_UNICODE, bytearray(c).decode('utf8'))
×
486
            _cbuf = _cbuf[n - 1:]
×
487
        else:
488
            k = Key(kc.TYPE_UNKNOWN, _cbuf)
×
489
            _cbuf = []
×
UNCOV
490
    return k
×
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