25
25
from __future__ import absolute_import
26
26
from __future__ import division
27
27
28
+ import ctypes
29
+
28
30
from pwnlib .context import context
29
31
from pwnlib .log import getLogger
30
32
from pwnlib .util .misc import python_2_bytes_compatible
31
- from pwnlib .util .packing import pack
33
+ from pwnlib .util .packing import pack , unpack
32
34
33
35
log = getLogger (__name__ )
34
36
62
64
22 :{name :'_offset' ,size :8 },
63
65
23 :{name :'_codecvt' ,size :length },
64
66
24 :{name :'_wide_data' ,size :length },
65
- 25 :{name :'unknown2' ,size :length },
66
- 26 :{name :'vtable' ,size :length }
67
+ 25 :{name :'_freeres_list' ,size :length },
68
+ 26 :{name :'_freeres_buf' ,size :length },
69
+ 27 :{name :'_pad5' ,size :length },
70
+ 28 :{name :'_mode' ,size :4 },
71
+ 29 :{name :'_unused2' ,size :length },
72
+ 30 :{name :'vtable' ,size :length }
67
73
}
68
74
69
75
del name , size , length
70
76
71
77
72
- def update_var (l ):
78
+ def _update_var (l ):
73
79
r"""
74
80
Since different members of the file structure have different sizes, we need to keep track of the sizes. The following function is used by the FileStructure class to initialise the lengths of the various fields.
75
81
@@ -82,8 +88,8 @@ def update_var(l):
82
88
83
89
Examples:
84
90
85
- >>> update_var (8)
86
- {'flags': 8, '_IO_read_ptr': 8, '_IO_read_end': 8, '_IO_read_base': 8, '_IO_write_base': 8, '_IO_write_ptr': 8, '_IO_write_end': 8, '_IO_buf_base': 8, '_IO_buf_end': 8, '_IO_save_base': 8, '_IO_backup_base': 8, '_IO_save_end': 8, 'markers': 8, 'chain': 8, 'fileno': 4, '_flags2': 4, '_old_offset': 8, '_cur_column': 2, '_vtable_offset': 1, '_shortbuf': 1, 'unknown1': 4, '_lock': 8, '_offset': 8, '_codecvt': 8, '_wide_data': 8, 'unknown2 ': 48 , 'vtable': 8}
91
+ >>> _update_var (8)
92
+ {'flags': 8, '_IO_read_ptr': 8, '_IO_read_end': 8, '_IO_read_base': 8, '_IO_write_base': 8, '_IO_write_ptr': 8, '_IO_write_end': 8, '_IO_buf_base': 8, '_IO_buf_end': 8, '_IO_save_base': 8, '_IO_backup_base': 8, '_IO_save_end': 8, 'markers': 8, 'chain': 8, 'fileno': 4, '_flags2': 4, '_old_offset': 8, '_cur_column': 2, '_vtable_offset': 1, '_shortbuf': 1, 'unknown1': 4, '_lock': 8, '_offset': 8, '_codecvt': 8, '_wide_data': 8, '_freeres_list ': 8, '_freeres_buf': 8, '_pad5': 8, '_mode': 4, '_unused2': 20 , 'vtable': 8}
87
93
"""
88
94
var = {}
89
95
for i in variables :
@@ -92,11 +98,109 @@ def update_var(l):
92
98
if var [i ]<= 0 :
93
99
var [i ]+= l
94
100
if l == 4 :
95
- var ['unknown2 ' ]= 56
101
+ var ['_unused2 ' ]= 40
96
102
else :
97
- var ['unknown2 ' ]= 48
103
+ var ['_unused2 ' ]= 20
98
104
return var
99
105
106
+ class IO_flags :
107
+ _IO_MAGIC = 0xFBAD0000 # Magic number
108
+ _IO_MAGIC_MASK = 0xFFFF0000
109
+ _IO_USER_BUF = 0x0001 # Don't deallocate buffer on close.
110
+ _IO_UNBUFFERED = 0x0002
111
+ _IO_NO_READS = 0x0004 # Reading not allowed.
112
+ _IO_NO_WRITES = 0x0008 # Writing not allowed.
113
+ _IO_EOF_SEEN = 0x0010
114
+ _IO_ERR_SEEN = 0x0020
115
+ _IO_DELETE_DONT_CLOSE = 0x0040 # Don't call close(_fileno) on close.
116
+ _IO_LINKED = 0x0080 # In the list of all open files.
117
+ _IO_IN_BACKUP = 0x0100
118
+ _IO_LINE_BUF = 0x0200
119
+ _IO_TIED_PUT_GET = 0x0400 # Put and get pointer move in unison.
120
+ _IO_CURRENTLY_PUTTING = 0x0800
121
+ _IO_IS_APPENDING = 0x1000
122
+ _IO_IS_FILEBUF = 0x2000
123
+ _IO_USER_LOCK = 0x8000
124
+
125
+ class IO_flags2 :
126
+ _IO_FLAGS2_MMAP = 1
127
+ _IO_FLAGS2_NOTCANCEL = 2
128
+ _IO_FLAGS2_USER_WBUF = 8
129
+ _IO_FLAGS2_NOCLOSE = 32
130
+ _IO_FLAGS2_CLOEXEC = 64
131
+ _IO_FLAGS2_NEED_LOCK = 128
132
+
133
+ class _FlagsUnionBase (ctypes .Union ):
134
+ def __getattr__ (self , name ):
135
+ if any (name == field [0 ] for field in self ._flags_bits ._fields_ ):
136
+ return getattr (self ._flags_bits , name )
137
+ return super ().__getattr__ (name )
138
+
139
+ def __setattr__ (self , name , value ):
140
+ if any (name == field [0 ] for field in self ._flags_bits ._fields_ ):
141
+ setattr (self ._flags_bits , name , value )
142
+ return super ().__setattr__ (name , value )
143
+
144
+ def __int__ (self ):
145
+ return int (self ._flags )
146
+
147
+ def __str__ (self ):
148
+ return "{:#x} ({})" .format (self ._flags , self ._flags_bits )
149
+
150
+ # https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/libio.h#L66
151
+ class _IOFileFlags_bits (ctypes .LittleEndianStructure ):
152
+ _pack_ = 1
153
+ _fields_ = [
154
+ ("_IO_USER_BUF" , ctypes .c_uint8 , 1 ), # Don't deallocate buffer on close.
155
+ ("_IO_UNBUFFERED" , ctypes .c_uint8 , 1 ),
156
+ ("_IO_NO_READS" , ctypes .c_uint8 , 1 ), # Reading not allowed.
157
+ ("_IO_NO_WRITES" , ctypes .c_uint8 , 1 ), # Writing not allowed.
158
+ ("_IO_EOF_SEEN" , ctypes .c_uint8 , 1 ),
159
+ ("_IO_ERR_SEEN" , ctypes .c_uint8 , 1 ),
160
+ ("_IO_DELETE_DONT_CLOSE" , ctypes .c_uint8 , 1 ), # Don't call close(_fileno) on close.
161
+ ("_IO_LINKED" , ctypes .c_uint8 , 1 ), # In the list of all open files.
162
+ ("_IO_IN_BACKUP" , ctypes .c_uint8 , 1 ),
163
+ ("_IO_LINE_BUF" , ctypes .c_uint8 , 1 ),
164
+ ("_IO_TIED_PUT_GET" , ctypes .c_uint8 , 1 ), # Put and get pointer move in unison.
165
+ ("_IO_CURRENTLY_PUTTING" , ctypes .c_uint8 , 1 ),
166
+ ("_IO_IS_APPENDING" , ctypes .c_uint8 , 1 ),
167
+ ("_IO_IS_FILEBUF" , ctypes .c_uint8 , 1 ),
168
+ ("_IO_BAD_SEEN__UNUSED" , ctypes .c_uint8 , 1 ), # No longer used, reserved for compat.
169
+ ("_IO_USER_LOCK" , ctypes .c_uint8 , 1 ),
170
+ ("_IO_MAGIC" , ctypes .c_uint16 , 16 ), # Magic number 0xFBAD0000.
171
+ ]
172
+
173
+ def __str__ (self ):
174
+ return " | " .join (name for name , _ , _ in self ._fields_ if getattr (self , name ))
175
+
176
+ class _IOFileFlags (_FlagsUnionBase ):
177
+ _fields_ = [
178
+ ("_flags" , ctypes .c_uint64 ),
179
+ ("_flags_bits" , _IOFileFlags_bits ),
180
+ ]
181
+
182
+
183
+ # https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/libio.h#L85
184
+ class _IOFileFlags2_bits (ctypes .LittleEndianStructure ):
185
+ _pack_ = 1
186
+ _fields_ = [
187
+ ("_IO_FLAGS2_MMAP" , ctypes .c_uint8 , 1 ),
188
+ ("_IO_FLAGS2_NOTCANCEL" , ctypes .c_uint8 , 1 ),
189
+ ("_IO_FLAGS2_USER_WBUF" , ctypes .c_uint8 , 1 ),
190
+ ("_IO_FLAGS2_NOCLOSE" , ctypes .c_uint8 , 1 ),
191
+ ("_IO_FLAGS2_CLOEXEC" , ctypes .c_uint8 , 1 ),
192
+ ("_IO_FLAGS2_NEED_LOCK" , ctypes .c_uint8 , 1 ),
193
+ ]
194
+
195
+ def __str__ (self ):
196
+ return " | " .join (name for name , _ , _ in self ._fields_ if getattr (self , name ))
197
+
198
+ class _IOFileFlags2 (_FlagsUnionBase ):
199
+ _fields_ = [
200
+ ("_flags" , ctypes .c_uint64 ),
201
+ ("_flags_bits" , _IOFileFlags2_bits ),
202
+ ]
203
+
100
204
101
205
@python_2_bytes_compatible
102
206
class FileStructure (object ):
@@ -113,7 +217,8 @@ class FileStructure(object):
113
217
114
218
>>> context.clear(arch='amd64')
115
219
>>> fileStr = FileStructure(null=0xdeadbeeef)
116
- >>> fileStr.flags = 0xfbad1807
220
+ >>> fileStr.flags = 0xfbad1807 # or use flags by name:
221
+ >>> fileStr.flags = IO_flags._IO_MAGIC | IO_flags._IO_USER_BUF | IO_flags._IO_UNBUFFERED | IO_flags._IO_NO_READS | IO_flags._IO_CURRENTLY_PUTTING | IO_flags._IO_IS_APPENDING
117
222
>>> fileStr._IO_buf_base = 0xcafebabe
118
223
>>> fileStr._IO_buf_end = 0xfacef00d
119
224
>>> payload = bytes(fileStr)
@@ -128,8 +233,10 @@ class FileStructure(object):
128
233
The definition for __repr__ orders the structure members and displays then in a dictionary format. It's useful when viewing a structure objet in python/IPython shell
129
234
130
235
>>> q=FileStructure(0xdeadbeef)
236
+ >>> q.flags = IO_flags._IO_MAGIC | IO_flags._IO_USER_BUF
237
+ >>> q.flags._IO_TIED_PUT_GET = 1
131
238
>>> q
132
- { flags: 0x0
239
+ { flags: 0xfbad0401 (_IO_USER_BUF | _IO_TIED_PUT_GET | _IO_MAGIC)
133
240
_IO_read_ptr: 0x0
134
241
_IO_read_end: 0x0
135
242
_IO_read_base: 0x0
@@ -144,7 +251,7 @@ class FileStructure(object):
144
251
markers: 0x0
145
252
chain: 0x0
146
253
fileno: 0x0
147
- _flags2: 0x0
254
+ _flags2: 0x0 ()
148
255
_old_offset: 0xffffffffffffffff
149
256
_cur_column: 0x0
150
257
_vtable_offset: 0x0
@@ -154,7 +261,11 @@ class FileStructure(object):
154
261
_offset: 0xffffffffffffffff
155
262
_codecvt: 0x0
156
263
_wide_data: 0xdeadbeef
157
- unknown2: 0x0
264
+ _freeres_list: 0x0
265
+ _freeres_buf: 0x0
266
+ _pad5: 0x0
267
+ _mode: 0x0
268
+ _unused2: 0x0
158
269
vtable: 0x0}
159
270
"""
160
271
@@ -164,19 +275,29 @@ class FileStructure(object):
164
275
def __init__ (self , null = 0 ):
165
276
self .vars_ = [variables [i ]['name' ] for i in sorted (variables .keys ())]
166
277
self .setdefault (null )
167
- self .length = update_var (context .bytes )
278
+ self .length = _update_var (context .bytes )
168
279
self ._old_offset = (1 << context .bits ) - 1
169
280
170
281
def __setattr__ (self ,item ,value ):
171
282
if item in FileStructure .__dict__ or item in self .vars_ :
172
- object .__setattr__ (self ,item ,value )
283
+ if hasattr (self , item ) and isinstance (getattr (self , item ), _FlagsUnionBase ):
284
+ if isinstance (value , (bytes , bytearray )):
285
+ getattr (self , item )._flags = unpack (value .ljust (context .bytes , b'\x00 ' ))
286
+ else :
287
+ getattr (self , item )._flags = value
288
+ else :
289
+ object .__setattr__ (self ,item ,value )
173
290
else :
174
291
log .error ("Unknown variable %r" % item )
175
292
176
293
def __repr__ (self ):
177
294
structure = []
178
295
for i in self .vars_ :
179
- structure .append (" %s: %#x" % (i , getattr (self , i )))
296
+ val = getattr (self , i )
297
+ if isinstance (val , int ):
298
+ structure .append (" %s: %#x" % (i , val ))
299
+ else :
300
+ structure .append (" %s: %s" % (i , val ))
180
301
return "{" + "\n " .join (structure )+ "}"
181
302
182
303
def __len__ (self ):
@@ -189,7 +310,7 @@ def __bytes__(self):
189
310
structure += getattr (self , val ).ljust (context .bytes , b'\x00 ' )
190
311
else :
191
312
if self .length [val ] > 0 :
192
- structure += pack (getattr (self , val ), self .length [val ]* 8 )
313
+ structure += pack (int ( getattr (self , val ) ), self .length [val ]* 8 )
193
314
return structure
194
315
195
316
def struntil (self ,v ):
@@ -217,13 +338,13 @@ def struntil(self,v):
217
338
if isinstance (getattr (self , val ), bytes ):
218
339
structure += getattr (self , val ).ljust (context .bytes , b'\x00 ' )
219
340
else :
220
- structure += pack (getattr (self , val ), self .length [val ]* 8 )
341
+ structure += pack (int ( getattr (self , val ) ), self .length [val ]* 8 )
221
342
if val == v :
222
343
break
223
344
return structure [:- 1 ]
224
345
225
346
def setdefault (self ,null ):
226
- self .flags = 0
347
+ self .flags = _IOFileFlags ()
227
348
self ._IO_read_ptr = 0
228
349
self ._IO_read_end = 0
229
350
self ._IO_read_base = 0
@@ -238,7 +359,7 @@ def setdefault(self,null):
238
359
self .markers = 0
239
360
self .chain = 0
240
361
self .fileno = 0
241
- self ._flags2 = 0
362
+ self ._flags2 = _IOFileFlags2 ()
242
363
self ._old_offset = 0
243
364
self ._cur_column = 0
244
365
self ._vtable_offset = 0
@@ -248,7 +369,11 @@ def setdefault(self,null):
248
369
self ._offset = 0xffffffffffffffff
249
370
self ._codecvt = 0
250
371
self ._wide_data = null
251
- self .unknown2 = 0
372
+ self ._freeres_list = 0
373
+ self ._freeres_buf = 0
374
+ self ._pad5 = 0
375
+ self ._mode = 0
376
+ self ._unused2 = 0
252
377
self .vtable = 0
253
378
254
379
def write (self ,addr = 0 ,size = 0 ):
@@ -271,8 +396,8 @@ def write(self,addr=0,size=0):
271
396
>>> payload
272
397
b'\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
273
398
"""
274
- self .flags &= ~ 8
275
- self .flags |= 0x800
399
+ self .flags . _IO_NO_WRITES = 0
400
+ self .flags . _IO_CURRENTLY_PUTTING = 1
276
401
self ._IO_write_base = addr
277
402
self ._IO_write_ptr = addr + size
278
403
self ._IO_read_end = addr
@@ -299,7 +424,7 @@ def read(self,addr=0,size=0):
299
424
>>> payload
300
425
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
301
426
"""
302
- self .flags &= ~ 4
427
+ self .flags . _IO_NO_READS = 0
303
428
self ._IO_read_base = 0
304
429
self ._IO_read_ptr = 0
305
430
self ._IO_buf_base = addr
0 commit comments