Jeśli jesteś właścicielem tej strony, możesz wyłączyć reklamę poniżej zmieniając pakiet na PRO lub VIP w panelu naszego hostingu już od 4zł!
2014-03-01    Programowanie, Crack me, Kod, C++, Asm, RE, Hacking

Tak jak wspominałem w poprzednim poście, planuję obecnie poszerzyć swoją wiedze z tematu RE na podstawie różnego rodzaju crack me. Dziś kolejny post z tej serii. Zaprezentuję proste crack me, analizuję, sposoby złamania oraz kilka metod na napisanie keygenu.

Jest to crack me ze stronki crackmes. Po odpaleniu uruchamia sie konsolka i prosi o podanie "name" oraz "key". Po wrzuceniu do IDA można się dowiedzieć, że jest to standardowe CUI, bez jakikolwiek packerów/protectorów. Kolejno po znalezieniu funkcji main(mam nadzieję, że nie macie z tym żadnego problemy), ukazuje się następujący algorytm szyfrowania:
crack I od razu widać sposoby na złamanie tego crack me. Pierwszy z nich to edycja jz na jnz. Inny sposób to ustawienie breakpointu(INT3 0xCC) przy pomocy debbugera(np. OllyDBG) i podejrzenie wartości, które są porównywalne. Jednak, głównym zadaniem jest napisanie keygenu.

W C/C++ nie jest to takie trywialne jak można byłoby się tego spodziewać. W programie zostały wykorzystane rejestry FPU, jednakże w tych językach nie ma takich zmiennych(80-bitowych zmiennoprzecinkowych). Wymaga to zaimplementowanie dwóch metod konwersji. Posłużyłem się kodem zaimplementowanym przez Malcolm Slaney i Ken Turkowski, który pozwala nam na swobodne konwertowanie pomiędzy liczbami zmiennoprzecinkowymi. Oto mój KeyGen w C++:
 
#include <iostream>
#include <math.h>
 
using namespace std;
 
#ifndef HUGE_VAL
    #define HUGE_VAL HUGE
#endif
 
#define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1)
#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
 
void ConvertToIeeeExtended(double num, char* bytes)
{
    int sign;
    int expon;
    double fMant, fsMant;
    unsigned long hiMant, loMant;
 
    if (num < 0)
    {
        sign = 0x8000;
        num *= -1;
    }
    else
        sign = 0;
 
    if (num == 0)
    {
        expon = 0; hiMant = 0; loMant = 0;
    }
    else
    {
        fMant = frexp(num, &expon);
        if ((expon > 16384) || !(fMant < 1))
        {
            expon = sign|0x7FFF; hiMant = 0; loMant = 0;
        }
        else
        {
            expon += 16382;
            if (expon < 0)
            {
                fMant = ldexp(fMant, expon);
                expon = 0;
            }
            expon |= sign;
            fMant = ldexp(fMant, 32);
            fsMant = floor(fMant);
            hiMant = FloatToUnsigned(fsMant);
            fMant = ldexp(fMant - fsMant, 32);
            fsMant = floor(fMant);
            loMant = FloatToUnsigned(fsMant);
        }
    }
 
    bytes[0] = expon >> 8;
    bytes[1] = expon;
    bytes[2] = hiMant >> 24;
    bytes[3] = hiMant >> 16;
    bytes[4] = hiMant >> 8;
    bytes[5] = hiMant;
    bytes[6] = loMant >> 24;
    bytes[7] = loMant >> 16;
    bytes[8] = loMant >> 8;
    bytes[9] = loMant;
}
 
double ConvertFromIeeeExtended(unsigned char* bytes)
{
    double f;
    int expon;
    unsigned long hiMant, loMant;
 
    expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
    hiMant    =    ((unsigned long)(bytes[2] & 0xFF) << 24)
            |    ((unsigned long)(bytes[3] & 0xFF) << 16)
            |    ((unsigned long)(bytes[4] & 0xFF) << 8)
            |    ((unsigned long)(bytes[5] & 0xFF));
    loMant    =    ((unsigned long)(bytes[6] & 0xFF) << 24)
            |    ((unsigned long)(bytes[7] & 0xFF) << 16)
            |    ((unsigned long)(bytes[8] & 0xFF) << 8)
            |    ((unsigned long)(bytes[9] & 0xFF));
 
    if (expon == 0 && hiMant == 0 && loMant == 0)
        f = 0;
    else
    {
        if (expon == 0x7FFF)
            f = HUGE_VAL;
        else
        {
            expon -= 16383;
            f  = ldexp(UnsignedToFloat(hiMant), expon -= 31);
            f += ldexp(UnsignedToFloat(loMant), expon -= 32);
        }
    }
 
    if (bytes[0] & 0x80)
        return -f;
    else
        return f;
}
 
int main()
{
    string serial, name;
 
    cout << "Pass your name: ";
    cin >> name;
 
    long long int l = name.length();
 
    long long int a = l * 0x875CD;
    long long int b = 0x51EB851F;
    long long int c = a * b;
 
    c >>= 37;
    c *= -0x370;
 
    unsigned char w[10];
 
    ConvertToIeeeExtended((double)c,(char *)w);
    double d = (unsigned int)(ConvertFromIeeeExtended(w));
    int *r = (int *)(&d);
 
    cout << "Your key: "<< r[0] << "-x019871" << endl;
 
    return 0;
}
 
Analogicznie, jednakże w FASM:
 
format PE Console 4.0
 
include "win32a.inc"
 
	;get name, max size: 50 characters
	call	label1
	db 	"Pass your name: ", 0
	label1:
	call 	[printf]
	add 	esp, 4
	sub 	esp, 50
	push 	esp
	call 	label2
	db 	"%s", 0
	label2:	
	call 	[scanf]
	add 	esp, 8
 
	;get size of name
	push 	esp
	call    [strlen]
	add 	esp, 4
 
	;code
	mov     edx,  eax
	imul    edx, 875CDh
	mov     eax, 51EB851Fh
	mul     edx
	mov     eax, edx
	shr     eax, 5
	imul    eax, 0FFFFFC90h
	mov     edx, 0	
	mov 	dword [var], eax;
	fild    qword [var]	
	fstp    qword [var]
 
	;show key
	push 	dword[var]
	call	label3
	db 	"Your key: %i-x019871", 0
	label3:
	call 	[printf]
	add 	esp, 8
 
	add 	esp, 50
	mov 	eax, 0
	ret
 
	var dq 0
 
data import
	library msvcrt, 'MSVCRT.DLL'
	import msvcrt,\
		printf, 'printf',\
	 	scanf , 'scanf',\			
	 	strlen, 'strlen'			
end data 
 
Co może zaskoczyć kod w ASM jest zdecydowanie krótszy od tego w C++. Tak jak wcześniej wspominałem jest to spowodowane tym, że w C++ nie ma zmiennych typu "80-bit extended precision floating-point", zaś w ASM mamy do dyspozycji rejestry FPU, które właśnie są tego rozmiaru.
Dodaj komentarz:
Nick:
URL(opcjonalnie):