Security Researcher | Full Stack Developer
14 نکته برای بهبود امنیت در اپلیکیشنهای اندرویدی
چند وقت پیش یکی از دوستان قدیمیم که تو زمینه امنیت وب فعالیت داره بهم پیشنهاد داد در مورد روشهای امن کردن اپلیکیشنهای موبایلی ویدیو درست کنم، چون نظرش این بود که نسبت به امنیت وب توجه خیلی کمتری بهش شده...
به نظرم پیشنهادش خوب اومد و اینجوری شد که تصمیم گرفتم سر فرصت چند تا از نکاتی که رعایت کردنشون باعث میشه امنیت اپلیکیشنهای اندرویدی چند پله بالاتر بره رو اینجا توضیح بدم که شاید بتونه برای کسی مفید باشه...
اول ببینیم منظور از اینکه یک اپلیکیشن امن داشته باشیم یعنی چی؟
این موضوع نسبت به هر اپلیکیشن میتونه معنی و روش پیادهسازی مختلفی داشته باشه، چند نمونه ازش رو مثال بزنم:
- اپلیکیشنی که کاربر میتونه در ازای پرداختی که انجام میده، سطح کاربریش رو ارتقا بده و یا مثلا محتوای خاصی رو دریافت کنه. امن بودن تو همچین اپلیکیشنی یعنی مطمئن بشیم پرداخت از هر نظر به طور صحیح انجام شده و کاربر نتونه بدون پرداخت و یا با پرداخت مبلغ کمتر جوری رفتار کنه که یک پرداخت موفق براش در نظر گرفته بشه...
- اپلیکیشنی که نیاز به لاگین داره ولی از طرفی امکان ثبت کاربر جدید رو نداره. مثلا فرض کنین یه سازمان اومده فقط مخصوص کارکنان خودش یه اپلیکیشن نوشته. امن بودن تو همچین اپلیکیشنی میتونه این باشه که مطمئن بشیم فقط کاربرای مجاز امکان استفاده از اپلیکیشن رو دارن...
- اپلیکیشنی که نیاز به لاگین داره، امکان ثبت کاربر جدید رو هم داره، ولی هر کاربر سطح دسترسی مخصوص به خودش رو داره. اینجا امن نوشتن میتونه این باشه که کاربری با سطح دسترسی پایینتر نتونه به محتوای کاربرای سطح بالاتر دسترسی داشته باشه...
- اپلیکیشنی که هیچ محتوای خاصی نداره و یا اگرم داره تمام نکات مرتبط با امنیت رو رعایت کرده، ولی APIی داره که روی امنیتش کار نشده، تو همچین اپلیکیشنی هدف باید این باشه که APIش رو امنتر کنیم...
مثال این مدلی زیاد میشه زد، خلاصه اینکه امن نوشتن اپلیکیشنهای موبایلی نسبت به هر اپلیکیشن تعریف و روش پیاده سازی مخصوص به خودش رو داره، ولی یکسری نکات هستن که عمومیان، که در ادامه چند تا از مهمترین هاشون به همراه اینکه چرا باید انجام شن رو توضیح دادم...
از SSL Pinning استفاده کنید
در خصوص اپلیکیشنهایی که به هر شکل با Web Server ارتباط دارن، از اولین نکاتی که باید رعایت کنیم اینه که حتما از SSL Pinning استفاده کنیم، چرا نیازه؟
درسته که زمانی که ارتباط با وب سرور به صورت SSL باشه ترافیک به صورت انکریپت شده ارسال و دریافت میشه، ولی خیلی راحت میشه یک CA رو به عنوان Trust اضافه کرد و با یه Proxy کردن ساده به ترافیک دسترسی داشت...
حالا SSL Pinning چکار میکنه؟ میاد میگه من کار ندارم چه CA هایی از نظر Browser و سیستم عامل Trust هستن، فقط در صورتی connection رو تایید میکنم که Certificate رو CA هایی که خودم مشخص کردم (Pin کردم) تایید کنن...
اگه دوست دارین بیشتر در موردش بدونین:
از Anti Emulator استفاده کنید
یکی دیگه از مهم ترین نکاتی که باید رعایت کنیم اینه که نباید اجازه اجرا شدن اپلیکیشن روی Emulator رو بدیم، حالا چرا؟
زمانی که شما تصمیم دارین یک اپلیکیشن موبایلی رو کرک کنید و یا محدودیتهایی که داره رو Bypass کنید، تو اکثر مواقع نیازه که اون اپلیکیشن روی یک دیوایس Root شده نصب و اجرا بشه، حالا موضوعی که هست Root کردن یک Emulator با یک کلیک انجام میشه ولی Root کردن دیوایس واقعی (تو ورژنهای جدید اندروید) دردسرهای خاص خودش رو داره...
پس اگه اپلیکیشنی داریم که امکان اجرا شدن روی Emulator رو داشته باشه در واقع اومدیم کار اون شخصی که تصمیم داره محدودیتهای اپلیکیشن ما رو Bypass کنه رو یک قدم راحتتر کردیم...
البته انجام این مورد تو بعضی از اپلیکیشن و بازیها باعث کاهش رضایت کاربری میشه، مثلا خیلیها ترجیح میدن بازیهای موبایلی رو روی شبیه سازهای دسکتاپی اجرا کنن، تو همچین حالتی باید ببینیم پیادهسازی این محدودیت، تا چه حد روی تجربه و رضایت کاربری تاثیر گذاره و بعد تصمیم بگیریم که بهتره پیادهسازی بشه یا اینکه نه...
از Root Detection استفاده کنید
همونطور که تو مورد قبل توضیح دادم، شخصی که میخواد یک اپلیکیشن رو کرک کنه و یا محدودیتهایی که داره رو Bypass کنه اکثر مواقع نیاز داره که اپلیکیشن مورد نظرش روی یک دیوایس Root شده نصب و اجرا بشه (چه Emulator و چه دیوایس واقعی)، پس یکی دیگه از نکاتی که باید رعایت کنیم اینه که اجازه اجرا شدن اپلیکیشن در صورتی که دیوایس Root شده بود رو ندیم...
خب قبل از اینکه بریم سراغ موارد بعدی چند نکته مهم رو در خصوص همین 3 مورد بالا توضیح بدم:
- پیاده سازی هر 3 مورد خیلی راحته و زمانی نمیگیره اصلا.
- هر 3 مورد رو میشه خیلی راحت Bypass کرد!
احتمالا الان با خودتون بگین اگه میشه راحت Bypassشون کرد پس چکاریه که پیادهسازی بشن!
اگه به همین 3-4 سال قبل برگردیم، Bypass این 3 مورد کار راحتی نبود (از این نظر که آموزش خوبی براش وجود نداشت)، ولی الان اگه یه سرچ کوچیک کنین کلی آموزش در خصوص نحوه Bypassشون پیدا میکنین که نحوه کار رو مرحله به مرحله توضیح داده...
در حال حاضر روش رایج برای Bypass کردن این 3 مورد، استفاده از Frida و hook کردنه (Frida کاربردهای خیلی زیادی تو زمینه امنیت، کرک و مهندسی معکوس داره و اینجوری نیست که فقط مخصوص اندروید و Bypass این 3 مورد بالا باشه)
اگه با Frida آشنا نیستین و یا میخواین در موردش بیشتر بدونین این دو لینک میتونه مفید باشه:
خبر خوب و یا شایدم بد اینکه استفاده از Frida اصلا سخت نیست...
باز برگشتیم به اینکه اگه 3 مورد بالا رو میشه راحت Bypass کرد پس چه فایده داره پیاده سازیشون!؟
نکته اینجاست که اگه شما بیاین برای پیاده سازی 3 مورد بالا، از کدهایی که به صورت عمومی در دسترس همهست (مثلا از Stackoverflow ، Github و خلاصه هر سورس کدی که به صورت عمومی در دسترس همه هست) استفاده کنید، شک نکنید که خیلی راحت میشه Bypassش کرد...
چکار کنیم پس...؟
اول از همه بگم که ما فقط میخوایم کاری کنیم که Bypassشون سختتر بشه، وگرنه اینکه راهی باشه که بیایم انجام بدیم و کلا جلوی Bypassشون گرفته شه نداریم اصلا...
بیایم خودمون رو بذاریم جای اون نفر (اونی که میخواد Bypass کنه)، تو اولین مرحله قطعا میریم سراغ اسکریپتهای آماده Frida و خلاصه هر اسکریپتی که توی اون زمینه (مثلا Bypass کردن SSL Pinning) پیدا میشد رو تست میکردیم ببینیم نتیجه میده یا نه، خب بیاین فرض کنیم هرچی اسکریپت آماده بود رو تست کردیم و نتیجه نگرفتیم، حالا این برمیگرده به ما (ما اینجا منظور شخصی که میخواد Bypass کنه)، اینکه Bypass کردنش اونقدری برامون اهمیت داره که بریم بخاطرش اپلیکیشن رو دیکامپایل کنیم و بعد سورس کدش رو تحلیل کنیم و بعد اسکریپت hook اختصاصی بنویسیم براش یا نه...
اگه اون نفر حوصلشو داشته باشه و مهمتر اینکه دانشش رو هم داشته باشه (البته کار سختی نیست) و بیاد اسکریپت اختصاصی و مرتبط با سورس کدی که تحلیل کرده رو بنویسه و با اون hook کنه که خب هیچی، Bypass میکنه و تموم...
ولی بیاین فرض رو روی این بذاریم که اون نفر اگه با اسکریپتهای آماده به نتیجه نرسه کلا بیخیال میشه، حالا یا حوصلش نیومده سورس کد رو تحلیل کنه، یا اصلا دانشش رو نداشته که بخواد خودش اسکریپت بنویسه و یا هر دلیل دیگهای، پس کافیه یه نگاهی به اسکریپتهای آمادهای که در این خصوص به صورت عمومی توی اینترنت موجوده بندازیم و بعد کدمون رو جوری بنویسیم که حداقل با اسکریپتهای آماده نشه Bypassش کرد...
اکثر اسکریپتهای آماده Frida رو میتونین اینجا پیدا کنین:
ولی بازم تاکید کنم که تو بهترین حالت فقط میتونیم Bypass رو سختتر کنیم، نمیتونیم غیرممکنش کنیم، در واقع ما با رعایت نکته بالا فقط میایم جلوی bypass شدن با اسکریپتهای آماده رو میگیریم، فقط همین...
و نکته بعدی اینکه حتی اگه حوصلتون نمیاد که پیاده سازی 3 مورد بالا رو به این روش که توضیح دادم انجام بدین، بازم حتما انجامشون بدین حتی با روش و کدهای آمادهای که به صورت عمومی تو اینترنت موجوده، چون بازم حداقل اومدین یک پله امنیت رو بردین بالاتر...
کدها رو Obfuscate کنید
این مورد که دیگه نیاز به توضیح نداره و همه میدونیم که باید انجام بدیم. خیلی مهمه که خوندن و تحلیل سورس کد برای کسی که اپلیکیشن ما رو دیکامپایل میکنه سخت باشه، درسته که بازم اگه وقت بذاره به نتیجه میرسه ولی اگه کدها تا یه حد خوبی Obfuscate شده باشن حداقلش اینه که برای تحلیل کدها زمان زیادی باید بذاره و ممکنه این وسط بیخیال بشه...
دسترسی کاربرها رو به IP داخل کشور محدود کنید
اگه اپلیکیشنی دارید که فقط مختص کاربرای ایرانیه و خارج از کشور هیچ کاربردی نداره، دسترسی کاربراتون رو فقط به IP های داخلی محدود کنین، چرا این کارو کنیم؟
زمانی که شخصی بخواد (منظور شخص کرکر و کسی که میخواد محدودیتهای اپلیکیشن ما رو Bypass کنه) سمت سرور اپلیکیشن ما وقت بذاره، مثلا میخواد چک کنه ببینه آسیب پذیر هست یا نه، اگه اپلیکیشن ما فقط به IP های داخل کشور محدود شده باشه، این باعث میشه یکسری چالش برای اون نفر به وجود بیاد، قطعا جلوش رو نمیگیره ولی ما باید هر چیزی که باعث میشه کار رو براش سختتر کنه رو انجام بدیم...
شاید بگین خب این روزا که اکثر کاربرای ایرانی از VPN استفاده میکنن، اون موقع چی! بله درسته این موضوع رو هم باید در نظر گرفت که پیاده سازی این روش تا چه حد میتونه روی رضایت و تجربه کاربری تاثیر منفی داشته باشه و بعد برای پیاده سازی کردن یا نکردنش تصمیم بگیریم...
در صورت فعال بودن VPN service اجازه کار با اپلیکیشن رو ندید
یکی دیگه از مهم ترین نکاتی که باید رعایت کنیم اینه که در صورت فعال بودن VPN Service اجازه ارسال/دریافت ترافیک رو ندیم، چرا اینکارو کنیم؟
سادهترین دلیلش اینه که برای دیدن ترافیک میشه به جای Proxy از VPN هم استفاده کرد، پس ما اجازش رو نمیدیم...
دقت کنید که این مورد نباید فقط زمان اجرای اپلیکیشن بررسی بشه، باید به صورت Listener باشه...
و اینکه باز اینجا سوال پیش میاد که الان اکثر کاربرای ایرانی از VPN استفاده میکنن، اگه این محدودیت رو بذاریم که به مشکل میخورن! بله درسته، برای پیاده سازی این محدودیت لازمه بین رضایت کاربری و امنتر کردن اپلیکیشن یکی رو انتخاب کنیم...
مقادیر مهم و حساس رو به هیچ وجه به صورت Hardcode استفاده نکنید
این مورد هم که دیگه نیاز به گفتن نداره، به هیچ عنوان مقادیر حساس مثل API Key ، کلید رمزنگاری ، توکن و کلا هر چیزی که احساس میکنید حساسه نباید به صورت Hardcode باشه. پس چکار کنیم؟ پارامترهای حساس رو از سرور بگیرین، شاید بگین خب چکاریه اینجوری که بازم اون نفر راه برای دیدن این پارامترها داره!
بله درسته، ولی حداقل دیگه به صورت مستقیم توی سورس کد نیست، یادتون باشه کلا هدف اینه که کارو برای اون نفر سختتر کنیم نه اینکه کامل جلوشو بگیریم (چون نمیتونیم)
نکته بعدی در همین خصوص اینکه تو خیلی از اپلیکیشنها پارامترهای حساس بعد از گرفتن از وب سرور، به صورت plain داخل shared preferences نگهداری میشن که اینم روش درستی نیست (درسته که تو حالت عادی دسترسی به shared preferences نیاز به روت بودن دیوایس داره، ولی حتی با فرض پیاده سازی root detection و اینکه نشه Bypassش کرد، بازم میشه به shared preferences دسترسی داشت)، پس بهتره پارامترهای حساسی که قراره local نگهداری بشن، حتما به صورت Encrypt شده باشه...
برای اجرا شدن Activity ها محدودیت بذارید
از اونجایی که Activityها رو میشه با ADB هم بالا اورد (نسبت به زبان و فریمورک فرق میکنه روش کار)، یکی از نکاتی که باید رعایت بشه اینه که اجرا شدن Activityهای حساس رو به پارامترهایی که از Activity قبلی میگیره محدود کنید...
این مورد زمانی حساسیتش بیشتر میشه که کاربر بتونه یک Activity رو (به صورت مستقیم) بالا بیاره و ترافیک ارسال/دریافت کنه...
البته درسته که با ADB میشه پارامترهای مورد نیاز رو هم ارسال کرد، ولی اینجوری اون نفر نیاز داره که اپلیکیشن رو دیکامپایل و سورس کد رو تحلیل کنه تا بتونه تعداد، طول و فرمت صحیح هر پارامتر رو متوجه بشه، که خب این باعث میشه ازش زمان زیادی بگیره و هدف ما هم فقط همینه...
روش دیگهش هم اینه که مشخص کنیم کدوم اپلیکیشن و class اجازه run کردن هر Activity رو داره، یک راهش اینجوری میشه:
مقادیر مهم و حساس رو داخل shared preferences نذارین
تا جای ممکن مقادیر حساس رو توی shared preferences نذارین، اگرم میذارین جوری بذارین که اون شخص با نگاه اول متوجه نشه که هر پارامتر چه کاربردی داره و یه پیشنهاد شاید عجیب اینکه اگه پارامترهای حساسی رو داخل shared preferences میذارین، همراه با پارامترهای اصلی یکسری پارامتر الکی با اسمهای ظاهرا حساس و مقادیر رندوم رو هم داخل shared preferences بذارید ، هدف اینه که زمانی که اون شخص shared preferences رو چک کرد حداقل سریع متوجه نشه هر پارامتر چیه و برای چه استفادهای اونجا گذاشته شده... (تاثیر داره)
نکته بعدی اینکه اگه پارامتر حساسی رو میخواین داخل shared preferences نگه دارین، در کنار اینکه باید به صورت Encrypt شده باشه، یک مقدار رندوم با طول مشخص رو به قبل و بعدش اضافه کنید، اینجوری:
ABC [Encrypted] DEF
برای Decrypt هم که خب اول مقادیری که اضافه کردین رو حذف میکنید ازش...
و باز تاکید میکنم که هدف اینه که کار رو برای اون شخص وقتگیر تر کنیم، وگرنه خب بله با یه نگاه به سورس کدی که دیکامپایل کرده متوجه اصل داستان میشه...
فقط دیتای مورد نیاز رو داخل response قرار بدین
این مورد رو با مثال توضیح میدم، خیلی از اپلیکیشنهایی که روشون کار کردم دیتا ای که قرار بوده به کاربر نشون داده نشه رو هم داخل response گذاشته بودن و بعد سمت کلاینت چک میکردن که اون دیتا رو به کاربر نشون بدن یا ندن، مثلا اگه کاربر premium بود بهش نشون بدن، اگه نبود ندن... (ولی کل دیتا داخل response هست!)
البته این موضوع فقط مختص به این حالت نمیشه، خیلی وقتا توی response دیتا ای هست که هیچ کاربردی نداره (یعنی اگه نباشه هم هیچ مشکلی پیش نمیاد) و همینجاست که اون نفر احتمال داره متوجه چیزایی بشه که نباید بشه و یا اینکه حداقل شاید به عنوان یک hint برای ادامه کار بتونه از اون دیتا استفاده کنه...
پس دقت کنید که اگه قراره محتوایی بر اساس سطح دسترسی کاربر بهش نشون داده بشه، اون شرط کاملا باید سمت سرور بررسی بشه و نکته دوم اینکه response فقط باید شامل دیتا ای باشه که بهش نیاز داریم، اگه پارامتری رو نیاز نداریم چرا اصلا باید داخل response بذاریمش!
مدیریت قابل Export بودن کامپوننتها
همونطور که میدونید خیلی وقتها نیاز داریم Service، Activity و یا Receiverی داشته باشیم که اپلیکیشنهای دیگه بتونن اجراش کنن و یا بهش دیتا ارسال کنن (یا هر دو)، که خب توی این حالت از قابلیت Export بودن کامپوننتها استفاده میکنیم...
ولی خیلی وقتها کامپوننتهایی که نیازی به دریافت دیتا و یا run شدن خارج از اپلیکیشن ندارن هم به اشتباه مقدار Export برابر با True در نظر گرفته میشه براشون (دقت کنید که در صورتی که intent-filter داشته باشیم، به صورت پیشفرض مقدار Exported برابر با True در نظر گرفته میشه مگه اینکه Falseش کنیم)
خلاصه که به قابل Export بودن کامپوننتها دقت داشته باشید و اگرم کامپوننتی نیاز داره که قابل Export باشه، در صورت امکان محدودیت بذارید براش که تا جای ممکن امنتر بشه...
اگه میخواین نکات بیشتری رو در این خصوص بدونین، لینکهای زیر میتونه مفید باشه:
ترافیک رو انکریپت کنید
یکی دیگه از نکاتی که برای امنتر شدن اپلیکیشنمون میتونیم انجام بدیم اینه که ترافیک رو انکریپت کنیم، فقط چند نکته:
شاید عجیب باشه ولی خیلی از اپلیکیشنها هستن که این مورد رو رعایت کردن ولی کاری که انجام دادن این بوده که دقیقا رفتن یه تیکه کد رو از یه جا پیدا کردن و دقیقا همون رو حالا با یکم تغییر جزئی استفاده کردن و بدتر اینکه حتی کلیدی که به فرض توی github یا حالا هرجایی که ازش کپی کردن به عنوان sample گذاشته بوده رو هم تغییر ندادن! تو همچین حالتی با یکم وقت گذاشتن روی سورس کد، میشه ترافیک رو Encrypt کرد...
نکته دیگه اینکه اگه ترافیک رو Encrypt میکنید، اینجوری نباشه که خیالتون ازش راحت باشه و با خودتون بگین حالا که داریم ترافیک رو Encrypt شده دریافت/ارسال میکنیم، پس کسی امکان دستکاری پارامترها رو نداره، کاملا نیازه که سمت سرور بعد از Decrypt شدن، پارامترها Validate و Sanitize بشن...
یه نکته مهم دیگه که البته نیازی به گفتن نداره اینکه هر چقدرم رمزنگاری پیچیدهای رو انتخاب کرده باشین، بازم اگه اون نفر وقت بذاره و دانشش رو داشته باشه امکان Decrypt هست، پس در واقع ما با Encrypt کردن ترافیک فقط اومدیم یک مرحله امنیت رو بردیم بالاتر و امکان دستکاری ترافیک رو سختتر کردیم (غیر ممکن نکردیم)
حتما از امن بودن API مطمئن بشید
شکی نیست که اگه اپلیکیشنی داریم که با وب سرور در ارتباطه، در کنار این که باید روی امنیت اپلیکیشن (سمت کاربر) کار کنیم، باید روی امنیت APIش هم کار کنیم و این 2 تا در کنار همدیگهست که باعث امن شدن یک اپلیکیشن میشه، اینکه چجوری میتونیم یک API امن داشته باشیم نیاز به توضیحات زیادی داره، سر فرصت یک پست مثل همین پست رو در خصوص روشهای امن کردن API هم آماده کنم...
آخرین نکته...
این مورد خیلی ارتباط مستقیمی با عنوان پست نداره ولی دیدم اینم گفتنش بد نیست...
خبلی از سازمان/شرکتها هستن که در کنار وب سایت، اپلیکیشن موبایلی هم دارن. اما از اونجایی که بیشتر کاربراشون از وب سایت استفاده میکنن یه جورایی اپلیکیشن موبایلیشون افتاده کنار و اهمیتی بهش نمیدن...
حالا این چه مشکلی ایجاد میکنه؟ بیاین فرض کنیم روی امنیت وبسایت کاملا کار شده و کسی که قصد پیدا کردن آسیب پذیری از وب سایت اون سازمان/شرکت رو داره موفق نمیشه، تو این مرحله یکی از کارایی که اون شخص میتونه انجام بده تحلیل اپلیکیشن اون سازمان با هدف بررسی API، پارامترها و خلاصه آشنا شدن با Flow ترافیکه...
پس اگه سازمان/شرکتی هستین که هم وب سایت دارین و هم اپلیکیشن موبایلی، ولی از اونجایی که بیشتر کاربراتون از وب سایت استفاده میکنن اپلیکیشن موبایلیتون افتاده کنار، حتما نیازه که تا قبل از اینکه روی امنیت اپلیکیشن موبایلیتون کار نکردین، فایلش رو کامل از روی سرور بردارین (چندین مورد پیش اومده که بهشون اطلاع دادم اپلیکیشناتون آسیب پذیره ولی کاری که کردن این بوده که لینک دانلودش رو از روی وب سایت برداشتن! راه برای پیدا کردن لینک های حذف شده زیاده، باید خود فایل رو حذف کنید)
به نظرم تا همینجا کافیه... از چیزی که انتظارش رو داشتم خیلی طولانیتر شد این پست!
سر فرصت دو پست در خصوص روشهای عمومی امن کردن یک وب سایت و API هم آماده میکنم...
تو این پست قصد داشتم یکسری از مهمترین نکات عمومی که رعایت کردنشون باعث امنتر شدن اپلیکیشنهای موبایلی میشه رو توضیح بدم، امیدوارم براتون مفید بوده باشه...
نکته مهمی که انتظار دارم بعد از خوندن این پست بهش رسیده باشین اینه که برقراری امنیت مثل یک زنجیر میمونه، اگه یک قسمت از کار درست انجام نشه باعث میشه زمانی که برای امن کردن سایر قسمتها گذاشتیم هم بی فایده بشه...
این رو هم حتما در نظر بگیرید نکاتی که توضیح دادم نکات عمومیان، یعنی اگه کلشون رو هم به بهترین شکل انجام بدیم بازم به این معنی نیست که یک اپلیکیشن امن داریم، امن کردن هر اپلیکیشن نسبت به فیچرهایی که داره روش پیادهسازی خاص خودش رو داره...
و آخرین مورد اینکه اگه این پست براتون جالب بود، شاید ویدیوهای زیر هم براتون جالب باشه:
- دوره کرک اپلیکیشن و بازیهای اندرویدی (10 جلسه)
- تحلیل و بررسی سورس کد اپلیکیشنهای جعلی ثنا که یه مدت خیلی زیاد شده بود:
شاد و موفق باشین...
مطلبی دیگر از این انتشارات
تپسی _ خندوانه
مطلبی دیگر از این انتشارات
|نقد و بررسی فیلم|
مطلبی دیگر از این انتشارات
رسیدن به درآمد بالا در اینستاگرام