czwartek, września 25, 2008

Sztuczki tropiciela błędów, part 3 - hackujemy klasy finalne ;-)

Dziś próbowałem się w pracy "włamać" do biblioteki odpowiedzialnej za pooling połączeń do bazy danych. Chodzi po prostu o to, że chcę ustalić czy pooling nie robi nieładnych rzeczy zaraz po starcie ;-)
Niestety autor klas poolingu był podejrzliwy i uczynił większość klas klasami finalnymi, przez to nie mogłem użyć mojej ulubionej techniki polegającej na nadpisaniu klasy i założenia breakpointów w metodach które chciałem obserwować...
I wtedy przyszło olśnienie ;-) A co gdyby "wstrzyknąć" swój kod do środka klasy "wroga" i zmusić ją do pracy na moją korzyść? ;-)

Na klasach poolingu tego jeszcze nie zrobiłem, ale idea wydaje się być słuszna. Szczególnie skuteczna powinna być w przypadku klas, które agregują lub są wrapperami dla klas które dziedziczą po czymś dostępnym, lub rozszerzają coś dostępnego.

Wyobraźmy sobie, że mamy kod interfejsu:

package org.przemelek.hack.secure;
public interface Form {
public void login(String user,String password);
}
Oraz finalną klasę SecureForm, która jest "tajna" i finalną klasę opakowującą SecureForm o nazwie SpecialForm, która to klasa używa naszej tajnej klasy.
package org.przemelek.hack.secure;
final class SecureForm implements Form {
protected SecureForm() {
System.out.println("Secure object created");
}
public void login(String user, String password) {
System.out.println("Secure login");
}
}
package org.przemelek.hack.secure;
public final class SpecialForm implements Form {
private Form form;
public SpecialForm() {
form = new SecureForm();
}
public void login(String user, String password) {
form.login(user, password);
}
}
Chcielibyśmy móc coś zrobić w momencie gdy wywołana zostanie metoda login(String,String), z tym że chodzi także o wywołania wewnątrz kodu klasy SpecialForm [tutaj akurat takich nie mamy].

I w tym momencie z pomocą przychodzą nam odbicia ;-)

package org.przemelek.hack;
import java.lang.reflect.Field;
import org.przemelek.hack.secure.Form;
import org.przemelek.hack.secure.SpecialForm;

public class Hack {
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Form form = new SpecialForm();
Field formField = SpecialForm.class.getDeclaredField("form");
formField.setAccessible(true);
final Form oldForm = (Form)formField.get(form);
formField.set(form, new Form() {
public void login(String user, String password) {
System.out.println("Hacked login :-)");
System.out.println("user:"+user+" password:"+password);
oldForm.login(user, password);
}
});
form.login("test", "tost");
}
}


Przez odbicia prosimy o zadeklarowane pole form, zapamiętujemy jego wartość, następnie tworzymy klasę anonimową na podstawie znanego nam interfejsu Form, i podmieniamy jej instancją wartość pola form.
I już działa ;-)
Gdy gdziekolwiek w naszym kodzie, czy kodzie biblioteki którą hackujemy nastąpi wywołanie metody login(String,String) z klasy SpecialForm wywołany zostanie także nasz kod :-) Sztuczki tropiciela błędów, part 2
Sztuczki tropiciela błędów, part 1


Podobne postybeta
Sztuczki tropiciela błędów, part 4
Sztuczki tropiciela błędów, part 2 ;-)
Niecne wykorzystanie refleksji... czyli jak poszukać tekstu w drzewie obiektów? ;-)
Ile to jest 1+1 w Java'ie?
Sztuczki tropiciela błędów ;-)