مهدی ایران نژاد
مهدی ایران نژاد
خواندن ۱۳ دقیقه·۳ سال پیش

آشنایی با App Cloning و جلوگیری از آن در اپلیکیشن های اندرویدی

مقدمه

تقلب و سواستفاده در هر زمینه ای یک امر اجتناب ناپذیره، ساختار و روانشناسی انسان به گونه ایه که تمایل به راحت طلبی داره، این راحت طلبی گاهی اوقات منجر به سواستفاده هایی میشه که یکی از اون موارد به تقلب اشاره میکنه.

یکی از مباحثی که در آن چالش های بسیاری در زمینه بستن راه های تقلب بوجود آورده، بحث توسعه نرم افزار و اپلیکیشن هاست؛ در واقع واژه "تقلب" یا "دور زدن" در این مبحث یک اصطلاح کلی هستش و به خیلی از موارد
و مباحث اشاره میکنه؛ یکی از مواردی که میتونه به کسب و کار و توسعه دهندگان نرم افزار ها خسارت بزنه، بحث Clone کردن نرم افزار ها هست که من قصد دارم در این مقاله به معرفی این راه تقلب و بررسی اون بپردازم و با یک راه حل قطعی نسبت به جلوگیری مشکلات به وجود آمده، اقدام کنم.

در این مقاله با یک روش قطعی جهت جلوگیری از تقلب در برنامه های اندرویدی آشنا شده و میتوانید از آن در داخل Android Studio برای اپلیکیشن ها یا Unity برای بازی های خود استفاده کنید.

معرفی مشکلات و شناخت فلسفه Clone کردن در بحث تقلب

یکی از راهکار هایی که توسعه دهندگان و مالکان یک نرم افزار جهت پیشرفت و افزایش تعداد کاربران خود، انجام میدهند؛ استفاده از روشی مرسوم به نام Invitation-User هست، در این روش کاربران در ازای معرفی کاربران جدید، از طرف نرم افزار خدمتی را به صورت جایزه یا امتیاز دریافت میکنند؛ نمونه های زیادی از این روش رو میتونم مثال بزنم، عمده بازی های موبایلی از این روش جهت بالا بردن تعداد کاربران خودشون استفاده میکنند یا برخی از نرم افزار های مربوط به ارزهای دیجیتال و سایر موارد مشابه نیز از این طریق به توسعه و پیشرفت خودشون مشغول هستند.

بیایید یک بار فرآیند روش Invitation-User رو باهم بررسی کنیم:

  1. کاربر a نرم افزاری رو نصب میکنه (یا از قبل نصب کرده) و متوجه میشه بعد از معرفی کاربران جدید، از طرف برنامه یک روز اشتراک vip جایزه میگیره
  2. کاربر a نرم افزار رو به کاربر b معرفی میکنه و ازش میخواد بعد از نصب، کدش رو به عنوان معرف وارد کنه
  3. کاربر b نرم افزار رو روی گوشی خودش نصب میکنه و کد معرفی که از کاربر a تحویل گرفته رو وارد برنامه میکنه
  4. کاربر a جایزه خودش رو دریافت میکنه
  5. حالا ممکنه کاربر a و کاربر b هر کدام نیز کاربران بیشتری رو معرفی و جوایز خودشون رو دریافت کنند

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

حالا از چه طریق میشه در این فرایند تقلب کرد؟ اگه مراحل 2 و 3 توسط یک کاربر انجام بشه اینجا ما شاهد تقلبی هستیم که به دفعات بینهایتی میتونه تکرار بشه در واقع روشی که با استفاده از آن کاربر میتونه اقدام به تقلب کنه روشی به اسم App-Cloning هستش که با استفاده از برنامه هایی همچون Multi-Parallel, Dual-Space و سایر موارد مشابه، کاربر میتونه نسخه متعددی رو بدون شناخته شدن روی دستگاه خودش نصب کنه.

روش کار این گونه برنامه ها به این شکله که در محیطی از سیستم عامل اندروید، محلی رو به صورت شبیه سازی شده در نظر میگیرند و فایل های اجرایی اپلیکیشن رو درون این محیط اجرا میکنند که باعث اجرای یک نسخه همسان با نسخه اصلی برنامه میشوند تا دقیقا مانند نسخه اصلی خودش رفتار کنه.

شاید از خودتون بپرسید چرا سیستم عامل اندروید چنین اجازه ای رو میده که نسخه های متفاوتی از یک برنامه قابلیت اجرایی داشته باشند؟ چه دلیلی وجود داره که گوگل با آن همه سخت گیری های خودش در زمینه حق کپی رایت و حفظ حریم خصوصی و سایر قوانین مرتبط، باز هم اجازه چنین کاری رو میده؟

جوابش خیلی ساده اس؛ چون این قابلیت یک قابلیت محشر، عالی و کاربردیه!

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

بله بازم میگم این قابلیت خیلی عالیه و کمک زیادی میکنه، بزارید یه مثالی بزنم، چاقو یکی از الزامات اصلی و از واجبات آشپزخانه است، ولی همین چاقو میتونه باعث قتل یک انسان بشه، نمیشه؟ هر چیز خوبی پتانسیل سواستفاده شدن داره و برعکس؛ بزارید مزیت و چرایی خوب بودن این قابلیت رو براتون با یک مثال ساده توضیح بدم شاید خودتون هم مجذوبش شدید.

فرض کنید شما توسعه دهنده یک اپلیکیشن پیام رسان هستید، این پیام رسان الان منتشر شده و در آن با دوستان و آشنایان خود در ارتباط هستید، اکنون قصد دارید در تیم برنامه نویسی خود نسبت به توسعه و بهبود قسمتی از این پیام رسان یا اضافه کردن قابلیتی جدید به آن اقدام کنید؛ قطعا نسخه در حال توسعه و نسخه پایدار و در دسترس کاربران از یک سرور و دیتابیس یکسانی استفاده نمیکنند و شما برای تست و توسعه تغییرات خودتون نیاز دارید که نسخه اصلی رو از گوشی خودتون حذف کنید و نسخه تستی خودتون رو جایگزین کنید؛ آیا این کار آزار دهنده نیست؟ اگه با یکی از دوستان خودتون کار داشته باشید چه؟ قطعا باید بین نسخه های اصلی و تستی جابجا بشید و هزینه آن نیز صرف زمان جهت حذف و نصب نسخه های برنامه است.

راه حل نسبتا خوب اینه که برای نسخه های تستی خودتون یک پکیج نیم مجزا در نظر بگیرید و موقع انشار، پکیج نیم اصلی رو جایگزین کنید؛ ولی آیا این روش مطمئن و جوابگوعه؟ پس تکلیف سرویس هایی که وابسته به پکیج نیم هستند چی میشه؟ سرویس هایی مثل FCM, Analytic Services و سرویس های پرداخت مثل پرداخت گوگل پلی و کافه بازار قطعا در این روش با مشکلاتی مواجه میشند و باعث مخلل کردن عملکرد برنامه میشوند.

راه حل و درمان قطعی این مشکل فقط با قابلیتی به اسم Clone Install حل میشه؛ در واقع گوگل و سیستم عامل اندروید با این قابلیت به ما اجازه میده در محیطی از سیستم عامل، فایل های اجرایی یک نرم افزار رو بارگذاری کنیم و یک نسخه کلون از نسخه اصلی همون برنامه رو اجرا کنیم که عملکرد و شباهت 99 درصدی با نسخه اصلی داشته باشه؛ البته جهت دسترسی به این قابلیت و استفاده از آن باید حتما دسترسی Root به سیستم عامل اندروید داشته باشیم؛ همین امر باعث شده کمتر کسی به سراغش بره و روش شناخته شده ای نباشه...

باز کردن دسترسی Root باعث باز شدن حفره ها و نقاط امنیتی سیستم عامل شده و از نظر کارشناسان امنیتی چنین کاری زیاد معقول نیست و به افراد عادی یا غیر حرفه ای پیشنهاد نمیشود.

حالا برنامه هایی که بالاتر اشاره کردم، کاری مشابه با روش Clone Install رو انجام میدن، اون برنامه ها دقیقا فایل های اجرایی برنامه نصب شده داخل گوشی رو کپی و اجرا میکنند ولی ما در روش بالا از فایل های اجرایی خودمون استفاده میکنیم تا برنامه ای رو مجزا از نسخه اصلی خودش اجرا کنیم...

راه حل جلوگیری از تقلب در روش App Cloning

جهت جلوگیری از تقلب در این روش راه حل های مختلف و زیادی وجود داره، روش هایی مثل استفاده از شناسه های نرم افزاری مثل Android ID, Unique ID, FCM Caller ID, Firebase Client ID، کد هایی مثل Mac Address, IMEI, Sim ID و سایر موارد مشابه یا استفاده از روش های ترکیبی و خلاقانه از کد هایی که اشاره کردم تا حدی میتونه جلوی این روش از تقلب رو بگیره ولی به هیچ وجه راه حل و درمان قطعی نیست و همه این شناسه ها توسط App Clonerها قابل شبیه سازی و دور زدن هستش، در واقع این قابلیته که به خاطر همان دلایل بالا توسط سیستم عامل در اختیار App Cloner قرار گرفته میشه...

جهت حل این مشکل باید در ابتدا با طرز کار App Cloner ها آشنا بشیم؛ همانطور که قبلا اشاره کردیم نجوه Clone کردن برنامه ها به این شکله که فایل های اجرای اون برنامه رو در یک محل تعریف شده با یک قالب و استاندارد مشخصی که از طرف سیستم عامل اندروید قابل شناسایی باشه، کپی کنیم؛ این کار باعث شناخته شدن یک نسخه جدید از همان نرم افزار خواهد بود.

به طور پیش فرض محل ذخیره سازی اطلاعات و فایل های اجرایی برنامه ها در چنین آدرسی ذخیره میشوند:

/data/user/0/com.example.my.application/files
ممکن است ساختار این آدرس روی برند های مختلف گوشی های اندروید و نسخه های مختلف رام های اندرویدی متفاوت باشد ولی ساختار کلی همه آنها یکسان بوده شباهت زیادی با یکدیگر دارند.

در واقع در سطح دسترسی Root، ساختار و سلسله مراتب ذخیره سازی فایل های یک برنامه در چنین قالبی قرار دارد و سیستم عامل از این ساختار به عنوان ساختار پیشفرض و اصلی خود جهت مدیریت فایل های برنامه ها استفاده میکند.

ولی App Clonerها فقط مجاز به استفاده از این دو ساختار و سلسله مراتب جهت ذخیره سازی و Clone کردن برنامه ها هستند:

/data/data/com.example.my.application/virtual/data/user/0/com.example.my.application/files /data/user/999/com.example.my.application/files
در حالت عادی تمام فایل ها و مسیر های پیش فرض ذخیره سازی اطلاعات و دسترسی های عادی کاربر در سیستم عامل اندروید از طریق آدرس /data/user/0/ امکان پذیر است این به این معناست که هر چیزی که ما از طریق برنامه مدیریت فایل اندروید به آن دسترسی داریم روی آدرس فوق ذخیره و بازیابی میشود و دسترسی مستقیم به هر جایی غیر از این سلسه مراتب نیازمند دسترسی Root است.

اکنون پس از بررسی آدرس های بالا به این نتیجه میرسیم که آدرس ذخیره شدن یک اپلیکیشن Clone شده، دارای یکی از این دو خصوصیت زیر است:

  1. فایل های برنامه، زیرمجموعه پوشه های متعددی با نام پکیج نیم یا همان شناسه برنامه هستند، یعنی پکیج نیم در آدرس دهی فایل ها، روی بیش از 1 پوشه نام گذاری شده است.
  2. فایل های برنامه، زیرمجموعه پوشه 999 قرار گرفته است.

پس ما با بررسی آدرس ذخیره سازی برنامه و مقایسه آن با دو شرط بالا میتوانیم نسخه Clone اپلیکیشن رو از نسخه Original تشخیص دهیم.

پیاده سازی فنی و شناسایی هویت برنامه در زمان اجرا

روش کلی کار به این صورت هست که ابتدا با بررسی آدرس برنامه مطمئن میشیم که پوشه 999 زیرمجموعه آدرس دهی برنامه قرار نگرفته باشد؛ درصورتی جواب مثبت باشد هویت برنامه مشخص شده ولی اگر پوشه 999 یافت نشد، تعداد نقطه های پکیج نیم برنامه رو شمارش کرده و آن را با تعداد نقطه های موجود در آدرس برنامه مقایسه میکنیم، بعد از مقایسه این دو مقدار ما به جواب نهایی خود میرسیم و هویت برنامه ما مشخص میشود.

استفاده از مکانیزم Anti-Clone برای Android Studio و اپلیکیشن های اندرویدی

ابتدا در محیطی از پروژه Android Studio خود یک کلاس از نوع Java به نام AntiCloner بسازید و دستورات زیر رو برای آن بنویسید:

package com.test.antiappcloner; import android.content.Context; public class AntiCloner { private static final AntiCloner ourInstance = new AntiCloner(); private static final String DUAL_APP_ID_999 = &quot999&quot private static final char DOT = '.'; public static AntiCloner getInstance() { return ourInstance; } public boolean isAppCloning(Context context, String packageName) { int appPackageDotCount = getDotCount(packageName); String appStoragePath = context.getFilesDir().getPath(); return appStoragePath.contains(DUAL_APP_ID_999) || getDotCount(appStoragePath) > appPackageDotCount; } private int getDotCount(String path) { int count = 0; for (int i = 0; i < path.length(); i++) { if (path.charAt(i) == DOT) count++; } return count; } }

حالا میتوانید در قسمتی از فرایند Loading برنامه خود، با دستور زیر از هویت اجرایی برنامه مطلع شوید و اقدام مناسبی را انجام دهید:

AntiCloner.getInstance().isAppCloning(context, &quotcom.example.my.application&quot);

استفاده از مکانیزم Anti-Clone برای Unity و بازی های اندرویدی

جهت استفاده از این مکانیزم در یونیتی ما نیاز به ساختن پلاگین مخصوصی داریم تا با استفاده از دستورات کتابخانه JavaAndroid به اجرای کد های قسمت قبل و بررسی هویت بازی خودمون بپردازیم.

کتابخانه JavaAndroid یک رابط بین کد زبان C#(سی چارپ) و زبان Java هستش که به ما اجازه میده در محیط بازی با پلاگین های اندرویدی مثل پلاگین های پرداخت یا امکانات سیستم عامل مثل GPS یا میکروفون، ارتباط بگیریم و اون ها رو کنترل کنیم؛ این کتابخانه ها فقط در یونیتی قابل دسترسی هست و یونیتی این کتابخانه ها رو صرفا برای توسعه بازی های اندرویدی در اختیار توسعه دهندگان خودش قرار داده.

به همین خاطر ما باید ابتدا از کد هایی که بالاتر برای برنامه های اندرویدی نوشتیم یک پلاگین اندرویدی با فرمت AAR یا JAR تولید کنیم و از آن خروجی بگیریم، در صورتی که با نحوه تولید و ساختن این پلاگین ها آشنایی ندارید میتوانید به مراجع آموزشی زیر مراجعه کنید:

Creating an Android plugin for Unity3D - YouTube

Creating Android Plugin (AAR) for Unity with Android Toast - YouTube

Creating An Android Java Plugin For Unity3D

Creating a native Android plugin for Unity3D

با کمک دانش قبلی خود یا مراجع آموزشی بالا، کلاسی را که در قسمت قبل ایجاد کردیم رو برای پلاگین خودمون تعریف میکنیم و از آن خروجی aar یا jar میگیریم؛ سپس فایل پلاگین رو در مسیر Assets\Plugins\Android داخل پروژه یونیتی خود کپی میکنیم.

فراموش نکنید در صورتی که در پروژه یونیتی خود از ProGuard استفاده میکنید حتما پکیج نیم پلاگین رو به دستورات ProGuard خود اضافه کنید؛ به عنوان مثال چیزی شبیه به این دستور را باید به فایل ProGuard اضافه کنید تا بعدا در حین استفاده از پلاگین دچار مشکل نشوید:

-keep class com.test.antiappcloner.** {*;}

اکنون باید در محیط پروژه یونیتی خود یک کلاس جهت کنترل پلاگین بالا بسازیم؛ به عنوان مثال یک کلاس با نام AntiClonerPlugin میسازیم و داخل آن دستورات زیر را تعریف میکنیم:

using System.Collections; using UnityEngine; public static class AntiClonerPlugin { private const string pluginName = &quotcom.test.antiappcloner.AntiCloner&quot private static AndroidJavaClass _pluginClass; private static AndroidJavaObject _pluginInstance; private static AndroidJavaObject _context = null; private static AndroidJavaClass PluginClass { get { if (_pluginClass == null) _pluginClass = new AndroidJavaClass(pluginName); return _pluginClass; } } private static AndroidJavaObject PluginInstance { get { if (_pluginInstance == null) _pluginInstance = PluginClass.CallStatic<AndroidJavaObject>(&quotgetInstance&quot); return _pluginInstance; } } private static AndroidJavaObject Context { get { if (_context == null) _context = new AndroidJavaClass(&quotcom.unity3d.player.UnityPlayer&quot).GetStatic<AndroidJavaObject>(&quotcurrentActivity&quot); return _context; } } public static bool IsCloneApp() { #if UNITY_EDITOR return false; #endif return PluginInstance.Call<bool>(&quotisAppCloning&quot, Context, &quotcom.example.my.unitygame&quot); } }

حالا میتوانید در قسمتی از فرایند Loading بازی خود، با دستور زیر از هویت اجرایی بازی مطلع شوید و اقدام مناسبی را انجام دهید یا از آن برای شناسایی کاربران متخلف استفاده کنید:

AntiClonerPlugin.IsCloneApp();

فراموش نکنید که توابع کتابخانه JavaAndroid فقط روی سیستم عامل اندروید و روی گوشی اجرا میشوند و در صورت اجرا روی یونیتی، خطا میدهند و اجرا نمیشوند، جهت تست عملکرد و نحوه اجرای پلاگین در داخل یونیتی میتوانید به صورت دستی مقدار بازگشتی تابع IsCloneApp را در بخش زیر تغییر دهید، این قسمت فقط در محیط ادیتور اجرا میشود و در زمان خروجی گرفتن، Compiler آن را در نظر نمیگیرد:

#if UNITY_EDITOR return false; #endif

منتظر نظرات سازنده و بازخورد های شما عزیزان و متخصص های این حوزه هستم

موفق و پیروز باشید

اندرویدandroidیونیتیunityبرنامه نویسی
برنامه نویس و توسعه دهنده بازی های اندرویدی در Unity، طراح UI
شاید از این پست‌ها خوشتان بیاید