[Linux] ImportC example: ptrace()
Posted: Sun Jan 24, 2010 6:30 pm
Hi,
the magic of ImportC is able to provide us with functions not present in the API that PB recognizes by default.
ptrace() allows a user to attach to a process in order to peek and poke (read and inject) into it.
The following code attaches to a running process and reads the contents of its data section (entailed from the /proc/ pseudofiles), then displays a chunk of it in a gadget.
Note: the program needs to be fed the PID of the to be debugged process. In the following code, pls set the 'pid' variable accordingly | add a ProgramParameter() accordingly etc.
Now for a question: I believe there should be some better way than continuously attaching and detaching. I tried for some time to have it do PTRACE_ATTACH only once, then PTRACE_CONT after the reading sweep, but then PTRACE_PEEKDATA would return errors (after the 'continue' issuance). I cannot find a way to 'break' the process again, without detaching or without actually breaking it the bad way.
Doubtful as an implementation as it is, the above works.
the magic of ImportC is able to provide us with functions not present in the API that PB recognizes by default.
ptrace() allows a user to attach to a process in order to peek and poke (read and inject) into it.
The following code attaches to a running process and reads the contents of its data section (entailed from the /proc/ pseudofiles), then displays a chunk of it in a gadget.
Note: the program needs to be fed the PID of the to be debugged process. In the following code, pls set the 'pid' variable accordingly | add a ProgramParameter() accordingly etc.
Code: Select all
pid = 6200 ; !!!! PROCESS TO ATTACH TO !!!!
;{ PTRACE CONSTANTS
#PTRACE_TRACEME = 0 ; Indicate that the process making this request should be traced. All signals received by this process can be intercepted by its parent, And its parent can use the other `ptrace' requests.
#PTRACE_PEEKTEXT = 1 ; Return the word in the process's text space at address ADDR.
#PTRACE_PEEKDATA = 2 ; Return the word in the process's data space at address ADDR.
#PTRACE_PEEKUSER = 3 ; Return the word in the process's user area at offset ADDR.
#PTRACE_POKETEXT = 4 ; Write the word DATA into the process's text space at address ADDR.
#PTRACE_POKEDATA = 5 ; Write the word DATA into the process's data space at address ADDR.
#PTRACE_POKEUSER = 6 ; Write the word DATA into the process's user area at offset ADDR.
#PTRACE_CONT = 7 ; Continue the process.
#PTRACE_KILL = 8 ; Kill the process.
#PTRACE_SINGLESTEP = 9 ; Single step the process. This is not supported on all machines.
#PTRACE_GETREGS = 12 ; Get all general purpose registers used by a processes. This is not supported on all machines.
#PTRACE_SETREGS = 13 ; Set all general purpose registers used by a processes. This is not supported on all machines.
#PTRACE_GETFPREGS = 14 ; Get all floating point registers used by a processes. This is not supported on all machines.
#PTRACE_SETFPREGS = 15 ; Set all floating point registers used by a processes. This is not supported on all machines.
#PTRACE_ATTACH = 16 ; Attach to a process that is already running.
#PTRACE_DETACH = 17 ; Detach from a process attached to with PTRACE_ATTACH.
#PTRACE_GETFPXREGS = 18 ; Get all extended floating point registers used by a processes. This is not supported on all machines.
#PTRACE_SETFPXREGS = 19 ; Set all extended floating point registers used by a processes. This is not supported on all machines.
#PTRACE_SYSCALL = 24 ; Continue and stop at the next (return from) syscall.
#PTRACE_SETOPTIONS = $4200 ; Set ptrace filter options.
#PTRACE_GETEVENTMSG = $4201 ; Get last ptrace message.
#PTRACE_GETSIGINFO = $4202 ; Get siginfo For process.
#PTRACE_SETSIGINFO = $4203 ; Set new siginfo for process.
;}
ImportC ""
ptrace(request, pid, *paddr, *pdata )
EndImport
Procedure Hex2Val( h.s )
h=UCase(h) : c=0
b=PeekB(@h) : m=Pow(16,Len(h)-1) : Repeat
If b>64 : b-7 : EndIf
b-48
v=v+(m*b) : m/16
c+1 : b=PeekB(@h+c)
Until b=0
ProcedureReturn v
EndProcedure
Procedure.s GetShellOutput( cmd.s )
prg = RunProgram("sh","-c "+#DQUOTE$+cmd+#DQUOTE$,"",#PB_Program_Open|#PB_Program_Read)
If prg
While ProgramRunning(prg) ;And AvailableProgramOutput(prg)
out.s=out+ReadProgramString(prg)+#LF$
Wend
EndIf
ProcedureReturn out
EndProcedure
Procedure GetMemoryMap( pid , *mstart.long , *mend.long )
s.s = GetShellOutput("ls -l /proc/"+Str(pid)+"/exe") : p = FindString(s," -> ",1)
exefullpath.s = Trim(Right(s,Len(s)-p-4))
s = GetShellOutput("cat /proc/"+Str(pid)+"/maps | grep rwxp | grep "+exefullpath)
s = StringField(s,1," ")
*mstart\l = Hex2Val(StringField(s,1,"-"))
*mend\l = Hex2Val(StringField(s,2,"-"))
ProcedureReturn *mstart\l
EndProcedure
Procedure FillPtraceBuffer( *mem.ascii , pid , mstart , mend )
err = ptrace( #PTRACE_ATTACH , pid ,0,0) : Delay(10)
If err=0
For a=mstart To mend-1
by.a=ptrace( #PTRACE_PEEKDATA , pid , a , 0 )
*mem\a=by : *mem+1
Next
ptrace( #PTRACE_DETACH , pid ,0,0) : Delay(10)
Else
Debug "Can't attach!"
EndIf
EndProcedure
Procedure UpdateMemoryView( gad , *mem.ascii , startaddr )
s.s="" : wl=60 : ww=512
For a=0 To ww-1
If Len(s)%wl=0 : s=s+"."+RSet(Hex(startaddr,#PB_Long),8,"0")+" " : startaddr+16 : EndIf
s = s+RSet(Hex(*mem\a,#PB_Byte),3) : *mem+1
If Len(s)%wl=59 : s=s+#LF$ : EndIf
Next
SetGadgetText(gad,s)
EndProcedure
ms.l : me.l : GetMemoryMap(pid,@ms,@me) : ml = me-mf
ptrc_buf = AllocateMemory(ml)
If OpenWindow(0,0,0,400,600,"Ptrace Example")
EditorGadget(0,0,0,400,600)
LoadFont(0,"Monospace",8) : SetGadgetFont(0,FontID(0))
AddWindowTimer(0,1,500)
Else : End : EndIf
Repeat
EvID=WaitWindowEvent()
Select EvID
Case #PB_Event_Timer
FillPtraceBuffer( ptrc_buf , pid , ms , me )
UpdateMemoryView( 0 , ptrc_buf , ms )
EndSelect
Until EvID=#PB_Event_CloseWindow
Doubtful as an implementation as it is, the above works.