mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-10-12 02:02:27 +00:00
* Add Type Annotations for parts of pwnlib/util * fix: use Callable in pwnlib/util/lists.py * fix: Import Errors * fix: use getLogger in pwnlib/util/net * feat: use collections.abc.Buffer where appropriate * fix: remove collections.abc.Buffer to use Sequence * add CHANGELOG.md entry * Improve packing and fiddling types * Introduce own `BytesLike` type instead of 3.12 Buffer * Improve web.wget types * Use TypedDict in util.net Specify generics properly. * Improve pwnlib.proc types * Improve pwnlib.util.lists types * Fix util.splash types * Make TypeVars private * Improve util.hexdump types * Remove partial types from packing.fit/flat Those require more work to be useful. * Fix implicit wget return * Fix term.text type export * Fix js_escape return type * Fix sh_string doctest types * Fix usage of ssh_channel as ssh_process We access the pid, so a plain ssh_channel is not enough. * Improve util.lists types * Improve packing.dd types * Fix relying on socket.AddressFamily type at runtime The type is only in typeshed and not the real socket module. Don't access it at runtime, only at type checking. * Fix lists.findall with string needle * Fix dd on two files * Update mypy-baseline.txt * Remove testing artifacts from mypy-baseline.txt --------- Co-authored-by: peace-maker <[email protected]>
160 lines
4.3 KiB
Python
160 lines
4.3 KiB
Python
from pwnlib.context import context
|
|
from pwnlib.util.packing import unpack
|
|
from pwnlib.log import getLogger
|
|
from enum import IntEnum
|
|
|
|
log = getLogger(__name__)
|
|
|
|
class Dtype(IntEnum):
|
|
DT_UNK = 0
|
|
DT_FIFO = 1
|
|
DT_CHR = 2
|
|
DT_DIR = 4
|
|
DT_BLK = 6
|
|
DT_REG = 8
|
|
DT_LNK = 10
|
|
DT_SOCK = 12
|
|
DT_WHT = 14
|
|
DT_SUBVOL = 16
|
|
|
|
class linux_dirent:
|
|
"""
|
|
Represent struct linux_dirent
|
|
|
|
.. code-block:: c
|
|
|
|
struct linux_dirent
|
|
{
|
|
unsigned long d_ino;
|
|
unsigned long d_off;
|
|
unsigned short d_reclen;
|
|
char d_name[];
|
|
};
|
|
// https://elixir.bootlin.com/linux/v6.14.4/source/fs/readdir.c#L244
|
|
// the 32version of linux_dirent stores d_type after d_name
|
|
|
|
struct linux_dirent64 {
|
|
u64 d_ino;
|
|
s64 d_off;
|
|
unsigned short d_reclen;
|
|
unsigned char d_type;
|
|
char d_name[];
|
|
};
|
|
// https://elixir.bootlin.com/linux/v6.14.4/source/include/linux/dirent.h#L5
|
|
|
|
enum
|
|
{
|
|
DT_UNKNOWN = 0,
|
|
DT_FIFO = 1,
|
|
DT_CHR = 2,
|
|
DT_DIR = 4,
|
|
DT_BLK = 6,
|
|
DT_REG = 8,
|
|
DT_LNK = 10,
|
|
DT_SOCK = 12,
|
|
DT_WHT = 14,
|
|
DT_SUBVOL = 16
|
|
};
|
|
// https://elixir.bootlin.com/linux/v6.14.4/source/include/linux/fs_types.h#L42
|
|
// https://elixir.bootlin.com/linux/v6.14.4/source/fs/bcachefs/dirent_format.h#L37
|
|
"""
|
|
|
|
d_ino: int
|
|
d_off: int
|
|
d_reclen: int
|
|
d_type: Dtype
|
|
d_name: str
|
|
|
|
def __init__(self, buf: bytes, is_dirent64: bool) -> None:
|
|
size_t = 8 if is_dirent64 else context.bytes
|
|
|
|
self.d_ino = unpack(buf[0:size_t], size_t * 8)
|
|
self.d_off = unpack(buf[size_t : 2 * size_t], size_t * 8)
|
|
self.d_reclen = unpack(buf[2 * size_t : 2 * size_t + 2], 16)
|
|
|
|
if is_dirent64:
|
|
d_type = unpack(buf[2 * size_t + 2 : 2 * size_t + 3], 8)
|
|
d_name = buf[2 * size_t + 3 : self.d_reclen - 1]
|
|
else:
|
|
d_type = unpack(buf[self.d_reclen - 1 : self.d_reclen], 8)
|
|
d_name = buf[2 * size_t + 2 : self.d_reclen - 1]
|
|
|
|
self.d_name = d_name.split(b'\x00', 1)[0].decode('utf-8')
|
|
self.d_type = Dtype(d_type)
|
|
|
|
def __len__(self) -> int:
|
|
return self.d_reclen
|
|
|
|
def __str__(self) -> str:
|
|
return self.d_name
|
|
|
|
def __repr__(self) -> str:
|
|
return f'{self.d_type.name:<8}{self.d_name}'
|
|
|
|
|
|
def dirents(buf: bytes) -> list[linux_dirent]:
|
|
"""dirents(buf: bytes) -> list[linux_dirent]:
|
|
|
|
Extracts data from a buffer emitted by getdents
|
|
|
|
Arguments:
|
|
buf(bytes): getdents result
|
|
|
|
Returns:
|
|
A list of file names
|
|
|
|
Example:
|
|
>>> with context.local(bytes = 4):
|
|
... buf = bytes.fromhex('5e843600c120fc1a1400746573742e63000000085f8436001f347e3010002e00be36ba040d002c00ffffff7f10002e2e00000004')
|
|
... print(dirents(buf))
|
|
...
|
|
[DT_REG test.c, DT_DIR ., DT_DIR ..]
|
|
"""
|
|
|
|
bpos = 0
|
|
buf_len = len(buf)
|
|
entries = []
|
|
|
|
while bpos < buf_len:
|
|
try:
|
|
dirent = linux_dirent(buf[bpos:], False)
|
|
bpos += dirent.d_reclen
|
|
entries.append(dirent)
|
|
except (ValueError, UnicodeDecodeError):
|
|
log.warning("Failed to parse struct linux_dirent at position %d", bpos)
|
|
break
|
|
return entries
|
|
|
|
def dirents64(buf: bytes) -> list[linux_dirent]:
|
|
"""dirents(buf: bytes) -> list[linux_dirent]:
|
|
|
|
Extracts data from a buffer emitted by getdents64
|
|
|
|
Arguments:
|
|
buf(bytes): getdents64 result
|
|
|
|
Returns:
|
|
A list of file names
|
|
|
|
Example:
|
|
>>> with context.local(bytes = 8):
|
|
... buf = bytes.fromhex('223a2c0000000000786a631cc120fc1a200008746573742e63007464040000000d002c00000000004802ee451f347e301800042e0000000002002c0000000000ffffffffffffff7f1800042e2e000000')
|
|
... print(dirents64(buf))
|
|
...
|
|
[DT_REG test.c, DT_DIR ., DT_DIR ..]
|
|
"""
|
|
|
|
bpos = 0
|
|
buf_len = len(buf)
|
|
entries = []
|
|
|
|
while bpos < buf_len:
|
|
try:
|
|
dirent = linux_dirent(buf[bpos:], True)
|
|
bpos += dirent.d_reclen
|
|
entries.append(dirent)
|
|
except (ValueError, UnicodeDecodeError):
|
|
log.warning("Failed to parse struct linux_dirent64 at position %d", bpos)
|
|
break
|
|
return entries
|