AutoIt Obfuscator v2.2

Claude Opus v4.8 trochę popracował nad analizą silnika mojego AutoIt Obfuscatora i znalazł masę błędów, a także znacznie przyspieszył jego działanie, zachowując 100% kompatybilność.

Obfuskacja kodu źródłowego AutoIt

Zmiany w v2.2

  • Znaczne przyspieszenie silnika — przepisane bajtowe operacje wejścia/wyjścia leksera, buforowana długość kodu źródłowego, wyszukiwanie symboli oparte na tablicach mieszających oraz lżejsze przechodzenie drzewa AST sprawiają, że obfuskacja jest znacznie szybsza, dając identyczny bajtowo wynik
  • Naprawiono awarię parsera w blokach Switch / Select — instrukcje umieszczone przed pierwszym Case zwracają teraz czysty błąd parsowania zamiast wyjątku krytycznego
  • Naprawiono parsowanie deklaracji z wieloma modyfikatorami np. Local Static Const $var = 1 (wcześniej uwzględniany był tylko pierwszy modyfikator)
  • Naprawiono generowanie nieprawidłowych nazw zmiennych, które mogą zaczynać się od cyfry, np. $9abc, co jest niedozwolone w AutoIt
  • Naprawiono błąd, w którym liczbowe wartości warunków Case / If były emitowane jako błędne zmienne np. $123456789 podczas mieszania przepływu kodu
  • Naprawiono błędne obliczanie poziomu zagnieżdżenia pętli dla ContinueLoop / ExitLoop przy wielu przebiegach mieszania przepływu kodu
  • Naprawiono szyfrowanie ciągów znaków: literał "0" jest teraz poprawnie szyfrowany, a nieudane szyfrowanie nie wstawia już uszkodzonej procedury deszyfrującej
  • Rozpoznawanie funkcji wbudowanych, makr i stałych bez uwzględniania wielkości liter (AutoIt jest niewrażliwy na wielkość liter), aby uniknąć błędnej zmiany nazw i podmiany stałych
  • Poprawiono kilka wartości stałych wbudowanych (flagi atrybutów plików) oraz usunięto zduplikowane / błędne wpisy z tablic wewnętrznych
  • Zwiększono odporność leksera: poprawna obsługa pustych plików, zakończeń linii typu LF oraz wykrywanie niezamkniętych ciągów znaków
  • Wyłączone opcje obfuskacji są teraz ściśle respektowane zamiast powracać do wartości domyślnych
  • Liczne wewnętrzne usprawnienia odporności i poprawki zgodności typów z PHP 8 w silniku obfuskacji

Obfuscator dostępny jest pod adresem:

https://www.pelock.com/pl/produkty/autoit-obfuscator

Wtyczka JObfuscator dla Maven

JObfuscator to mój obfuscator kodów źródłowych Javy. Największym minusem tego projektu zawsze było to, że wymagało używania zewnętrznych aplikacji, narzędzia online lub API do zabezpieczania kodów źródłowych. Do dzisiaj.

Obfuskator dla kodów źródłowych Java - JObfuscator.

Nie było to najwygodniejsze rozwiązanie. Przyznaję bez bicia.

Myślałem o tym problemie od dawna i najlepszym rozwiązaniem, jakie mi wpadło do głowy, była wtyczka dla systemu Maven. Maven to standardowe narzędzie do budowy aplikacji bazujących na języku Java.

Automatyczna obfuskacja

I tak powstała wtyczka JObfuscator Maven Plugin.

Wtyczka JObfuscatora działa na zasadzie przedwstępnego kompilatora.

Kod źródłowy przed kompilacją poddawany jest automatycznej obfuskacji i dopiero te pliki wysyłane są do kompilatora javac.

Jak z tego skorzystać? Najpierw musisz mieć projekt kompatybilny z systemem Maven i odpowiednio zmodyfikować plik konfiguracyjny pom.xml, który definiuje wszystkie zależności i ustawienia.

Taka minimalna konfiguracja może wyglądać następująco:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>11</maven.compiler.release>
    <jobfuscator.apiKey><!-- TWOJ-KLUCZ-API lub pozostaw puste dla WERSJI DEMO --></jobfuscator.apiKey>
    <jobfuscator.version><!-- np. 1.0.3 --></jobfuscator.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.pelock</groupId>
      <artifactId>jobfuscator-annotations</artifactId>
      <version>${jobfuscator.version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>com.pelock</groupId>
        <artifactId>jobfuscator-maven-plugin</artifactId>
        <version>${jobfuscator.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>obfuscate-sources</goal>
            </goals>
            <phase>generate-sources</phase>
          </execution>
        </executions>
        <configuration>
          <apiKey>${jobfuscator.apiKey}</apiKey>
          <enableCompression>true</enableCompression>
          <mixCodeFlow>true</mixCodeFlow>
          <renameVariables>true</renameVariables>
          <renameMethods>true</renameMethods>
          <shuffleMethods>true</shuffleMethods>
          <intsMathCrypt>true</intsMathCrypt>
          <cryptStrings>true</cryptStrings>
          <stringSplit>true</stringSplit>
          <intsToArrays>true</intsToArrays>
          <dblsToArrays>true</dblsToArrays>
          <dblsMathCrypt>true</dblsMathCrypt>
          <stringCharVault>true</stringCharVault>
          <intsFromDoubleMath>true</intsFromDoubleMath>
          <opaqueMixerChain>true</opaqueMixerChain>
          <complexifyBooleans>true</complexifyBooleans>
          <tryFinallyNoise>true</tryFinallyNoise>
          <selfCheck>true</selfCheck>
          <arrayIntCrypt>true</arrayIntCrypt>
          <arrayCharCrypt>true</arrayCharCrypt>
          <arrayDoubleCrypt>true</arrayDoubleCrypt>
          <arrayStringCrypt>true</arrayStringCrypt>
          <selfCheck>true</selfCheck>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
        <configuration>
          <release>${maven.compiler.release}</release>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Modyfikacja źródeł

Do poprawnej obfuskacji potrzebne jest oznaczenie (tzw. annotation) na poziomie klasy czy metody, że w ogóle chcemy dokonać obfuskacji, dzięki czemu można dokładnie kontrolować co ma być poddane obfuskacji, z jakimi parametrami, a co ma być pominięte.

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

//
// MUSISZ dodać adnotację @Obfuscate do
// kodu, aby włączyć obfuskację na poziomie
// całej klasy lub pojedynczej metody
//
@Obfuscate
class Ideone
{
    //@Obfuscate
    public static double calculateSD(double numArray[])
    {
        double sum = 0.0, standardDeviation = 0.0;
        int length = numArray.length;

        for(double num : numArray) {
            sum += num;
        }

        double mean = sum/length;

        for(double num: numArray) {
            standardDeviation += Math.pow(num - mean, 2);
        }

        return Math.sqrt(standardDeviation/length);
    }

    //
    // poszczególne strategie obfuskacji
    // mogą być włączane lub wyłączane
    // na poziomie całej klasy lub
    // pojedyncznej metody (domyślnie
    // wszystkie strategie obfuskacyjne
    // są włączone jeśli użyta jest
    // sama adnotacja @Obfuscate bez
    // wyszczególnionych strategii)
    //
    //@Obfuscate(
    //  ints_math_crypt = true,
    //  dbls_math_crypt = true,
    //  string_split = true,
    //  crypt_strings = true,
    //  string_char_vault = true,
    //  rename_methods = false,
    //  rename_variables = true,
    //  shuffle_methods = true,
    //  array_int_crypt = true,
    //  array_double_crypt = true,
    //  array_char_crypt = true,
    //  array_string_crypt = true,
    //  mix_code_flow = true,
    //  ints_from_double_math = true,
    //  opaque_mixer_chain = true,
    //  complexify_booleans = true,
    //  try_finally_noise = true,
    //  ints_to_arrays = true,
    //  dbls_to_arrays = true,
    //  self_check = true
    // )
    public static void main(String[] args) {

        double[] numArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        double SD = calculateSD(numArray);

        System.out.format("Standard Deviation = %.6f", SD);
    }
}

I tyle! Potem normalnie budujesz przez Mavena, a proces obfuskacji jest automatyczny.

Darmowy klucz aktywacyjny do potestowania F4FA-DCDD-58A7-9FE1

PS. A, zapomniałem dodać o nowej strategii obfuskacyjnej bazującej na refleksji w runtime i sprawdzaniu poprawności metod, pól i typów, tak żeby nie za łatwo dało się zdeobfuskować finalny kod.

JObfuscator v1.30 – Obfuskacja Javy

Zaktualizowałem swój obfuskator dla kodów źródłowych Javy, obsługujący język w wersji do 21.

Lista zmian jest ogromna i obejmuje dziesiątki bugfixów oraz mnóstwo nowych strategii obfuskacyjnych, częściowo kierowanych przeciwko LLMom (aby wypalić najwięcej tokenów przy próbach deobfuskacji). Do tego doszły nowe pakiety programistyczne dla PHP, Python, JS, Rust i C#.

Dodatkowo zaktualizowane zostały narzędzia desktopowe i CLI (np. masowe zabezpieczenia wielu plików).

JObfuscator Obfuskator dla kodów źródłowych Javy.

Na stronie znajdują się instrukcje, jak zdobyć darmowy kod aktywacyjny.

Dekoder haseł FTP dla Total Commandera

Z okazji rocznicy, okrągłej oczywiście — odkodowania 133541 (tak — stu trzydziestu trzech tysięcy pięciuset czterdziestu jeden) haseł przez mój dekoder online, publikuję kody źródłowe algorytmu dekodowania haseł FTP Total Commandera w formie paczek dla popularnych języków programowania.

Dostępne są paczki dla PHP, Pythona, JS i waszego ukochanego Rust. Kody źródłowe wszystkich paczek są dostępne na moim GitHubie.

https://www.pelock.com/pl/produkty/total-commander-odzyskanie-hasla-ftp/sdk

Więc, jakby ktoś potrzebował szybko wdrożyć dekodowanie w jakichś narzędziach forensic to polecam – za darmo to dobra cena 🙂

from binascii import hexlify

from total_commander_ftp_password import TotalCommanderPasswordDecoder

cipher_hex = "00112233445566778899aabbccddeeff"

decoder = TotalCommanderPasswordDecoder()
plain = decoder.decrypt_password(cipher_hex)

if plain is None:
    raise RuntimeError("Invalid ciphertext (bad hex, odd length, or too short).")

print(plain)
print(hexlify(plain).decode("ascii"))

Wszystkie pakiety mają dodatkowo wbudowane proste narzędzie CLI.

PS. Wspierajmy twórców oprogramowania

Polimorficzne szyfrowanie w Cursor

Wykorzystaj polimorficzne szyfrowanie stringów i plików StringEncrypt w narzędziach AI, takich jak edytor Cursor i inne bazujące na VS Code.

Szyfrować można stringi bezpośrednio w kodzie źródłowym oraz wskazane pliki:

Szyfrowanie stringów w StringEncrypt

Kod źródłowy rozszerzenia dostępny na GitHubie pod adresem

https://github.com/PELock/StringEncrypt-VSCode-Extension

StringEncrypt – polimorficzne szyfrowanie

Zachęcony pozytywnymi komentarzami dotyczącymi mojego projektu StringEncrypt, postanowiłem zaktualizować wszystko, co się dało i spełnić marzenia fanów tego niezwykłego rozwiązania.

Co to jest StringEncrypt?

To aplikacja do szyfrowania stringów i plików, generująca polimorficzny kod dekryptora. Ze stroną, WebAPI, dedykowanym rozszerzeniem do VSCode (ponad 4k instalacji) i aplikacją okienkową. Coś dla prawdziwych hakerów!

Silnik projektu jest wykorzystany w moich pozostałych narzędziach do masowego, polimorficznego szyfrowania stringów (w AutoIt Obfuscator i JObfuscator).

Szyfrować i generować kod można w następujących językach programowania:

Nowa wersja przynosi ogrom zmian, w tym najważniejsze – wsparcie dla wielu nowych języków programowania, tony bugfixów i najważniejsze – zagnieżdżone pętle polimorficzne.

Zagnieżdżone pętle polimorficzne

Jednym z zarzutów i wektorów ataku na StringEncrypt (w przypadku generatorów np. C/C++) było wykorzystanie narzędzi do ekstrakcji stringów, takich jak flare-floss. Narzędzie to potrafi przeskanować gotowy plik EXE, przeanalizować kod i wyciągnąć nawet zaszyfrowane stringi.

Zgłębiłem temat i cała filozofia tego rozwiązania bazuje na detekcji tzw. tight-loops, czyli ciasnych pętli. flare-floss potrafi rozpoznać krótkie pętle deszyfrujące i poprzez ich emulację wyciągnąć zaszyfrowane stringi, jeśli takie pętle zostały wykorzystane do ich odszyfrowania.

Rozwiązaniem tego problemu było wygenerowanie wielopoziomowo zagnieżdżonych pętli z polimorficznym kodem deszyfrującym co nie wpisuje się już w detekcję prostych pętli.

Przykładowy kod C

// encrypted with https://www.stringencrypt.com (v1.5.0) [C/C++]
// nie_do_wyciagniecia = "Nie tak prosto kolego!"
wchar_t nie_do_wyciagniecia[23] = { 0x0052, 0x71A4, 0x0690, 0x0900, 0x6830, 0x25B1, 0xF185, 0x0009,
                                    0x0074, 0x01D8, 0x82B4, 0x1FC7, 0x5830, 0xFD51, 0x4002, 0xD71F,
                                    0x0073, 0x01C0, 0x0690, 0x18C3, 0xA2D0, 0xD4DE, 0x4000 };

for (volatile unsigned int UCSag = 0, JkERu = 0; UCSag < 23; UCSag++)
{
	JkERu = nie_do_wyciagniecia[UCSag];
	for (unsigned int XptZj = 0; XptZj < 2; XptZj++)
	{
		JkERu = (((JkERu & 0xFFFF) >> (UCSag % 16)) | ((JkERu & 0xFFFF) << (16 - (UCSag % 16)))) & 0xFFFF;
		for (unsigned int HBXqW = 0; HBXqW < 2; HBXqW++)
		{
			JkERu = (((JkERu & 0xFFFF) >> 8) | ((JkERu & 0xFFFF) << 8)) & 0xFFFF;
			JkERu = ((JkERu ^ 0xFFFF) & 0xFFFF);
		}
	}
	for (unsigned int WAUOq = 0; WAUOq < 4; WAUOq++)
	{
		JkERu = ((JkERu & 0xFFFF) - 1) & 0xFFFF;
	}
	for (unsigned int HIbTj = 0; HIbTj < 2; HIbTj++)
	{
		JkERu = (((JkERu & 0xFFFF) << 8) | ((JkERu & 0xFFFF) >> 8)) & 0xFFFF;
		for (unsigned int Dswmt = 0; Dswmt < 2; Dswmt++)
		{
			JkERu = ((JkERu & 0xFFFF) + UCSag) & 0xFFFF;
			JkERu = (JkERu ^ (((JkERu & 0xFFFF) << 9) & 0xFFFF)) & 0xFFFF;
			JkERu = (JkERu ^ UCSag) & 0xFFFF;
		}
	}
	nie_do_wyciagniecia[UCSag] = JkERu;
}

I efekt pracy flare-floss, czyli brak znalezionych stringów metodą skanowania tight-loops:

Poprawki błędów

  • Poprawiłem formatowanie kodu Pythona, żeby było zgodne z regułami PEP.
  • Naprawiłem generator Ruby i sposób zapisywania zaszyfrowanych znaków.
  • Poprawki dotknęły też generatora AutoIt.

6 nowych instrukcji szyfrujących VM:

  • neg — klasyczna negacja (nie not)
  • swap — zamiana bitów w szyfrowanych 8 bitowych bajtach oraz 16 bitowych wartościach dla unicode
  • c_rol / c_ror — rotacje bitowe z użyciem klucza, którym jest licznik pętli
  • xor_shr / xor_shl — szyfrowanie z miksowaniem XOR, tak jak w algorytmach MurmurHash i SplitMix

Kompletna liczba instrukcji VM wykorzystanych do szyfrowania obecnie wynosi 17 (z 11), co jeszcze bardziej urozmaica wygenerowany kod wyjściowy.

Nowe podświetlanie składni

Strona zyskała nowe podświetlanie składni wygenerowanego kodu, porzucony został stary highlighter Syntax Highlighter na rzecz biblioteki Prism.

Pakiety SDK

Przykłady automatyzacji z poziomu Pythona

#!/usr/bin/env python

###############################################################################
#
# String Encrypt WebApi interface usage example.
#
# In this example we will encrypt sample string with default options.
#
# Version        : v1.0.1
# Language       : Python
# Author         : Bartosz Wójcik
# Project page   : https://www.stringencrypt.com
# Web page       : https://www.pelock.com
#
###############################################################################

#
# include StringEncrypt module
#
from stringencrypt import StringEncrypt

#
# if you don't want to use Python module, you can import it directly from the file
#
#from stringencrypt.stringencrypt import StringEncrypt

#
# create StringEncrypt class instance (we are using our activation code)
#
myStringEncrypt = StringEncrypt("YOUR-API-KEY-HERE") # leave empty for demo mode

#
# encrypt a string using all the default options
#
result = myStringEncrypt.encrypt_string("Hello, world!", "label_encrypted")

#
# result[] array holds the encryption results as well as other information
#
# result["error"] (int) - error code
# result["source"] (string) - decryptor source code
# result["expired"] (boolean) - expiration flag
# result["credits_left"] (int) - number of credits left
# result["credits_total"] (int) - initial number of credits

if result and "error" in result:

    # display source code of the decryption code
    if result["error"] == StringEncrypt.ErrorCodes.ERROR_SUCCESS:
        print(result["source"])
    else:
        print(f'An error occurred, error code: {result["error"]} ({result["error_string"]})')

else:
    print("Something unexpected happen while trying to encrypt the string.")

Pełna wersja

Klucz można zakupić za śmieszne pieniądze lub za darmo go zdobyć, pisząc o tym rozwiązaniu na social mediach, artykułach, na forach czy gdziekolwiek się da z linkiem do strony projektu i krótkim opisem oraz screenshotem. Niewiele, prawda?

Na koniec chciałbym jeszcze raz podziękować wszystkim fanom, którzy w dobie AI zmotywowali mnie do tak dużych aktualizacji – bez waszych słów wsparcia by mi się nie chciało aż tak 🙂