JObfuscator v1.09 – Obfuskator Java dla Androida

Wypuściłem właśnie małą aktualizację dla swojego obfuskatora dla języka Java – JObfuscator. Poprawka naprawia błąd w obsłudze adnotacji, który powodował problemy z szyfrowaniem stringów.

https://www.pelock.com/products/jobfuscator

Jeśli chcecie żeby wasz kod tak wyglądał i sprawiał nie lada uciechę każdemu kto rzuci się na waszą aplikację na Androida z dekompilatorem, polecam swój produkt.

Kod Java po obfuskacji obfuscatorem JObfuscator dla Android i Android Studio.

Antidebugging w aplikacjach Android

Dzisiaj analizowałem sobie jedną starszą aplikację i natknąłem się na ciekawy kod, sprawdzający kilka rzeczy, których autorzy sobie nie życzą (to nie jest koncert życzeń), marnie zakamuflowanych pod fałszywymi nazwami 😀

Sprawdzane są m.in.

  • Czy urządzenie było zrootowanie i czy dostępne są narzędzia takie jak np. komenda su.
  • Czy podpięty jest debugger
  • Czy zainstalowane są ehem wrogie aplikacje

Kod mówi więcej niż słowa, dlatego spójrzcie sami, może komuś ta wiedza się kiedyś przyda, z zastrzeżeniem, że to starsza aplikacja i kilka rzeczy mogło się już zmienić.

using System;
using System.Collections.Generic;
using System.IO;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Xamarin.Forms;

namespace Abc
{
	// Token: 0x02000010 RID: 16
	public class CalendarService : ICalendarService
	{
		// Token: 0x06000044 RID: 68 RVA: 0x000080A5 File Offset: 0x000062A5
		public bool IsDateCorrect()
		{
			return this.IsDayCorrect() || this.IsProperUtcFormat() || this.IsMonthCorrect() || this.IsCurrentMonth() || this.IsItReallyCurrentYearAlready();
		}

		// Token: 0x06000045 RID: 69 RVA: 0x000080D0 File Offset: 0x000062D0
		public bool IsDayCorrect()
		{
			using (List<string>.Enumerator enumerator = new List<string>
			{
				"/system/app/Superuser.apk",
				"/sbin/su",
				"/system/bin/su",
				"/system/xbin/su",
				"/data/local/xbin/su",
				"/data/local/bin/su",
				"/system/sd/xbin/su",
				"/system/bin/failsafe/su",
				"/data/local/su",
				"/su/bin/su"
			}.GetEnumerator())
			{
				while (enumerator.MoveNext())
				{
					if (File.Exists(enumerator.Current))
					{
						return true;
					}
				}
			}
			string[] array = System.Environment.GetEnvironmentVariable("PATH").Split(':', StringSplitOptions.None);
			for (int i = 0; i < array.Length; i++)
			{
				if (File.Exists(Path.Combine(array[i], "su")))
				{
					return true;
				}
			}
			foreach (ActivityManager.RunningAppProcessInfo runningAppProcessInfo in ActivityManager.FromContext(Forms.Context).RunningAppProcesses)
			{
				if (runningAppProcessInfo.ProcessName.Contains("supersu") || runningAppProcessInfo.ProcessName.Contains("superuser"))
				{
					return true;
				}
			}
			return false;
		}

		// Token: 0x06000046 RID: 70 RVA: 0x00008240 File Offset: 0x00006440
		public bool IsProperUtcFormat()
		{
			return Build.Tags.Contains("test-keys");
		}

		// Token: 0x06000047 RID: 71 RVA: 0x00008251 File Offset: 0x00006451
		public bool IsMonthCorrect()
		{
			return (Forms.Context.ApplicationContext.ApplicationInfo.Flags & ApplicationInfoFlags.Debuggable) > ApplicationInfoFlags.None;
		}

		// Token: 0x06000048 RID: 72 RVA: 0x0000826C File Offset: 0x0000646C
		public bool IsCurrentMonth()
		{
			return Debug.IsDebuggerConnected;
		}

		// Token: 0x06000049 RID: 73 RVA: 0x00008274 File Offset: 0x00006474
		public bool IsItReallyCurrentYearAlready()
		{
			foreach (ApplicationInfo applicationInfo in Forms.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MetaData))
			{
				string packageName = applicationInfo.PackageName;
				if (packageName == "de.robv.android.xposed.installer" || packageName == "com.saurik.substrate" || packageName == "com.android.vending.billing.InAppBillingService.LUCK" || packageName == "com.android.vending.billing.InAppBillingService.CLON" || packageName == "com.android.vending.billing.InAppBillingService.COIN")
				{
					return true;
				}
			}
			return false;
		}
	}
}

Zaciemnianie kodu źródłowego Javy

Realizując stopniową rozbudowę mojego obfuskatora dla Javy – JObfuscatora, dodałem nową strategię do zaciemniania kodu.

Polega ona na tym, że dla każdej metody pobierane są wszystkie możliwe wartości liczbowe (o ile nie kłóci się to ze składnią języka Java), które następnie przenoszone są do tablic.

https://www.pelock.com/pl/jobfuscator/

To sprawia, że analiza jest trudniejsza, ponieważ wymaga wyszukiwania po indeksach w tabeli każdej wartości numerycznej. Jeśli kiedykolwiek analizowałeś skompilowany kod, wiesz, że wartości zapisane w tablicach i odczytywane po indeksach sprawiają trudności w zrozumieniu, jaka wartość kryje się faktycznie w tablicy.

Razem z tą nową funkcjonalnością, zostały zaktualizowane pakiety SDK obfuskatora (dla PHP i Pythona) oraz pakiety dla Windows i Linuxa.

https://www.pelock.com/pl/produkty/jobfuscator/api

https://www.pelock.com/pl/produkty/jobfuscator/pobierz

Przykładowy kod Javy po zaciemnianiu czy też obfuskacji:

////////////////////////////////////////////////////////////////////////////////
//
// An output from JObfuscator v1.01 - Bartosz Wójcik
//
// Website   : https://www.pelock.com/products/jobfuscator
// Version   : v1.01
// Params    : /MixCodeFlow /RenMethods /RenVars /ShuffleMethods /CryptStr /IntsToArray
// New lines : Windows (CRLF - \r\n)
// Date      : 29.07.2021
//
////////////////////////////////////////////////////////////////////////////////

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone {

    public static void main(String[] args) {
        int[] _a4joq_o8b_tUOPFS_YaaYlTU = { 25, 781851063, 0, 186043733, 308536662, 2099166591, 65535 };
        int sSlkJY4h_2s0_1o_z_PAe = _a4joq_o8b_tUOPFS_YaaYlTU[2];
        double pA6la_BMPq7e_41T_ = 0;
        double[] mXJrn_ywhskgJT0__c = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        sSlkJY4h_2s0_1o_z_PAe = _a4joq_o8b_tUOPFS_YaaYlTU[4];
        while (sSlkJY4h_2s0_1o_z_PAe != 2099166591) {
            switch(sSlkJY4h_2s0_1o_z_PAe) {
                case 904343992:
                    String var_798 = "";
                    int[] __HoJ2lBVb_XLauhwa = { 0x0053, 0x0075, 0x0063, 0x0071, 0x0068, 0x0066, 0x0078, 0x006B, 0x0028, 0x004D, 0x006F, 0x0081, 0x0075, 0x006E, 0x0082, 0x0078, 0x007F, 0x007F, 0x0032, 0x0050, 0x0034, 0x003A, 0x0044, 0x004D, 0x007E };
                    for (int DQYVAM = 0, g_pIlsvecdqKrqqcjTotyogafe = 0; DQYVAM < _a4joq_o8b_tUOPFS_YaaYlTU[0]; DQYVAM++) {
                        g_pIlsvecdqKrqqcjTotyogafe = __HoJ2lBVb_XLauhwa[DQYVAM];
                        g_pIlsvecdqKrqqcjTotyogafe -= DQYVAM;
                        var_798 += (char) (g_pIlsvecdqKrqqcjTotyogafe & _a4joq_o8b_tUOPFS_YaaYlTU[6]);
                    }
                    System.out.format(var_798, pA6la_BMPq7e_41T_);
                    sSlkJY4h_2s0_1o_z_PAe = _a4joq_o8b_tUOPFS_YaaYlTU[5];
                    break;
                case 1090387725:
                    pA6la_BMPq7e_41T_ = PknrplwspWzftaZejcnrx(mXJrn_ywhskgJT0__c);
                    sSlkJY4h_2s0_1o_z_PAe -= _a4joq_o8b_tUOPFS_YaaYlTU[3];
                    break;
                case 308536662:
                    double[] var_3179 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                    sSlkJY4h_2s0_1o_z_PAe -= -_a4joq_o8b_tUOPFS_YaaYlTU[1];
                    break;
            }
        }
    }

    public static double PknrplwspWzftaZejcnrx(double[] numArray) {
        int[] yqAA2v4_8wxeB__XX0 = { 239790208, 0, 2003280558, 1806986744, 1639573144, 144062149, 2, 1567196536, 220961938, 834696410 };
        int pVtkndrLgwazueYclthlx = yqAA2v4_8wxeB__XX0[1];
        double var_2075 = 0;
        int var_2673 = 0;
        double GOSLX = 0, BWSIM_HPGKSCO = 0;
        pVtkndrLgwazueYclthlx = yqAA2v4_8wxeB__XX0[2];
        while (pVtkndrLgwazueYclthlx != yqAA2v4_8wxeB__XX0[3]) {
            switch(pVtkndrLgwazueYclthlx) {
                case 1639573144:
                    var_2075 = GOSLX / var_2673;
                    pVtkndrLgwazueYclthlx += -yqAA2v4_8wxeB__XX0[8];
                    break;
                case 961954335:
                    for (double SQZGBDCX_NPDPSRK_NMYXHDJC : numArray) {
                        GOSLX += SQZGBDCX_NPDPSRK_NMYXHDJC;
                    }
                    pVtkndrLgwazueYclthlx = yqAA2v4_8wxeB__XX0[4];
                    break;
                case 1567196536:
                    pVtkndrLgwazueYclthlx -= -yqAA2v4_8wxeB__XX0[0];
                    break;
                case 1418611206:
                    for (double ZWGMm8X_5d_8buIiNh_x_h : numArray) {
                        BWSIM_HPGKSCO += Math.pow(ZWGMm8X_5d_8buIiNh_x_h - var_2075, yqAA2v4_8wxeB__XX0[6]);
                    }
                    pVtkndrLgwazueYclthlx = yqAA2v4_8wxeB__XX0[7];
                    break;
                case 144062149:
                    var_2673 = numArray.length;
                    pVtkndrLgwazueYclthlx ^= yqAA2v4_8wxeB__XX0[9];
                    break;
                case 2003280558:
                    BWSIM_HPGKSCO = 0.0;
                    pVtkndrLgwazueYclthlx = yqAA2v4_8wxeB__XX0[5];
                    break;
            }
        }
        return Math.sqrt(BWSIM_HPGKSCO / var_2673);
    }
}

Jeśli macie jakieś ciekawe pomysły na inne obfuskacje w kodzie źródłowym – dajcie znać, czekają na was darmowe kody aktywacyjne.