Scraper do Steam Store

Bardzo ciekawy artykuł prezentujący analizę kodu HTML oraz Java Script w Steam Store i stworzenia scrapera w C# do wyciągnięcia danych ze struktur strony, tematy bardzo bliskie reversingowi.

Chyba jeszcze nie dostali C&D od żadnej firmy, więc na razie możemy sobie poczytać o technicznych aspektach scrapingu, bo o prawnych to chyba za dużo nie wiedzą (jeszcze).

https://www.malwarebytes.com/blog/news/2023/01/untraceable-surveillance-firm-sued-for-scraping-facebook-and-instagram-data

https://news.ycombinator.com/item?id=12345952

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;
		}
	}
}

Patcher dla C#

Czekałem na koniec świata, ale jakoś go nie było, tymczasem mała klasa w C# do patchowania plików z paroma przydatnymi opcjami jak np. patchowanie stringow Delphi.

using System;
////////////////////////////////////////////////////////////////////////////////
//
// Simple C# Patcher Class for Windows (Complex)
//
// You can patch:
//
// - byte arrays
// - single bytes
// - integers
// - strings
// - delphi strings
//
// Language : C#
// Author   : Bartosz Wójcik
// Website  : https://www.pelock.com
//
////////////////////////////////////////////////////////////////////////////////

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace NetPatcher
{
    class Patcher
    {
        public FileStream inputFile;

        public Patcher()
        {
        }

        ~Patcher()
        {
            CloseFile();
        }

        public bool OpenFile(string filePath)
        {
            try
            {
                inputFile = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);

                return inputFile == null ? false : true;
            }
            catch (Exception e)
            {
                return false;
            }

            return false;
        }

        public void CloseFile()
        {
            if (inputFile != null)
            {
                inputFile.Close();
            }

            inputFile = null;
        }

        public long PatchArray(long fileOffset, byte[] patchBytes)
        {
            inputFile.Seek(fileOffset, SeekOrigin.Begin);
            inputFile.Write(patchBytes, 0, patchBytes.Length);

            return inputFile.Position;
        }

        public long PatchByte(long fileOffset, byte patchByte)
        {
            inputFile.Seek(fileOffset, SeekOrigin.Begin);
            inputFile.WriteByte(patchByte);

            return inputFile.Position;
        }

        public long PatchString(long fileOffset, string patchString)
        {
            System.Text.Encoding asciiEncoding = System.Text.Encoding.ASCII;
            byte[] encodedPatchString = asciiEncoding.GetBytes(patchString);

            return PatchArray(fileOffset, encodedPatchString);
        }

        public long PatchDelphiString(long fileOffset, string patchString)
        {
            System.Text.Encoding asciiEncoding = System.Text.Encoding.GetEncoding(1250);
            
            byte[] encodedPatchString = asciiEncoding.GetBytes(patchString);

            PatchByte(fileOffset, (byte)encodedPatchString.Length);
            PatchArray(fileOffset + 1, encodedPatchString);

            return inputFile.Position;
        }

        public long PatchInt32(long fileOffset, Int32 patchInt32)
        {
            byte[] encodedInt32 = BitConverter.GetBytes(patchInt32);
            PatchArray(fileOffset, encodedInt32);

            return inputFile.Position;
        }

        public long PatchFill(long fileOffset, long Length, byte patchByteFill)
        {
            while (Length-- != 0)
            {
                PatchByte(fileOffset++, patchByteFill);
            }

            return inputFile.Position;
        }

    }
}

Źródła dostępne także na https://github.com/PELock/Simple-DotNet-Patcher-Class