Cel	: Asystent 1.8.5.183
Opis	: PIM
URL	: http://www.al.insite.com.pl/asystent
Toolz	: IDA

Program napisany jest w Delphi 5, po uruchomieniu niezarejestrowanej
wersji nie ma dostepu do wszystkich funkcji programu.Gdy zamkniemy
okno glowne aplikacji pojawi sie dialog, gdzie mamy mozliwosc
wprowadzenia kodu rejestracyjnego.Zauwazmy tez, ze obok edita gdzie
mamy wpisac serial znajduje sie tez jakis numer, ktory nalezy wyslac
autorowi i po uiszczeniu oplaty (35zl) otrzymamy numer rejestracyjny,
mozna sie domyslec, ze ten kod jest inny dla roznych maszyn w
zaleznosci od konfiguracji systemu etc.Znalezienie samego numeru
seryjnego to bulka z dzemem, ale to nie jest naszym celem(przynajmniej
moim), po wpisaniu seriala standard dla delphi bpx hmemcpy i mamy
poprawny serial po prostym strcmp, ale skad wzial sie ten poprawny
bo z choinki chyba nie spadl?Sledzenie zmian w pamieci poprzez
stosowanie bpm-ow do niczego mnie nie doprowadzilo ale zauwazylem,
ze po zarejestrownaiu programu, numer rejestracyjny jest zapisywany
do pliku asystent.ini, przyklad:

[Asystent]
Wlasciciel=bart
Klucz_Nowy=154423

Program na starcie musi wiec sprawdzac czy dane rejestracyjne zapisane
w pliku ini byly poprawne i potem decyduje czy uruchomi sie jako
full czy trial.Po wladowaniu pliku do IDA, zaczalem szukac funkcji,
ktore bylyby odpowiedzialne za odczytywanie danych z rejestru,
ciskamy ctrl-p i wedrujemy przez spis funkcji, moja uwage zwrocila
funkcja _TForm_Main_INIOdczytaj, klikamy na nia i widzimy kod:

004B0413 mov  ecx, offset _str_Klucz_Nowy_0.Text
004B0418 mov  edx, offset _str_Asystent_2.Text
004B041D mov  eax, [ebp+var_8]
004B0420 mov  esi, [eax]
004B0422 call dword ptr [esi]
004B0424 mov  eax, [ebp+var_1C]
004B0427 lea  edx, [ebp+var_18]
004B042A call @Trim
004B042F mov  edx, [ebp+var_18]
004B0432 lea  eax, [ebx+338h]
004B0438 call sub_403C9C
004B043D lea  eax, [ebp+var_24]
004B0440 call sub_0049ACB8       <-- tutaj nastepuje odczytanie klucza
004B0445 mov  eax, [ebp+var_24]
004B0448 lea  edx, [ebp+var_20]
004B044B call sub_49AB94         <-- generacja magicznego stringa
004B0450 mov  edx, [ebp+var_20]
004B0453 lea  eax, [ebx+330h]
004B0459 call sub_403C9C
004B045E lea  edx, [ebp+var_28]
004B0461 mov  eax, [ebx+330h]
004B0467 call sub_49AC24         <-- generacja seriala
004B046C mov  edx, [ebp+var_28]
004B046F lea  eax, [ebx+334h]
004B0475 call sub_403C9C
004B047A mov  eax, [ebx+338h]
004B0480 mov  edx, [ebx+334h]
004B0486 call sub_403FD8         <-- porownanie obliczonego seriala i
004B048B jz   short loc_4B04B1       zapisanego w ini

Najpierw zobaczmy jak generowany jest ten magiczny kod ktory ma
byc indywidualny dla naszego systemu, zagladamy do sub_49AB94:

0049ABBC mov  eax, [ebp+var_4]
0049ABBF call magic              <-- wazne
0049ABC4 mov  ebx, eax           <-- x=y ;
0049ABC6 mov  eax, ebx           <-- bez sensu ale mniejsza o to
0049ABC8 imul ebx                <-- x*=y ;
0049ABCA imul ebx                <-- x*=y ;
0049ABCC mov  edx, eax           <-- z=y ;
0049ABCE shl  eax, 3             <-- y*=8 ;
0049ABD1 sub  eax, edx           <-- y-=z ;
0049ABD3 mov  ecx, 0F4240h
0049ABD8 cdq
0049ABD9 idiv ecx                <-- y%=0xF4240 ;
0049ABDB mov  eax, edx           <-- reszta z dzielenia do y
0049ABDD cdq                     <-- czysc edx, jesli eax < 0x80000000
                                     inaczej ustaw edx na 0xFFFFFFFF
                                     (kopiowanie najbardziej znaczacego bitu
                                     z eax na wszystkie pozycje edx)
0049ABDE xor  eax, edx           <-- y^=z ;
0049ABE0 sub  eax, edx           <-- y-=z ;
0049ABE2 inc  eax                <-- y++ ;
0049ABE3 lea  edx, [ebp+var_8]
0049ABE6 call @IntToStr          <-- jak sama nazwa mowi

Procka mixuje pewna wartosc wyliczona w magic, zamienia ja na
stringa i jak potem sie przekonamy wlasnie to jest ten kod
ktory pojawia sie w dialogu rejestracyjnym.Warto by looknac
co robi procka magic:

0049AB1D mov  eax, [ebp+var_4]   <-- wskaznik do name(reg)
0049AB20 call unknown_libname_19
0049AB25 test eax, eax           <-- sprawdz rozmiar
0049AB27 jle  short loc_49AB3C   <-- wyjdz jesli <=0
0049AB29 mov  edx, 1             <-- inicjalizacja edx
0049AB2E 
0049AB2E loc_49AB2E:
0049AB2E mov  ecx, [ebp+var_4]   <-- wskaznik do name
0049AB31 movzx ecx, byte ptr [ecx+edx-2] <-- pobierz bajt name[t-1]
0049AB36 add  ebx, ecx           <-- suma bajtow
0049AB38 inc  edx
0049AB39 dec  eax
0049AB3A jnz  short loc_49AB2E

Procka liczy sume bajtow wartosci name zapisanej w rejestrze
pod kluczem:
HKEY_CURRENT_USER\Software\Microsoft\MS Setup (ACME)\User Info

ale trzeba zwrocic uwage, ze sumowane sa wszystkie bajty name
oprocz ostatniego, ale nie mowie o 00h, tylko jesli mamy name
np. 'frog'(hi :) to sumowane sa bajty 'f'+'r'+'o' bez 'g'.Czyli
wiemy juz jak generowany jest ten magiczny kod, teraz czas
zobaczyc jak generowany jest poprawny serial, idziemy pod
sub_49AC24:

0049AC55 mov  eax, [ebp+var_8]   <-- wskaznik do magicznej wartosci
                                     wygenrowanej o pietro wyzej
0049AC58 call sub_4089D8         <-- funkcja str2int, zamienia
                                     ciag dec string do formatu hex
                                     i zapisuje wynik w eax
0049AC5D mov  ebx, eax           <-- x=magic;
0049AC5F mov  eax, ebx
0049AC61 add  eax, eax           <-- magic+=magic ; (lub *2, albo << 1)
0049AC63 lea  eax, [eax+eax*2]   <-- magic=(magic << 1)+magic; (lub magic*3)
0049AC66 add  eax, 7CFh          <-- magic+=0x7CF ;
0049AC6B mov  ecx, 0F4240h
0049AC70 cdq
0049AC71 idiv ecx
0049AC73 mov  ebx, edx           <-- serial=magic % 0xF4240 ;
0049AC75 lea  edx, [ebp+var_C]
0049AC78 mov  eax, ebx
0049AC7A call @IntToStr


Czyli spoko, algo widac jak na dloni mozna zabrac sie za robienie
keygena, jesli bedziemy pisali go w win32asm to mozna zerznac
cale algo nie obawiajac sie, ze wystapi jakis blad, ale jesli
chcielibysmy napisac keyg w c to nalezy zwrocic uwage na pare
szczegolow.Zauwazmy, ze algorytm uzywa instrukcji dzielenia idiv,
stosowana jest ona do dzielenia liczb ze znakiem i tak jesli np.
mamy dzielna ze znakiem i uzyjemy instrukcji idiv to reszta
ktora otrzymamy w edx otrzyma znak dzielnej(jednyki).Aby miec
pewnosc, ze kompilator przetlumaczy nasz kod podobnie jak wyglada
oryginalny algorytm nalezy zadeklarowac zmienne jak signed np.

signed int magic ;

Mozemy miec wtedy pewnosc, ze gdyby jakims cudem dzielnik zawieral
znak to dzielenie odbedzie sie dokladnie tak samo jak w programie
asystent, jesli uzylibysmy zmiennej nieoznaczonej:

unsigned int magic ;

wtedy w kodzie zamiast instrukcji idiv generowana jest instrukcja
div ktora zafalszowalaby wynik jesli dzielnik zawieralby znak.
Warto tez powiedziec troche o optymalizacji, wiem w c to prawie
niemozliwe na poziomie kodu, dzisiaj istnieje na rynku wiele
kompilatorow ktore zoptymalizuja kod za nas, ale tez nie sa
w stanie idealnie tego zrobic.Mnozenie i dzielenie przez potegi
dwojki, zamiast pisac np.

magic = magic * 2 ;

co da w kodzie programu instrukcje mul/imul ktorej wykonywanie
trwa o wiele dluzej(max 11 cykli procesora, dodatkowo nie paruje
sie z zadnymi instrukcjami) niz przesuwanie bitow(max 4 cykle),
a wiec zamiast mnozenia wystarczy dac:

magic = magic << 1 ;

Wiemy:

x << 1 == x * 2
x << 2 == x * 4
x << 3 == x * 8
...
i w druga strone:

x >> 1 == x / 2
x >> 2 == x / 4
x >> 3 == x / 8
...

Tu wystepuje pewien wyjatek, mnozenie * 2 lepiej zrealizowac
poprzez dodawanie:

magic = magic + magic ;

Operacja dodawania jest szybsza od przesuwania bitow.

Mnozenie przez liczby rozne od poteg dwojki mozna tez lepiej
zrealizowac, zamiast uzywac mnozenia jakiejs liczby przez 5
mozna uzyc takiego schematu:

5=4+1 to wie kazde dziecko, 4 to potega dwojki, w asm tego typu
mnozenie mozna bardzo prosto zrealizowac uzywajac instrukcji lea
ktora:

mov eax,[wartosc]
lea eax,[eax*4+eax]

W C mozna to zrealizowac w bardzo podobny sposob:

wartosc = ( wartosc << 2 ) + wartosc ;

co nie gwarantuje, ze w kodzie zostanie wstawiona instrukcja lea
ale zawsze zmniejszy czas wykonywania programu przy intensywnych
obliczeniach np. jakims brute force zapuszczonym na unixie.Jeszcze
jeden przyklad, chcemy np. przemnozyc jakas wartosc przez 7, 7
blizsza jest 8 a od bajtla wiemy, ze 7=8-1 wiec:

wartosc = ( wartosc << 3 ) - wartosc ;

Jesli chcemy np. uzyskac reszte z dzielenia liczby przez 2, zamiast
operatora modulo(%) ktory w kodzie wygeneruje czasochlonna
instrukcje div/idiv lepiej zastosowac instrukcje iloczynu logicznego
(AND &), czyli np. zamiast:

magic = magic % 2 ;

lepiej uzyc:

magic = magic & 1 ;

Dobra juz chyba zanudzilem was na smierc a przeciez wiekszosc juz
o tych reczach wie, wiec do zobaczenia nastepnym razem.

===============================================================

//
// Asystent 1.8.5.183 keygen
//
// 21.02.2001
//
// bart^CrackPl
// cryogen@box43.pl

#include <stdio.h>
#include <conio.h>

unsigned char magic[64];
signed int x;

int main()
{
	printf("Asystent 1.8.5.183 keygen by bart^CrackPl\n");
	printf("=========================================\n");
	printf("Wprowadz kod : ");
	gets(magic);

	if ( strlen(magic) )		// jesli user cos wprowadzil
	{				// licz sn

	x=atoi(magic);			// zamien na int stringa

	x = ( x << 3 ) - (x << 1 ) ;	// (x*8)-(x*2)=x*6 hehe

	x += 0x7CF ;			// dodaj magiczna wartosc

	x %= 0x0F4240 ;			// reszta z dzielenia

	printf("Twoj serial  : %d",x);
	
	}
	else
	{
		printf("Niepoprawny kod!");
	}

	printf("\n\nNacisnij enter . . .\n");
	gets(magic);

return 0;
}
===============================================================

bart^CrackPl
cryogen@box43.pl
