2013-04-14 18 views
7

Istnieje kilka sposobów uzyskania dostępu do kodu źródłowego biblioteki z kodu Ruby, który wymaga/ładuje tę bibliotekę. Wśród tych sposobów niektórzy czytają plik biblioteki bezpośrednio i analizują go. Inni uzyskują dostęp do źródła za pomocą wbudowanych metod dostarczających informacji o źródle (takich jak abstrakcyjne drzewo składniowe). W sytuacji, w której nie mam dostępu do bezpośredniego odczytu zawartości pliku (tak jak w poprzednich przypadkach), jedynym sposobem uzyskania dostępu do źródła byłoby uzyskanie dostępu do wbudowanych metod dostarczających informacje. Poprzez ponowne zdefiniowanie tych metod, aby zrobić coś innego, całkowicie stracę dostęp do kodu źródłowego. Jaki jest minimalny zestaw metod, że jeśli zmienię je na coś innego, całkowicie stracę dostęp do kodu źródłowego biblioteki na zewnętrznym pliku?Jakie metody umożliwiają dostęp do kodu źródłowego?


Aby przeformułować pytanie

Załóżmy:

  • znajduje się użytkownik, który może pisać żadnego kodu Ruby w pliku A.
  • Jest statyczny plik Ruby B napisany przez mnie, który ładuje plik A i wywołuje główną procedurę zdefiniowaną w A, a także definiuje niektóre klasy/metody, z których użytkownik może korzystać w A.
  • Użytkownik nie ve + r (odczyt) lub + w (zapis) pozwolenie na B.

Które (standardowe Ruby) metody muszę przedefiniować (anulować) lub usunąć pisząc tak w pliku B, aby uniemożliwić dla użytkownika, aby uzyskać dostęp do źródła zapisanego w pliku B (za pomocą dowolnego kodu, jaki użytkownik może zapisać w pliku A), kiedy uruchamiam plik B?

Istnieje kilka bibliotek, takich jak czarnoksiężnik, podważa, że ​​można wyodrębnić kod źródłowy metod, do których ma dostęp. Musi istnieć kilka prostych poleceń w prostym języku Ruby, na których opierają się te biblioteki, aby umożliwić im dostęp do kodu źródłowego. Jakie są metody, dzięki którym tego rodzaju rzeczy są możliwe?

Jeśli nie znasz pełnej odpowiedzi, ale wiesz, jak konkretna biblioteka wyodrębnia źródło jakiejś metody, to nadal pomoże.

+0

Myślę, że możesz zdefiniować jakąś klasę abstrakcyjną lub coś podobnego do interfejsu w Javie, która niczego nie implementuje. Interfejs będzie publicznie dostępny z zewnętrznych komponentów. Następnie możesz dziedziczyć klasę abstrakcyjną, aby ją zaimplementować wewnętrznie. –

+0

Podczas redefiniowania takich metod, możesz użyć 'Module # alias_method', aby upewnić się, że masz" kopię "oryginalnej metody, którą modyfikujesz. http://apidock.com/ruby/Module/alias_method – fmendez

+1

_system_, _backtick_, _fork/exec_, itp., będą musiały być wyłączone. –

Odpowiedz

6

TL; DR: Ruby-tylko rozwiązania może korzystać tylko source_location, więc po prostu przedefiniować to, aby powrócić coś jak ['/some/empty/file', 1]. C hacki do interpretera nie używają source_location, ale można zapobiec użyciu rozszerzeń C przez blokowanie/białe listy require i przyjaciół.


Z jednej strony, aby móc wykonać skrypt Ruby, trzeba być w stanie go odczytać ...

Ale wracając do pytania. Wiem, że Sourcify nie stosuje żadnej mistycznej metody poza małą metodą na Proc i Metodzie o nazwie source_location, która podaje nazwę pliku i numer linii jako metodę/proc. Z doświadczenia wiem, że to podejście jest bardzo delikatne, wymaga napisania jakiegoś analizatora składni i tylko czasami działa w uzasadnionych sytuacjach. Tak więc, Sourcify jest już wyłączony, jeśli przedefiniujesz source_location w B, aby zwrócić coś podobnego do /dev/null, line 0 i pozwolić Sourcify rzucić wyjątek źródła nie-Rubinowego.

Z Pry's source, wydaje się, że Pry używa tego samego podejścia source_location, więc dwie ptaki z jednym kamieniem.

Teraz jest jeszcze jedna opcja dla wszystkich tych bibliotek, która ma zejść do C i zhackować interpreter, aby zapisać kod źródłowy. To prawie bezbłędne. Ale wciąż możemy uniknąć niebezpieczeństwa w bardzo prosty sposób. Ktoś może dołączyć cały kod do źródła metody Pry'ego w A. Nie można jednak włączyć wbudowanych rozszerzeń C/C bez konieczności biblioteki C. Rozwiązanie jest więc oczywiste: Przedefiniuj require i require_relative i , aby nie działały lub zezwalały tylko na określone biblioteki. W ten sposób możesz zapobiec atakom C.

W przypadku rezonansu magnetycznego nie ma sposobu (z kodu Ruby), poza tym, aby wykonać to w postaci source_location. Więc idź!

Edycja: Według @banister, z MRI 2.0+ istnieje wbudowana metoda binding_of_caller, która może zastąpić lokalizację źródłową. Nuke też. ;)

Ostrzeżenie: Ruby nie jest dobrym językiem do tego. Jeśli możesz metaprogramować je, prawdopodobnie mogą metaprogramować, chyba że jesteś w innym procesie.

+0

Dzięki. W końcu dostałem odpowiedź, która jest do rzeczy. Poprzez unieważnienie 'location_location', myślisz, że mogę również uniknąć dostępu do drzewa składni abstrakcyjnej (AST)? – sawa

+2

@sawa, co próbujesz zrobić, jest bardzo dziwne - ale nie wystarczy rozmowa z adresem źródłowym. Kombinacja binding_of_caller i Binding # eval ("__ FILE__") itp zwróci znacznie te same informacje co lokalizacja_źródłowa. Każdy, kto ma wystarczająco dużo inteligencji, będzie mógł obejść każdy blok, w którym się znajduje, naprawdę, Ruby nie jest * językiem dla tego rodzaju rzeczy, nawet nie próbuj :) – horseyguy

+0

@sawa: Z tego, co mogę powiedzieć, ty może uzyskać dostęp do AST tylko przez rozszerzenie C, którego nie można załadować bez kontroli nad środowiskiem Ruby lub używając 'require'. – Linuxios

0

To naprawdę proste, jeśli używasz niesamowitego klejnotu "method_source" autorstwa Johna Maira (producenta Pry): Metoda musi być zaimplementowana w Ruby (nie C) i musi być załadowana z pliku (nie irb).

Oto przykład wyświetlania kodu źródłowego metoda w konsoli Rails z method_source:

$ rails console 
    > require 'method_source' 

    # the following prints out the method code for #lookup in the Rails I18n Backend: 

    > I18n::Backend::Simple.instance_method(:lookup).source.display 
    def lookup(locale, key, scope = [], options = {}) 
     init_translations unless initialized? 
     keys = I18n.normalize_keys(locale, key, scope, options[:separator]) 

     keys.inject(translations) do |result, _key| 
     _key = _key.to_sym 
     return nil unless result.is_a?(Hash) && result.has_key?(_key) 
     result = result[_key] 
     result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol) 
     result 
     end 
    end 
    => nil 

Kod Ruby, który jest wyświetlany musi pochodzić z pliku, który został już załadowany, (oczywiście to musi być czytelnym) i musi to być natywny kod Ruby (to nie działa dla bibliotek skompilowanych/połączonych).

Zobacz także:

Powiązane problemy