کرک کردن برنامه های UnCrackable اندروید: راهنمای گام به گام (قسمت 1)

بهترین راه برای یادگیری نحوه کار چیزی این است که آن را از هم جدا کنید – یا خودتان بسازید. من فرض می کنم شما یک توسعه دهنده اندروید هستید و با قسمت بعدی آشنایی دارید ، بنابراین بیایید با شکستن دو برنامه اندروید و استخراج یک رشته مخفی از آنها ، روی مورد قبلی تمرکز کنیم.

کرک کردن برنامه های  UnCrackable اندروید: راهنمای گام به گام (قسمت ۱)

چرا کِرَک کردن (شکستن)؟

  • برای وصله یا patch کردن عیوب و باگها در نرم افزاری که code-base هستند هیچ تاثیری روی آنها ندارید.
    برای اینکه پروژه های خود را با یادگیری روش هایی که ممکن است افراد بد در معرض خطر قرار دهند ، ایمن تر کنید.
  • برای کشف اسرار 🙂

این یک نسخه وبلاگی از وبینار Cracking UnCrackable Android Apps من است.

آنچه این راهنما پوشش می دهد

یک منبع آنلاین عالی برای امنیت تلفن همراه اختصاص داده شده است: راهنمای تست امنیت موبایل (MSTG). من قصد دارم در اینجا راه حلی برای دو CRACKME اندروید ارائه شده توسط آن ارائه دهم. CRACKME چیست؟ تصور کنید که یک برنامه است که به هدف کرک شدن ساخته شده است . MSTG چندین CREACKME را با سطح دشواری متفاوت ارائه می دهد. من قصد دارم موارد ابتدایی ، سطح 1 و سطح 2 را مرور کنم (در پست وبلاگ بعدی من).

سطح 1: راز در سمت Java است ، اشکال زدایی کد جاوا.

سطح 2: این راز در سمت محلی (native side) است ، اشکال زدایی و وصله (patching) کتابخانه محلی.

مخزن MSTG همچنین شامل پیوندهایی به راه حلهای دیگر همان چالشهای CRACKME است – من شما را تشویق می کنم که پس از مطالعه این راهنما آنها را بررسی کنید.

ابزارها

  • adb – install apk, get shell
  • apktool – unpack/re-package apk
  • dex2jar – convert .dex files to .class files
  • jd-gui – java decompiler with GUI
  • jdb – Java debugger
  • gdb – GNU project debugger
  • cutter – binary patching with GUI
  • radare2 – binary patching

سطح یک

برنامه سطح 1 یک برنامه ساده یک صفحه ای است ، دارای یک فیلد ورودی و یک دکمه VERIFY است. با فشار دادن دکمه ، هر آنچه در قسمت ورودی است با رشته مخفی مقایسه می کند. هدف اصلی چالش شکستن، کشف مقدارآن رشته مخفی است.

هدف این است که اپلیکیشن را در حالت اشکال زدایی (debug) قرار داده و از آن اشکال زدایی کنید. اشکال زدایی به شما امکان می دهد مقدار رشته مخفی را مشاهده کنید و همچنین مکانیسم های ایمنی به کار رفته در اپلیکیشن را دور بزنید.

ببینید با چه کاری سر و کار دارید: اپلیکیشن را نصب و حذف (install and uninstall) کنید

شبیه سازهای (emulators) موجود را لیست کنید:

که در آن مقدار متغیر محیطی $ANDROID_HOME معمولاً ~/Android/Sdk است. اگر پس از اجرای دستور قبلی مشکلی پیش نیامد ، با استفاده از ابزار avdmanager یا از طریق Android Studio GUI یک شبیه ساز ایجاد کنید.

$ANDROID_HOME/emulator/emulator -list-avds

شبیه ساز(امولاتور) را روشن کنید:

$ANDROID_HOME/emulator/emulator -avd <name_of_the_avd> &

اپ را نصب کنید:

adb install UnCrackable-Level1.apk

با راه اندازی اپ روی شبیه ساز ، گفتگوی “Root detected” را نشان می دهد و با بستن گفتگو (دیالوگ) توسط دکمه گفتگو ، از برنامه خارج می شود. این به این دلیل است که اپ دارای مکانیزم تشخیص ریشه برای جلوگیری از تمپرینگ (tempering) است و شبیه ساز (امولاتور) به عنوان یک دستگاه ریشه دار در نظر گرفته می شود. ما قصد داریم این گفتگو (دیالوگ) را دور بزنیم.

app_level_1_root_detected

قطعه اصلی در شکستن (کرک کردن) برنامه ،قابل اشکال زدایی کردن (debuggable) آن است. در حال حاضر ، برنامه نصب شده قابلیت اشکال زدایی ندارد ، بنابراین برنامه را uninstall (حذف) کنید. ابتدا نام بسته (package) آن را پیدا کنید:

adb shell pm list packages | grep mstg

سپس آن را uninstall کنید:

adb uninstall owasp.mstg.uncrackable1

برنامه را قابل اشکال زدایی کنید

اگر برنامه ای به عنوان یکی در فایل AndroidManifest.xml پرچم گذاری شود ، باید قابل اشکال زدایی شود. برای باز کردن بسته بندی .apk از apktool استفاده کنید و سپس آن را با پرونده manifest تغییر یافته دوباره بسته بندی کنید.

Unpack .apk:

apktool d -s UnCrackable-Level1.apk -o decoded

جایی که d مخفف دِکُد است.

-s به معنای عدم رمزگشایی منابع است(ما به آنها نیازی نداریم. تمام آنچه که ما نیاز داریم مانیفیست است).

-o – دایرکتوری خروجی است.

اکنون ، با گزینه -d دوباره بسته بندی کنید ، که به طور خودکار debuggable = “true”را به AndroidManifest.xml اضافه می کند:

apktool b decoded -d -o Uncrackable-Level1-repackaged-with-d-option.apk

جایی که b مخفف build است.

گزینه -d بسیار مفید است ، زیرا باید AndroidManifest.xml را به صورت دستی تغییر دهید تا صفت app:debuggable=true به تگ <application> اضافه شود.

Sign the app

برنامه باید امضا شود ، در غیر این صورت نصب برنامه بدون امضا انجام نمی شود. از apksigner استفاده کنید تا بررسی کنید آیا برنامه هنگام مرحله نصب تایید را انجام می دهد یا خیر.

apksigner verify –print-certs –verbose <path_to_apk>

برای امضای برنامه می توان از الگوی زیر استفاده کرد:

apksigner sign -v –in UnCrackable-Level1-repackaged-with-d-option.apk –v2-signing-enabled –ks <path_to_keystore_file> –ks-key-alias $KEYSTORE_KEY_ALIAS –ks-pass env:KEYSTORE_PASSWORD –ks-type pkcs12

امضای برنامه در محدوده این مقاله نیست ، بنابراین توضیح بیشتری در مورد دستور فوق ارائه نمی شود. اگر با امضا در خط فرمان (پایانه) راحت نیستید ، همیشه می توانید برنامه خود را در Android Studio امضا کنید. یک فایل keystore نیز می تواند با کمک اندروید استودیو ایجاد شود.

Install debuggable app

adb install UnCrackable-Level1-repackaged-with-d-option.apk

پس از راه اندازی ، اپ دیالوگ “App is debuggable” را نشان می دهد – مکانیسم دیگری برای جلوگیری از هک. این باید دور زده شود.

app_level_1_debuggable

گذشتن از سد دیالوگ -Bypass dialogs

وقتی شخص نتواند به نقطه ای که باید اشکال زدایی شود ، یک برنامه قابل اشکال زدایی استفاده چندانی ندارد. به عبارت دیگر ، دیالوگ “App is debuggable” مانع از اشکال زدایی مکانی در کد می شود که با فشار دادن دکمه VERIFY فراخوانی می شود. برای رسیدن به آن مرحله ، باید گفتگو را دور بزنید.

برنامه را دوباره کامپایل کنید – کد منبع را ببینید

ابتدا .apk را به .jar تبدیل کنید:

d2j-dex2jar.sh -f UnCrackable-Level1.apk

دوم ، باز کردن ابزار GUI:

java -jar /opt/jd-gui/jd-gui.jar &

سوم ، با استفاده از ابزار GUI ، آرشیو .jar را برای بازرسی کد منبع باز کنید. پس از بازرسی ، مشهود میشود که تنظیم شده است که دیالوگ باکس با کلیک در خارجش از آن رد نشود بلکه فقط با فشار دادن دکمه دیالوگ باکس می توانید آن را ببندید. مشکل این است که شنونده کلیک دکمه از برنامه خارج می شود. بنابراین ، راه ساده آن است: با کلیک کردن بر روی خارج از آن ، گفتگو را از حالت آماده خارج کنید ، یعنی در alertDialog.setCancelable “false” را به true تغییر دهید (false). این می تواند با تنظیم متغیر دیباگر در زمان اجرا به دست آید.

Attach the debugger -اشکال یاب را پیوست کنید

برنامه را در حالت “wait for debugger” اجرا کنید:

adb shell am start -D -n “owasp.mstg.uncrackable1/sg.vantagepoint.uncrackable1.MainActivity”

جایی که هستم ActivityManager است.

start یک کامپوننت را شروع می کند.

-D اشکال زدایی را فعال می کند.

بعد ، یک دیباگر باید به برنامه متصل شود. اشکا لیاب در دستگاه محلی (local machine) قرار دارد. برای انتقال اطلاعات اشکال زدایی از دستگاه (شبیه ساز- emulator) به دستگاه محلی ، باید یک اتصال اتصال سوکت ایجاد کنید. نصب ساده است: با استفاده از adb اتصال سوکت بین پروسس برنامه و یک سوکت در localhost برقرار می شود.

adb forward LOCAL REMOTE

یا:

adb forward tcp:4321 jdwp:PID

جایی که tcp: 4321 مخفف “استفاده از پورت 4321 در localhost و TCP به عنوان transport protocol”. 4321 برای سهولت تایپ کردن و به خاطر سپردن انتخاب شده است.

jdwp: PID مخفف “استفاده از process id اپلیکیشن و JDWP به عنوان transport protocol” است.

با اجرا کردن کد زیر PID را پیداکنید:

adb shell ps | grep mstg

در دستگاه یونیکس می توان با اجرای سوکت 4321 گوش دادن را ممیزی کرد:

lsof -i -P -n | grep LISTEN

توصیف گزینه ها برای lsof از این پست خارج است.

اگر در این مرحله کلیه فرمانهای موجود در دستگاه خود را دنبال کرده اید ، در این مرحله:

این برنامه منتظر می ماند تا یک اشکال یاب (دیباگر) به آن متصل شود.

از طریق localhost ارتباطی به پروسس برنامه وجود دارد: 4321.

تنها چیزی که باقی مانده این است که آن دیباگر را خاموش کنید (این دستور را اجرا نکنید):

jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=4321

که در آن jdb ابزاری است که توسط جاوا ارائه می شود – یک دیباگر جاوا.

-connect – با استفاده از کانکتور com.sun.jdi.SocketAttach با مقادیر آرگومان لیست شده پس ازعلامت دونقطه (:) اگر می خواهید درباره ارتباطات بین دیباگر و VM بیشتر بدانید ، این مطلب را بخوانید.

این فرمان jdb را ضمیمه می کند تا به سوکت localhost 4321 برای اطلاعاتی که توسط ADB از آنجا در آنجا منتقل می شود گوش دهد. مشکلی که در دستور بالا وجود دارد و دلیلی که من از شما خواسته ام آن را اجرا نکنید این است که به محض اینکه دیباگر شروع به گوش دادن به سوکت می کند ، برنامه از سر گرفته می شود: کد برای نمایش دیالوگ باکس اجرا می شود و ما نمی خواهیم زیرا ما می خواهیم آن کد را اشکال زدایی (دیباگ) کنیم.

برای معلق کردن (suspend ) اجرای برنامه هنگام اتصال دیباگر به آن ، دستور تعلیق (suspend ) را به jdb فشار دهید:

(echo suspend && cat) | jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=4321

که در آن تعلیق یک دستور قانونی برای دستور تعاملی jdb است. برای دیدن اینکه jdb چه چیزی را پشتیبانی می کند ، jdb را اجرا کنید – این یک پوسته تعاملی به ما میدهد- سپس help را اجرا کنید.

خروجی:

droid@droid:~/dev/hack/mstg-level-1$ (echo suspend && cat) | jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=4321 Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb … > All threads suspended. >

و در آن لحظه دیباگر پیوست می شود و برنامه قبل از اجرای هرگونه کد آن به حالت تعلیق (suspended) در می آید.

متغیرها را در زمان اجرا تغییر دهید

زمانی که دیالوگ غیرفعال نمی شود ، نقطه شکست (breakpoint) را در خط تنظیم کنید:

Initializing jdb … > All threads suspended. > stop in android.app.Dialog.setCancelable Set breakpoint android.app.Dialog.setCancelable >

توجه داشته باشید که آن android.app.Dialog.setCancelable است و android.app.AlertDialog.setCancelable نیست..

اجرا را از سر بگیرید:

> resume All threads resumed. > Breakpoint hit: “thread=main”, android.app.Dialog.setCancelable(), line=1,251 bci=0

main[1]

با استفاده از دستور locals همه متغیرهای محلی را در قاب پشته کنونی چاپ کنید:

> resume All threads resumed. > Breakpoint hit: “thread=main”, android.app.Dialog.setCancelable(), line=1,251 bci=0

main[1] locals Method arguments: flag = true Local variables: main[1]

flag = true اشاره می کند که این مکان مورد نظر نیست – android.app.Dialog.setCancelable“() توسط چیز دیگری در جای دیگری فراخوانی می شود. آنچه می خواهیم ببینیم flag = false است.

از سر بگیرید تا خروجی مورد نظر بدست آید:

main[1] resume All threads resumed. > Breakpoint hit: “thread=main”, android.app.Dialog.setCancelable(), line=1,251 bci=0

main[1] locals Method arguments: flag = false Local variables: main[1]

مکان مورد علاقه ضربه خورد است. پرچم را با دستور set و resume

(set <value> = <expr>

تغییر دهید و مقدار جدیدی را به عنصر field/variable/array element اختصاص می دهد):

main[1] set flag = true flag = true = true main[1] resume All threads resumed. > Breakpoint hit: “thread=main”, android.app.Dialog.setCancelable(), line=1,251 bci=0

main[1]

با set flag = true ادامه دهید و ادامه دهید تا جایی که اجرا دیگر در نقطه شکست متوقف نشود. باید دو بار دیگر طول بکشد. پس از از سرگیری برنامه ، باید بتوانید دیالوگ را با کلیک کردن در خارج از آن ببندید.

راز (secret) را بدست آورید

این تکنیک برای برکنارکردن دیلوگ باکس است – به کد منبع نگاه کنید و سعی کنید ببینید چه چیزی می تواند مورد استفاده قرار گیرد. راز (secret) در برنامه ذخیره شده است ، اما با استفاده از java.lang.String.equals با ورودی کاربرمقایسه می شود.

code_level_1_equals_call_site

بنابراین ، یک نقطه شکست روی java.lang.String.equals تنظیم کنید و پارامترها را ببینید! خبر بد این است که ، اگر سعی کنید یک نقطه شکست برای متد equals تعیین کنید ، به سرعت متوجه می شوید که این یک متد کاملاً محبوب است – بازدیدهای (hits) زیادی از آن خواهید داشت که بیشتر آنها (به جز یکی) را نمی خواهید . بنابراین ، بیایید قبل از جایی که equals فراخوانی شود یک نقطه انفصال تنظیم کنیم – خط را در یک بلوک try… catch در تصویر بالا را ببینید. این می تواند javax.crypto.Cipher.doFinal باشد:

code_level_1_cipher

ابتدا نقطه شکست را تنظیم کنید ، سپس چیزی را در فیلد ورودی برنامه تایپ کنید و روی دکمه VERIFY کلیک کنید. نقطه شکست باید بعد از این کار ضربه زده شود(hit).

> stop in javax.crypto.Cipher.doFinal(byte[]) Set breakpoint javax.crypto.Cipher.doFinal(byte[]) > Breakpoint hit: “thread=main”, javax.crypto.Cipher.doFinal(), line=2,047 bci=0

main[1]

اکنون زمان خوبی است که یک نقطه شکست را در متد java.lang.String.equals تنظیم کنید و اشکال زدایی را از سر بگیرید.

پس از شروع مجدد اجرا با استفاده از cont و زدن نقطه شکست equals ، پارامترها را به equals با دستور locals از jdb تنظیم کنید. ممکن است چندین تکرار لازم باشد.

main[1] locals Method arguments: Local variables: anObject = “RAW” main[1] cont > Breakpoint hit: “thread=main”, java.lang.String.equals(), line=997 bci=0

main[1] locals Method arguments: Local variables: anObject = “UTF-8” main[1] cont > Breakpoint hit: “thread=main”, java.lang.String.equals(), line=997 bci=0

main[1] locals Method arguments: Local variables: anObject = “I want to believe” main[1]

راز (secret) این است: من می خواهم باور کنم. توجه داشته باشید که هیچ نشانه ای وجود ندارد که این راز (secret) است. می تواند RAW باشد. شما فقط باید هرچه بدست آورده اید جمع کنید و آن را بررسی کنید.

راهنمای شکستن برنامه های اندرویدِ کرک نشدنی، در سطح 2 را می توانید در اینجا بیابید.

 


از سایت منبع این نوشته دیدار فرمایید ( اینجا کلیک کنید)

ترجمه با استفاده از گوگل ترانسلیت

ویراستار: علیرضا صدرثقةالاسلامی

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *