Kompresja aplikacji .NET

.netshrink v2.2

.netshrink to kompresor (tzw. exe-packer) aplikacji .NET-owych, wykorzystujący kompresję LZMA, DLL binder pozwalający scalić aplikację oraz jej dodatkowe moduły DLL do jednego pliku EXE, .netshrink pozwala także na ochronę aplikacji na hasło przed uruchomieniem.

W zaktualizowanej wersji poprawione zostało uruchamianie skompresowanych aplikacji ze ścieżek UNC (zasoby sieciowe) z linii komend i zachowywanie oryginalnej architektury CPU dla skompresowanych plików.

Strona domowa:
https://www.pelock.com/products/netshrink

Wersje demonstracyjną można ściągnąć z:

Setup:
https://www.pelock.com/download/netshrink.exe (701 kB)

Archiwum zip:
https://www.pelock.com/download/netshrink.exe (415 kB)

.netshrink v2.1 – kompresor i DLL binder dla aplikacji .NET

.netshrink v2.1

Właśnie ukazała się nowa wersja mojego małego narzędzia do kompresji aplikacji .NET.

.netshrink to kompresor (tzw. exe-packer)  aplikacji .NET-owych, wykorzystujący kompresję LZMA.

DLL Binder

W nowej wersji została dodana opcja dołączania bibliotek dynamicznych DLL do aplikacji, dzięki czemu można swoją aplikację i jej pomocnicze biblioteki skompresować do pojedynczego pliku wykonywalnego.

Wykrywanie narzędzi do łamania aplikacji

Zaktualizowana wersja pozwala również skompresowanym aplikacjom na wykrywanie popularnych narzędzi do łamania i rozpakowywania oprogramowania poprzez stałe monitorowanie systemu na ich obecność, w razie ich wykrycia, skompresowana aplikacja zostanie zamknięta.

Ochrona na hasło

.netshrink pozwala także na zaszyfrowanie aplikacji hasłem, bez poprawnego hasła nie można uruchomić skompresowanej aplikacji. Zabezpieczenie na hasło wykorzystuje weryfikację bazującą na funkcji skrótu SHA256 i szyfrowanie danych algorytmem AES / Rijndael z kluczem 256 bitowym bazującym na wartości hash z hasła.

.netshrink kompresor i DLL binder dla aplikacji .NET

Nowości obejmują także:

  • tworzenie kopii zapasowej kompresowanych aplikacji
  • tworzenie skryptu wsadowego .BAT pozwalającego na szybkie przywrócenie oryginalnego pliku z kopii zapasowej
  • zachowywanie oryginalnych atrybutów kompresowanego assembly (nazwa aplikacji, dane firmy etc.)

Strona domowa:
https://www.pelock.com/products/netshrink

Wersje demonstracyjną można ściągnąć z:

Setup:
https://www.pelock.com/download/netshrink.exe (692 kB)

Archiwum zip:
https://www.pelock.com/download/netshrink.exe (405 kB)

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

Wykrywanie VMware w C#

Jak wykryć VMware

VMware jest często wykorzystywane w celu uruchamiania złamanego oprogramowania lub aby ominąć zabezpieczenia autora oprogramowania.

Często to wygląda tak, że kupujący po zakupie oprogramowania otrzymuje licencję zablokowaną na sprzętowy identyfikator jednego komputera, który okazuje się być obrazem VMware i tak później oprogramowanie jest dystrybuowane (czytaj sprzedawane) dalej.

Wersji natywnych, wykorzystujących systemowe różnice w działaniu na wykrywanie VMware są dziesiątki, chciałem przedstawić krótki snippet w C# do wykrywania może nie samego VMware, ale narzędzi VMware Tools, które instaluje się na wirtualnej maszynie, aby zapewnić komfortową pracę między wirtualną maszyną, a maszyną użytkownika (np. zapewnić funkcje drag&drop plików etc.), jest to zwykle jedna z pierwszych czynności jakie robi się po zainstalowaniu systemu na wirtualnej maszynie.

using System;
using System.Runtime.InteropServices;

// potrzebne importy
[DllImport("KERNEL32.dll", SetLastError=true)]
static extern IntPtr OpenEvent(uint dwDesiredAccess, bool bInheritHandle, string lpName);

[DllImport("KERNEL32.dll", SetLastError=true)]
static extern IntPtr CreateMutex(IntPtr lpMutexAttributes, bool bInitialOwner, string lpName);

[DllImport("KERNEL32.dll")]
private extern static Boolean CloseHandle(IntPtr handle);

public const int ERROR_ALREADY_EXISTS = 183;

/// 
/// wykrywanie obecności VMWare po zainstalowanych narzędziach VMWare Tools
/// na wirtualnej maszynie
/// 
/// true jeśli wykryto zainstalowane narzędzia VMWare Tools
public static bool IsVMWare()
{
    // otwórz obiekt "VMwareDnDManagerEvent"
    IntPtr eventHandle = OpenEvent(0x001F0003, false, "VMwareDnDManagerEvent");

    // jeśli udało się otworzyć event, to znaczy, że obecne są narzędzia VMWare Tools
    if (eventHandle != IntPtr.Zero)
    {
        CloseHandle(eventHandle);
        return true;
    }

    // utwórz mutex o nazwie wykorzystywanej przez VMWare Tools
    IntPtr mutexHandle = CreateMutex(IntPtr.Zero, false, "VMwareGuestDnDDataMutex");

    // zamknij uchwyt mutexa
    if (mutexHandle != IntPtr.Zero)
    {
        CloseHandle(mutexHandle);
    }

    // jeśli mutex o tej nazwie istnieje, to znaczy, że są obecne narzędzia VMware Tools
    if (Marshal.GetLastWin32Error() == ERROR_ALREADY_EXISTS)
    {
        return true;
    }

    return false;
}

Wykrywanie dotyczy VMware w wersji 7, w wersji 8 należy już zastosować inne nazwy obiektów ze względu na zaktualizowane VMware Tools, jeśli ktoś jest zainteresowany wykrywaniem VMware w najnowszych wersjach (i nie tylko tego środowiska, np. VirtualBox etc.), proszę o kontakt.

Sprzętowy identyfikator w C#

Sprzętowy identyfikator komputera

Krótki snippet w C# dla .NET 4 (jak skorygowali mnie koledzy, zadziała także od .NET 2) pobierający sprzętowy identyfikator maszyny, wykorzystując interfejs WMI, podobne algorytmy stosowane są w aplikacjach, których klucze licencyjne przypisywane są do jednego komputera

using System.Management;
using System.Security.Cryptography;

/// 
/// pobiera sprzętowy identyfikator maszyny korzystając z interfejsu WMI
/// 
/// identyfikator sprzętowy w formie hasha SHA1
public static String hardwareId()
{
    // tymczasowy string
    String tempString = "";

    // "baza danych", z której pobieramy informacje o systemie
    String wmiScope = @"root\CIMV2";

    // tabela zapytań WMI i pobieranych elementów
    String[,] wmiQueries = new String[,]
    {
        { "SELECT ProcessorId FROM Win32_Processor", "ProcessorId" },
        { "SELECT Caption FROM Win32_Processor", "Caption" },
        { "SELECT Manufacturer FROM Win32_Processor", "Manufacturer" },
        { "SELECT Caption FROM Win32_BIOS", "Caption" }
    };

    // wywołujemy kolejne zapytania WMI
    for (int i = 0; i < wmiQueries.GetLength(0); i++)
    {
        try
        {
            // wykonaj zapytanie WMI
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiScope, wmiQueries[i, 0]);

            // zbuduj string z kolejnymi elementami odpowiedzi po zapytaniach WMI
            foreach (ManagementObject obj in searcher.Get())
            {
                tempString += obj[wmiQueries[i, 1]].ToString();
            }
        }
        catch (ManagementException exception)
        {
            // loguj błąd odczytu danych z WMI
        }
    }

    // generuj hash SHA1 ze wszystkich zebranych danych WMI
    SHA1 hash = SHA1CryptoServiceProvider.Create();

    // ciąg znaków zamień na tablicę bajtów
    byte[] plainTextBytes = Encoding.ASCII.GetBytes(tempString);

    // oblicz hash SHA1
    byte[] hashBytes = hash.ComputeHash(plainTextBytes);

    // konwertuj hash SHA1 na postać tekstową (i usuń zbędne znaki "-")
    tempString = BitConverter.ToString(hashBytes).Replace("-", "");

    return tempString;
}

Więcej o WMI na CodeProject:
http://www.codeproject.com/KB/system/WQLByExample.aspx