Autoryzacja pluginów w komunikatorze Tlen.pl
• tech • 957 słów • 5 minut czytania
Ta notatka jest częścią serii Tlen.pl bez tajemnic. Zapoznaj się z pozostałymi wpisami.
Kolejna publikacja z serii “Tlen.pl bez tajemnic” prezentująca tajemnice systemu autoryzacji pluginów. Oczywiście, tak jak w przypadku połączeń szyfrowanych, i tym razem udało się w to wmieszać andka, który nieco prędzej opublikował swoje wnioski na temat autoryzacji pluginów.
System pluginów w komunikatorze tlen.pl opiera się na łączonych dynamicznie bibliotekach - DLL (Dynamic Link Library) ze zmienionym rozszerzeniem na .tpl, co ułatwia identyfikacje i odróżnienie pluginu od innych bibliotek.
Oficjalne pluginy wydane przez twórców tlen.pl są również typowymi bibliotekami DLL, ale dodatkowo zostały troszkę wzbogacone o pewne informacje. Dzięki którym możliwa jest autoryzacja pluginów, na podstawie której, klient potrafi odróżnić oficjalne pluginy od nieoficjalnych i ewentualnie ostrzec przed próbą uruchomienia nie autoryzowanego (pochodzącego z nieznanych źródeł) pluginu.
W niniejszej publikacji zajmiemy się systemem autoryzacji pluginów w komunikatorze tlen.pl. Opiszemy format dodatkowych danych dołączanych do plików, dzięki którym możliwa jest autoryzacja pluginu oraz wzbogacenie wtyczki o kilka dodatkowych, niestandardowych właściwości, niedostępnych innym autorom wtyczek.
Autoryzacja pluginów opiera się na porównaniu wartości funkcji skrótu SHA-1 obliczonej dla pliku pluginu z zaszyfrowaną wartością znajdującą się w danych dopisanych do końca pliku. Dane autoryzacyjne nie są częścią pliku, dla której obliczany jest hash SHA-1.
W celu łatwiejszego zrozumienia i zobrazowania dalszego opisu, poniżej zaprezentowano końcowy fragment pluginu video.tpl (Videokonferencje).
Obecność sygnaturki i danych autoryzacyjnych identyfikowane jest na podstawie wartości dwóch 4-bajtowych liczb typu int znajdujących się na końcu pliku.
Ostatnia liczba (pierwsza patrząc od końca pliku, kolor zielony), posiada stałą wartość równą 1A2B3C4Dh
.
Przedostatnia (kolor czerwony) określa offset do początku danych autoryzacyjnych, licząc od swojej pozycji, czyli określa ich rozmiar, a całkowita długość dodatkowych danych dołączonych do pliku jest równa jej wartości powiększonej o 8 (dwie ostatnie liczby 4 bajtowe).
Patrząc na zamieszczony fragment kodu pluginu, widzimy, że dane zawierające informacje o autoryzacji zaczynają się od pozycji 2F050h
, co można łatwo obliczyć, według podanych wyżej informacji:
0002F050h = 0002F210h - 000001C0h
Dla oficjalnych pluginów firmowanych przez o2.pl wartość tej przedostatniej liczby wynosi 000001C0h
, co odpowiada długości danych równej 448 bajtów.
Z 448 bajtów danych najistotniejsze są początkowe 64 bajty (zaznaczone kolorem czerwonym), które zawierają zaszyfrowaną wartość skrótu SHA-1 zawartości pluginu, pozostałe to losowe dane.
Wartość hasza SHA-1 znajduje się w zaszyfrowanym 512-bitowym algorytmem RSA bloku danych, który można odszyfrować za pomocą stałego klucza publicznego:
e = 10001
pq = 807cef99098604b50cdf71e7e4ad7be882c04cec2511423a78da9abdea91
cf861b0695955d980ba5cbf77759d1780eb0a8912bce30d69215234782d2
5a8f8199
Pożądana wartość SHA-1 zawiera się w ostatnich 20 bajtach odszyfrowanego bloku. Jeśli obliczona wartość jest równa odszyfrowanej, plugin uważany jest za autoryzowany.
Po poprawnym stwierdzeniu autoryzacji pluginu, można “dobrać się” do właściwej sygnaturki zawierającej informacje m.in. o dodatkowych, niestandardowych właściwościach wtyczki.
Tuż nad danymi autoryzacyjnymi znajduje się 4-bajtowa liczba typu int (kolor niebieski) określająca offset do danych sygnaturki (znaczonych kolorem czerwonym) zapisanych bezpośrednio przed nią. Wartość tej liczby można również interpretować jako długość sygnaturki, w przypadku oficjalnych pluginów firmowanych przez o2.pl, rozmiar ten wynosi 0000004Ch
(76 bajtów).
Sygnaturka zapisana jest w postaci poniższej struktury:
struct PluginSignatureStruct {
int structSize; // rozmiar struktury
int Kind; // rodzaj
int Flags; // flagi
char Name[64]; // nazwa
};
Rodzaje pluginu:
// plugin wyprodukowany przez MediaOne (dawna nazwa o2.pl)
#define PLUGIN_SIGNATURE_KIND_MEDIAONE 32
Flagi, określające dodatkowe, niestandardowe, właściwości pluginu:
// ładowany automatycznie przy pierwszym "użyciu"
#define PLUGIN_SIGNATURE_FLAG_LOADWHENUSEDFIRSTTIME 0x00000001
// ładowany automatycznie bez możliwości wyłączenia
#define PLUGIN_SIGNATURE_FLAG_PERSISTENPLUGIN 0x00000002
Jak pisałem wyżej, sygnaturka jest pobierana tylko w przypadku poprawnej autoryzacji pluginu, a jej zawartość jest również częścią pliku, dla której obliczana jest funkcja skrótu SHA-1, przez co jakakolwiek modyfikacja sygnaturki bez utraty autoryzacji nie jest możliwa.
Podsumowując, aby komunikator Tlen.pl skorzystał z sygnaturki, musza zostać spełnione po sobie warunki:
- ostatnie 4 bajty muszą być równe liczbie typu `int` o wartości `1A2B3C4Dh`
- rozmiar pliku musi być większy od rozmiaru danych dołączonych do końca pliku
- wygenerowany hasz SHA-1 musi być równy odszyfrowanemu
- rozmiar offsetu do danych sygnaturki musi być równy rozmiarowi struktury sygnaturki
Mając listę wymaganych warunków możemy zaimplementować prostą funkcję CheckPluginSignature
służącą do sprawdzania wtyczek. Jako argumenty podajemy adres do nazwy pliku wtyczki oraz wskaźnik na strukturę PluginSignatureStruct
, gdzie zostaną zapisane dane sygnaturki.
bool CheckPluginSignature(char* fname, PluginSignatureStruct* dest) {
bool ret = false;
FILE* fp = fopen(fname, "rb");
if (fp != NULL) {
struct stat st;
stat(fname, &st); // rozmiar pliku znajduje sie w st.st_size
unsigned char* buf = new unsigned char[st.st_size];
fread(buf, 1, st.st_size, fp);
fclose(fp);
int y = *(int*)&buf[st.st_size - 8]; // tlenowe plugi = 1C0h = 448
int z = *(int*)&buf[st.st_size - 4]; // ostatni int pliku = 1A2B3C4Dh
if (z == 0x1A2B3C4D && st.st_size > y + 8) {
z = st.st_size - y - 8; // z okresla poczatek danych auth
unsigned char sha1_tpl[20];
unsigned char sha1_org[20];
// generujemy hasz pluginu
sha1_context ctx;
sha1_starts(&ctx);
sha1_update(&ctx, buf, z);
sha1_finish(&ctx, sha1_tpl);
memcpy(sha1_tpl, ctx.state, sizeof(sha1_tpl));
CLINT cl1, cl2, clf, clm;
char* k1 = "10001";
char* k2 = "807cef99098604b50cdf71e7e4ad7be882c04cec2511423a78da9abdea91cf86"
"1b0695955d980ba5cbf77759d1780eb0a8912bce30d69215234782d25a8f8199";
str2clint_l(cl1, k1, 16);
str2clint_l(cl2, k2, 16);
byte2clint_l(clf, buf + z, 64);
mexp_l(clf, cl1, clm, cl2);
int len = 64;
unsigned char* out = clint2byte_l(clm, &len);
memcpy(sha1_org, out + len - sizeof(sha1_org), sizeof(sha1_org));
// porownujemy oba hasze, gdy sa takie same to plugin autoryzowany
if (memcmp(sha1_org, sha1_tpl, sizeof(sha1_org)) == 0) {
ret = true;
// int (4 bajty) przed danymi autoryzacyjnymi okresla offset do struktury
// znajdujacej sie 'wyzej' zawierajacej sygnaturke pluginu
int offset = *(int*)&buf[z - 4];
if (offset == sizeof(PluginSignatureStruct))
memcpy(dest, buf + z - 4 - offset, sizeof(PluginSignatureStruct));
}
}
delete buf;
}
return ret;
}
W zamieszczonej implementacji do wygenerowania funkcji skrótu SHA-1 została użyta implementacja Christophe Devine’a, a do obliczeń związanych z algorytmem RSA wykorzystano funkcje dostępne w bibliotece lint, która jest częścią książki “Cryptography in C and C++”. Bibliotekę ta również wykorzystuje oficjalny klient nie tylko w implementacjach RSA.
Tak jak poprzednio w sprawę został także wmieszany andk ;) Na swojej stronie udostępnił własną wersje opisu autoryzacji pluginów w komunikatorze tlen.pl.
[Za często użyto słowa plugin, który można zastąpić polskim słowem wtyczka.]
Komentarze (0)