Konkurs na czas?

WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
2

Cześć wszystkim,
Jestem amatorem ale od dłuższego czasu, dla siebie , piszę programy w chwilach wolnych.
W jednym z wątków w Pascalu, Delphi i Lazarusie wywiązał się temat szybkości i łatwości rozbrojenia nieoptymalnie zaszyfrowanej bazy danych obsługiwanej przez program, który potrafi ją odszyfrować i wyświetlić po wpisaniu w edit poprawnego hasła. Nie wnikając w szczegóły tam wywiązanej dyskusji wyszło na to, że to zajmie max 15-20 min ,może szybciej.
Jednak po dobie nikt się za to nie wziął , albo zabrał się ale nie może sobie poradzić.(banalnie proste jest to, choć ja bym nie podołał)

Wklejam tu link do katalogu konkurs.zip:
Jest w nim program skompilowany w lazarusie oraz zaszyfrowana bardzo prosta baza danych.
Po wpisaniu poprawnego hasła program
Wyświetla sposób w jaki dane zostały zakodowane, gdyż opisałem to w samej bazie w kolejnych wierszach i ją program wyświetli w memo po wpisaniu poprawnego hasła. I o taki zrzut ekranu z opisem w memo proszę jako dowód że się udało. Proszę podajcie też czas w jakim się to udało. Bo amatorzy tacy jak ja i początkujący potrzebują dowodu jak bardzo należy szyfrować aby zabezpieczać dane.

Https://www.windowbee.com/konkurs.zip

edytowany 1x, ostatnio: Riddle
Zobacz pozostałe 13 komentarzy
obscurity
@cerrato: w windowsie wystarczy odpalić sandbox, nie trzeba nic stawiać
enedil
daj kompilację na linuksa, bo inaczej to mi nie chce się bawić
WI
Mogę na mac os dać, nie mam w linuksie zainstalowanego lazarusa i nie znam linuksa na tyle żeby szybko przebrnąć instalację i skompilować 🥴 nikt mnie nie prosił nigdy o program pod linuksa więc zgłebianie linuksa odstawiłem a tutaj jest widzę sporo fanów jednak.
PR
  • Rejestracja:prawie 4 lata
  • Ostatnio:około 9 godzin
  • Postów:220
0
cerrato
Z tego co kojarzę to ma związek z Lazarusem. Czasem (jak wychodzi nowa wersja tego IDE) to nawet jak ją ściągam z ich oficjalnej strony to ochrona Windowsowa potrafi brzęczeć, że to zagrzybione ;)
WY
WY
  • Rejestracja:ponad 2 lata
  • Ostatnio:ponad 2 lata
  • Postów:101
0

Mogłeś ten algorytm szyfrowania podać na stronie, do tego input jakiś w base64.
Jak tu się rozchodzi o tylko rozszyfrowanie bazy danych nie znając hasła, to cała aplikacja nie ma żadnego sensu w tym zadaniu.

Teoretycznie jak będziesz miał nawet takie banalne szyfrowanie jak xor zwykły, ale hasło będzie o długości danych szyfrowanych to będzie trzeba po prostu przejść po wszystkich możliwych kombinacjach i potem w jakiś sposób stwierdzić, który dał nam odszyfrowane dane.

Możliwe, że nie pójdzie tego złamać jako tako nie znając hasła.

edytowany 1x, ostatnio: Wypierdzistyy
WI
No generalnie chodziło o to czy szyfrowane takimi sposobami dane w formie łańcucha bajtów, jeśli ktoś ma klienta fizycznie, są do odszyfrowania przez osoby postronne. Zwłaszcza jeśli idą przez port. Odpowiedź brzmiała ,że debbugowanie klienta pozwoli poznać mega szybko sposób szyfrowania i uzyskać dostęp do wszystkich danych. Jednak sposób szyfrowania zna na początku jedynie twórca programu i nikomu go nie zdradza.
WY
Wypierdzistyy
Miałem na myśli tylko jednorazowe użycie hasła, jeśli będziesz używał cały czas tego samego do szyfrowania i miał niezbyt kryptograficznie bezpieczny algorytm to pewnie pójdzie w jakiś sposób wywnioskować, np. dane nie będą zupełnie randomowe po zaszyfrowaniu, to mając dostateczną ilość takich zakodowanych wiadomości i algorytm szyfrowania/deszyfrowania, dobry spec od kryptografii to lekko połamie, a jak nie to jakiś szybszy sposób z brute force, np. taka optymalizacja łamania może wyglądać tak, że zamiast cały klucz łamać to pójdzie bajt po bajcie itp.
WI
Klucz się zmienia randomowo po każdym zapisie do bazy. Tutaj nie ma czegoś takiego. Baza jest stałym plikiem. Hasło do bazy znajduje się w bazie. Wydaje mi się że ten sposób rozszyfrowania o jakim mówisz jest niemożliwy za bardzo bo baza fizycznie znajduje się u mnie. Klient operuje na danych mu przesłanych i nigdzie ich nie zapisuje. Przesyłając do serwera tworzy nowy klucz szyfruje dane i wysyła serwerowi. Te same dane za każdym razem będą wyglądać inaczej i będą miały różną długość.
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
0

Gdy się poddacie napiszcie proszę, podam hasło i pokaże się sposób szyfrowania.

msm
Jest opcja przytrzymania wątku do weekendu? Nie obiecuję, ale chętnie spojrzę, tylko w środku tygodnia ciężko. Nie będzie to też pewnie 15 minut, a po robieniu podobnych rzeczy 8 godzin w pracy już się potem trochę nie chce danego dnia ;).
obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 3 godziny
0

Z tego co rozumiem to "konkurs" nie ma sensu, równie dobrze mogłeś spakować plik tekstowy hasłem do zipa zabezpieczonego hasłem. O ile użyłeś w miarę sensownego algorytmu szyfrowania to jedynym sposobem na złamanie tego jest brute force a gdzie tu zabawa. Nie sztuką jest zaszyfrowanie bazy danych, sztuką by było zrobienie bazy danych której nie można na stałe odszyfrować w całości i stworzyć "cracka" po jednorazowym podaniu hasła


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
WI
Ta druga opcja wchodzi w grę. Program został dodany gdyż podobno mając program rozszyfrowujący bazę można do niej się szybko dostać rozkładając kod maszynowy programu w dekompilatorze… Baza jest szyfrowana prymitywnym i prostym wg mnie sposobem, choć skutecznym - też wg mnie. Brut force nic nie da bo hasło w programie jest sprawdzane w timerze co 1 Sek wiec szybkie sprawdzanie kombinacji nic nie da bo zanim timer to sprawdzi to już będzie inne, a z szybkością jedna próba co sek... Brut force z samą bazą też nie wyjdzie bo jest zbyt długa i zbyt dużo kombinacji.
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
1

Zostawię temat przez tydzień może komuś się uda a jak nie to uznam ,że prymitywne sposoby nikomu nie zdradzone są bardzo skuteczne dla moich potrzeb bez szyfrowania liczbami pierwszymi itd…
To samo dotyczy wysłania wiadomości przez port zaszyfrowanych w ten sam sposób.
Podpowiedź jest jeszcze taka że klucz znajduje się w samej bazie, hasło programu deszyfrującego również.

PA
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 3 godziny
  • Postów:3866
1

@Windowbee: daj linka do wątku o którym wspominasz. Mnie ciekawi kontekst dla którego robisz konkurs.

Ciekawi ponieważ przypomniało mi się moje podejście z początku pracy jako programista. Teraz po ponad 20 latach (sic!), Mam zupełnie inne spojrzenie na to czy szyfrować...

WY
WY
  • Rejestracja:ponad 2 lata
  • Ostatnio:ponad 2 lata
  • Postów:101
0

Eh...
Strasznie pogmatwane to wszystko napisałeś, jakiś plik, jakieś szyfrowanie, zawsze zmienia się hasło, hasło jest w bazie.

Nic nie rozumiem, hasło potrzebne do rozszyfrowania, ale hasło jest w programie....

Jeszcze jakby program działał to bym zrozumiał.
Weź tu znajdź funkcję szyfrująca jak program nie działa.

Nawet jak się znajdzie to trzeba assembler do pythona przepisać i potem satisfiability modulo theories albo da True czyli złamie, albo nie.

Anyway komu się chce jak ty tak nieudolnie ten konkurs zrobiłeś.
Powinieneś zrobić jakiś przykład, a nie dać zepsuty program, który nic nie robi.

I ja to mówię ze szczerego serca, c**** to zrobiłeś.

edytowany 1x, ostatnio: Wypierdzistyy
Zobacz pozostały 1 komentarz
WY
Wypierdzistyy
Ja nie dałem żadnego takiego argumentu, proszę mnie nie mieszać w to :> I tak może to być mega skomplikowane czasem.
WI
Program działa : ma proste zadanie pokazać całą baze jak wpiszesz w edita hasło prawidłowe. W bazie w 6 linijkach napisany jest sposób szyfrowania i jest ona wyświetlana w memo po wpisaniu prawidłowego hasła. Więc masz w zasadzie klienta deszyfrującego i samą bazę czyli dużo więcej niż na początku było mówione. Z samym przesyłem przez port i zmiennym sposobem szyfrowania.
WY
Wypierdzistyy
Jaki przesył przez port jak ta baza to zwykły plik. ja kompletnie nie wiem jak to działa i jak wygląda struktura bazy bo tam jest tylko niedziałający exe i jakiś plik bazy. Daj po prostu algorytm szyfrujący i input z bazą i hasłem gdzie przy prawidłowym prawidłowo odkoduje, a gdy jest nieprawidłowy to nie. Prawdopodobnie nie da się złamać twojego szyfrowania, a ja nie chcę tracić tygodni czasu analizując jakieś pierdoły. I ja nawet się na kryptografii nie znam, kiedyś był tu koks Shalom, ale odszedł są jeszcze inni. Mogę ci to spróbować w SMT solverze rozwiazać.
WY
Wypierdzistyy
Ale mnie wkurwiasz sugerując, że to ja cię zmusiłem do tego konkursu jakiegoś. Ale walić to daj mi ten algorytm działający to ci go przepuszczę przez SMT solver i ci powiem czy jest rozwiązanie dla twojej bazy jak nie będzie to znaczy, że jest bezpieczna i trudno to złamać, chodź jak dasz kiepskie hasło to brute force znajdzie w skończonym czasie.
WI
Zwracam honor nie widzę tego w tamtych wątkach. Chyba najzwyczajniej w świecie odniosłem wrażenie że szyfruję te dane (zresztą nie bankowe nie jakieś wrażliwe) w bardzo niebezpieczny sposób dla Was. (Bo na tym forum poza studentami chcącymi,żeby napisać im algorytm na zaliczenie) to jednak działają ludzie znający się na programowaniu i z tego żyjący. Moją intencją nie była rywalizacja ,a chęć dowiedzenia się jak szybko pokonacie zabezpieczenia dotarcia do bazy. (Która też nie jest plikiem trzymającym format baz danych)najbardziej zbliżona jest do pliku csv (rozszyfrowana)
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
0

@Wypierdzistyy: zwracam honor, nic nie wyszydzałeś :) więc przepraszam

WI
Obiecuję ,że podam Hasło , niech to chwile jeszcze potrwa, jeśli komuś się uda uchylę czoło i znacząco ruszę w naukę kryptografii (mam nadzieję ze 2 tyg dla moich potrzeb wystarczą). Jeśli nie uda się rozszyfrować execa i za nim sposobu szyfrowania pliku tzn że ten sposób dla sieci lokalnych jest wystarczający. Dla przesyłu danych np o zakażeniach(nie imiennych) w szpitalu.
WI
@Wypierdzistyy: włączyłeś się po prostu, w trakcie :) i niesłusznie się tłumaczyłeś biedaku :) @Patryk27 napisał w komentarzu, że odpalenie tego execa (po to go dodałem w linku z bazą) pod debbugerem w bardzo krótkim czasie pozwoli odszyfrować tą bazę i sposób jaki używam. Dlatego powstał konkurs. Wywoławca :) konkursu się nie odezwał ponownie. Nie mówię ,że nie ma racji nie wiem tego.
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:4 miesiące
9

Ok, dostajemy program Protector.exe. On prosi o jakieś hasło, i dekryptuje bazę albo nie.
To jeden z najbardziej typowych schematów crackme, więc na razie żadnych zaskoczeń.

Po otwarciu w ghidrze nieprzyjemna niespodzianka - program napisany w Lazarusie (można było też przeczytać opis, wiem). Reversowanie nie będzie zbyt przyjemne, do tego nie mam w tym środowisku za dużo doświadczenia (malware w Lazarusie raczej nie ma wiele). Pierwsze wrażenie: pisze się "Decrypted" a nie "Decripted" (tekst na stronie).

screenshot-20221217215418.png

Na start trochę analizy dynamicznej żeby zobaczyć o co w ogóle chodzi. W procmonie widać że program przy starcie czyta bazę. Co ciekawe, czyta ją kilkoma readami po 128 bajtów pod rząd - jakaś struktura, czy tak działa runtime Lazarusa? Nie wiadomo na razie.
O tyle ważne, że czyta plik raz przy starcie, a nie przy wpisaniu hasła.

screenshot-20221217214543.png

Procmon daje nam od razu stos, więc przechodzę po stosie od kernela do góry (nazywając funkcje) żeby dostać się do czegoś ciekawego. Po przejściu trzy ramki stosu do góry (nazwałem funkcje ReadFile, ReadFileWrap, ReadFileWrap2, ReadFileWrap3) trafiam do funkcji która woła ReadFileWrap3 kilka razy pod rząd, w dodatku z ciekawymi parametrami (hint: 0x80 to 128).

screenshot-20221217214737.png

Zresztą, pierwsze co mi się tam rzuciło w oczy to komunikat błedu

Kopiuj
local_408 = "Reading of base: ";
local_400 = local_18;
local_3f8 = " faild !";

Literówka - nie wygląda jak coś z biblioteki standardowej, więc może to być kod naszego programu. Gdybym robił to pracowo sprawdziłbym w 5 sekund przez VTGrep, ale jako że to prywatnie to trzeba sobie radzić za pomocą Google - zero wyników, więc jest OK. Mamy funkcję czytającą bazę.

No dobra, czas zmienić podejście z powrotem na dynamiczne. Ustawiam sobie breakpoint na 0x10002c6dc w x64dbg i singlestepuje porównując z ghidrą. Nazywam sobie przy okazji funkcje "ProbStrcat", "ProbFileExists", "SetText" i śledzę jak program czyta (w programie ani śladu sprawdzania jakichkolwiek błędów btw ) treść bazy do pamięci (dwa bufory, nazwałem sobie dataBuf0 i dataBuf1). Później zaczyna coś parsować i liczyć przez odejmowanie pierwszych 128 bajtów od drugich 128 bajtó... zaraz, czy to jest plaintext? Jeszcze przecież nie podałem hasła nigdzie.

screenshot-20221217214808.png

No ale nie da się ukryć, program dostaje jakieś plaintextowe bajty. No to szybki python:

Kopiuj
data = open("konkurs/baza.dbx", "rb").read()

plain = b""
buf0 = data[0:0x80]
while data:
    buf1 = data[0x80:0x100]

    out = []
    for i in range(len(buf1)):
        out += [(buf1[i] - buf0[i]) % 256]

    plain += bytes(out[:-1])
    data = data[0x80:]

print(plain.decode(errors="ignore"))

Wykonujemy:

Kopiuj
$ python3 hack.py
44908.635924;1;10#0;1#1;Gratulacje udało się Tobie rozszyfrować tą bazę.#2;Jeśli jednak upłynął czas konkursu i usyskaeś dostęp poprzez hasło to poniżej przedstawiam sposób szyfrowania:#3;1) W pierwszych 128 bajtach program umieszcza losow klucz#4;2)Następnie do każdego bajtu właściwej bazy leżącej poza tymi 128 bajtami program dodaje po kolei bajt z klucza.5;3)Odszyfrowywanie odbywa się poprzez odczytanie klucza i odejmowanie pokolei wartości klucza od bajtu zaszyfrowanej bazy waściwej#6;4)Hasło pozwalające odszyfrować bazę - i załadować ją do tmemo leży w komórce 7,1 bazy za słowem password:7;Password:tu mnie masz#8;#9;#10;

Lekko formatując:

Kopiuj
44908.635924;1;10
0;1
1;Gratulacje udało się Tobie rozszyfrować tą bazę.
2;Jeśli jednak upłynął czas konkursu i usyskaeś dostęp poprzez hasło to poniżej przedstawiam sposób szyfrowania:
3;1) W pierwszych 128 bajtach program umieszcza losow klucz
4;2)Następnie do każdego bajtu właściwej bazy leżącej poza tymi 128 bajtami program dodaje po kolei bajt z klucza.5;3)Odszyfrowywanie odbywa się poprzez odczytanie klucza i odejmowanie pokolei wartości klucza od bajtu zaszyfrowanej bazy waściwej
6;4)Hasło pozwalające odszyfrować bazę - i załadować ją do tmemo leży w komórce 7,1 bazy za słowem password:
7;Password:tu mnie masz
8;
9;
10;

Trochę się tego nie spodziewałem :P. Zazwyczaj jeśli baza potrzebuje hasła do odszyfrowania, to jednak jest gdzieś jakaś kryptografia którą trzeba złamać. Chyba że tym się własnie różni "Decryption" od "Decription"? :P

Całość, razem z pisaniem tego writeupa i zrobieniem sobie yerby, zajęła 45 minut (@shalom mi świadkiem). Zależnie przed kim się bronisz, może to być dużo albo mało - na pewno można komuś tak trochę czasu zmarnować. Aczkolwiek gdybym się spodziewał że hasło jest tylko dla zmyłki, po prostu bym zobił dump pamięci procesu i miałbym wszystko w 5 minut :P.

edytowany 5x, ostatnio: msm
WI
@msm: ” Chyba że tym się własnie różni "Decryption" od "Decription"? :P” najwyraźniej 😂 dzięki bardzo
WI
Tym sposobem, w sumie co bym nie wymyślił jest do zobaczenia na kliencie. Co prawda do bazy (umieszczonej u mnie na serwerze) nikt się nie dostanie bo fizycznie programu serwera ani plików bazy nie tknie, to jednak rozszyfrowanie komend serwera poprzez podejrzenie pracy klienta jest bardzo łatwe. Mogę to w sumie ominąć poprzez poziomy dostępu do danych na poziomie samego serwera. I tylko tu. Czyli dodawać użytkowników na poziomie serwera. Co psuje trochę funkcjonalność ale zabezpiecza chyba dużo lepiej?
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
0

wow gratulacje!!!! Tak, muszę nauczyć się kryptografii i użyć bezpiecznych sposobów jednak 🥴 cieszę się że to zrobiłeś i poświęciłeś czas!
Bardzo dziękuję :)
Hasło nie było dla zmyłki. Myślałem że ktoś użyje go aby spojrzeć co robi program w momencie wpisania. Po wpisaniu: ‚tu mnie masz’ wyświetla bazę w memo.
Program odczytuje bazę pobierając pierwszych 128 bajtów gdzie jest klucz losowo stawiany przy zapisie bazy.
Następnie od każdej wartości bajtu odejmuje sukcesywnie kolejny bajt klucza. A wynik wpisuje jako kolejny char do stringa. Gdyby baza była dłuższa niż klucz , to wraca na początek klucza itd.
Kolejne stringi kończące się # zapisuje do tstringlist i przekazuje ostatecznie do memo jeśli wpisane hasło się zgadza z tym zapisanym do bazy.
Baza w pierwszym stringu ma czas utworzenia oraz liczbę kolumn i wierszy. Średnik oddziela komórki danych.
Nauczka i dla mnie! Dziękuję !

msm
Z tym dla zmyłki trochę żartowałem, bardziej że tak naprawdę nie ma tam szyfrowania po drodze (bo szyfrowanie zakłada że poprawne hasło jest sekretem potrzebnym do odzyskania danych). Ale ofc takie hasło spełnia definicję hasła, tylko nie kryptograficzną.
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
0

Obiecany kod deszyfrujący , nic nie zmieniałem go w stosunku do tego co odszyfrował @msm, część jest żywcem wzięta i zaadoptowana z projektu, który tworzę. Dlatego ma kilka zmiennych więcej nie wykorzystanych tutaj. Nie wieszajcie na mnie psów i wilków za nieczytelny kod. Pozdrawiam i dzięki.

Kopiuj
function czytaj(baza:tstringlist;name:string):boolean; // odczytuje baze i odszyfrowywuje
label 1,2;
var
  s,s1,s2,s3:string;
  ch:char;
  f:file;
  a,b,c,x,i,y,z:integer;
  bajt:byte;
  buf:array[0..127] of byte;
  key:array[0..127] of byte;
begin
result:=false;
 baza.Clear;

if fileexists(name+'.dbx') then begin

assignfile(f,name+'.dbx');
x:=0;
1:
try
reset(f,1);

except
  x:=x+1;
  sleep(500);
end;

if x>10 then goto 2; // 10X próbuje otworzyć plik czyli max trwa to 5sek, jeśli się nie powiedzie kończy
if x>0 then goto 1;

blockread(f,key,128,x);// zapisanie klucza
a:=1;c:=0;s:='';
s:='';
while x>0 do begin
blockread(f,buf,128,x);
for i:=0 to x-1 do
begin
y:=key[c];
ch:=chr(buf[i]-y);
if ch<>'#' then s:=s+ch else begin baza.Add(s);s:='';end;

c:=c+1;// licznik klucza
if c>127 then c:=0;
end;
end;
 close(f);
result:=true;
x:=0;
 end;
2:
 if x>10 then result:=false; // nie udało sie otworzyć pliku

 if result=false then baza.add('Reading of base: '+name+' faild !');
 if fileexists(name+'.dbx')=false then baza.add('There is no such base as: '+name+' !');
end; 
edytowany 1x, ostatnio: cerrato
WI
  • Rejestracja:około 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:138
0

@msm , Czy mogę o coś jeszcze zapytać?

Czy warunki też tak łatwo widać w Twoich programach dekompilujących?
Chodzi o to czy nie uruchomione funkcje, gdyż warunek nie został spełniony, też są łatwo widoczne?
Chodzi o to ,że masz klienta który logując się do serwera (na chronionym komputerze), pyta o poziom dostępu danego użytkownika. Serwer odpowiada : poziom niski…
Czy zobaczysz funkcje, które się nie uruchamiają z powodu niskiego poziomu dostępu?
Czy widzisz tylko na bieżąco to co robi program z pamięcią i zmiennymi czyli aktualnie wykonywane funkcje czy też funkcje nie uruchamiane?
Zobaczysz coś takiego : (oczywiście słowo admin i dostęp nie padnie bezpośrednio w plaintext).
Jeżeli dostęp=admin wtedy uruchom funkcja.użytkownicy? Ta funkcja dla osoby nieuprawnionej nie zostanie wywołana ale jeśli jesteś wstanie zobaczyć ten warunek i prześledzić jak działa wywoływana przez niego funkcja to łatwo ją sobie ustawisz tak aby nadać sobie odpowiednie uprawnienia… np…
Klient nie posiada żadnej bazy - w zasadzie żadnych plików poza graficznymi i tłumaczeniowymi. O każdą daną i zmianę ustawień prosi serwer i przez niego kontaktuje się z bazą danych.

edytowany 1x, ostatnio: Windowbee
WY
WY
  • Rejestracja:ponad 2 lata
  • Ostatnio:ponad 2 lata
  • Postów:101
2

W sumie stwierdziłem, że też spróbuję, wiadomo już rozwiązane, ale trzeba sprawdzić czy by się udało samemu zanalizować problem.

Podejście @msm czyli procmon i tam widać jak program sobie syscallami CreateFileW i ReadFile korzysta,
To breakpoint na syscalle w kernel.dll, potem tylko sprawdzałem, czy plik był otwierany z nazwą baza.dbx, sprawdziłem jaki handle uchwyt jest zwracany.
I potem w drugim breakpoincie na ReadFile, sprawdzałem czy to ten sam uchwyt.

Dwie ramki do góry wyszedłem, aż opuściłem moduły kernel32.dll, aż do programu, tam (aktualny_adres - adres_poczatku_modulu) gdzie jest załadowany moduł dawał mi offset, który prosto można dodać do offset sekcji .text i dostać miejsce w kodzie w pliku binarny.

Tam też pogram czytał plik kilka razy ReadFile(file_handle,file_buffer,0x80,&xxx), gdzie te 0x80 to 128 szesnastkowo.
I tak czytał raz do buffora, który był kluczem, drugi do odczytywanych danych.
Jeśli index w rax był wyższy od 0x7F to wczytywała się kolejna porcja 128bajtów danych.

Buffory były dwa MOVZX EAX,byte ptr [RBP + RAX*0x1 + -0x358] ten wczytywał do eax, jeden bajt gdzie counterem był rax z buffora ze stosu rbp-0x358 to były dane do odszyfrowania.
Bajt był schowany na stosie pod MOV [RBP + buf_char], eax
Drugi MOVZX EAX,byte ptr [RBP + RAX*0x1 + -0x3d8] tak samo do eax, ale klucz wczytywał, rax co kółko było zwiększane o 1 dopóki było mniejsze od 0x7f.
SUB EAX,dword ptr [RBP + buf_char]
Teraz w eax się znajduje odszyfrowany bajt.

Wiem, że rozwiązanie było ja dam inne, czyli z dumpem :>
Wiem, że pewnie można było memo użyć, ale nie mogłem znaleźć pointera na tę strukturę, muszę ogarnąć jak to znaleźć w pamięci.

Kopiuj
import r2pipe
import time

r2 = r2pipe.open('Protector.exe')
print(r2.cmd('ood'))
print(r2.cmd('db 0x00010002C889'))
r2.cmd('dc')
time.sleep(1)

o = []
for i in range(12):
  r2.cmd('dc')
  time.sleep(0.1)
  o.append(r2.cmd('ps @rdx'))
print(''.join(o))
edytowany 1x, ostatnio: Wypierdzistyy
WI
Jak to uruchomiłeś pokazało Tobie cały tekst? Jak @msm ?
WY
Wypierdzistyy
to jest skrypt do radare2 w pythonie, zatrzymuje program w miejscu gdzie odszyfrowałeś dane i zrzuca je na ekran.
WI
Czyli wszystko widać 🤪, dobrze wiedzieć, że klient nie może mieć danych w pamięci zapisanych ,poza danymi ze swojego poziomu dostępu przydzielonymi przez serwer. Bo inaczej dane nie są bezpieczne! Dzięki Wam!
msm
O, fajne rozwiązanie. Plus za radare. @Windowbee tak, dokładnie - jeśli klient ma jakiekolwiek dane w pamięci to użytkownik do którego należy ten proces może je przeczytać.
TheWypierdzisty
TheWypierdzisty
  • Rejestracja:prawie 2 lata
  • Ostatnio:prawie 2 lata
  • Postów:43
0

Akurat było sprawdzona jedna część czyli wczytanie i dekodowanie pliku.

A jest jeszcze druga cześć, która sprawdza hasło.

Edit Field, te pole edycji jakoś powinno czytać znaki i sprawdzać czy hasło poprawne, wydawać by się mogło, że to będzie jakieś onChangeEvent, czyli po każdym znaku jest sprawdzana poprawność hasła, ewentualnie po zatwierdzeniu enterem.

A tu ciekawe zaskoczenie.
SetTimer(0, 0, 1000, 0x1000faf70)

I potem w PostMessageBox od nt!KiUserCallbackMessage otrzymuje (NULL, WM_TIMER-x133, 0x1000faf70) (czyżby można było za pomocą postmessage robić remoteProcedureCall? używając WM_TIMER)
Trafia to do DispatchMessage, tam w user32.dll -> user32.dll
I niebezpośrednio trafiamy do funkcji 0x1000faf70, czyli do tej funkcji obsługującej timer.

Pierwsza funkcja, nic ciekawego iterujemy strukturę timerów, szukamy w niej z naszym id timera, jest i tak tylko jeden element, i w następnych 8 bajtach struktury jest adres następnej funkcji :)

Kopiuj
void timer_proc_0x1000faf70
               (undefined8 param_1,double param_2,undefined8 hwnd,undefined8 message,
               longlong timer_id)

{
  longlong *timer_struct;
  undefined8 extraout_XMM0_Qa;
  uint idx;
  
  if ((DAT_10019e3e0 == 0) || (*(char *)(DAT_10019e3e0 + 0x89) == 0)) {
    idx = *(uint *)(DAT_100280cf0 + 2);
    do {
      if ((int)idx < 1) {
        return;
      }
      idx = idx - 1;
      timer_struct = (longlong *)get_timer_struct(param_1,param_2,DAT_100280cf0,idx);
      param_1 = extraout_XMM0_Qa;
    } while (*timer_struct != timer_id);
    (*(code *)timer_struct[1])(timer_struct[2]);
  }
  return;
}

Druga funkcja, jakiś check, nie mam pojęcia co sprawdza ciężko to wydedukować.

Kopiuj
void timer_wrapper1_0x10016a630(longlong *param_1)

{
                    /* security check */
  if ((*(char *)(param_1 + 0x14) != 0) && (*(int *)(param_1 + 0xc) != 0)) {
    (**(code **)(*param_1 + 0x1c8))(param_1);
  }
  return;
}

Trzecia funkcja też niebezpośrednio wywołana też jakieś sprawdzenie, które nigdy się nie wykonuje jak poprzednie.

Kopiuj
void timer_wrapper2_0x10016a720(longlong param_1)

{
                    /* security check */
  if (*(longlong *)(param_1 + 0x90) != 0) {
    (**(code **)(param_1 + 0x90))(*(undefined8 *)(param_1 + 0x98),param_1);
  }
  return;
}

No i teraz jesteśmy we właściwej funkcji, która odpowiada za wyświetlanie rozszyfrowanej bazy.

Kopiuj
void show_hide_password_func_0x10002cff0(undefined8 param_1,undefined8 param_2)

{
  int number_of_readed_lines;
  int memo_field_length;
  int memo_field_length2;
  longlong b_check;
  longlong offset;
  longlong is_that_same;
  longlong is_equal;
  longlong user_input;
  longlong p_str2;
  longlong p_str;
  uint i;
  
  p_str = 0;
  p_str2 = 0;
  user_input = 0;
  set_pointer(&p_str,0);
  set_pointer(&p_str2,0);
  number_of_readed_lines = (**(code **)(*database + 0x108))(database);
  if (-1 < number_of_readed_lines + -1) {
                    /* i = -1 */
    i = 0xffffffff;
    do {
      i = i + 1;
                    /* 0xf8 - *p_str = database.readLineNumber(i) */
      (**(code **)(*database + 0xf8))(database,&p_str,(ulonglong)i);
      b_check = check_if_contains_substring("Password:",p_str,1);
      if (b_check != 0) {
        offset = find_first_occurance(':',p_str,1);
        get_substring_after_offset(&p_str,1,offset);
        set_pointer(&p_str2,p_str);
                    /* (Object + 0x7b8) == edit_field */
        getWindowTextWrap(*(longlong **)(Object + 0x7b8),&user_input);
        is_that_same = strcmp(p_str2,user_input);
        if (is_that_same == 0) {
                    /* (Object + 0x7c0) == memo_field */
          memo_field_length =
               (**(code **)(**(longlong **)(*(longlong *)(Object + 0x7c0) + 0x610) + 0x108))
                         (*(undefined8 *)(*(longlong *)(Object + 0x7c0) + 0x610));
          if (memo_field_length == 0) {
            getWindowTextWrap(*(longlong **)(Object + 0x7b8),&user_input);
            if (user_input != 0) {
                    /* Set memo field as database content */
              (**(code **)(**(longlong **)(*(longlong *)(Object + 0x7c0) + 0x610) + 0x160))
                        (*(undefined8 *)(*(longlong *)(Object + 0x7c0) + 0x610),database);
            }
          }
        }
        getWindowTextWrap(*(longlong **)(Object + 0x7b8),&user_input);
        is_equal = strcmp(p_str2,user_input);
        if (is_equal != 0) {
          memo_field_length2 =
               (**(code **)(**(longlong **)(*(longlong *)(Object + 0x7c0) + 0x610) + 0x108))
                         (*(undefined8 *)(*(longlong *)(Object + 0x7c0) + 0x610));
          if (memo_field_length2 != 0) {
            clear_memo_field(*(longlong **)(Object + 0x7c0));
          }
        }
      }
    } while ((int)i < number_of_readed_lines + -1);
  }
  __stack_check((longlong)&stack0xfffffffffffffff8);
  return;
}

Funkcja pobiera ile jest linii w bazie, tej rozszyfrowanej.
Potem pobiera pokolej linie.
Sprawdza czy mają podciąg "Password:", jeśli nie ma continue, jeśli ma to wchodzimy w if.
Potem znajdujemy pozycję ':' w stringu i kopiujemy wszystkie litery za tym do nowego buffera.
Pobieramy tekst z edit_field czyli tego co wpisujemy tekst i porównujemy z tym nowym stringiem uzyskanym przed chwilą.
Jeśli równe pobieramy ilość znaków memo_field jak nie jest puste to wykonujemy daną funkcję, jeśli nie to opuszczamy.
Sprawdzamy czy użytkownik wprowadził jakieś dane, jeśli tak to wyświetlamy bazę, jeśli nie to nie? trochę dziwne, jeśli hasło jest poprawne to po co sprawdzać czy ktoś coś wprowadził XD
Pobieramy edit_field, sprawdzamy znowu hasło, jeśli niepoprawne.
To pobieramy ilość znaków w memo field i jak nie jest puste to
Czyścimy, jak jest to opuszamy.

I teraz żeby objeść te zabezpieczenia to musimy z patchować 2 rzeczy lub 3.

  1. Sprawdzanie czy hasło jest takie samo,
  2. Sprawdzanie czy user coś wprowadził? optionalne, gdyż można tam losowe rzeczy wpisać w edit field ręcznie.
  3. musimy zablokować czyszczenie memo, gdyż nawet jak będzie coś wpisane, to i tak zostanie wyczyszczone po tym więc nie zdążymy zobaczy.

A to patch w pythonie.

Kopiuj
file = bytearray(open('Protector.exe', 'rb').read())

cmp = 0x2c507 # any password
for i in range(cmp, cmp+0x6):
    file[i] = 0x90

inp = 0x2c560 # no input needed
file[inp] = 0x90
file[inp+1] = 0x90

memo = 0x2c5c3 # disable cleaning memo
file[memo] = 0xeb

open('Protector_patched.exe', 'wb').write(file)

Widzę, że można jeszcze ręcznie wywołać wpisywanie tej bazy, ale id timera się zmienia, a go by było trzeba wyłączyć syscallem killTimer gdyż jak go nie wyłączymy to i tak by potem po jednej sekundzie wyzerował memo.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)