| Trees | Indices | Help |
|---|
|
|
1 """
2 Linux Platform Module
3 """
4 # Copyright (C) 2007 Invisigoth - See LICENSE file for details
5 import os
6 import struct
7 import signal
8 import traceback
9 import platform
10
11 import envi.memory as e_mem
12 import envi.registers as e_reg
13
14 import vtrace
15 import vtrace.archs.i386 as v_i386
16 import vtrace.archs.amd64 as v_amd64
17 import vtrace.platforms.base as v_base
18 import vtrace.platforms.posix as v_posix
19
20 from ctypes import *
21 import ctypes.util as cutil
22
23 libc = CDLL(cutil.find_library("c"))
24
25 libc.lseek64.restype = c_ulonglong
26 libc.lseek64.argtypes = [c_uint, c_ulonglong, c_uint]
27 libc.read.restype = c_long
28 libc.read.argtypes = [c_uint, c_void_p, c_long]
29 libc.write.restype = c_long
30 libc.write.argtypes = [c_uint, c_void_p, c_long]
31
32 O_RDWR = 2
33 O_LARGEFILE = 0x8000
34
35 MAP_ANONYMOUS = 0x20
36 MAP_PRIVATE = 0x02
37
38 # Linux specific ptrace extensions
39 PT_GETREGS = 12
40 PT_SETREGS = 13
41 PT_GETFPREGS = 14
42 PT_SETFPREGS = 15
43 PT_ATTACH = 16
44 PT_DETACH = 17
45 PT_GETFPXREGS = 18
46 PT_SETFPXREGS = 19
47 PT_SYSCALL = 24
48 PT_SETOPTIONS = 0x4200
49 PT_GETEVENTMSG = 0x4201
50 PT_GETSIGINFO = 0x4202
51 PT_SETSIGINFO = 0x4203
52 # PT set options stuff. ONLY TRACESYSGOOD may be used in 2.4...
53 PT_O_TRACESYSGOOD = 0x00000001 # add 0x80 to TRAP when generated by syscall
54 # For each of the options below, the stop signal is (TRAP | PT_EVENT_FOO << 8)
55 PT_O_TRACEFORK = 0x00000002 # Cause a trap at fork
56 PT_O_TRACEVFORK = 0x00000004 # Cause a trap at vfork
57 PT_O_TRACECLONE = 0x00000008 # Cause a trap at clone
58 PT_O_TRACEEXEC = 0x00000010 # Cause a trap at exec
59 PT_O_TRACEVFORKDONE = 0x00000020 # Cause a trap when vfork done
60 PT_O_TRACEEXIT = 0x00000040 # Cause a trap on exit
61 PT_O_MASK = 0x0000007f
62 # Ptrace event types (TRAP | PT_EVENT_FOO << 8) means that type
63 # when using GETEVENTMSG for most of these, the new pid is the data
64 PT_EVENT_FORK = 1
65 PT_EVENT_VFORK = 2
66 PT_EVENT_CLONE = 3
67 PT_EVENT_EXEC = 4
68 PT_EVENT_VFORK_DONE = 5
69 PT_EVENT_EXIT = 6
70
71 # Used to tell some of the additional events apart
72 SIG_LINUX_SYSCALL = signal.SIGTRAP | 0x80
73 SIG_LINUX_CLONE = signal.SIGTRAP | (PT_EVENT_CLONE << 8)
76 _fields_ = (
77 ("ebx", c_ulong),
78 ("ecx", c_ulong),
79 ("edx", c_ulong),
80 ("esi", c_ulong),
81 ("edi", c_ulong),
82 ("ebp", c_ulong),
83 ("eax", c_ulong),
84 ("ds", c_ushort),
85 ("__ds", c_ushort),
86 ("es", c_ushort),
87 ("__es", c_ushort),
88 ("fs", c_ushort),
89 ("__fs", c_ushort),
90 ("gs", c_ushort),
91 ("__gs", c_ushort),
92 ("orig_eax", c_ulong),
93 ("eip", c_ulong),
94 ("cs", c_ushort),
95 ("__cs", c_ushort),
96 ("eflags", c_ulong),
97 ("esp", c_ulong),
98 ("ss", c_ushort),
99 ("__ss", c_ushort),
100 )
101
104 _fields_ = (
105 # NOTE: Expand out the user regs struct so
106 # we can make one call to _rctx_Import
107 ("regs", user_regs_i386),
108 ("u_fpvalid", c_ulong),
109 ("u_tsize", c_ulong),
110 ("u_dsize", c_ulong),
111 ("u_ssize", c_ulong),
112 ("start_code", c_ulong),
113 ("start_stack",c_ulong),
114 ("signal", c_ulong),
115 ("reserved", c_ulong),
116 ("u_ar0", c_void_p),
117 ("u_fpstate", c_void_p),
118 ("magic", c_ulong),
119 ("u_comm", c_char*32),
120 ("debug0", c_ulong),
121 ("debug1", c_ulong),
122 ("debug2", c_ulong),
123 ("debug3", c_ulong),
124 ("debug4", c_ulong),
125 ("debug5", c_ulong),
126 ("debug6", c_ulong),
127 ("debug7", c_ulong),
128 )
129
131 _fields_ = [
132 ('r15', c_uint64),
133 ('r14', c_uint64),
134 ('r13', c_uint64),
135 ('r12', c_uint64),
136 ('rbp', c_uint64),
137 ('rbx', c_uint64),
138 ('r11', c_uint64),
139 ('r10', c_uint64),
140 ('r9', c_uint64),
141 ('r8', c_uint64),
142 ('rax', c_uint64),
143 ('rcx', c_uint64),
144 ('rdx', c_uint64),
145 ('rsi', c_uint64),
146 ('rdi', c_uint64),
147 ('orig_rax', c_uint64),
148 ('rip', c_uint64),
149 ('cs', c_uint64),
150 ('eflags', c_uint64),
151 ('rsp', c_uint64),
152 ('ss', c_uint64),
153 ('fs_base', c_uint64),
154 ('gs_base', c_uint64),
155 ('ds', c_uint64),
156 ('es', c_uint64),
157 ('fs', c_uint64),
158 ('gs', c_uint64),
159 ]
160
161 # Modern linux only lets us write to these...
162 dbgregs = (0,1,2,3,6,7)
165 """
166 The mixin to take care of linux specific platform traits.
167 (mostly proc)
168 """
169
171 # Wrap reads from proc in our worker thread
172 v_posix.PtraceMixin.__init__(self)
173 v_posix.PosixMixin.__init__(self)
174 self.nptlinit = False
175 self.memfd = None
176
177 self.fireTracerThread()
178
179 self.initMode("Syscall", False, "Break On Syscalls")
180
181 @v_base.threadwrap
183 print 'FIXME: known bug with thread create events from execd linux trace!'
184 pid = v_posix.PtraceMixin.platformExec(self, cmdline)
185 self.pthreads = [pid,]
186 self.setMeta("ExeName",self._findExe(pid))
187 return pid
188
190 """
191 A utility to open (if necissary) and seek the memfile
192 """
193 if self.memfd == None:
194 self.memfd = libc.open("/proc/%d/mem" % self.pid, O_RDWR | O_LARGEFILE, 0755)
195
196 x = libc.lseek64(self.memfd, offset, 0)
197
198 @v_base.threadwrap
200 #FIXME this is intel specific and should probably go in with the regs
201 sp = self.getStackCounter()
202 pc = self.getProgramCounter()
203
204 # Xlate perms (mmap is backward)
205 realperm = 0
206 if perms & e_mem.MM_READ:
207 realperm |= 1
208 if perms & e_mem.MM_WRITE:
209 realperm |= 2
210 if perms & e_mem.MM_EXEC:
211 realperm |= 4
212
213 #mma is struct of mmap args for linux syscall
214 mma = struct.pack("<6L", suggestaddr, size, realperm, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0)
215
216 regsave = self.getRegisters()
217
218 stacksave = self.readMemory(sp, len(mma))
219 ipsave = self.readMemory(pc, 2)
220
221 SYS_mmap = 90
222
223 self.writeMemory(sp, mma)
224 self.writeMemory(pc, "\xcd\x80")
225 self.setRegisterByName("eax", SYS_mmap)
226 self.setRegisterByName("ebx", sp)
227 self._syncRegs()
228
229 try:
230 # Step over our syscall instruction
231 tid = self.getMeta("ThreadId", 0)
232 self.platformStepi()
233 os.waitpid(tid, 0)
234 eax = self.getRegisterByName("eax")
235 if eax & 0x80000000:
236 raise Exception("Linux mmap syscall error: %d" % eax)
237 return eax
238
239 finally:
240 # Clean up all our fux0ring
241 self.writeMemory(sp, stacksave)
242 self.writeMemory(pc, ipsave)
243 self.setRegisters(regsave)
244
246 for tid in self.threadsForPid(self.pid):
247 if tid == self.pid:
248 continue
249 self.attachThread(tid)
250 v_posix.PosixMixin.posixCreateThreadHack(self)
251
252 @v_base.threadwrap
254 """
255 A *much* faster way of reading memory that the 4 bytes
256 per syscall allowed by ptrace
257 """
258 self.setupMemFile(address)
259 # Use ctypes cause python implementation is teh ghey
260 buf = create_string_buffer(size)
261 x = libc.read(self.memfd, addressof(buf), size)
262 if x != size:
263 #libc.perror('libc.read %d (size: %d)' % (x,size))
264 raise Exception("reading from invalid memory %s (%d returned)" % (hex(address), x))
265 # We have to slice cause ctypes "helps" us by adding a null byte...
266 return buf.raw
267
268 @v_base.threadwrap
270 """
271 A *much* faster way of writting memory that the 4 bytes
272 per syscall allowed by ptrace
273 """
274 self.setupMemFile(address)
275 buf = create_string_buffer(data)
276 size = len(data)
277 x = libc.write(self.memfd, addressof(buf), size)
278 if x != size:
279 libc.perror('write mem failed: 0x%.8x (%d)' % (address, size))
280 raise Exception("write memory failed: %d" % x)
281 return x
282
284 exe = os.readlink("/proc/%d/exe" % pid)
285 if "(deleted)" in exe:
286 if "#prelink#" in exe:
287 exe = exe.split(".#prelink#")[0]
288 elif ";" in exe:
289 exe = exe.split(";")[0]
290 else:
291 exe = exe.split("(deleted)")[0].strip()
292 return exe
293
294 @v_base.threadwrap
296 self.pthreads = [pid,]
297 self.setMeta("ThreadId", pid)
298 if v_posix.ptrace(PT_ATTACH, pid, 0, 0) != 0:
299 raise Exception("PT_ATTACH failed!")
300 self.setupPtraceOptions(pid)
301 self.setMeta("ExeName", self._findExe(pid))
302
304 pslist = []
305 for dname in os.listdir("/proc/"):
306 try:
307 if not dname.isdigit():
308 continue
309 cmdline = file("/proc/%s/cmdline" % dname).read()
310 cmdline = cmdline.replace("\x00"," ")
311 if len(cmdline) > 0:
312 pslist.append((int(dname),cmdline))
313 except:
314 pass # Permissions... quick process... whatev.
315 return pslist
316
318 self.doAttachThread(tid,attached=attached)
319 self.setMeta("ThreadId", tid)
320 self.fireNotifiers(vtrace.NOTIFY_CREATE_THREAD)
321
322 @v_base.threadwrap
324 # Blocking wait once...
325 pid, status = os.waitpid(-1, 0x40000002)
326 self.setMeta("ThreadId", pid)
327 # Stop the rest of the threads...
328 # why is linux debugging so Ghetto?!?!
329 if not self.stepping: # If we're stepping, only do the one
330 for tid in self.pthreads:
331 if tid == pid:
332 continue
333 try:
334 # We use SIGSTOP here because they can't mask it.
335 os.kill(tid, signal.SIGSTOP)
336 os.waitpid(tid, 0x40000002)
337 except Exception, e:
338 print "WARNING TID is invalid %d %s" % (tid,e)
339 return status
340
341 # If it's linux 2.4 we must threadwrap wait...
342 if platform.release().startswith("2.4"):
343 platformWait = v_base.threadwrap(platformWait)
344
345 @v_base.threadwrap
347 cmd = v_posix.PT_CONTINUE
348 if self.getMode("Syscall", False):
349 cmd = PT_SYSCALL
350 pid = self.getPid()
351 sig = self.getCurrentSignal()
352 if sig == None:
353 sig = 0
354 # Only deliver signals to the main thread
355 if v_posix.ptrace(cmd, pid, 0, sig) != 0:
356 raise Exception("ERROR ptrace failed for tid %d" % pid)
357
358 for tid in self.pthreads:
359 if tid == pid:
360 continue
361 if v_posix.ptrace(cmd, tid, 0, 0) != 0:
362 pass
363
364 @v_base.threadwrap
366 self.stepping = True
367 tid = self.getMeta("ThreadId", 0)
368 if v_posix.ptrace(v_posix.PT_STEP, tid, 0, 0) != 0:
369 raise Exception("ERROR ptrace failed!")
370
371 @v_base.threadwrap
373 libc.close(self.memfd)
374 for tid in self.pthreads:
375 tid,v_posix.ptrace(PT_DETACH, tid, 0, 0)
376
377 @v_base.threadwrap
379 """
380 Do the work for attaching a thread. This must be *under*
381 attachThread() so callers in notifiers may call it (because
382 it's also gotta be thread wrapped).
383 """
384 if not attached:
385 if v_posix.ptrace(PT_ATTACH, tid, 0, 0) != 0:
386 raise Exception("ERROR ptrace attach failed for thread %d" % tid)
387 os.waitpid(tid, 0x40000002)
388 self.setupPtraceOptions(tid)
389 self.pthreads.append(tid)
390
392 """
393 Called by doAttachThread to setup ptrace related options.
394 """
395 opts = PT_O_TRACESYSGOOD
396 if platform.release().startswith("2.6"):
397 opts |= PT_O_TRACECLONE
398 x = v_posix.ptrace(PT_SETOPTIONS, tid, 0, opts)
399 if x != 0:
400 libc.perror('ptrace PT_SETOPTION failed for thread %d' % tid)
401 #print "WARNING ptrace SETOPTIONS failed for thread %d (%d)" % (tid,x)
402
404 ret = []
405 tpath = "/proc/%s/task" % pid
406 if os.path.exists(tpath):
407 for pidstr in os.listdir(tpath):
408 ret.append(int(pidstr))
409 return ret
410
412 # Skim some linux specific events before passing to posix
413 tid = self.getMeta("ThreadId", -1)
414 if os.WIFSTOPPED(status):
415 sig = status >> 8
416 if sig == SIG_LINUX_SYSCALL:
417 self.fireNotifiers(vtrace.NOTIFY_SYSCALL)
418
419 elif sig == SIG_LINUX_CLONE:
420 # Handle a new thread here!
421 newtid = self.getPtraceEvent()
422 self.attachThread(newtid, attached=True)
423
424 #FIXME eventually implement child catching!
425 else:
426 self.handlePosixSignal(sig)
427
428 return
429
430 v_posix.PosixMixin.platformProcessEvent(self, status)
431
432 @v_base.threadwrap
434 """
435 This *thread wrapped* function will get any pending GETEVENTMSG
436 msgs.
437 """
438 p = c_ulong(0)
439 tid = self.getMeta("ThreadId", -1)
440 if v_posix.ptrace(PT_GETEVENTMSG, tid, 0, addressof(p)) != 0:
441 raise Exception('ptrace PT_GETEVENTMSG failed!')
442 return p.value
443
445 ret = {}
446 for tid in self.pthreads:
447 ret[tid] = tid #FIXME make this pthread struct or stackbase soon
448 return ret
449
451 maps = []
452 mapfile = file("/proc/%d/maps" % self.pid)
453 for line in mapfile:
454
455 perms = 0
456 sline = line.split(" ")
457 addrs = sline[0]
458 permstr = sline[1]
459 fname = sline[-1].strip()
460 addrs = addrs.split("-")
461 base = long(addrs[0],16)
462 max = long(addrs[1],16)
463 mlen = max-base
464
465 if "r" in permstr:
466 perms |= e_mem.MM_READ
467 if "w" in permstr:
468 perms |= e_mem.MM_WRITE
469 if "x" in permstr:
470 perms |= e_mem.MM_EXEC
471 #if "p" in permstr:
472 #pass
473
474 maps.append((base,mlen,perms,fname))
475 return maps
476
478 fds = []
479 for name in os.listdir("/proc/%d/fd/" % self.pid):
480 try:
481 fdnum = int(name)
482 fdtype = vtrace.FD_UNKNOWN
483 link = os.readlink("/proc/%d/fd/%s" % (self.pid,name))
484 if "socket:" in link:
485 fdtype = vtrace.FD_SOCKET
486 elif "pipe:" in link:
487 fdtype = vtrace.FD_PIPE
488 elif "/" in link:
489 fdtype = vtrace.FD_FILE
490
491 fds.append((fdnum,fdtype,link))
492 except:
493 traceback.print_exc()
494
495 return fds
496
497 ############################################################################
498 #
499 # NOTE: Both of these use class locals set by the i386/amd64 variants
500 #
501 @v_base.threadwrap
503 ctx = self.archGetRegCtx()
504 u = self.user_reg_struct()
505 if v_posix.ptrace(PT_GETREGS, tid, 0, addressof(u)) == -1:
506 raise Exception("Error: ptrace(PT_GETREGS...) failed!")
507
508 ctx._rctx_Import(u)
509
510 for i in dbgregs:
511 offset = self.user_dbg_offset + (self.psize * i)
512 r = v_posix.ptrace(v_posix.PT_READ_U, tid, offset, 0)
513 ctx.setRegister(self.dbgidx+i, r & self.reg_val_mask)
514
515 return ctx
516
517 @v_base.threadwrap
519 u = self.user_reg_struct()
520 # Populate the reg struct with the current values (to allow for
521 # any regs in that struct that we don't track... *fs_base*ahem*
522 if v_posix.ptrace(PT_GETREGS, tid, 0, addressof(u)) == -1:
523 raise Exception("Error: ptrace(PT_GETREGS...) failed!")
524
525 ctx._rctx_Export(u)
526 if v_posix.ptrace(PT_SETREGS, tid, 0, addressof(u)) == -1:
527 raise Exception("Error: ptrace(PT_SETREGS...) failed!")
528
529 for i in dbgregs:
530 val = ctx.getRegister(self.dbgidx + i)
531 offset = self.user_dbg_offset + (self.psize * i)
532 if v_posix.ptrace(v_posix.PT_WRITE_U, tid, offset, val) != 0:
533 libc.perror('PT_WRITE_U failed for debug%d' % i)
534 #raise Exception("PT_WRITE_U for debug%d failed!" % i)
535
536 -class Linuxi386Trace(
537 vtrace.Trace,
538 LinuxMixin,
539 v_i386.i386Mixin,
540 v_posix.ElfMixin,
541 v_base.TracerBase):
542
543
544 user_reg_struct = user_regs_i386
545 user_dbg_offset = 252
546 reg_val_mask = 0xffffffff
547
549 vtrace.Trace.__init__(self)
550 v_base.TracerBase.__init__(self)
551 v_posix.ElfMixin.__init__(self)
552 v_i386.i386Mixin.__init__(self)
553 LinuxMixin.__init__(self)
554
555 # Pre-calc the index of the debug regs
556 self.dbgidx = self.archGetRegCtx().getRegisterIndex("debug0")
557
558 -class LinuxAmd64Trace(
559 vtrace.Trace,
560 LinuxMixin,
561 v_amd64.Amd64Mixin,
562 v_posix.ElfMixin,
563 v_base.TracerBase):
564
565 user_reg_struct = user_regs_amd64
566 user_dbg_offset = 848
567 reg_val_mask = 0xffffffffffffffff
568
570 vtrace.Trace.__init__(self)
571 v_base.TracerBase.__init__(self)
572 v_posix.ElfMixin.__init__(self)
573 v_amd64.Amd64Mixin.__init__(self)
574 LinuxMixin.__init__(self)
575
576 self.dbgidx = self.archGetRegCtx().getRegisterIndex("debug0")
577
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Fri Nov 16 18:22:25 2012 | http://epydoc.sourceforge.net |