Ostatnio czytając specyfikację języka Java natknęło mnie do napisania posta na ten temat. Pomimo umiejętności programowania w danym języku programista może go wcale tak dobrze nie znać. Pierwszym miejscem jakie warto w ogóle odwiedzić jest dokument ze specyfikacją danego języka. Często znajdują się tam bardzo ciekawe zagadnienia o których mogliśmy wcześniej nie mieć bladego pojęcia.
Jedną z takich rzeczy jest na przykład struktura pliku z kodem źródłowym dla kompilatora Javy. To, że kompilator chętnie połknie plik zakodowany w UTF’ie nie jest dla nikogo niespodzianką, ale to że przed procesem kompilacji wykonywany jest proces przywracania znaków z zakresu unicode poddanych modyfikacji (ang. escape characters). Czyli teoretycznie w kod możemy równie dobrze wstawić znak w postaci \uXXXX gdzie X zastępuje dowolną cyfrę. Notacja ta jest przydatna, kiedy w kodzie chcemy umieścić znaki specjalne. Niestety niesie za sobą tez pewne dodatkowe możliwości. Jesteśmy bowiem w stanie poddać całość lub fragment kodu procesowi zamiany znaków na ich wyescapeowane odpowiedniki. Jest to więc sposób na najłatwiejszą obfuskację w kodzie źródłowym.
Przykład:
1 2 3 4 5 6 7 8 9 10 11 |
public class Main { public static void main(String[] args) { \u0053\u0079\u0073\u0074\u0065\u006d \u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e \u0028\u0022\u0053\u006e\u0065\u0061\u006b\u0079\u0020\u003a\u003e\u0022 \u0029\u003b } } |
Powyższy kod po kompilacji, która oczywiście przebiegnie pomyślnie, wyświetli napis: Sneaky :>
I to własnie rodzi możliwość wykonania kodu z wnętrza komentarza. Ponieważ możemy przerwać ten komentarz i wykonać operację. Co wcale nie będzie takie proste do identyfikacji na pierwszy rzut oka.
1 2 3 4 5 6 7 8 9 |
public class Main { public static void main(String[] args) { int a = 2; // to jest moja zmienna a i wynosi ona 2 \u000a\u0061\u002b\u002b\u003b System.out.println(a); //jednak 3? \u000aa++; System.out.println(a); //a teraz nawet 4 } } |
Wynikiem będą kolejno cyfry 3 i 4, dzieję się tak ponieważ znak \u000a jest znakiem nowej linii, który kończy komentarz liniowy. Oczywiście komentarz blokowy tez jesteśmy w stanie w taki sam sposób złamać.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Main { public static void main(String[] args) { int a = 2; /* * Dokumentacja w komentarzu do kodu * \u002a\u002f\u0061\u002b\u002b\u003b\u002f\u002a */ System.out.println(a); // 3 /* * Dokumentacja w komentarzu do kodu * \u002a\u002fa++;\u002f\u002a */ System.out.println(a); // 4 } } |
Oczywiście, jeśli ktoś jest spostrzegawczy to nie będzie miał problemu ze stwierdzeniem, że coś tu jest nie tak. Jednak często kolorowanie kodu w ogóle sobie z tym nie radzi, a w ramach ciekawostki to bardzo fajny trik.
Bonus:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
\u0070\u0075\u0062\u006c\u0069\u0063 \u0063\u006c\u0061\u0073\u0073 \u004d\u0061\u0069\u006e\u007b \u0070\u0075\u0062\u006c\u0069\u0063 \u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064 \u006d\u0061\u0069\u006e \u0028\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0061\u0072\u0067\u0073\u0029 \u007b\u0053\u0079\u0073\u0074\u0065\u006d \u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u0028 \u0022\u0048\u006f\u0077\u0020\u0063\u006f\u006f\u006c\u0020\u0069\u0073\u0020\u0074\u0068\u0061\u0074\u003f\u0022 \u0029\u003b\u007d\u007d |
Tak, to się skompiluje i nawet uruchomi 🙂