محققای Synacktiv یه ابزار جدیدی بنام Frinet توسعه دادن که ترکیبی از Frida و Tenet هستش، که باعث تسهیل فرایند بررسی برنامه های بزرگ، کشف آسیب پذیری و آنالیز فنی عمیق در پلتفرم های ویندوز ، لینوکس ، اندروید، iOS و … میشه. در این پست نگاهی به این ابزار انداختیم.
در فرایند مهندسی معکوس نرم افزارها ، عموما دو رویکرد داریم :
- آنالیز استاتیک : بررسی مستقیم باینری از طریق دیکامپایلرها و دیس اسمبلرها مانند IDA Pro یا Ghidra ( آنالیز بدون اجرای برنامه هدف)
- آنالیز داینامیک : بررسی روند اجرای برنامه از طریق دیباگرها یا شبیه سازها (آنالیز با اجرای برنامه هدف)
هر دو رویکرد یسری مزایا و معایب دارن. مثلا در بررسی داینامیک، ما مقادیری که هر ساختار میگیره رو ، میتونیم ببینیم و بنابراین نیاز نیست که بریم ، چندین تابع و ساختار قبلی رو بررسی کنیم. از یه طرفی هم با آنالیز داینامیک میتونیم روند اجرای برنامه مثلا اینکه کدوم Branchها اجرا میشن رو مشاهده کنیم. با این حال در برخی موارد، آنالیز داینامیکی که یه دیباگر ارائه میده، یه تصویر بزرگی از برنامه ارائه نمیده و معمولا روی جزییات تمرکز داره. اما ما با آنالیز استاتیک ، درک بهتری از برنامه بعنوان یه کل داریم چون حرکت بین توابع و cross-referenceها ساده تر هستش.
اما یه رویکرد سومی هم وجود داره و اون اینکه بیاییم و مزایای هر دو رویکرد ، استاتیک و داینامیک رو با هم ترکیب کنیم که اصطلاحا بهش Execution Trace میگن. این رویکرد اینجوری هستش که یه تابعی رو اجرا میکنن و اطلاعاتی که یه دیباگر میده، مثلا اطلاعات رجیسترها ، دسترسی نوشتن و خوندن حافظه و … ، ذخیره میشه که بهش execution trace میگن. حالا اگه این اطلاعات رو از طریق یه پلاگین وارد ابزارهای آنالیز استاتیک بکنیم مثلا IDA Pro ، میتونیم قابلیت آنالیز استاتیک رو بالا ببریم.
در حقیقت با این روش، هم اطلاعات و مقادیر زمان اجرا رو داریم و هم سرعت و سهولت حرکت بین کدها رو از دست نمیدیم. همچنین یه ویژگی دیگه ای که این روش داره اینه که ، تو آنالیز معمولی مثلا اگه BreakPoint رو رد کنید یا یه قسمت مهم رو رد کنید، مجبور هستید که برنامه رو دوباره اجرا کنید و BreakPoint ها رو دوباره تنظیم کنید، اما در این روش ، میتونید این مشکلات رو مدیریت کنید. تقریبا شبیه ویژگی TTD در Windbg Preview هستش.
کلا این رویکرد از مدت ها پیش معرفی شده اما ابزارهایی که استفاده میکنیم، سهولت استفاده از این رویکرد رو در سناریوهای مختلف به خوبی فراهم نمیکنن. البته این تا زمانی بود که Markus Gaasedelen ، یه ابزار بنام Tenet رو در قالب پلاگین IDA Pro معرفی کرد که سهولت استفاده از این قابلیت رو برای کاربران ساده تر میکنه.
مشاهده Execution Trace در Tenet :
پلاگین Tenet ، در مسابقات پلاگین نویسی Hex-Rays Plug-In Contest 2021 مقام اول رو کسب کرد و دلیلشم مشخصه : استفاده از اون خیلی ساده تر از حتی رابط ساده IDA هستش. سازنده این ابزار در یه پست بلاگ ، بطور کامل ویژگی های مهم این ابزار رو معرفی کرده.
Frinet Frida Tracer :
تولید Execution Trace ، یکی از مشکلات اصلی پلاگین Tenet هستش که اونو برای همه ی سناریوها قابل استفاده نمیکنه. در قسمت روش های Tenet Trace به این موضوع اشاره شده :
در حال حاضر Tenet ، برای Trace هایی که توسط Snapshot-Based Fuzzer ها ایجاد میشن، توسعه داده شده. اینا شاید بدیهی ترین و واقعی ترین نمونه ها برای استفاده از این فناوری باشن تا زمانیکه سرمایه گذاری های بیشتری برای گسترش اون انجام بشه.
در اهداف پیچیده MultiThread ، استفاده از Intel PIN برای ضبط هر چیزی موثر نیست. همچنین چیز خاصی هم برای معماریهای مختلف ارائه نشده. مثلا خود Tenet از معماری x86 پشتیبانی میکنه ، با اینکه وابسته به معماری نیست و اضافه کردن معماری جدید هم، کار سختی نیست.
بنابراین اگه یه راه حلی برای تولید Execution Trace داشته باشیم که روی همه ی معماری ها کار کنه و بشه در یه تابع ایزوله مورد استفاده قرار بگیره، امکان استفاده از اون در اکثر سناریوهای مهندسی معکوس نرم افزار ، فراهم هستش.
اینجاست که Frida Stalker میتونه جالب باشه. Stalker موتور Code Tracing در Frida هستش و این امکان رو میده که اجرای Threadها رو دنبال کنید و هر تابع و بلوک و دستورالعملی رو لاگ کنید. Stalker در حال حاضر از معماری AArch64 که معمولا در موبایلها و تبلتهایی که دارای اندروید و iOS هستن و همچنین معماری اینتل 64 و IA-32 که معمولا در سیستم های رومیزی و لپ تاپ ها مورد استفاده قرار میگیره، پشتیبانی میکنه.
این قابلیت رو هم داره که میشه اجرای یه تابع خاصی رو رهگیری کرد. مثلا فرض کنید یه برنامه ی بزرگی رو آنالیز میکنید که کلی تابع دارن اجرا میشن و شما فقط نیاز به رهگیری یه تابع خاصی دارید که کارش تجزیه ی گواهینامه هاست. بنابراین این ابزار میتونه به شما کمک کنه. این ابزار در پلتفرم های ویندوز ، لینوکس ، اندروید و iOS قابل اجراست.
Frida Stalker – Javascript API
برای آشنایی بیشتر با این قسمت، مستندات خود Frida رو ببینید، چون یه داستانیه برا خودش. در Stalker ما برای اینکه یه تابع خاصی رو Trace کنیم ، یه Interceptor روی این تابع تعریف میکنیم. برای تعریف onEnter callback ،که در صورت فراخوانی یه تابعی فعال میشه، ابزار Stalker بصورت زیر عمل میکنه :
1 |
Stalker.follow(this.threadId, {transform: js_transform}) |
موقع اجرا وقتی Stalker برای اولین بار به Basic Block میرسه ، تابع transform برای دستکاری این بلوک خاص اجرا میشه. یه Basic Block دنباله ای از دستورالعملهایی هستش که یه نقطه ورود و یه نقطه خروج داره. موتور Stalker با تخصیص حافظه و نوشتن یه کپی از کدی که قابلیت ارزیابی رو داره، امکان Trace رو فراهم میکنه. با اجرای این کد کپی شده، هر زمان که یه Basic Block اجرا میشه، Stalker تابع onEnter رو فراخوانی میکنه. در ابزار محققین، برای هر دستورالعمل یه callback برای testcb تنظیم شده . این تابع testcb برای لاگ کردن محتوا (context) مورد استفاده قرار میگیره.
1 2 3 4 5 6 7 8 9 10 11 12 |
transform(iterator) { let instruction = iterator.next(); do { // [...] iterator.putCallout(function(context) { testcb(context, read, write); }); } iterator.keep(); } while ((instruction = iterator.next()) !== null); } |
بعد از اجرای callback برای هر دستور اجرا شده، رجیستر و مقادیر خوانده شده/نوشته شده حافظه به یه اسکریپت پایتون ارسال میشن، که یه Execution Trace در قالب متن، Tenet ارائه میده.جزییات زیادی در اجرا وجود داره، اما این مهمترین قسمت هستش. Stalker یه ابزار قدرتمند هستش که امکان استفاده در برنامه های دیگه رو هم میده.
Frida Stalker – Native callbacks :
اگه مستندات Stalker رو بخونید، این قسمت میتونه جالب باشه :
1 2 3 4 5 |
// Want better performance? Write the callbacks in C: const cm = new CModule(` #include <gum/gumstalker.h> ... |
چیزی که از این ویژگی میشه درک کرد اینه که ، میشه از همون ویژگی Stalker به همون روشی که در بالا برای جاوااسکریپت استفاده کردیم، برای callbackهای مبتنی بر C هم استفاده کرد. با توجه به اینکه ما میخواییم ، callback رو ، روی هر دستورالعملی بعنوان یه Tracer اجرا کنیم، بدیهیه که بهترین راه حل استفاده از Native Code سریعتر، هستش. منظور از Native Code هم باینریهایی هستن که برای اجرا ، کامپایل شدن. اما این راه حل مشکلاتی رو هم داره از جمله اینکه مانند جاوااسکریپت ، جدا از معماری نیست و همچنین پیاده سازی درست اون سختتره.با وجود این مشکلات، محققا تونستن ، ماژولهای Stalker بهینه سازی شده رو برای x86 و x86-64 و ARM64 پیاده سازی کنن. البته همچنان میشه از JavaScript Tracer هم استفاده کرد. برای اینکه از JavaScript Tracer بصورت force استفاده کنید باید از فلگ slow– استفاده کنید.
اگه موقع اجرای JavaScript Tracer و Native Tracer با کرش مواجه شدید، احتمالا مشکل در اجرای Frida Stalker هستش. محققا گفتن که این ویژگی بدون ایراد نیست و در برخی سناریوها با مشکلاتی روبرو شدن. اما تو ارزیابی امنیتی ، روی اهداف واقعی کار میکنه. محققا تونستن با سرعت 400,000 دستورالعمل در ثانیه ، عمل Tracing رو انجام بدن که از نظر خودشون یه معیار مناسب برای ارزیابی هم نیست.
Frinet Tenet viewer :
محققا در حین توسعه ی این ابزار، پلاگین Tenet رو هم اصلاح کردن. یسری تغییرات در viewها دادن از جمله اضافه کردن چندین Memory View و یسری گزینه های محتوایی جالب دیگه. همچنین دو ویژگی مهم رو هم به این ابزار اضافه کردن که در ادامه بررسی میکنیم.
این پلاگین الان میتونه ASLR slide offset رو از خط اول Trace کنه. منظور از ASLR slide offset ، اختلاف بین Base Address یه پروسس و Base Address ماژولهای اون هستش. از این آفست برای محاسبه ی آدرس واقعی یه تابع یا متغیر در فضای آدرس یه پروسس استفاده میکنن. مثلا اگه Base Address یه ماژول 0x10000000 و ASLR slide offset برابر 0x2000 باشه، Base Address واقعی ماژول در حافظه برابر 0x10002000 هستش.
اضافه شدن Call Tree view :
یکی از مهمترین ویژگی هایی که محققای Synacktiv به پلاگین Tenet اضافه کردن Call Tree view هستش. این یه نمای کلی از توابع فراخونی شده هستش، شبیه Stack Trace اما همه ی فرایند Execution Trace رو شامل میشه و بصورت گره هایی نمایش داده میشه که اگه روش کلیک کنید، گسترده میشن.
در فرایند مهندسی معکوس ، بخصوص زمانی که شما با تابعی سر و کار دارید که توابع زیاد دیگه ای رو بصورت داینامیک فراخوانی میکنه، مانند تجزیه کننده های ASN1 ، میتونه بسیار کاربردی باشه. همچنین برای C++ Virtual Call ها یا فراخوانی هایی غیرمستقیم، که در اون دنبال کردن جریان اجرای برنامه سخته، میتونه مفید باشه.
Call Tree view رو میشه با regex فیلتر کرد مثلا :
- وقتی با توابعی مانند malloc ، روبرو میشید که مکررا فراخوانی میشن و بررسی رو سختتر میکنن
- یا وقتی که با توابعی سروکار دارید که اسمشون از یه قاعده خاصی پیروی میکنه ، مثلا تو اسمشون Network هستش .
برای فیلتر کردن کافیه که راست کلیک و گزینه ی Filter رو انتخاب کنید. همچنین با راست کلیک کردن روی یه گره و انتخاب Collapse all but selection ، فقط فراخوانی های اون تابع رو مشاهده کنید. این باعث مشاهده ساده تر Stack Trace ،به این گره خاص میشه.
این View هنوز در مرحله تسته و ممکنه با معماریهایی که هنوز تست نشدن، مشکلاتی داشته باشه. اما در آنالیز برنامه های واقعی ، برای معماری های ARM64 و x86-64 عملکرد نسبتا خوبی داشته . با توجه به اینکه از Instruction Pointer برای تشخیص فراخوانی ها استفاده میکنه، یه جورایی مستقل از معماری هم هستش.
جستجوی حافظه :
با توجه به اینکه موقع Trace، محتوای حافظه با هر دستور العملی تغییر میکنه، بنابراین باید یه نمایش از بایتها در هر نقطه از زمان داشته باشیم. برای همین منظور محققا چندین نمایش مموری جدید به Tenet اضافه کردن تا امکان نمایش و جستجوی دنباله ای از بایتها در هر زمان و در هر حافظه قابل دسترس رو فراهم کنه. برای دسترسی به این ویژگی ، کافیه راست کلیک کنید و در منوی Memory View گزینه ی Search bytes رو انتخاب کنید.
دمو :
محققا یه دمو هم از ابزارشون ارائه دادن. برای اینکار ، curl نسخه ی 7.88.1 رو روی دبیان کامپایل کردن. برای ساده تر شدن دمو ، اونو روی x86_64 ارائه کردن. پیکربندی هم که برای build اون استفاده کردن :
1 |
./configure --enable-debug -with-openssl --prefix=/tmp/curl-7.88.1 --disable-shared |
بعدش اومدن آفست تابع main رو در IDA بدست آوردن و از دستور زیر برای Trace تابع main در Curl استفاده کردن :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
user@debian:~/frinet/tracer$ python3 trace.py spawn /tmp/curl-7.88.1/bin/curl curl 0x256d9 -a "curl,-k,https://127.0.0.1:5000/test" 2023-11-21 17:28:15,139 INFO | Using local device 2023-11-21 17:28:15,142 INFO | Connected to device (x64 linux Debian GNU/Linux 12). 2023-11-21 17:28:15,145 INFO | Spawning process 2023-11-21 17:28:15,179 INFO | Loading JS module... 2023-11-21 17:28:15,183 INFO | Loading C module... 2023-11-21 17:28:15,184 INFO | Tracing... 2023-11-21 17:28:15,185 INFO | Received ASLR slide: 0x55793cfb4000. 2023-11-21 17:28:15,188 INFO | CTRL+C to interrupt trace Entering function 2023-11-21 17:28:15,336 INFO | Received trace data for new thread 148030. 2023-11-21 17:28:15,337 INFO | Writing trace data of 1048261 bytes... 2023-11-21 17:28:15,440 INFO | Writing trace data of 1048600 bytes... 2023-11-21 17:28:15,555 INFO | Writing trace data of 1048576 bytes... ... 2023-11-21 17:28:30,781 INFO | Writing trace data of 1035407 bytes... STDOUT : <html><body> STDOUT : <h1>hello</h1> STDOUT : </body></html> 2023-11-21 17:28:30,873 INFO | Writing trace data of 1038204 bytes... 2023-11-21 17:28:30,987 INFO | Writing trace data of 1027347 bytes... Leaving function 2023-11-21 17:28:31,026 INFO | Writing trace data of 488267 bytes... ... user@debian:~/frinet/tracer$ cp traces/curl_1700478104_89808.tenet /tmp/curl-7.88.1/ |
همونطور که مشاهده میکنید، فرایند Trace حدود 16 ثانیه طول کشیده.
ویدیوی زیر هم نحوه استفاده از ویژگی های جدید رو میده و میتونید بدونید که درخواستهای curl کجا ارسال میشن و کجا header/content پاسخ ، تجزیه میشن .
برای نصب این ابزار میتونید به صفحه ی گیتهاب پروژه Frinet برید.