The code for this entry originally came in one single overlong line — had I tried to render that here your browser would probably start hating me so I have decided to insert escaped newlines:
require'zlib';eval(Zlib::Inflate.inflate("x\332\355WKo\333F\020\276\367W\250\ \262\001\222\tM\357\246M\017\242\211\242h\200\036\212`\201\026\350\205`\f=h\233\ \301Zt%\273A-2\277\275\363\315\222\334\241,#v\214\366T\331\262\326\303y\3177\ \263\243M\371\347]\265)\203UuYnoO\257Wo\203\364>[T\353U\265\276L\257\353\325\ \235-'\277\226\233ui\323Uy1\251\027\027\341\253\371\346r\e\245u\366\216\205f\ \263\367\357\336&\353\362S\010zr=\277\3315w\315]r[\237o\333\344c]\255#>\343O\ \025\352\037\334\177\341\367\364\271\t\003\245\337|\027\304\364aM@:\363\260\316\ >\237\232\323(\326\252(\327\253\t\275\323\332h\253\224V\306d\247\037\362\371\ \311}\321\314f\356\363C\016\311\342\365\361ij\026\037\313\345\355\3577\363e\231\ \224\363\345\325y\315\204]\263l\3620\177\317\241\024M\376\263\235o\267Et\222/\ \223%\037\213\374D\323\373M\3214Kv-\373<\361\026\233&\\\304\253,\354\270\263\ \314)\232\3748\311\247]z\216v\3136\235\306\323\243\035\262\263\214\332\f\024\ \342\257\327\345\264\230\205\313o36\3122\254e2\260\236\2610\202\354\037\260\256\ (f=/\313:Z\024\245\313\244Zoo\347\353ey~]\336^\325\253-\a\273k\252fqv6\235\333\ j\276\355\236tV\252\230\377F\276\n\333\277\257\241\345\206\262\323\306G\273\352\ \340\203t\332\246\2441`'\316\316\266\245\275H\0032\377l\253\017,=42E\002\360\ \236\246\345_s;Y\274^\305\367Q\233\036\233\276\016\312\2450=\256=\305U\202\230\ \254\"\222\265\004\217\237~\373\345\017\"h\243\210\307j\235\251\205V8\353\304X\ \372!1CGc-\251\240\337\020\317\361#\036\023\n\2556\254Cg3\002}\265\356s\235\202\ K[K\022\020 \243\206\216\241p3\33255\350\232\036\030q$\233\344!\363\204^},$\023\ Xg\235:\364r1\"1\344\277\261\207\031(\301DE\260\344\026Y\177\345\036\221\204mP\ \263\266Mk\305\366\210%3\220\302S\322\306IR\316\377!\203 S\336\310\216\215\203\ \315\002-\211 5D2\257\210\302\321p\234\364\205\222Jj\220\022E\321h\347\223RQ*94\ K\022\243\314H`4{LV\003\021N\f\333\364I\347l\327UR\305t\340\332i>\241x=Mu4R\245\ \373\223\244\251NB\211\247\236\3465\253^bx\332Yc\263\252M\220b\253\220\310\004\ \331\242\020,`\005T\021Y\251P@\020\365Ax\310z\364\264\240\265vj2\037?0\v\"en\ \244\374\251\032\225\253v\346\253\3712\215\032\322(o\206~A\006\010\f\324\22357\ \026\"\316\024\365\021\360@\277:\363.$\f\342\016$\200\v\341\302\230\020\340\341\ \201K\017\270+i\326-\312\313j\235\n[\376({\330u\254\266\334\034\031\367%:CK\210\ {\311h\aQH\333Q\023\250\210;e\360\322\362\213\202\247\216\266\340C&(p\274HT7\ \336&B\352\300\036z\206\204\375 \032z\304\233\217\034\267AK\207R\363\213\324u\ \334\203\272h\234 \304&\364S\302]|\024\233b\000\023E\034\005\300!\330\2274\026\ \205\316\363\203\364\"\316\245!\242\360Y?4\204b\023.\2009\036X\300\213p\200]\ \304\324\200$^\204\025\222D\325X \363\324\004\223\205\207\241M\245\352\341(s\ \3415\260w\226\313=\2422 \200\177\344\355\211\3350\004\341\217\207\215r%x\030\ \302\304\230\335{#\250#o\204h\327;\220\242\275B%j&\343e\005\226/\r\200\035\035\ \206K\243\027\216Z\230\323.\335\356^!\vF\002K\366\246kG\321\364E\301\362\250\ \275a\f\031\207i%\216\342&ie\205\260\324}\272\252ho\222\306\370\362!}6\364C\003\ \2717\206'!.\315\036mhMm\370\252\241\365\221g\275\326A\302\254\270X,\371\353\ \232:\222\321\253\025\217v%\222\023!\243r\272\364(\376\177\236\374\233\363\3048\ \330b\241xdTp\325\321\377\3428F\234\214\263\357\255f\324\306\226\257\022\"\000\ \354\003\024C\207\na\353\240&O\305\376\004ncy\350\f\276\357+Q|\201bBi\206\277\ \345u\251\273\310\367\242\303*\204d\n\271}\016\2345r8\034\201[\343:>\364*\242\ \266\025+HZ\263e\212\0247q\357\310X\267[\333(9_o}P\201\324>\266\364\000\217hh\ \352\225a\213q\260\031\334\022sg\360\e\206\234B=\246\2421\341e\364\270\321\224\ \347\0056L\267\227)\244\210\307\027\257<\343\257\000\303\264u{\235\326\352i\303\ ^\332\200\n\236\243a\277\034J#~S\335'2\371\001q\3745$\356\027^\371\325\344\331\ \036\362\004\267\330\251<\212\237\257\345kr\371\302d\362r\376\344d\252C\311\374\ R6\017e\375\005\271yAV\363/\257\345\261(\340hW\020\222\a\027k)60\354\217\363\ \3501\263rt\0364\025\025|\265\031\355\276d\357\3159\367\225\025\223U\273n\027\ \324\321H\031\030\036\357\356\377\010\266\337\374\003\3375Q\335"))
As you see there's a huge string which gets passed through Zlib::Inflate.inflate and then executed via eval. That string contains lots of octal escapes which means that this likely is a string representation that was created by Ruby's String#inspect.
Zlib::Inflate.inflate takes a zip-encoded string and decodes it.
Let's have a look at the actual code hidden in the string (again, rewrapped at the 80 character boundary):
require'digest/md5';z=binding;module Kernel;def obf(*args);o=Digest::MD5.new( args.map{|u|u.to_s}.join).to_s.to_i(16).to_s(4).tr('0123','01lO');o[(o=~/O/),10] end end;oO1l0010OO=/^[a-z]|::[a-z]|^[01lO]+$/;ObjectSpace.each_object{|c|[([ Module]|[Class])-[c.class]][-1][-2]||c.to_s=~ oO1l0010OO||(b,d=( c.class==Module ? ["module #{c};","#{obf(c)}=#{c}.clone"]:(c!=Class ? [ "class #{obf(c)}<#{c};end;class #{c};",'']:["class Class;",''])); c.instance_methods.each{|i|b<<"alias_method #{obf(i,obf(i)).to_s.to_sym. inspect},#{i.to_s.to_sym.inspect};"};b<<'class<<self;';c.methods.each{|i|b<< "alias_method #{obf(i,obf(i)).inspect},#{i.to_sym.inspect};"};b<<"end;end;";eval b+d,z)};$OO1l0010O0=Class;$oO1l0010O0=Module;$Ol0l00llOO=ARGV;$O1O0001l11= 0b1000011.OlOlO0O0O1.OOll010010(0b101010.OlOlO0O0O1);ol1OO1O001=:define_method OlOl1ll101.OlO00lO101($OO1l0010O0){|oO00l1O10O|oO00l1O10O.OO0ll0010O(ol1OO1O001, $OO1l0010O0.O0O0lO00l1(oO00l1O10O,O000O011Ol.O0O0lO00l1(oO00l1O10O)). O0l01l1ll1){|*oO00l1011l|self}};o01OO1O1l1=O0l1Ol101l.O1O0000101($Ol0l00llOO. O1Ol110lOl,'rb');o0lO11101l=o01OO1O1l1.OO11l00O0l(o01OO1O1l1.O0l0lO1000. O101l1ll00);o01OO1O1l1.O1OO11lO0l.OO0ll0010O('O11011llOl'.O0l01l1ll1, o0lOll11ll={0b100000,0b1100101,0b1110100,0b1100001,0b1101111,0b1101001, 0b1101110,0b1110011,0b1101000,0b1110010,0b1100100,0b1101100,0b1110101,0b1001010 }.O00ll0Ol10.Ol1110OlO1.Oll01l0O00($O1O0001l11).O0O110l101).OO0ll0010O( 'O11011llOl'.O0l01l1ll1,o0111l0O00=o0lOll11ll.OOl1l0OlO0($O1O0001l11)). OO0ll0010O('O11011llOl'.O0l01l1ll1,o0101O0000=o0lO11101l.OOl1l0OlO0( $O1O0001l11)).OO0ll0010O('O11011llOl'.O0l01l1ll1,o1OOl11ll0=1);o110O00OO0= ol0100O0O1=0;ol0l0ll0l1=o0101O0000.O0lO0O1000.Ol0lOO1lOO(1.O0010OO100(0b10)) begin;o1OOl11ll0=o1OOl11ll0.OOll010010(1);ol11l0lO00=OlO00O10O1.O1O0000101( o1OOl11ll0){OOllOll0ll.O1O0000101(0)};o0101O0000.Ollllll011{|olO1O01101, o111110ll1|ol11l0lO00.O0l1l1ll00(o111110ll1.O00O1l000l(o1OOl11ll0)).O0l1lO11Ol( olO1O01101,ol11l0lO00.O0l1l1ll00(o111110ll1.O00O1l000l(o1OOl11ll0)).O0l1l1ll00( olO1O01101).OOll010010(1))};ol11l0lO00.OlOOOlllll{|o0l1110lOO|o0l1110lOO. O00ll0Ol10};ol0100O0O1,o110O00OO0=[[ol0100O0O1,o110O00OO0],[ol11l0lO00. OOO01l0lO1{|o0l1110lOO|o0l11010O1=o1l1100l10=0;o0l1110lOO.OlOOOlOO1O{ |o00000Ol0O|ol0ll1100l=o00000Ol0O.O1l11O10Ol;o0l11010O1=o0l11010O1.OOll010010( ol0ll1100l.OOO0l1O000(ol0ll1100l.O00001l001(1))).OO0ll0010O('O11011llOl'. O0l01l1ll1,o1l1100l10=o1l1100l10.OOll010010(ol0ll1100l))};oOO1O11l00=o1l1100l10. OOO0l1O000(o1l1100l10.O00001l001(1));oOO1O11l00.Ol0lOOOOO0 ? 0.0:o0l11010O1. O1l110l1Ol.OOllO1lOO1(oOO1O11l00)}.OOO10l1110.O0O0l001Ol(o1OOl11ll0.Ol0lOO1lOO( 0b10)),o1OOl11ll0]].OOO1O0O1Ol{|o0OO0l1lO0|o0OO0l1lO0.O1Ol110lOl}.O1l11O10Ol end while o1OOl11ll0.O0llOOO0Ol(ol0l0ll0l1).OOOOl0O00O(ol0100O0O1.Oll1OO0l0l( '00111111'.OOOl0lO0ll(0b000010).OOllO1lOO1(0d001000.O1l110l1Ol)));o1OOl11ll0= o110O00OO0;ol11l0lO00=OlO00O10O1.O1O0000101(o1OOl11ll0){OOllOll0ll.O1O0000101(0) };o0101O0000.Ollllll011{|olO1O01101,o111110ll1|ol11l0lO00.O0l1l1ll00(o111110ll1. O00O1l000l(o1OOl11ll0)).O0l1lO11Ol(olO1O01101,ol11l0lO00.O0l1l1ll00(o111110ll1. O00O1l000l(o1OOl11ll0)).O0l1l1ll00(olO1O01101).OOll010010(1))};ol11l0lO00. OlOOOlllll{|o0l1110lOO|o0l1110lOO.O00ll0Ol10};oOOOOOO01l=ol11l0lO00.OOO01l0lO1{ |oOOl0OOOlO|oOOl0OOOlO.OOO1O0O1Ol{|olO1O01101,ol0ll1100l|(ol0ll1100l.OlOO100l0l) }.O1Ol110lOl(10).OOO01l0lO1{|olO1O01101,ol0ll1100l|olO1O01101}};oO01lOl00l= oOOOOOO01l.OOO01l0lO1{|olO0O0001O|ol1l1lO0ll=o0111l0O00.OOO01l0lO1{|oO1O1lll01| olO0O0001O.OOO01l0lO1{|oO101l01OO|oO101l01OO.O1lOlOOOOO(oO1O1lll01)}}.Ol1110OlO1 olO0ll10l0=ol1l1lO0ll.OOO01l0lO1{|o1l00O0OO1|olO0O0001O.OOO01l0lO1{|oO101l01OO| oO101l01OO.O1lOlOOOOO(o1l00O0OO1)}.Oll01l0O00($O1O0001l11).O11OlO0Ol1(o0lOll11ll )};ol1l1lO0ll.O0O0l001Ol(olO0ll10l0.O1l01l0O01(olO0ll10l0.O100OOOO01))}. Oll01l0O00($O1O0001l11);OOOOl1OO10.Ol110lll0O(0b111101.OlOlO0O0O1.OOO0l1O000( 0b1001110)).OO0ll0010O('O11011llOl'.O0l01l1ll1,OOOOl1OO10.O0l1OOO10l(0b1001011. OlOlO0O0O1,0b1100101.OlOlO0O0O1,0b1111001.OlOlO0O0O1,0b111010.OlOlO0O0O1, 0b100000.OlOlO0O0O1)).OO0ll0010O('O11011llOl'.O0l01l1ll1,OOOOl1OO10.OOO1lOl100( oO01lOl00l)).OO0ll0010O('O11011llOl'.O0l01l1ll1,OOOOl1OO10.Ol110lll0O(0b111101. OlOlO0O0O1.OOO0l1O000(0b1001110))).OO0ll0010O('O11011llOl'.O0l01l1ll1, OOOOl1OO10.O0l1OOO10l(0b1010100.OlOlO0O0O1,0b1100101.OlOlO0O0O1,0b1111000. OlOlO0O0O1,0b1110100.OlOlO0O0O1,0b100000.OlOlO0O0O1,0b111010.OlOlO0O0O1)). OO0ll0010O('O11011llOl'.O0l01l1ll1,OOOOl1OO10.Ol110lll0O).OO0ll0010O( 'O11011llOl'.O0l01l1ll1,OOOOl1OO10.Ol110lll0O([o0lO11101l.OOl1l0OlO0( $O1O0001l11),(oO01lOl00l.OOO0l1O000(o0lO11101l.O0lO0O1000.Ol0lOO1lOO(oO01lOl00l. O0lO0O1000).OOll010010(1))).OOl1l0OlO0($O1O0001l11).O1Ol110lOl(o0lO11101l. O0lO0O1000)].OlllO10011.OOO01l0lO1{|oO1OO0O0lO,ol01O0O0l0|oO1OO0O0lO.O1lOlOOOOO( ol01O0O0l0)}.Oll01l0O00($O1O0001l11))).OO0ll0010O('O11011llOl'.O0l01l1ll1, OOOOl1OO10.Ol110lll0O(0b111101.OlOlO0O0O1.OOO0l1O000(0b1001110)))
Yup, this guy used very descriptive identifier and method names. One glance and you know what's going on!
Anyway, I'll try reformating this into something so we have a place to start...
require 'digest/md5' z = binding module Kernel def obf(*args) o = Digest::MD5.new(args.map { |u| u.to_s }.join). to_s.to_i(16).to_s(4).tr('0123', '01lO') o[(o =~ /O/), 10] end end oO1l0010OO = /^[a-z]|::[a-z]|^[01lO]+$/ ObjectSpace.each_object { |c| [([Module] | [Class]) - [c.class]][-1][-2] || c.to_s =~ oO1l0010OO || ( b = case when c == Class then "class Class;" when c.class == Module then "module #{c};" else "class #{obf(c)} < #{c}; end; class #{c};" end d = c.class == Module ? "#{obf(c)} = #{c}.clone" : '' c.instance_methods.each { |i| b << "alias_method #{obf(i, obf(i)).to_s.to_sym.inspect}, " + "#{i.to_s.to_sym.inspect};" } b << 'class << self;' c.methods.each { |i| b << "alias_method #{obf(i, obf(i)).inspect}, #{i.to_sym.inspect};" } b << "end; end;" eval b + d, z ) } $OO1l0010O0 = Class $oO1l0010O0 = Module $Ol0l00llOO = ARGV $O1O0001l11 = 0b1000011.OlOlO0O0O1.OOll010010(0b101010.OlOlO0O0O1) ol1OO1O001 = :define_method OlOl1ll101.OlO00lO101($OO1l0010O0) { |oO00l1O10O| oO00l1O10O.OO0ll0010O( ol1OO1O001, $OO1l0010O0.O0O0lO00l1( oO00l1O10O, O000O011Ol.O0O0lO00l1(oO00l1O10O) ).O0l01l1ll1 ) { |*oO00l1011l| self } } o01OO1O1l1 = O0l1Ol101l.O1O0000101($Ol0l00llOO.O1Ol110lOl, 'rb') o0lO11101l = o01OO1O1l1.OO11l00O0l(o01OO1O1l1.O0l0lO1000.O101l1ll00) o01OO1O1l1.O1OO11lO0l.OO0ll0010O( 'O11011llOl'.O0l01l1ll1, o0lOll11ll = { 0b100000, 0b1100101, 0b1110100, 0b1100001, 0b1101111, 0b1101001, 0b1101110, 0b1110011, 0b1101000, 0b1110010, 0b1100100, 0b1101100, 0b1110101, 0b1001010 }.O00ll0Ol10.Ol1110OlO1.Oll01l0O00($O1O0001l11).O0O110l101 ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, o0111l0O00 = o0lOll11ll.OOl1l0OlO0($O1O0001l11) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, o0101O0000 = o0lO11101l.OOl1l0OlO0($O1O0001l11) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, o1OOl11ll0 = 1 ) o110O00OO0 = ol0100O0O1 = 0 ol0l0ll0l1 = o0101O0000.O0lO0O1000.Ol0lOO1lOO( 1.O0010OO100(0b10) ) begin o1OOl11ll0 = o1OOl11ll0.OOll010010(1) ol11l0lO00 = OlO00O10O1.O1O0000101(o1OOl11ll0) { OOllOll0ll.O1O0000101(0) } o0101O0000.Ollllll011 { |olO1O01101, o111110ll1| ol11l0lO00.O0l1l1ll00( o111110ll1.O00O1l000l(o1OOl11ll0) ).O0l1lO11Ol( olO1O01101, ol11l0lO00.O0l1l1ll00( o111110ll1.O00O1l000l(o1OOl11ll0) ).O0l1l1ll00(olO1O01101).OOll010010(1) ) } ol11l0lO00.OlOOOlllll { |o0l1110lOO| o0l1110lOO.O00ll0Ol10 } ol0100O0O1, o110O00OO0 = [ [ol0100O0O1, o110O00OO0], [ol11l0lO00.OOO01l0lO1 { |o0l1110lOO| o0l11010O1 = o1l1100l10 = 0 o0l1110lOO.OlOOOlOO1O { |o00000Ol0O| ol0ll1100l = o00000Ol0O.O1l11O10Ol o0l11010O1 = o0l11010O1.OOll010010( ol0ll1100l.OOO0l1O000( ol0ll1100l.O00001l001(1) ) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, o1l1100l10 = o1l1100l10.OOll010010(ol0ll1100l) ) } oOO1O11l00 = o1l1100l10.OOO0l1O000( o1l1100l10.O00001l001(1) ) oOO1O11l00.Ol0lOOOOO0 ? 0.0 : o0l11010O1.O1l110l1Ol.OOllO1lOO1(oOO1O11l00) }.OOO10l1110.O0O0l001Ol( o1OOl11ll0.Ol0lOO1lOO(0b10) ), o1OOl11ll0 ] ].OOO1O0O1Ol { |o0OO0l1lO0| o0OO0l1lO0.O1Ol110lOl }.O1l11O10Ol end while o1OOl11ll0.O0llOOO0Ol(ol0l0ll0l1).OOOOl0O00O( ol0100O0O1.Oll1OO0l0l( '00111111'.OOOl0lO0ll(0b000010).OOllO1lOO1( 0d001000.O1l110l1Ol ) ) ) o1OOl11ll0 = o110O00OO0 ol11l0lO00 = OlO00O10O1.O1O0000101(o1OOl11ll0) { OOllOll0ll.O1O0000101(0) } o0101O0000.Ollllll011 { |olO1O01101, o111110ll1| ol11l0lO00.O0l1l1ll00( o111110ll1.O00O1l000l(o1OOl11ll0) ).O0l1lO11Ol( olO1O01101, ol11l0lO00.O0l1l1ll00( o111110ll1.O00O1l000l(o1OOl11ll0) ).O0l1l1ll00(olO1O01101).OOll010010(1) ) } ol11l0lO00.OlOOOlllll { |o0l1110lOO| o0l1110lOO.O00ll0Ol10 } oOOOOOO01l = ol11l0lO00.OOO01l0lO1 { |oOOl0OOOlO| oOOl0OOOlO.OOO1O0O1Ol { |olO1O01101, ol0ll1100l| (ol0ll1100l.OlOO100l0l) }.O1Ol110lOl(10).OOO01l0lO1 { |olO1O01101, ol0ll1100l| olO1O01101 } } oO01lOl00l = oOOOOOO01l.OOO01l0lO1 { |olO0O0001O| ol1l1lO0ll = o0111l0O00.OOO01l0lO1 { |oO1O1lll01| olO0O0001O.OOO01l0lO1 { |oO101l01OO| oO101l01OO.O1lOlOOOOO(oO1O1lll01) } }.Ol1110OlO1 olO0ll10l0 = ol1l1lO0ll.OOO01l0lO1 { |o1l00O0OO1| olO0O0001O.OOO01l0lO1 { |oO101l01OO| oO101l01OO.O1lOlOOOOO(o1l00O0OO1) }.Oll01l0O00($O1O0001l11).O11OlO0Ol1(o0lOll11ll) } ol1l1lO0ll.O0O0l001Ol( olO0ll10l0.O1l01l0O01(olO0ll10l0.O100OOOO01) ) }.Oll01l0O00($O1O0001l11) OOOOl1OO10.Ol110lll0O( 0b111101.OlOlO0O0O1.OOO0l1O000(0b1001110) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.O0l1OOO10l( 0b1001011.OlOlO0O0O1, 0b1100101.OlOlO0O0O1, 0b1111001.OlOlO0O0O1, 0b111010.OlOlO0O0O1, 0b100000.OlOlO0O0O1 ) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.OOO1lOl100(oO01lOl00l) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.Ol110lll0O( 0b111101.OlOlO0O0O1.OOO0l1O000(0b1001110) ) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.O0l1OOO10l( 0b1010100.OlOlO0O0O1, 0b1100101.OlOlO0O0O1, 0b1111000.OlOlO0O0O1, 0b1110100.OlOlO0O0O1, 0b100000.OlOlO0O0O1, 0b111010.OlOlO0O0O1 ) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.Ol110lll0O ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.Ol110lll0O( [ o0lO11101l.OOl1l0OlO0($O1O0001l11), (oO01lOl00l.OOO0l1O000( o0lO11101l.O0lO0O1000.Ol0lOO1lOO( oO01lOl00l.O0lO0O1000 ).OOll010010(1) )).OOl1l0OlO0($O1O0001l11).O1Ol110lOl( o0lO11101l.O0lO0O1000 ) ].OlllO10011.OOO01l0lO1 { |oO1OO0O0lO, ol01O0O0l0| oO1OO0O0lO.O1lOlOOOOO(ol01O0O0l0) }.Oll01l0O00($O1O0001l11) ) ).OO0ll0010O( 'O11011llOl'.O0l01l1ll1, OOOOl1OO10.Ol110lll0O( 0b111101.OlOlO0O0O1.OOO0l1O000(0b1001110) ) )
Yes, that's 243 lines of code which is a lot. Sorry for all the scrolling, but there's not much I can do about this.
Anyway, we'll have lots of small pieces to deobfuscate in this one, won't we?
The key to understanding all that unreadable stuff at the end is understanding what the slightly more readable stuff at the beginning does.
require 'digest/md5' z = binding module Kernel def obf(*args) o = Digest::MD5.new(args.map { |u| u.to_s }.join). to_s.to_i(16).to_s(4).tr('0123', '01lO') o[(o =~ /O/), 10] end end
Line 1 requires the digest/md5 library which will let us compute MD5 hashs via Digest::MD5.
Line 2 sets the z variable to the current context. That's not important just yet.
The rest of this code then creates a global obf() method.
That method takes as many arguments as it gets supplied and through MD5 computes a 10 character hash value which is a string that only consists of the characters 0, 1, l, and O which will always start with an O.
oO1l0010OO = /^[a-z]|::[a-z]|^[01lO]+$/ ObjectSpace.each_object { |c| [([Module] | [Class]) - [c.class]][-1][-2] || c.to_s =~ oO1l0010OO || ( b = case when c == Class then "class Class;" when c.class == Module then "module #{c};" else "class #{obf(c)} < #{c}; end; class #{c};" end d = c.class == Module ? "#{obf(c)} = #{c}.clone" : '' c.instance_methods.each { |i| b << "alias_method #{obf(i, obf(i)).to_s.to_sym.inspect}, " + "#{i.to_s.to_sym.inspect};" } b << 'class << self;' c.methods.each { |i| b << "alias_method #{obf(i, obf(i)).inspect}, #{i.to_sym.inspect};" } b << "end; end;" eval b + d, z ) }
Line 12 sets an oddly named variable to a Regexp. It is only used here which is why I won't rename it. The regular expression matches things starting with a lowercase letter or things that contain a lowercase letter after two colons. It also matches things which are only composed of the characters that the obf() method's output can contain.
Line 14 uses the ObjectSpace module which is part of Ruby to iterate over all objects that Ruby currently knows of — the objects will be yielded to c.
The following huge block abuses the short-circuiting logical or operator as a conditional construct. It will only execute its right side when the left side is not a true value.
Line 15 is an odd way of writing not [Class, Module].include?(c.class). This filters out objects which are neither classes nor modules.
Line 16 filters out lines matching the Regexp from earlier — it will not process classes or modules with obfuscated names or special classes that are for Ruby's internal use only and which thus start with a lowercase character.
Lines 17 through 40 do the actual work — they construct a code string and evaluate it in the toplevel binding (which is in the z variable). The code string will create a new and obfuscated name for modules and classes — for classes it does this by sub-classing and for modules by assigning a duplicate of the module to a new constant. It also creates obfuscated aliases for all instance methods and the method on the module or class itself. It does not need create a new name for Class, however.
Knowing how all those obfuscated names are created we can now use that knowledge for converting them back to the original names in the rest of the code.
I did quite a bit of variable renaming, code reformating and I even replaced some method calls with simpler operators. That ought to make the code more readable.
ObjectSpace.each_object(Class) { |cls| cls.__send__(:define_method, Class.obf(cls, Object.obf(cls)).to_sym ) { |*args| self } } input = File.new(ARGV.first, 'rb') data = input.read(input.lstat.size) input.close freq = " etaoinshrdlu" freq_bytes = freq.unpack("C*") bytes = data.unpack("C*") test_key_length = 1 key_length = cur_count = 0 quarter_length = bytes.length / 4 begin test_key_length += 1 stats = Array.new(test_key_length) { Hash.new(0) } bytes.each_with_index { |byte, index| stats[index % test_key_length][byte] += 1 } stats.map! { |item| item.to_a } cur_count, key_length = [ [cur_count, key_length], [stats.map { |item| weighted_count = total_count = 0 item.each { |stat| char_count = stat.last weighted_count += char_count * (char_count - 1) total_count += char_count } comp_count = total_count * (total_count - 1) comp_count.zero? ? 0.0 : weighted_count.to_f / comp_count }.sort.at(test_key_length.div(2)), test_key_length ] ].sort_by { |tuple| tuple.first }.last end while (test_key_length <= quarter_length) & (cur_count < 0.063) found_key_length = key_length stats = Array.new(found_key_length) { Hash.new(0) } bytes.each_with_index { |byte, index| stats[index % found_key_length][byte] += 1 } stats.map! { |item| item.to_a } best_guesses = stats.map { |stat| stat.sort_by { |byte, count| -count }.first(10).map { |byte, count| byte } } key = best_guesses.map { |char_guesses| possible_chars = freq_bytes.map { |freq_byte| char_guesses.map { |char_guess| char_guess ^ freq_byte } }.flatten good_counts = possible_chars.map { |possible_char| char_guesses.map { |char| possible_char ^ char }.pack("C*").count(freq) } possible_chars.at(good_counts.index(good_counts.max)) }.pack("C*") puts "=" * 78 print "Key: "; p key puts "=" * 78 print "Text :"; puts puts [data.unpack("C*"), (key * (data.length / key.length) + 1)).unpack("C*").first(data.length) ].transpose.map { |raw_byte, key_byte| raw_byte ^ key_byte }.pack("C*") puts "=" * 78
But let us take that apart in parts. Note that I'm going to skim through this as the exact details of the algorithm are still beyond me.
ObjectSpace.each_object(Class) { |cls| cls.__send__(:define_method, Class.obf(cls, Object.obf(cls)).to_sym ) { |*args| self } }
This code defines another set of method