piątek, stycznia 19, 2018

Gradle - narzędzie do artystycznego odstrzeliwania sobie stopy ;-)

Patrzę ostatnio na różne pliki Gradle'owe i stwierdzam, że w przypadku różnych narzędzi powinna zawsze obowiązywać zasada wujka Bena* ze Spidermana**, mówiąca, że "z wielką mocą przychodzi wielka odpowiedzialność".

Gradle daje olbrzymie możliwości, jego moc jest praktycznie nieograniczona...
Ale posiadanie tak wielkiej mocy znaczy też, że można robić różne rzeczy, których się nie powinno robić....
A to tworzyć taski, które mają dynamicznie generowane nazwy, a to używać "generyków" dla tasków, albo budować listę dependencji w locie i pobierać w locie JARy....

Stąd uważam, że każda strona z komendami Gradle'a powinna zaczynać się i kończyć od słów "with great power comes great responsibility"

* - a wcześniej np. Winstona Churchilla,
** - nie znoszę Spidermana, jest to jeden z najbardziej urągających logice superbohaterów ;-)


Podobne postybeta
Nowe jednostki pomiarowe - chyba metry i tiptopki
Jak zmusić Gradle'a do używania ramdysku (na OS X)
Czemu nie znoszę Spidermana.... ;-)
Prawdziwie przenośny komputer ;-)
Chumbawamba "Charlie" - wypijmy za Darwina :-)

niedziela, stycznia 07, 2018

Spectre powinno działać nawet w Java'ie ;-)

[Update: żeby było zabawniej dziś uruchomiłem na służbowym MBP15 i tutaj nie działa :-), co znaczy najpewniej, że coś jest nie tak w mojej implementacji, albo co mniej prawdopodobne, że już macOS został poprawiony ;-)]

Jak rozumiem Spectre działa dzięki temu, że CPU wykonuje spekulatywnie obie gałęzie warunku. Do tego dochodzi to, że jeśli w którejś z tych gałęzi mamy dostęp do pamięci głównej to jest fragment tej pamięci ładowany do cache'a procesora.
Dzięki temu później mierząc czasy dostania się do różnych fragmentów pamięci można próbować zgadnąć jaka była wartość zmiennej użytej do wyliczenia adresu tego ładowanego spekulatywnie fragmentu pamięci.

Chciałem sprawdzić czy podobne podejście pozwoli napisać w Java'ie kawałek kodu, który będzie miał pewien String, ale nigdzie go nie wypisze... i czy da się jego zawartość wypisać ;-)

Na razie odpowiedź jest, że tak, ale nie cały, co pewnie oznacza, że jeszcze nie do końca rozumiem działanie tego mechanizmu ;-)

Kod wygląda mniej więcej tak:

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class Spectre {

    public static void main(String[] args) {
        Map<Integer,Map<Character,Integer>> map = new LinkedHashMap<>();
        String s = "this is a secret string";
        int[] array1 = new int[1024 * 1024 * 10];
        int[] array2 = new int[100 * 1024 * 1024];
        for (int k=0; k<100; k++) {
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (c > array1[i * 16384 * 16] && i > array1[i * 4096 + array1.length / 2]) {
                    int y = array2[c * 8192 * i];
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                        //                    }
                }
                // lest try to guess c...                char cCandidate = 0;
                long min = Long.MAX_VALUE;
                String chars = "abcdefghijklmnopqrstuvwxyz ";
                for (int j = 0; j < chars.length(); j++) {
                    char a = chars.charAt(j);
                    long start = System.nanoTime();
                    int y = array2[a * 8192 * i];
                    long val = System.nanoTime() - start;
                    if (val < min) {
                        min = val;
                        cCandidate = a;
                    }
                }
                System.out.print(cCandidate);
                map.computeIfAbsent(i,key->new HashMap<>());
                map.get(i).put(cCandidate, map.get(i).getOrDefault(cCandidate,0)+1);
            }
            System.out.println();
        }
        System.out.println();
        for (Integer pos:map.keySet()) {
            char maxChar = 0;
            int max=Integer.MIN_VALUE;
            for (Map.Entry<Character,Integer> entry:map.get(pos).entrySet()) {
                if (entry.getValue()>max) {
                    max=entry.getValue();
                    maxChar=entry.getKey();
                }
            }
            System.out.print(maxChar);
        }
    }
}

Jakby co zastrzeżenie, to tutaj jest tylko w celach edukacyjnych.
 No i jeśli ktokolwiek byłby w stanie zbudować na tym co napisałem narzędzie do ataku czegokolwiek to i tak jest o wiele lepszym programistą niż ja i to tutaj mu lub jej wcale nie pomoże.

Jak to działa? :-)
A tak, że cud się dzieje w linii w której robimy sprawdzenie warunku z c (czyli kolejną literką w tajnym łańcuchu).
Ponieważ CPU by ustalić którą ścieżkę wybrać musi czytać z pamięci głównej to nie wie którędy pójdzie wykonanie programu.
Przez to spekulatywnie wykonuje obie ścieżki, chociaż prawdziwy jest tylko ta druga.
Ale jak wykonuje się spekulatywnie pierwsza ścieżka to wartość naszej szukanej zmiennej używana jest do zaadresowania elementu w tablicy, a że ten element nie znajduje się w cache'u to CPU musi załadować też ten fragment pamięci...

Następnie mierzymy czasy dostępu do pamięci odpowiadające poszczególnym literą.
Mierzymy czas dostępów, ten najkrótszy oznacza, że ten kawałek jest już w cache'u... czyli najpewniej ten znak się tam pojawił ;-)

Na moim i7-3610QM z Ubuntu najbliżej wypisuje się:
dhis is a secret string

Ogólnie stabilność znaków na końcu jest dużo wyższa, co pewnie znaczy, że trzeba by było trochę poczarować by nabrało to rumieńców ;-)

A sam mechanizm jest genialny....
Chociaż nie mam pojęcia jak odnajdywantom tego problemu udało się jeszcze czytać inne obszary pamięci, należące do Kernela i innych aplikacji..


Podobne postybeta
Niecne wykorzystanie refleksji... czyli jak poszukać tekstu w drzewie obiektów? ;-)
Zdradliwa Java i 8 królowych ;-)
Sztuczki tropiciela błędów ;-)
Potfór ;-) czyli generator z yield w Java'ie
Hackowanie odczytu danych Exif ;-)