Google Translation API Hacking

Google oferuje Google Translation API ze strukturą kosztów opartą na użytkowaniu w ramach Google Cloud. Istnieje również nieudokumentowane API, którego można używać bez klucza , ale które odmawia działania po zaledwie kilku żądaniach. Korzystając z funkcji tłumaczenia stron internetowych w Google Chrome, można zauważyć, że strony mogą być tłumaczone w bardzo dobrej jakości bez zauważalnych ograniczeń.


Podobno zaawansowany model nmt jest już tutaj używany. Ale którego interfejsu API Google Chrome używa wewnętrznie do tłumaczenia treści i czy można również bezpośrednio adresować ten interfejs - nawet po stronie serwera? Do analizy ruchu sieciowego zalecane są narzędzia takie jak Wireshark lub Telerik Fiddler , które mogą również analizować ruch zaszyfrowany. Ale Chrome nawet bezpłatnie dostarcza żądania tłumaczenia strony, które wysyła: można je łatwo wyświetlić za pomocą narzędzi Chrome DevTools:

Jeśli wykonujesz tłumaczenie, następnie przechwyć kluczowe żądanie POST do https://translate.googleapis.com za pomocą „Kopiuj> Kopiuj jako cURL (bash)” i wykonaj je w narzędziu takim jak np. Postman , możesz bez problemu ponownie wysłać żądanie:

Znaczenie parametrów adresu URL jest również w dużej mierze oczywiste:

KluczPrzykładowa wartośćZnaczenie
anno3Tryb adnotacji (wpływa na format zwrotu)
klientte_libInformacje o kliencie (różne, wartość to „aplikacja internetowa” za pośrednictwem interfejsu internetowego Tłumacza Google; ma wpływ na format zwrotów i ograniczenie szybkości)
formathtmlFormat ciągu (ważny przy tłumaczeniu tagów HTML)
v1.0Numer wersji Tłumacza Google
kluczAIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgwKlucz API (patrz poniżej)
logldvTE_20200210_00Wersja protokołu
sldeJęzyk źródłowy
tlenJęzyk docelowy
spnmtModel ML
tc1nieznany
sr1nieznany
tk709408.812158Token (patrz poniżej)
Moda1nieznany

Niektóre nagłówki żądań również są ustawione - ale najczęściej można je zignorować. Po ręcznym odznaczeniu wszystkich nagłówków, w tym tych pochodzących z klienta użytkownika , podczas wprowadzania znaków specjalnych (tutaj podczas tłumaczenia „ Hello World ”) zostaje wykryty problem z kodowaniem:

Jeśli ponownie aktywujesz klienta użytkownika (to zazwyczaj nie powoduje żadnej szkody), API dostarcza zakodowane znaki UTF-8:

Czy już tam jesteśmy i czy mamy wszystkie informacje potrzebne do korzystania z tego interfejsu API poza Google Chrome? Jeśli zmienisz ciąg do przetłumaczenia (pole danych q żądania POST) z, na przykład „Hello world” na „Hello world ! „, Otrzymujemy komunikat o błędzie:

Teraz tłumaczymy ten zmodyfikowany ponownie w Google Chrome za pomocą funkcji tłumaczenia witryny i stwierdzamy, że oprócz parametru q zmienił się również parametr tk (wszystkie inne parametry pozostały takie same):

Oczywiście jest to token zależny od łańcucha, którego strukturę nie jest łatwo dostrzec. Po uruchomieniu tłumaczenia strony ładowane są następujące pliki:

  • 1 plik CSS: translateelement.css
  • 4 grafiki: translate_24dp.png (2x), gen204 (2x)
  • 2 pliki JS: main_de.js, element_main.js

Te dwa pliki JavaScript są zaciemnione i zminimalizowane. Narzędzia takie jak JS Nice i de4js pomagają nam teraz uczynić te pliki bardziej czytelnymi. Aby zdebugować je na żywo, zalecamy rozszerzenie Chrome Requestly, które tuneluje zdalne pliki lokalnie w locie:

Teraz możemy debugować kod (najpierw CORS musi być aktywowany na serwerze lokalnym). Wydaje się, że odpowiednia sekcja kodu do generowania tokenu jest ukryta w pliku element_main.js w tej sekcji:

b7739bf50b2edcf636c43a8f8910def9

Tutaj tekst jest haszowany za pomocą kilku przesunięć . Ale niestety wciąż brakuje nam jednego elementu układanki: oprócz argumentu a (który jest tekstem do przetłumaczenia), do funkcji Bp () jest przekazywany kolejny argument b - rodzaj ziarna, które wydaje się zmieniać od czasu do czasu i obejmuje również wpada do haszowania. Ale skąd on pochodzi? Jeśli przejdziemy do wywołania funkcji Bp () , znajdziemy następującą sekcję kodu:

b7739bf50b2edcf636c43a8f8910def9

Funkcja Hq jest wcześniej zadeklarowana w następujący sposób:

b7739bf50b2edcf636c43a8f8910def9

Tutaj Deobfuscater zostawił trochę śmieci; Po zastąpieniu String.fromCharCode ('...') odpowiednimi ciągami znaków, usuń przestarzałe a () i złóż razem wywołania funkcji [c (), c ()] , wynikiem jest:

b7739bf50b2edcf636c43a8f8910def9

Albo jeszcze łatwiej:

b7739bf50b2edcf636c43a8f8910def9

Funkcja yq została wcześniej zdefiniowana jako:

b7739bf50b2edcf636c43a8f8910def9

Wydaje się, że ziarno znajduje się w globalnym obiekcie google.translate._const._ctkk , który jest dostępny w czasie wykonywania. Ale gdzie to jest ustawione? W drugim, wcześniej załadowanym pliku JS main_de.js, przynajmniej jest on również dostępny na początku. Na początku dodajemy następujący tekst:

b7739bf50b2edcf636c43a8f8910def9

W konsoli faktycznie otrzymujemy aktualne ziarno:

To pozostawia Google Chrome, który najwyraźniej zapewnia ziarno jako ostatnią opcję. Na szczęście jego kod źródłowy (Chromium, w tym komponent Tłumacz) jest open source i dlatego jest publicznie dostępny. Pobieramy repozytorium lokalnie i znajdujemy wywołanie funkcji TranslateScript :: GetTranslateScriptURL w pliku translate_script.cc w folderze Components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

Zmienna z adresem URL jest trwale zdefiniowana w tym samym pliku:

b7739bf50b2edcf636c43a8f8910def9

Jeśli teraz przyjrzymy się bliżej plikowi element.js (po ponownym rozogniskowaniu go), znajdziemy stały wpis c._ctkk - obiekt google.translate również jest odpowiednio ustawiony i uruchamiane jest ładowanie wszystkich odpowiednich zasobów (które już odkryliśmy wcześniej):

b7739bf50b2edcf636c43a8f8910def9

Teraz do rozważenia pozostaje klucz parametru (z wartością AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Wygląda na to, że jest to ogólny klucz API przeglądarki (który można również znaleźć w niektórych wynikach Google ). Jest ustawiony w Chromium w pliku translate_url_util.cc w folderze Components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

Klucz jest generowany w google_apis / google_api_keys.cc z fikcyjnej wartości:

b7739bf50b2edcf636c43a8f8910def9

Jednak test pokazuje, że wywołania API działają tak samo bez tego parametru klucza. Jeśli eksperymentujesz z API, otrzymasz z powrotem kod statusu 200, jeśli ci się powiedzie. Jeśli następnie napotkasz limit, otrzymasz z powrotem kod stanu 411 z komunikatem „ Żądania POST wymagają nagłówka o długości zawartości ”. Dlatego zaleca się dołączenie tego nagłówka (który jest automatycznie ustawiany jako tymczasowy nagłówek w programie Postman).

Format zwracanych przetłumaczonych ciągów jest nietypowy, gdy w jednym żądaniu jest kilka zdań. Poszczególne zdania są otoczone znacznikami i- / b-HTML:

Ponadto Google Chrome nie wysyła pełnego kodu HTML do interfejsu API, ale zapisuje w żądaniu wartości atrybutów, takie jak href (zamiast tego ustawia indeksy, aby tagi można było później przypisać po stronie klienta):

Jeśli zmienisz wartość klienta klucza POST z te_lib (Google Chrome) na webapp ( strony pomocną ), można uzyskać ostateczną tłumaczone ciąg:

Problem polega na tym, że istnieje większe prawdopodobieństwo, że napotkasz ograniczenie szybkości niż przez te_lib (dla porównania: w przypadku aplikacji webowej jest to osiągane po 40 000 znaków, w przypadku te_lib nie ma ograniczenia szybkości). Musimy więc przyjrzeć się bliżej, jak Chrome analizuje wynik. Znajdziemy go tutaj w element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Jeśli wyślesz cały kod HTML do interfejsu API, atrybuty zostaną pozostawione w przetłumaczonej odpowiedzi. Dlatego nie musimy imitować całego zachowania parsowania, a jedynie wyodrębnić końcowy, przetłumaczony ciąg z odpowiedzi. Aby to zrobić, tworzymy mały parser tagów HTML, który odrzuca najbardziej zewnętrzne tagi <i> wraz z ich zawartością i usuwa najbardziej zewnętrzne tagi <b>. Dzięki tej wiedzy (po zainstalowaniu zależności z kompozytorem wymagają fzaninotto / faker vielhuber / stringhelper ) możemy teraz zbudować wersję API tłumaczenia po stronie serwera:

b7739bf50b2edcf636c43a8f8910def9

Poniżej przedstawiono wyniki wstępnego testu przeprowadzonego na pięciu różnych systemach o różnych przepustowościach i adresach IP:

PostaćZnaki na żądanieTrwanieWskaźnik błędówKoszt za pośrednictwem oficjalnego interfejsu API
13.064.662~25003:36:170%237,78€
24.530.510~25011:09:130%446,46€
49.060.211~25020:39:100%892,90€
99.074.487~100061:24:370%1803,16€
99.072.896~100062:22:200%1803,13€
Σ284,802,766~ Ø550Σ 159: 11: 37h0%Σ 5183,41 €

Uwaga: ten wpis na blogu zawierający wszystkie skrypty został napisany wyłącznie do celów testowych. Nie stosować skrypty do użytku produkcyjnego, zamiast pracować z oficjalnego API pomocna .

Plecy