Cinco herramientas más contra los tramposos en un proyecto móvil con un DAU de 1 millón de usuarios

Érase una vez, tuvimos que reelaborar por completo la protección del popular juego de disparos PvP. El resultado fue una serie de herramientas que preparamos y lanzamos al mismo tiempo para evitar que los tramposos rastreen gradualmente las actualizaciones. 





«» — , , —  . , : 





  • .





  • Photon Plugin.





  • .





  • .





  • .





, .





, , .





№6.

, ( Android) ( iOS), .





(iOS)

Jailbreak Cydia , . ( *.plist), .





/Library/MobileSubstrate/DynamicLibraries/ ( ).





, Jailbreak, , .





string finalPath = string.Empty;
string substratePath = "/Library/MobileSubstrate/DynamicLibraries/";

bool bySymlink = false;

if (!Directory.Exists(substratePath)) //    (  xCon),          
{
	string symlinkPath = CreateSymlimk(substratePath);

	if (!string.IsNullOrEmpty(symlinkPath))
	{
		bySymlink = true;
		finalPath = symlinkPath;
	}
}
else
{
	finalPath = substratePath;
}


bool detected = false;
string detectedFile = string.Empty;

try
{
	if (!string.IsNullOrEmpty(finalPath))
	{
		string[] plistFiles = Directory.GetFiles(finalPath, "*.plist"));

		foreach (var plistFile in plistFiles)
		{
			if (File.Exists(plistFile))
			{
				StreamReader file = File.OpenText(plistFile);
				string con = file.ReadToEnd();

				string bundle = "app_bundle"; 

				if (con.Contains(bundle))
				{
					detectedFile = plistFile;
					detected = true;
					break;
				}
			}
		}
	}
}
catch (Exception ex)
{
	Debug.LogError(ex.ToString());
}
      
      



, (KernBypass, A-Bypass). , .





, .





KernBypass ( ):





if (File.Exists("/var/mobile/Library/Preferences/jp.akusio.kernbypass.plist") 
{
	StreamReader file = File.OpenText("/var/mobile/Library/Preferences/jp.akusio.kernbypass.plist"); 
	string con = file.ReadToEnd();

	if (con.Contains("app_bundle") 
	{
		//detected
	}
}
      
      



(Android)

— , , ( Parallel Space). , root-. : , Application Info .





, . — (dataDir applicationInfo) access ( ). , , Persistent Data .





C:





JavaVM*		java_vm;

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    
    java_vm = vm;
    return JNI_VERSION_1_6;
}

int CheckParentDirectoryAccess()
{
    JNIEnv* jni_env = 0;
    (*java_vm)->AttachCurrentThread(java_vm, &jni_env, NULL);

    jclass uClass = (*jni_env)->FindClass(jni_env, "com/unity3d/player/UnityPlayer");
    jfieldID activityID = (*jni_env)->GetStaticFieldID(jni_env, uClass, "currentActivity", "Landroid/app/Activity;");
    jobject obj_activity = (*jni_env)->GetStaticObjectField(jni_env, uClass, activityID);
    jclass classActivity = (*jni_env)->FindClass(jni_env, "android/app/Activity");
    
    jmethodID mID_func = (*jni_env)->GetMethodID(jni_env, classActivity,
                                                      "getPackageManager", "()Landroid/content/pm/PackageManager;");
    
    jobject pm = (*jni_env)->CallObjectMethod(jni_env, obj_activity, mID_func);
    
    jmethodID pmmID = (*jni_env)->GetMethodID(jni_env, classActivity,
                                                      "getPackageName", "()Ljava/lang/String;");
    
    jstring pName = (*jni_env)->CallObjectMethod(jni_env, obj_activity, pmmID);
    jclass pm_class = (*jni_env)->GetObjectClass(jni_env, pm);

    jmethodID mID_ai = (*jni_env)->GetMethodID(jni_env, pm_class, "getApplicationInfo","(Ljava/lang/String;I)Landroid/content/pm/ApplicationInfo;");

    jobject ai = (*jni_env)->CallObjectMethod(jni_env, pm, mID_ai, pName, 128);
    jclass ai_class = (*jni_env)->GetObjectClass(jni_env, ai);
    
    jfieldID nfieldID = (*jni_env)->GetFieldID(jni_env, ai_class,"dataDir","Ljava/lang/String;");
    jstring nDir = (*jni_env)->GetObjectField(jni_env, ai, nfieldID);
    
    const char *nDirStr = (*jni_env)->GetStringUTFChars(jni_env, nDir, 0);

    char parentDir[200];
    snprintf(parentDir, sizeof(parentDir), "%s/..", nDirStr);

    if (access(parentDir, W_OK) != 0)
    {
         return 1;
    }
	else
	{
		 return 0;
	}
}
      
      



apk (Android)

apk- , . , . 





, root-, . - , .





# Java-:





Lazy<byte[]> defaultResult = new Lazy<byte[]>(() => new byte[20]);

            if (Application.platform != RuntimePlatform.Android)
                return defaultResult.Value;

#if UNITY_ANDROID
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

	if (unityPlayer == null)
		throw new InvalidOperationException("unityPlayer == null");

	var _currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

	if (_currentActivity == null)
		throw new InvalidOperationException("_currentActivity == null");


            var packageManager = _currentActivity.Call<AndroidJavaObject>("getPackageManager");
            if (packageManager == null)
                throw new InvalidOperationException("getPackageManager() == null");

            // http://developer.android.com/reference/android/content/pm/PackageManager.html#GET_SIGNATURES
            const int getSignaturesFlag = 64;
            var packageInfo = packageManager.Call<AndroidJavaObject>("getPackageInfo", PackageName, getSignaturesFlag);
            if (packageInfo == null)
                throw new InvalidOperationException("getPackageInfo() == null");

            var signatures = packageInfo.Get<AndroidJavaObject[]>("signatures");
            if (signatures == null)
                throw new InvalidOperationException("signatures() == null");

            using (var sha1 = new SHA1Managed())
            {
                var hashes = signatures.Select(s => s.Call<byte[]>("toByteArray"))
                    .Where(s => s != null)
                    .Select<byte[], byte[]>(sha1.ComputeHash);

                var result = hashes.FirstOrDefault() ?? defaultResult.Value;
                return result;
            }
#else
            return defaultResult.Value;
#endif
      
      



№7. Photon Plugin

Photon Cloud, . .





Photon Server , , . , Photon, Photon Plugin.





Photon Plugin Enterprise Cloud #. Photon , , : 













  • ;





  • http- .





, , (, , , , ), , .





, . .





№8.

. , . .





:





  1. . .





  2. . .





, (, Android — id ). 





/ . (, ) .





, , . , .





— , , . .





id — , . , , id .





, .









№9.  

, , (, GameGuardian Android). . , , , .





«» :





 internal int Value
{
	get { return _salt ^ _saltedValue; }
	set { _saltedValue = _salt ^ value; }
}
      
      



, . . , 0 1000 ( , , ).





private static int[] refNumbers;

internal static void Start()
{
	refNumbers = new int[1000];

	for (int i = 0; i < refNumbers.Length; i++) 
	{
		refNumbers[i] = i;
	}
}

internal static bool Check()
{
	for (int i = 0; i < 1000; i++) 
	{
		if (!refNumbers [i].Equals(i))
			return true;
	}
}
      
      



№10.

devtodev Flurry. . .





. Data-Driven . ., , . , 500 - , 500 , — . , , .





SQL- . ( , ), . , , - , 0. , - . . , , , - id . — .





. , . 9999 — , - . . , , 15 30, , , , . , . id , , , 1000 , — . .





, , . , .





, . , , , . .





, . « » , .





, , . , . .









— . , .





, , .





. , .








All Articles