هر فرآیندی که روی دسکتاپ شما اجرا می شود از تماس های سیستمی برای برقراری ارتباط با سیستم عامل استفاده می کند. با استفاده از strace می توانید چنین تماس های سیستمی را به راحتی پیگیری کنید.
هنگامی که برنامه های در حال اجرا بر روی لینوکس می خواهند از منابع مدیریت شده توسط سیستم عامل (خواندن فایل ها، ایجاد فرآیندها و غیره) استفاده کنند، آنها سیستم عامل را فراخوانی می کنند. فراخوانی های سیستم در سطح هسته کار می کنند و عملیات لازم را انجام می دهند و کنترل را به برنامه فراخوانی باز می گردانند. ابزار strace امکان ردیابی این تماس های سیستمی را در لینوکس فراهم می کند.
استفاده معمولی از دستور strace
برای نظارت بر فراخوان های سیستم برای یک برنامه، کافیست دستور با strace را در قالب زیر فراخوانی کنید:
strace ls /tmp
با این حال، اغلب فرآیندهایی وجود دارند که خیلی زودتر شروع می شوند و در پس زمینه به کار خود ادامه می دهند. با توجه به هر گونه مشکل، ممکن است بخواهید اطلاعات اضافی مرتبط با چنین فرآیندهایی را جمع آوری کنید. با دادن شناسه فرآیند فرآیند به پارامتر -p میتوانید strace را به هر برنامه در حال اجرا متصل کنید:
strace -p 2759
خروجی:
موضوعات و فورک های یک برنامه را پیگیری کنید
با strace، میتوانید تمام رشتهها و سایر پردازشهای فرزند را که یک فورک برنامه هستند با استفاده از پرچم -f بررسی کنید.
strace -f -p 2759
خروجی:
تماس های سیستمی خاص را با strace بررسی کنید
خروجی strace پیشفرض میتواند گاهی اوقات بسیار شلوغ باشد. اگر فقط می خواهید تماس های سیستمی خاصی را ردیابی کنید، می توانید این کار را با پارامتر -e انجام دهید:
strace -f -e trace=open,write,close,connect,select -p 19770
برای ردیابی فقط تماس های سیستمی مربوط به عملیات فایل، از -e trace=file استفاده کنید:
strace -e trace=file -p 19770
برای فیلتر کردن فقط تماس های سیستمی مرتبط با شبکه، -e trace=network را در دستور مشخص کنید:
strace -e trace=network -p 19770
دریافت اطلاعات زمان در ثانیه
هنگام خروجی تماس های سیستمی، می توانید از پارامتر -t برای دریافت اطلاعات زمان با دقت در چند ثانیه استفاده کنید. بیشتر اوقات دقت برای نیازهای شما کافی نخواهد بود. در چنین شرایطی، می توانید از پارامتر -tt برای دریافت اطلاعات زمان با دقت میکروثانیه استفاده کنید:
strace -tt ls /tmp
جمع آوری آمار در مورد تماس های سیستمی
با پارامتر -c، می توانید آمار تماس های سیستم را تا زمانی که بخواهید جمع آوری کنید:
strace -f -c -p 19770
گزارشها را در یک فایل ذخیره کنید
اگر strace را برای مدت طولانی اجرا میکنید و میخواهید گزارشهای حاصل را بعداً با جزئیات بیشتری بررسی کنید، باید لاگها را ذخیره کنید. با پارامتر -o می توانید فایلی را مشخص کنید که در آن strace باید لاگ ها را ذخیره کند:
strace -f -o /tmp/strace.log -e trace=file ls /tmp
فرآیند مسدود کردن ptrace
با استفاده از فراخوانی سیستم prctl، هر برنامه ای تحت لینوکس می تواند از کنترل خود توسط کاربران غیر ریشه با استفاده از ptrace جلوگیری کند. اگر برنامه از طریق prctl پرچم PR_SET_DUMPABLE را برای خود پاک کند، کاربرانی غیر از root نمی توانند این برنامه را با ptrace کنترل کنند، حتی اگر حق سیگنال دادن به برنامه را داشته باشند.
یکی از معمولی ترین کاربردهای این ویژگی در نرم افزار عامل احراز هویت OpenSSH دیده می شود. بنابراین، کنترل برنامه توسط برنامه دیگری با ptrace در احراز هویت کاربر جلوگیری می شود.
ptrace و امنیت
با توجه به امکانات ptrace که در مدل فرآیند سنتی لینوکس تنظیم شده است، هر نرم افزاری که روی سیستم خود با کاربر خود اجرا می کنید، این اختیار را دارد که کدهای مخرب را در آن وارد کند. از سادهترین ابزار xterm گرفته تا برنامههای پیشرفته مرورگر وب، چنین بدافزارهایی میتوانند کنترل همه برنامههای در حال اجرا دیگر شما را در دست بگیرند – به لطف تماس سیستم ptrace – و بدون توجه شما اطلاعات مهم را کپی کنند.
در پاسخ به این وضعیت که بسیاری از کاربران از آن اطلاعی ندارند، مکانیزم حفاظتی با ماژول امنیتی به نام Yama در هسته لینوکس ایجاد شده است.
شما می توانید پاسخ به تماس سیستم ptrace را از طریق فایل /proc/sys/kernel/yama/ptrace_scope کنترل کنید. به طور پیش فرض، این فایل مقدار 0 را می نویسد.
مقادیر زیر قابل قبول است:
ارزش
معنی
0
رفتار متعارف: همه برنامه هایی که حق ردیابی دارند قابل بررسی هستند.
1
Ptrace محدود: فقط والد مستقیم برنامه یا برنامههای اشکالزدایی مجاز توسط برنامه با گزینه PR_SET_PTRACER کنترل را دارند. بنابراین، استفاده از gdb program_name و strace program_name به کار خود ادامه میدهد، اما پس از آن نمیتوانید یک برنامه در حال اجرا را پیوست کنید.
2
Ptrace به مدیر سیستم: فقط برنامه هایی با ویژگی CAP_SYS_PTRACE تعریف شده یا پردازش های فرزند که گزینه PTRACE_TRACEME را با prctl تعریف می کنند، قابل کنترل هستند.
3
کاملاً غیر فعال شده: تحت هیچ شرایطی هیچ اثری مجاز نیست. اگر این ویژگی یک بار تعریف شده باشد، در زمان اجرا نمی توانید دوباره آن را تغییر دهید.
بسیاری از توسعه دهندگان نمی دانند که برنامه ها می توانند ptrace خود را از طریق prctl غیرفعال کنند، به جز برای کاربر اصلی. اگرچه نرم افزارهای مرتبط با امنیت مانند عامل OpenSSH این عملیات را انجام می دهند، انتظار رفتار یکسان از همه نرم افزارهای در حال اجرا بر روی سیستم درست نیست.
اخیراً، برخی از توزیعهای لینوکس شروع به تنظیم مقدار پیشفرض فایل ptrace_scope، که در بالا توضیح داده شد، روی 1 کردهاند. بنابراین، با محدود شدن عملیات ptrace، محیط کاری امنتری در سراسر سیستم فراهم میشود.
با استفاده از یک نمونه خطی
نمونه درخواست زیر را با نام ministrace.c ثبت کنید. سپس با دستور زیر می توانید آن را کامپایل کنید:
gcc -o ministrace ministrace.c
کد:
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int wait_for_syscall (pid_t child)
{
int status;
while (1) {
ptrace(PTRACE_SYSCALL, child, 0, 0);
waitpid(child, &status, 0);
if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
return 0;
if (WIFEXITED(status))
return 1;
}
}
int do_child (int argc, char **argv)
{
char *args [argc+1];
memcpy(args, argv, argc * sizeof(char*));
args[argc] = NULL;
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
return execvp(args[0], args);
}
int do_trace (pid_t child)
{
int status, syscall, retval;
waitpid(child, &status, 0);
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
while(1) {
if (wait_for_syscall(child) != 0) break;
syscall = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*ORIG_RAX);
fprintf(stderr, "syscall(%d) = ", syscall);
if (wait_for_syscall(child) != 0) break;
retval = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*RAX);
fprintf(stderr, "%d\n", retval);
}
return 0;
}
int main (int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "Usage: %s prog args\n", argv[0]);
exit(1);
}
pid_t child = fork();
if (child == 0) {
return do_child(argc-1, argv+1);
} else {
return do_trace(child);
}
}
پس از کامپایل برنامه، می توانید هر دستوری را با ministrace اجرا کنید و خروجی را بررسی کنید:
شما می توانید از strace برای بسیاری از اهداف استفاده کنید
strace می تواند به یافتن اشکالات در برنامه هایی که به طور غیر ضروری از منابع سیستم استفاده می کنند کمک کند. به همین ترتیب، مشخصه ای که یک برنامه در هنگام استفاده از منابع سیستم عامل نشان می دهد نیز می تواند با strace آشکار شود.
از آنجایی که strace مستقیماً به تماسهای سیستم گوش میدهد، میتواند پویایی زمان اجرا را بدون توجه به باز/بسته بودن کد برنامه در حال اجرا آشکار کند. این امکان وجود دارد که در مورد این که چرا برنامه ها هنگام شروع استفاده از strace خطا می دهند، ایده ای دریافت کنید.
به طور مشابه، strace به شما کمک می کند تا بفهمید چرا یک برنامه به طور غیر منتظره خاتمه می یابد. بنابراین، آشنایی با strace در توسعه هسته لینوکس و مدیریت سیستم بسیار مهم است.