Playing with ptrace 2.docx
《Playing with ptrace 2.docx》由会员分享,可在线阅读,更多相关《Playing with ptrace 2.docx(16页珍藏版)》请在冰豆网上搜索。
Playingwithptrace2
InPartIIofhisseriesonptrace,Pradeeptacklesthemoreadvancedtopicsofsettingbreakpointsandinjectingcodeintorunningprocesses.
InPartIofthisarticle[LJ,November2002],wesawhowptracecanbeusedtotracesystemcallsandchangesystemcallarguments.Inthisarticle,weinvestigateadvancedtechniqueslikesettingbreakpointsandinjectingcodeintorunningprograms.Debuggersusethesemethodstosetupbreakpointsandexecutedebugginghandlers.AswithPartI,allcodeinthisarticleisi386architecture-specific.
AttachingtoaRunningProcess
InPartI,werantheprocesstobetracedasachildaftercallingptrace(PTRACE_TRACEME,..).Ifyousimplywantedtoseehowtheprocessismakingsystemcallsandtracetheprogram,thiswouldbesufficient.Ifyouwanttotraceordebugaprocessalreadyrunning,thenptrace(PTRACE_ATTACH,..)shouldbeused.
Whenaptrace(PTRACE_ATTACH,..)iscalledwiththepidtobetraced,itisroughlyequivalenttotheprocesscallingptrace(PTRACE_TRACEME,..)andbecomingachildofthetracingprocess.ThetracedprocessissentaSIGSTOP,sowecanexamineandmodifytheprocessasusual.Afterwearedonewithmodificationsortracing,wecanletthetracedprocesscontinueonitsownbycallingptrace(PTRACE_DETACH,..).
Thefollowingisthecodeforasmallexampletracingprogram:
intmain()
{inti;
for(i=0;i<10;++i){
printf("Mycounter:
%d\n",i);
sleep
(2);
}
return0;
}
Savetheprogramasdummy2.c.Compileandrunit:
gcc-odummy2dummy2.c
./dummy2&
Now,wecanattachtodummy2byusingthecodebelow:
#include
#include
#include
#include
#include/*Foruser_regs_struct
etc.*/
intmain(intargc,char*argv[])
{pid_ttraced_process;
structuser_regs_structregs;
longins;
if(argc!
=2){
printf("Usage:
%s\n",
argv[0],argv[1]);
exit
(1);
}
traced_process=atoi(argv[1]);
ptrace(PTRACE_ATTACH,traced_process,
NULL,NULL);
wait(NULL);
ptrace(PTRACE_GETREGS,traced_process,
NULL,®s);
ins=ptrace(PTRACE_PEEKTEXT,traced_process,
regs.eip,NULL);
printf("EIP:
%lxInstructionexecuted:
%lx\n",
regs.eip,ins);
ptrace(PTRACE_DETACH,traced_process,
NULL,NULL);
return0;
}
Theaboveprogramsimplyattachestoaprocess,waitsforittostop,examinesitseip(instructionpointer)anddetaches.
Toinjectcodeuseptrace(PTRACE_POKETEXT,..)andptrace(PTRACE_POKEDATA,..)afterthetracedprocesshasstopped.
SettingBreakpoints
Howdodebuggerssetbreakpoints?
Generally,theyreplacetheinstructiontobeexecutedwithatrapinstruction,sothatwhenthetracedprogramstops,thetracingprogram,thedebugger,canexamineit.Itwillreplacetheoriginalinstructiononcethetracingprogramcontinuesthetracedprocess.Here'sanexample:
#include
#include
#include
#include
#include
constintlong_size=sizeof(long);
voidgetdata(pid_tchild,longaddr,
char*str,intlen)
{char*laddr;
inti,j;
unionu{
longval;
charchars[long_size];
}data;
i=0;
j=len/long_size;
laddr=str;
while(idata.val=ptrace(PTRACE_PEEKDATA,child,
addr+i*4,NULL);
memcpy(laddr,data.chars,long_size);
++i;
laddr+=long_size;
}
j=len%long_size;
if(j!
=0){
data.val=ptrace(PTRACE_PEEKDATA,child,
addr+i*4,NULL);
memcpy(laddr,data.chars,j);
}
str[len]='\0';
}
voidputdata(pid_tchild,longaddr,
char*str,intlen)
{char*laddr;
inti,j;
unionu{
longval;
charchars[long_size];
}data;
i=0;
j=len/long_size;
laddr=str;
while(imemcpy(data.chars,laddr,long_size);
ptrace(PTRACE_POKEDATA,child,
addr+i*4,data.val);
++i;
laddr+=long_size;
}
j=len%long_size;
if(j!
=0){
memcpy(data.chars,laddr,j);
ptrace(PTRACE_POKEDATA,child,
addr+i*4,data.val);
}
}
intmain(intargc,char*argv[])
{pid_ttraced_process;
structuser_regs_structregs,newregs;
longins;
/*int0x80,int3*/
charcode[]={0xcd,0x80,0xcc,0};
charbackup[4];
if(argc!
=2){
printf("Usage:
%s\n",
argv[0],argv[1]);
exit
(1);
}
traced_process=atoi(argv[1]);
ptrace(PTRACE_ATTACH,traced_process,
NULL,NULL);
wait(NULL);
ptrace(PTRACE_GETREGS,traced_process,
NULL,®s);
/*Copyinstructionsintoabackupvariable*/
getdata(traced_process,regs.eip,backup,3);
/*Putthebreakpoint*/
putdata(traced_process,regs.eip,code,3);
/*Lettheprocesscontinueandexecute
theint3instruction*/
ptrace(PTRACE_CONT,traced_process,NULL,NULL);
wait(NULL);
printf("Theprocessstopped,puttingback"
"theoriginalinstructions\n");
printf("Presstocontinue\n");
getchar();
putdata(traced_process,regs.eip,backup,3);
/*Settingtheeipbacktotheoriginal
instructiontolettheprocesscontinue*/
ptrace(PTRACE_SETREGS,traced_process,
NULL,®s);
ptrace(PTRACE_DETACH,traced_process,
NULL,NULL);
return0;
}
Herewereplacethethreebyteswiththecodeforatrapinstruction,andwhentheprocessstops,wereplacetheoriginalinstructionsandresettheeiptooriginallocation.Figures1-4clarifyhowtheinstructionstreamlookswhenaboveprogramisexecuted.
Figure1.AftertheProcessIsStopped
Figure2.AftertheTrapInstructionBytesAreSet
Figure3.TrapIsHitandControlIsGiventotheTracingProgram
Figure4.AftertheOriginalInstructionsAreReplacedandeipIsResettotheOriginalLocation
Nowthatwehaveaclearideaofhowbreakpointsareset,let'sinjectsomecodebytesintoarunningprogram.Thesecodebyteswillprint“helloworld”.
Thefollowingprogramisasimple“helloworld”programwithmodificationstofitourneeds.Compilethefollowingprogramwith:
gcc-ohellohello.c
voidmain()
{
__asm__("
jmpforward
backward:
popl%esi#Gettheaddressof
#helloworldstring
movl$4,%eax#Dowritesystemcall
movl$2,%ebx
movl%esi,%ecx
movl$12,%edx
int$0x80
int3#Breakpoint.Herethe
#programwillstopand
#givecontrolbackto
#theparent
forward:
callbackward
.string\"HelloWorld\\n\""
);
}
Thejumpingbackwardandforwardhereisrequiredtofindtheaddressofthe“helloworld”string.
WecangetthemachinecodefortheaboveassemblyfromGDB.FireupGDBanddisassembletheprogram:
(gdb)disassemblemain
Dumpofassemblercodeforfunctionmain:
0x80483e0:
push%ebp
0x80483e1:
mov%esp,%ebp
0x80483e3:
jmp0x80483fa
Endofassemblerdump.
(gdb)disassembleforward
Dumpofassemblercodeforfunctionforward:
0x80483fa:
call0x80483e5
0x80483ff:
dec%eax
0x8048400:
gs
0x8048401:
insb(%dx),%es:
(%edi)
0x8048402:
insb(%dx),%es:
(%edi)
0x8048403:
outsl%ds:
(%esi),(%dx)
0x8048404:
and%dl,0x6f(%edi)
0x8048407:
jb0x8048475
0x8048409:
or%fs:
(%eax),%al
0x804840c:
mov%ebp,%esp
0x804840e:
pop%ebp
0x804840f:
ret
Endofassemblerdump.
(gdb)disassemblebackward
Dumpofassemblercodeforfunctionbackward:
0x80483e5:
pop%esi
0x80483e6:
mov$0x4,%eax
0x80483eb:
mov$0x2,%ebx
0x80483f0:
mov%esi,%ecx
0x80483f2:
mov$0xc,%edx
0x80483f7:
int$0x80
0x80483f9:
int3
Endofassemblerdump.
Weneedtotakethemachinecodebytesfrommain+3tobackward+20,whichisatotalof41bytes.ThemachinecodecanbeseenwiththexcommandinGDB:
(gdb)x/40bxmain+3
:
eb155eb804000000
:
bb0200000089f1ba
:
0c000000cd80cc
:
e6ffffff48656c6c
:
6f20576f726c640a
Nowwehavetheinstructionbytestobeexecuted.Whywait?
Wecaninjectthemusingthesamemethodasinthepreviousexample.Thefollowingisthesourcecode;onlythemainfunctionisgivenhere:
intmain(intargc,char*argv[])
{pid_ttraced_process;
structuser_regs_structregs,newregs;
longins;
intlen=41;
charinsertcode[]=
"\xeb\x15\x5e\xb8\x04\x00"
"\x00\x00\xbb\x02\x00\x00\x00\x89\xf1\xba"
"\x0c\x00\x00\x00\xcd\x80\xcc\xe8\xe6\xff"
"\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f"
"\x72\x6c\x64\x0a\x00";
charbackup[len];
if(argc!
=2){
printf("Usage:
%s\n",
argv[0],argv[1]);
exit
(1);
}
traced_process=atoi(argv[1]);
ptrace(PTRACE_ATTACH,traced_process,
NULL,NULL);
wait(NULL);
ptrace(PTRACE_GETREGS,traced_process,
NULL,®s);
getdata(traced_process,regs.eip,backup,len);
putdata(traced_process,regs.eip,
insertcode,len);
ptrace(PTRACE_SETREGS,traced_process,
NULL,®s);
ptrace(PTRACE_CONT,traced_process,
NULL,NULL);
wait(NULL);
printf("Theprocessstopped,Puttingback"
"theoriginalinstructions\n");
putdata(traced_process,regs.eip,backup,len);
ptrace(PTRACE_SETREGS,traced_process,
NULL,®s);
printf("Lettingitcontinuewith"
"originalflow\n");
ptrace(PTRACE_DETACH,traced_process,
NULL,NULL);
return0;
InPartIIofhisseriesonptrace,Pradeeptacklesthemoreadvancedtopicsofsettingbreakpointsandinjectingcodeintorunningprocesses.
InjectingtheCodeintoFreeSpace
Inthepreviousexampleweinjectedthecodedirectlyintotheexecutinginstructionstream.However,debuggerscangetconfusedwiththiskindofbehaviour,solet'sfindthefreespaceintheprocessandinjectthecodethere.Wecanfindfreespacebyexaminingthe/proc/pid/mapsfileofthetr