Discussion:
Pytanie o ClassLoader
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
Bartłomiej Słowik
2006-01-17 13:51:24 UTC
Permalink
Czesc

Wiecie moze czy sie da jak sie da to jak ustawic zeby maszyna wirtualna
nie korzystala z domyslnego ClassLoader'a tylko z napisanego przeze mnie?

MojClassLoader - dziedziczacy po java.lang.ClassLoader
KlasaTestowa - klasa ktora powinna byc ladowana moim classloaderem

MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)e.loadClass("KlasaTestowa").newInstance();

To dziala i wyglada ok ale jak zrobic zeby tworzenie obiektow operatorem
new korzystalo z innego niz standardowy ClassLoader?

Dzikuje za pomoc.
--
Pozdrawiam
Bartlomiej `baslow` Slowik
Piotr Kobzda
2006-01-17 16:03:26 UTC
Permalink
Post by Bartłomiej Słowik
MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)e.loadClass("KlasaTestowa").newInstance();
To dziala i wyglada ok ale jak zrobic zeby tworzenie obiektow operatorem
new korzystalo z innego niz standardowy ClassLoader?
Zrobić new w KlasaTestowa.


piotr
Bartłomiej Słowik
2006-01-17 16:07:25 UTC
Permalink
Post by Piotr Kobzda
Post by Bartłomiej Słowik
MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)e.loadClass("KlasaTestowa").newInstance();
To dziala i wyglada ok ale jak zrobic zeby tworzenie obiektow
operatorem new korzystalo z innego niz standardowy ClassLoader?
Zrobić new w KlasaTestowa.
Hmm mozesz bardziej szczegolowo, bo szczerze to nie bardzo rozumiem.

Dzikeuje.
--
Pozdrawiam
Bartlomiej `baslow` Slowik
Piotr Kobzda
2006-01-17 17:26:28 UTC
Permalink
Post by Bartłomiej Słowik
Hmm mozesz bardziej szczegolowo, bo szczerze to nie bardzo rozumiem.
Mogę. :)

Po pierwsze, słowo kluczowe new używa klasy, która wynika z kontekstu
jego użycia. Kontekstem tym jest klasa, w której kodzie new zostało użyte.

Po drugie, klasy zależne od konkretnej klasy ładowane są przez jej
class-loader.

Ładowanie lub (jak w Twoim przykładzie) dopiero tworzenie instancji
klasy załadowanej Twoim class-loaderem spowoduje zapytanie go o klasy
zależne. A w konsekwencji, new wydane z kodu Twojej klasy używać będzie
klas załadowanych przez Twój class-loader.

Oczywiście, jeśli to co napisałeś to poprawny class-loader (znaczy
napisany zgodnie ze sztuką), to ładowanie klas delegował będzie w
pierwszej kolejności do swego rodzica, więc jeśli używasz klas, które
załadował, lub załaduje inny class-loader w hierarchii (zwykle np. klasy
String), to za ładowanie klas od nich zależnych odpowiadał będzie już
ich własny class-loader, a nie Twój. Klasom załadowanych Twoim
class-loaderem, nie powinno to jednak w niczym przeszkadzać.


Podmiana bootstrap'owego class-loadera, o którą pytałeś -- nawet jeśli
wykonywalna (choć nigdy nie próbowałem) -- to nienajlepszy pomysł i
mocno odradzam. Jaki zresztą miałaby cel, przyznam, że też trudno mi
odgadnąć...


HTH,
piotr
Bartłomiej `baslow` Słowik
2006-01-17 23:05:58 UTC
Permalink
Post by Piotr Kobzda
Mogę. :)
...
Przepraszam za przydlugi post...

Dzieki ale nadal mi to nie rozjasnia ;( a myslalem ze dobrze znam jave
lol...
Wbrew wszelkim regula grup dyskusyjnych napisze wiekszego posta, zalacze
przyklady i prosilbym jak mozesz (mozecie) pomoc mi na tych przykladach
rozwiazac ten moj problem.
Generalnie do czego mi to potrzebne, chcialbym zeby wszystkie klasy
napisane przeze mnie poza klasa z mainem byly zaszyfrowane i podczas
ladowania zostaly rozszyfrowywane...
Jest tak:
- klasa mojeg loadera (narazie malo porzadnie ale dziala jak powinna):
public class ECL extends ClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println( "loadClass: " + name );
byte[] data = null;
try { FileInputStream fi = new FileInputStream( "bin\\" + name +
".class" );
data = new byte[ fi.available() ];
fi.read( data ); }
catch ( FileNotFoundException e ) { e.printStackTrace(); }
catch ( IOException e ) { e.printStackTrace(); }
try { return findSystemClass(name); }
catch( Exception e ) {}
Class klasa = defineClass( name, data, 0, data.length, null );
return klasa;
}
}
- klasa testowa ktora w koncowej wersji ma byc po kompilacji szyfrowana
a podczas ladowania rozszyfrowana:
public class Testowa {
public Testowa() {
System.out.println( "konstruktor Testowa" );
}
public void metoda() {
System.out.println( "metoda" );
}
}
- no i klasa uruchamiajaca wszystko:
public class Start {
public Start() {
((Testowa)newInst( "Testowa" )).metoda();
}
public Object newInst( String name ) {
ECL ecl = new ECL();
Object obj = null;
try {
try { obj = ecl.loadClass( name ).newInstance(); }
catch (InstantiationException e) { e.printStackTrace(); }
catch (IllegalAccessException e) { e.printStackTrace(); } }
catch ( ClassNotFoundException e ) { e.printStackTrace(); }
return obj;
}
public static void main( String[] args ) {
new Start();
}
}

Wszystko dziala wynik dostaje:
loadClass: Testowa
konstruktor Testowa
metoda
wiec wyglada ze jest niezle... ale co zrobic zeby nie trzeba bylo robic
calej szopki z newInstance itp?? Czy to wogole da sie zrobic??

Jest cos takiego jeszcze:
Thread t = Thread.currentThread();
t.setContextClassLoader( new ECL() );
Tylko nie wiem czy to wystarczy to jeden a dwa jak dokladnie tego uzyc...

Bede wdieczny za pomoc, dziekuje.
--
Pozdrawiam
Bartlomiej `baslow` Slowik
Piotr Kobzda
2006-01-18 03:20:28 UTC
Permalink
Post by Bartłomiej `baslow` Słowik
public class ECL extends ClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println( "loadClass: " + name );
byte[] data = null;
try { FileInputStream fi = new FileInputStream( "bin\\" + name +
".class" );
data = new byte[ fi.available() ];
fi.read( data ); }
W ten sposób się plików nie czyta! Poczytaj o available() i obsłudze
strumieni w ogóle.
Choć akurat w tej chwili nie ma to tu większego znaczenia, jako że ...
Post by Bartłomiej `baslow` Słowik
catch ( FileNotFoundException e ) { e.printStackTrace(); }
catch ( IOException e ) { e.printStackTrace(); }
try { return findSystemClass(name); }
^ ... to i tak próbujesz.
Post by Bartłomiej `baslow` Słowik
catch( Exception e ) {}
Class klasa = defineClass( name, data, 0, data.length, null );
return klasa;
}
}
Ogólnie ClassLoaderów tak się nie pisze. Polecam dokładną lekturę API i
wskazówek odnośnie implementacji. A i na sieci sporo przykładów i
pomocnych materiałów znajdziesz.
Post by Bartłomiej `baslow` Słowik
public class Start {
public Start() {
((Testowa)newInst( "Testowa" )).metoda();
^ tu jest jawne odwołanie
^ i tu
Post by Bartłomiej `baslow` Słowik
loadClass: Testowa
konstruktor Testowa
metoda
wiec wyglada ze jest niezle...
Niestety tylko tak wygląda. :/
Post by Bartłomiej `baslow` Słowik
ale co zrobic zeby nie trzeba bylo robic
calej szopki z newInstance itp??
Ta "szopka" jest tu akurat niezbędna.
Post by Bartłomiej `baslow` Słowik
Czy to wogole da sie zrobic??
Owszem.
Post by Bartłomiej `baslow` Słowik
Thread t = Thread.currentThread();
t.setContextClassLoader( new ECL() );
Tylko nie wiem czy to wystarczy to jeden a dwa jak dokladnie tego uzyc...
ContextClassLoader warto też ustawić na ten Twój już utworzony (byle nie
kolejny nowy!). Tyle, że to właściwie ozdobnik, który niczego tu nie
rozwiąże.


Jeśli nadal będziesz miał z tym problemy, to spróbuj może po prostu zdać
się na intuicję. Ja idę spać...


piotr
Micha³ Olek
2006-01-18 11:12:41 UTC
Permalink
Post by Bartłomiej `baslow` Słowik
Generalnie do czego mi to potrzebne, chcialbym zeby wszystkie klasy
napisane przeze mnie poza klasa z mainem byly zaszyfrowane i podczas
ladowania zostaly rozszyfrowywane...
Co do szyfrowania klasa w ten sposób to to już było.
Nie zabezpieczysz się tym wcale, ponieważ da się to w miarę prosto obejść.
Po szczegóły odsyłam do któregoś z artykulów w JavaWorld (chyba).

Generaalnie sprawa polega na tym, że gdy masz juz nawet program i swoje
klasy
w zaszyfrownej postaci to by java go wykonywała to musi w pewnym momencie
dostać kod tej klasy w postaci "czystej", nie zaszyfrowanej. Załóżmy że za
obsługę tej czystej postaci
jest odpowiedzialna klasa X z któregoś z pakietów jdk. (Nie pamietam juz
jaka jest jej
nazwy ale jest taka klasa). W tym momencie wszystko co trzeba zrobić to
lokalnie podmienić
ją na taką która będzie każdy kod zrzucać gdzieś dodatkowo na dysk.

Pozdrawiam,
Michał Olek
Piotr Kobzda
2006-01-18 12:34:12 UTC
Permalink
Post by Micha³ Olek
Post by Bartłomiej `baslow` Słowik
Generalnie do czego mi to potrzebne, chcialbym zeby wszystkie klasy
napisane przeze mnie poza klasa z mainem byly zaszyfrowane i podczas
ladowania zostaly rozszyfrowywane...
Co do szyfrowania klasa w ten sposób to to już było.
Nie zabezpieczysz się tym wcale, ponieważ da się to w miarę prosto obejść.
Po szczegóły odsyłam do któregoś z artykulów w JavaWorld (chyba).
Pytanie tylko, przed czym to zabezpieczyć miało? Takie szyfrowanie
przydatne być może jeśli klient ma u siebie np. tylko jara startowego z
deszyfratorem, a cały kluczowy fragment aplikacji ciągnięty jest siecią.
A chcemy zabezpieczyć się wyłącznie przed niepożądanym obserwatorem
transferu tych klas. Pewnie, że są protokoły, które w tym pomóc mogą,
nie zawsze jednak na takie pozwolić sobie można.
Post by Micha³ Olek
Załóżmy że za
obsługę tej czystej postaci
jest odpowiedzialna klasa X z któregoś z pakietów jdk. (Nie pamietam juz
jaka jest jej
nazwy ale jest taka klasa).
Klasa nazywa się ClassLoader, a podmienić wystarczy jej metody
defineClass().
Post by Micha³ Olek
W tym momencie wszystko co trzeba zrobić to
lokalnie podmienić
ją na taką która będzie każdy kod zrzucać gdzieś dodatkowo na dysk.
Jasne. I choć jest to zawsze coś do zrobienia "ekstra", to żadne to
utrudnienie. Sam algorytm dekodowania zaszyfrowany nie będzie, więc na
dobrą sprawę, wystarczy się dekodującemu ClassLoaderowi przyjrzeć, by
oryginały pozostałych klas wydobyć.


Jeśli jednak chodzi o ochronę klas przed wścibskim klientem, to jedynym
sensownym rozwiązaniem są obfuskatory.
Oczywiście ukryć działania aplikacji nie będą w stanie i wytrwały haker
bez problemów istotę działania z bajtkodu wyciągnie. Nie jest już jednak
w stanie odzyskać oryginalnego kodu z dokładnością choćby do nazw klas,
metod itp, co znacznie czytelność kodu potrafi już zaciemnić.
Niezależnie jednak od wszelakich utrudnień bajtkod po stronie klienta to
zawsze otarta aplikacja, reszta zależy już od umiejętności, czasu i
chęci ewentualnego ciekawskiego.


piotr
Micha³ Olek
2006-01-18 13:15:59 UTC
Permalink
Post by Piotr Kobzda
Jeśli jednak chodzi o ochronę klas przed wścibskim klientem, to jedynym
sensownym rozwiązaniem są obfuskatory.
Oczywiście ukryć działania aplikacji nie będą w stanie i wytrwały haker
bez problemów istotę działania z bajtkodu wyciągnie. Nie jest już jednak w
stanie odzyskać oryginalnego kodu z dokładnością choćby do nazw klas,
metod itp, co znacznie czytelność kodu potrafi już zaciemnić.
Niezależnie jednak od wszelakich utrudnień bajtkod po stronie klienta to
zawsze otarta aplikacja, reszta zależy już od umiejętności, czasu i chęci
ewentualnego ciekawskiego.
Jesli to ma służyć tylko do do zabezpieczenia przed obserwatorem transferu
to da rade.
Co do prostego wyciągniecia kodu z bajtkodu to służe przykładami klas tak
zaobfuskowanymi,
że i z bajtkodem moga być problemy. Oczywiście ponosząc dość duże nakłady
można
dojść co się będzie działo, ale kodu źródłowego (nawet z zamazanymi nazwami)
raczej się nie
odzyska. Ewentualnie fragmenty :) Tak więc sytuacja nie jest tak
beznadziejna.

Michał Olek
Piotr Kobzda
2006-01-18 14:14:13 UTC
Permalink
Post by Micha³ Olek
Co do prostego wyciągniecia kodu z bajtkodu to służe przykładami klas tak
zaobfuskowanymi,
że i z bajtkodem moga być problemy. Oczywiście ponosząc dość duże nakłady
można
dojść co się będzie działo, ale kodu źródłowego (nawet z zamazanymi nazwami)
raczej się nie
odzyska. Ewentualnie fragmenty :) Tak więc sytuacja nie jest tak
beznadziejna.
Jest. :) Sam bajtkod, to już wystarczająco dobry kod źródłowy, reszta to
tylko kwestia samozaparcia. Nie ma takiego bajtkodu, którego nie dałoby
się rozwalić, przynajmniej dopóki możliwe jest jego wykonanie przez JVMę.
Odnośnie zaś Twej propozycji podzielnia się przykładami, to dziękuję,
nie skorzystam :) sam ma takich niemało... A z perspektywy doświadczeń
wiem, że szkoda po prostu czasu na takie zabawy, istnieją ciekawsze
zajęcia... :) Choć fakt, bywało, że do jednych z ciekawszych także
rzeczone zabawy zaliczałem... :) szczęściem, wyrosłem wydaje mi się już
z tego... :))


piotr
Micha³ Olek
2006-01-19 10:53:38 UTC
Permalink
Post by Piotr Kobzda
Post by Micha³ Olek
Co do prostego wyciągniecia kodu z bajtkodu to służe przykładami klas tak
zaobfuskowanymi,
że i z bajtkodem moga być problemy. Oczywiście ponosząc dość duże nakłady
można
dojść co się będzie działo, ale kodu źródłowego (nawet z zamazanymi
nazwami) raczej się nie
odzyska. Ewentualnie fragmenty :) Tak więc sytuacja nie jest tak
beznadziejna.
Jest. :) Sam bajtkod, to już wystarczająco dobry kod źródłowy, reszta to
tylko kwestia samozaparcia. Nie ma takiego bajtkodu, którego nie dałoby
się rozwalić, przynajmniej dopóki możliwe jest jego wykonanie przez JVMę.
Odnośnie zaś Twej propozycji podzielnia się przykładami, to dziękuję, nie
skorzystam :) sam ma takich niemało... A z perspektywy doświadczeń wiem,
że szkoda po prostu czasu na takie zabawy, istnieją ciekawsze zajęcia...
:) Choć fakt, bywało, że do jednych z ciekawszych także rzeczone zabawy
zaliczałem... :) szczęściem, wyrosłem wydaje mi się już z tego... :))
Eee, no co Ty ?!? Wymiękasz ??? ;)))
Rafał Markiewicz
2006-01-19 11:26:43 UTC
Permalink
[..] reszta to
tylko kwestia samozaparcia.
I opłacalności :) Jeżeli obfuscator wygeneruje taki kod, że dekompilacja i
analiza jest nieopłacalna, to jest to sukces :)

Rufik
Piotr Kobzda
2006-01-19 12:03:04 UTC
Permalink
Post by Rafał Markiewicz
[..] reszta to
tylko kwestia samozaparcia.
I opłacalności :)
Też. Jedno z drugim powiązane, "czas, to pieniądz"... :)
Post by Rafał Markiewicz
Jeżeli obfuscator wygeneruje taki kod, że dekompilacja i
analiza jest nieopłacalna, to jest to sukces :)
Stąd napisałem, że jest to jedyne sensowne rozwiązanie jakie znam.
Nie zmienia to jednak faktu, że bezpieczne to rozwiązanie nie jest.

I rozsądnym nie nazwę nikogo, kto jakieś tajne dane zaszyje w
class-file'u, zobfuskuje Bóg wie czym, wystawi publicznie do pobrania w
internecie i z radosnym uśmiechem oznajmi szefowi:

-- Don't worry Boss, everything is secure!. :))

Choć oczywiście zdaję sobie sprawę, że co kraj to obyczaj... ;))


piotr

Piotr Kobzda
2006-01-17 22:17:13 UTC
Permalink
Post by Bartłomiej Słowik
Post by Piotr Kobzda
Post by Bartłomiej Słowik
MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)e.loadClass("KlasaTestowa").newInstance();
To dziala i wyglada ok ale jak zrobic zeby tworzenie obiektow
operatorem new korzystalo z innego niz standardowy ClassLoader?
Zrobić new w KlasaTestowa.
Hmm mozesz bardziej szczegolowo, bo szczerze to nie bardzo rozumiem.
A! i jeszcze jedno. Dopiero zauważyłem, że Ty nie robisz tego jak
trzeba. Bo choć to działa, to (zakładając, że Twój class-loader napisany
jest zgodnie z zasadami) jest równoważne z takim kodem:

MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt = new KlasaTestowa();

Samo jawne referowanie w kodzie klasy KlasaTestowa powoduje, że jej
loaderem jest loader klasy zawierającej Twój kod. A jak już pisałem,
sekret tkwi w tym, by klasy, których chcesz użyć były ładowane w
kontekście Twojego loadera (i tylko jemu były dostępne).

Aby Twój "kod startowy" nie referował tych klas, musisz posłużyć się
tylko refleksją (bez jakichkolwiek jawnych referencji do ładowanych
klas), czyli zrobić np.

new MojClassLoader().loadClass("KlasaTestowa").newInstance();

Pamiętaj też, że jeśli domyślny class-loader będzie w stanie załadować
Twoją klasę, to nawet jeśli nie referujesz jej jawnie w kodzie, Twój
class-loader i tak poprosi najpierw rodzica o załadowanie.
Trzeba, więc albo delegacji Twoich klas mu zabronić (co choć nieładne,
to praktykowane bywa), albo po prostu usunąć te klasy z classpath i
zaszyć w class-loaderze sposób dostępu do nich.

Daj znać, czy tym razem coś pomogło?


piotr
Bartłomiej `baslow` Słowik
2006-01-18 00:40:12 UTC
Permalink
Post by Piotr Kobzda
Post by Bartłomiej Słowik
Post by Piotr Kobzda
Post by Bartłomiej Słowik
MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)e.loadClass("KlasaTestowa").newInstance();
To dziala i wyglada ok ale jak zrobic zeby tworzenie obiektow
operatorem new korzystalo z innego niz standardowy ClassLoader?
Zrobić new w KlasaTestowa.
Hmm mozesz bardziej szczegolowo, bo szczerze to nie bardzo rozumiem.
new MojClassLoader().loadClass("KlasaTestowa").newInstance();
U mnie to tez jest tak samo tylko blad mi sie wkradl jak pisalem kod w
mailu ;)

MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)mcl.loadClass("KlasaTestowa").newInstance();
I tak dziala... ale co zrobic zeby loadera mcl wywolalo takie polecenie:
new KlasaTestowa(); da sie to wogole zrobic??
Post by Piotr Kobzda
Daj znać, czy tym razem coś pomogło?
A nie dalo by sie zeby loadera mcl wywolalo polecenie new KlasaTestowa();??

Dziekuje
--
Pozdrawiam
Bartlomiej `baslow` Slowik
Piotr Kobzda
2006-01-18 02:17:13 UTC
Permalink
Post by Bartłomiej `baslow` Słowik
Post by Piotr Kobzda
new MojClassLoader().loadClass("KlasaTestowa").newInstance();
U mnie to tez jest tak samo tylko blad mi sie wkradl jak pisalem kod w
mailu ;)
Nie jest tak samo. I nie chodziło mi o tę literówkę...
Post by Bartłomiej `baslow` Słowik
MojClassLoader mcl = new MojClassLoader();
KlasaTestowa kt =
(KlasaTestowa)mcl.loadClass("KlasaTestowa").newInstance();
Zauważ, że w Twoim kodzie jest *jawne* odwołanie do KlasaTestowa. Jawne
tzn. zapisane w klasie z tym kodem (czego konsekwencje juz tłumaczyłem).
W tym co Ci podałem odwołania jawnego nie ma, jest tylko string z nazwą
tej klasy.
Post by Bartłomiej `baslow` Słowik
new KlasaTestowa(); da sie to wogole zrobic??
Przez new, nie. W sposób jaki opisałem, tak. :)
Post by Bartłomiej `baslow` Słowik
Post by Piotr Kobzda
Daj znać, czy tym razem coś pomogło?
A nie dalo by sie zeby loadera mcl wywolalo polecenie new KlasaTestowa();??
Przez new, nie. W sposób jaki opisałem, tak. :)


piotr
Loading...