یک چلنج فوق العادههههههههه زیبا. (بینهایت هم gdb یاد میگیرید با حلش.) فردا توضیحش میدم چجوری حل میشه. شوخوش.
❤8👎1
خب برسیم به توضیح این سوال. کد پایتونش رو اگر ببینید، اومده یک لایبرری ایمپورت کرده به اسم gdb.
خب حالا gdb چیه؟gdb یا به عبارتی GNU Debugger یک دیباگری هست که برای زبونهای سطح پایینی مثل اسمبلی و c و cpp و ایناها هست. میدونم که بعضا go یا جاوا هم حتی باهاش دیباگ میکنن. کلا یک ابزار خیلی خفنیه که روی سیستمهای یونیکسی میتونه اجرا شه و یک باینری رو دیباگ کنه.
همه کار هم میشه باهاش کرد. از سادهترین چیزا مثل بریکپوینت گذاشتن و رفتن به دستور بعدی و در همون لحظه دیدن مقادیر متغیرا، ورودیهای تابعها، مقادیر رجیسترها، استک، هیپ و ... هرچی که فکرشو کنید.
تا یک سری کارای خفنتر، مثلا در لحظه (runtime) عوض کردن مقادیر. مثلا ورودی یک تابع اگر هست ۲ اون رو تغییر بدید به ۵. یا در لحظه اگر روی خط ۵ برنامه هستید جامپ کنید برید به خط ۱۰.
کلا تقریبا هرکاریییی که دلتون بخواد با این دیباگر با اون برنامه میتونید انجام بدید
خب حالا gdb چیه؟gdb یا به عبارتی GNU Debugger یک دیباگری هست که برای زبونهای سطح پایینی مثل اسمبلی و c و cpp و ایناها هست. میدونم که بعضا go یا جاوا هم حتی باهاش دیباگ میکنن. کلا یک ابزار خیلی خفنیه که روی سیستمهای یونیکسی میتونه اجرا شه و یک باینری رو دیباگ کنه.
همه کار هم میشه باهاش کرد. از سادهترین چیزا مثل بریکپوینت گذاشتن و رفتن به دستور بعدی و در همون لحظه دیدن مقادیر متغیرا، ورودیهای تابعها، مقادیر رجیسترها، استک، هیپ و ... هرچی که فکرشو کنید.
تا یک سری کارای خفنتر، مثلا در لحظه (runtime) عوض کردن مقادیر. مثلا ورودی یک تابع اگر هست ۲ اون رو تغییر بدید به ۵. یا در لحظه اگر روی خط ۵ برنامه هستید جامپ کنید برید به خط ۱۰.
کلا تقریبا هرکاریییی که دلتون بخواد با این دیباگر با اون برنامه میتونید انجام بدید
👍4❤1👎1
برای حل این سوال حقیقتا یک دانش پایهای از c اگر داشته باشید و با gdb مقداری کار کرده باشید و یک مقدار خیلی کوچک و ریزی هم os بلد باشید (نباشید هم با سرچ در میاد) کار تمومه
👍4❤1👎1
اگر کد پایتون رو نگاه کنید، داره چکار میکنه؟ داره
رو اجرا میکنه. بیایید یکم دیپتر شیم که با این کار دقیقا چه اتفاقی میوفته.
دستور cat یک کامند هست که شما باهاش میتونید محتوای یک فایل رو بخونید. مثلا cat flag.txt میاد محتوایی که توی فایل flag.txt هست رو براتون چاپ میکنه.
اما خود دستور cat خالی رو اگر بزنید چی میشه؟ اگر دستور cat خالی رو بزنید، هر ورودیای که بهش بدید،همونو بهتون خروجی میده. به عبارتی میاد هرچی از stdin هست رو میگیره و میریزه تو stdout
یه نمونهش رو پایین بهتون نشون میدم.
/bin/cat
رو اجرا میکنه. بیایید یکم دیپتر شیم که با این کار دقیقا چه اتفاقی میوفته.
دستور cat یک کامند هست که شما باهاش میتونید محتوای یک فایل رو بخونید. مثلا cat flag.txt میاد محتوایی که توی فایل flag.txt هست رو براتون چاپ میکنه.
اما خود دستور cat خالی رو اگر بزنید چی میشه؟ اگر دستور cat خالی رو بزنید، هر ورودیای که بهش بدید،همونو بهتون خروجی میده. به عبارتی میاد هرچی از stdin هست رو میگیره و میریزه تو stdout
یه نمونهش رو پایین بهتون نشون میدم.
👍5❤1👎1
خب بذارید یکم بیشتر باهاش ور بریم. زمانی که دستور cat صدا زده میشه، چه توابعی از libc کال میشن؟ منطقا اگر خیلی ابسترکت نگاه کنیم، باید بیاد اون فایل رو open کنه، read کنه و در نهایت write میکنه که ما بتونیم خروجی رو ببینیم. حالا یک دستور trace روی این cat میزنیم تا ببینیم واقعا چه توابعی از libc دارن صدا زده میشن.
من برای تست یک فایل مینویسم به اسم flag که توی اون فایل محتواش رو مینویسم hello
من برای تست یک فایل مینویسم به اسم flag که توی اون فایل محتواش رو مینویسم hello
👍4❤1👎1🔥1
بیایید یکم دیپتر شیم باز.
زمانی که دستور open صدا زده میشه، میاد اون فایل رو باز میکنه و یک فایلدیسکریپتور براش میسازه و الان یک فایلدیسکریپتور داریم که یک عددی داره (۰ مال stdin هست و ۱ ماه stdout و ۲ مال stderr) پس زمانی که میخواد flag رو open کنه شمارهی فایلدیسکریپتور بعدی، میشه ۳. یعنی زمانی که فایل flag رو open کردیم و لینک کردیم به فایلدیسکریپتور ۳، حالا تابع read از فایلدیسکریپتور ۳ میره شروع میکنه به خوندن و در نهایت هم میره writeش میکنه.
زمانی که دستور open صدا زده میشه، میاد اون فایل رو باز میکنه و یک فایلدیسکریپتور براش میسازه و الان یک فایلدیسکریپتور داریم که یک عددی داره (۰ مال stdin هست و ۱ ماه stdout و ۲ مال stderr) پس زمانی که میخواد flag رو open کنه شمارهی فایلدیسکریپتور بعدی، میشه ۳. یعنی زمانی که فایل flag رو open کردیم و لینک کردیم به فایلدیسکریپتور ۳، حالا تابع read از فایلدیسکریپتور ۳ میره شروع میکنه به خوندن و در نهایت هم میره writeش میکنه.
👍3❤1👎1🔥1
اما زمانی که ما دستور cat خالی بزنیم، همونجور که دیدیم، همون چیزی که ما بهش دادیم رو میره پرینت میکنه. دیدید که گفتیم دقیقا همونی که میخونه رو میره مینویسه دوباره. یعنی اگر دقت کنید، زمانی که اون read داره صدا زده میشه، فایلدیسکریپتوری که بهش پاس داده میشه، فایلدیسکپریتور 0 یا همون stdin هست. ولی خب این به کار ما نمیاد که. ما میخوایم اون فایلدیسکریپتوری که به فلگ میرسه رو بره بخونه و read کنه و مجدد write کنه. حالا پس باید چکار کنیم؟ اینجاس که دیگه چلنج تازه شروع میشه و ما نیاز داریم به gdb که بتونیم یک سری مقادیر رو عوض بکنیم در لحظه که بتونیم اون چیزی نیاز داریم رو پرینت کنیم دقیقا.
👍3❤1👎1🔥1
اون پایتونه داره چکار میکنه؟ میاد gdb رو ران میکنه. دستور cat خالی رو اجرا میکنه و سر تابع read یک break point میذاره. البته چلنجش یک مقدار سخته. یعنی گفته شما هر دستوری نمیتونید بزنید و صرفا دستور break و set و continue رو میتونید صدا بزنید. چلنجش اینجوری محدود شده خلاصه.
break
که میاد یک آدرس میگیره و روی اون چیزی که میخوایم break point میذاره.
continue
میاد ادامه میده تا برسه سر break point بعدی و ایست کنه.
set
جلوش یه سری چیز میاد که یه سری مقادیر رو ست کنه. مثلا
set $rax=0x0
یعنی توی رجیستر rax بریز ۰
break
که میاد یک آدرس میگیره و روی اون چیزی که میخوایم break point میذاره.
continue
میاد ادامه میده تا برسه سر break point بعدی و ایست کنه.
set
جلوش یه سری چیز میاد که یه سری مقادیر رو ست کنه. مثلا
set $rax=0x0
یعنی توی رجیستر rax بریز ۰
👍3👎2❤1
من یک break point روی read هم گذاشتم. تابع read رو که ببینید. fd یا همون فایلدیسکریپتورش همونجوری که گفتیم 0هست. یعنی stdin. یک بافری هم داره که آدرسش هست 0x7ffff7f88000 که مقداری که خوند رو بریزه توی این آدرسه.
💩3👍2❤1
چلنح در واقع از همینجا شروع میشه و حدودا دیشب ۳ ۴ ساعت وقت گرفت از من. لذا من دیگه از اینجا تخصصیتر و خلاصهتر میگم که نخوام ۴۰۰تا پیام بدم دیگه.
❤2👍2💩2
کاری که باید برای حلش کرد چیه؟ ما در نهایت باید کاری کنیم که به جای اینکه این cat خالی صرفا همون ورودیای که ما میدیم خروجی بده، بیاد محتوای فایل مدنظر ما که اسمش هست flag.txt رو چاپ کنه.
پس ما باید یه سری کار کنیم. اینکه بریم فایل flag.txt رو open کنیم تا fdش ساخته شه و لینک شه تا ما بتونیم readش کنیم چرا که read از روی یک fd اتفاق میوفته.
بعد که read کردیم بیاییم بریزیمش توی یک مقداری که همون مقدار بافر خودش کافی هست، و حالا write رو صدا بزنیم که بره مقداری که از read داره رو برامون write کنه.
پس ما باید یه سری کار کنیم. اینکه بریم فایل flag.txt رو open کنیم تا fdش ساخته شه و لینک شه تا ما بتونیم readش کنیم چرا که read از روی یک fd اتفاق میوفته.
بعد که read کردیم بیاییم بریزیمش توی یک مقداری که همون مقدار بافر خودش کافی هست، و حالا write رو صدا بزنیم که بره مقداری که از read داره رو برامون write کنه.
👍2💩2❤1
تابع open یک ورودی میگیره که اونم اسم فایل هست و مقدار توی rdi ریخته میشه. یعنی ما زمانی که میخوایم open رو صدا بزنیم باید مقدار rdi مون برابر با اسم فایلی باشه که میخوایم openش کنیم. یعنی همون flag.txt.
❤2💩2👍1
خب حالا ما چجوری این مقدار رو بگیریم و بریزیم توی rdi؟ همونطور که گفتیم، این cat خالی میاد ورودی مارو خروجی میده. پس اگر یه دور ما cat خالی رو صدا بزنیم و بدیم flag.txt، این میره توی بافرش مینویسه flag.txt و تنها کاری که ما نیازه بکنیم اینه که آدرس اون flag.txt رو بیاییم بریزیم توی rdi و سپس open رو صدا بزنیم که بره فایل flag.txt رو برامون open کنه و fdشو بسازه.
❤2💩2👍1
این کار با دستورای زیر انجام میشه.
توی break اولی ما اومدیم سر جایی که ورودی گرفتن تموم میشه بریک گذاشتیم.
بعدش contunue زدیم که اجرا شه و از ما ورودی بگیره و روی break گیر کنه.
اون ورودیای که میدیم همون flag.txt هست. بعدش اومدیم مقدار rsi رو ریختیم توی rdi. زمانی که ما ورودی بدیم flag.txt، این آدرس این مقداره ریخته میشه توی rsi و ما اومدیم ریختیم همون رو توی rdi که کارای صدا زدن open رو انجام بدیم.
break *($rip + 0x12)
continue
set $rdi=$rsi
set $rip=$rip-0x302
توی break اولی ما اومدیم سر جایی که ورودی گرفتن تموم میشه بریک گذاشتیم.
بعدش contunue زدیم که اجرا شه و از ما ورودی بگیره و روی break گیر کنه.
اون ورودیای که میدیم همون flag.txt هست. بعدش اومدیم مقدار rsi رو ریختیم توی rdi. زمانی که ما ورودی بدیم flag.txt، این آدرس این مقداره ریخته میشه توی rsi و ما اومدیم ریختیم همون رو توی rdi که کارای صدا زدن open رو انجام بدیم.
❤2💩2👍1
اونجا که زدیم:
این 0x302 مقدار افستی هست که rip فعلی ما فاصله داره تا سر open. به عبارتی اینجا من سعی دارم مقدار rip رو بذارم سر آدرس open که بعدش برم جامپ کنم مستقیم اونجا.
البته اینجا کار سختی کردم و بعدش فهمیدم که میشد اینجوری هم ست کرد:
دستورای بعدی ما اینه:
ما سر تابع open یک break گذاشتیم و بعدش continue زدیم که سریع جامپ کنیم به open. حالا وارد تابع open شدیم و مقدار rdi هم flag.txt هست که میره واسمون اون فایل رو open میکنه و یک fd براش اساین میکنه.
در نهایت روی return تابع open باز بریک گذاشتیم. دلیلش اینه که ما نمیخوایم open تموم شه و return شه. میخوایم قبل از اینکه تموم شه بیاییم مجدد read رو صدا بزنیم که بره fd جدید ما که همون flag.txt هست رو بخونه.
بعدش continue میکنیم و میره سر return وایمیسته ولی return نمیکنه.
set $rip=$rip-0x302
این 0x302 مقدار افستی هست که rip فعلی ما فاصله داره تا سر open. به عبارتی اینجا من سعی دارم مقدار rip رو بذارم سر آدرس open که بعدش برم جامپ کنم مستقیم اونجا.
البته اینجا کار سختی کردم و بعدش فهمیدم که میشد اینجوری هم ست کرد:
set $rip=open
دستورای بعدی ما اینه:
break open
continue
break *open+0x82
continue
ما سر تابع open یک break گذاشتیم و بعدش continue زدیم که سریع جامپ کنیم به open. حالا وارد تابع open شدیم و مقدار rdi هم flag.txt هست که میره واسمون اون فایل رو open میکنه و یک fd براش اساین میکنه.
در نهایت روی return تابع open باز بریک گذاشتیم. دلیلش اینه که ما نمیخوایم open تموم شه و return شه. میخوایم قبل از اینکه تموم شه بیاییم مجدد read رو صدا بزنیم که بره fd جدید ما که همون flag.txt هست رو بخونه.
بعدش continue میکنیم و میره سر return وایمیسته ولی return نمیکنه.
❤3👎1