IORCC Deobfuscation: "/slash, is this C++ or ruby?" by Simon Strandgaard

To answer the question imposed by the title: It's actually both!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "ruby.h"   /*
       /sLaSh        *
  oBfUsCaTeD  RuBy   *   
   cOpYrIgHt 2005    *   
bY SiMoN StRaNdGaArD *
 #{X=320;Y=200;Z=20}  */

#define GUN1 42:
#define GUN2 43:
#define bo do
#define when(gun) /**/
#define DATA "p 'Hello embedded world'"
#define DIRTY(argc,argv)\
argc,argv,char=eval(\
"#{DATA.read}\n[3,2,1]"\
);sun=O.new\
if(0)

int 
sun[]={12,9,16,9,2,1,7,1,3,9,27,4, 13,2,11,5,4,1,25,
5,0,1,14,9,15,4,26,9,23,2,17,6,31, 6,10,8,22,9,21,1,
24,8,20,8,18,9,29,5,9,5,1,1,28,8,8,1,30, 9,6,8, 5,1,
19,9,36,19,43, 9,34,11,50,19,48,18,49,9, 35,8,42,18,
51,8,44,11,32, 11,47,9,37,1,39,9,38,19,  45,8,40,12,
41,9,46,12,33,1,57,1,85,5,88,28,83,4,87, 6,62,28,89,
9,80,28,60,21,52,21,72,29,54,21,75,8,70,29,58,28,65,
9,91,8,74,29,79,2,77,1,53,1,81,5, 69,2,64,21, 86,29,
67,9,59,1,61,5,73,6,76,28,56,21,68,29,78,29,63,5,66,
28,90,29, 71,4,55,9,84,28,82,29,101,5, 103,9, 98,35,
97,1,94,35,93,1,100,35,92,31,99,5,96,39,95,5,102,35};

void run(int gun=0) {        // [gun]=[:GUN1,:GUN2]
        printf("run() %i\n", gun);
        switch(gun) {            
        case GUN1 when(2)
                printf("when2\n"); 
                break; // end	
        case GUN2 when(3)
                printf("when3\n"); 
                break; // end	
        }
}

int main(int argc, char** argv) {
        printf("hello world.   number of arguments=%i\n", argc);
        int fun=5;
        bo {
                fun -= 1; //.id - gun = fun
                run(fun);
        } while(fun>0);
        ruby_init();
        rb_eval_string(DATA);
        return 0;
}

#if 0  // nobody reads un-defined code  
__END__
def goto*s;$s=[];Y.times{s=[];X.times{s<<[0]*3};$s<<s}end;A=0.5
include Math;def u g,h,i,j,k,l;f,*m=((j-h).abs>(k-i).abs)?[proc{
|n,o|      g[o]  [n   ]=l      },[h  ,i   ],[j,k]]:[proc{
|p,q|  g[   p][  q]   =l}  ,[   i,h  ],   [k,j]];b,a=m.sort
c,d=a  [1   ]-b  [1   ],a  [0   ]-b  [0   ];d.times{|e|f.
call(      e+b[  0]   ,c*      e/d+b     [1])};end;V=0;def bo&u
$u||=  V;   ;$u  +=   1+V  ;;   return u.call if$u>1;q=128.0
;x=(V  ..   255  ).   map  {|   y|f1,z =sin(y.to_f*PI/q),
sin((  y.   to_f    + 200      )*PI/(   q));[(f1*30.0+110.0).
to_i,((f1+z)*10.0+40.0).to_i,(z*20.0+120.0).to_i]};Y.times{|i|X.
times{|j|i1=((i*0.3+150)*(j*1.1+50)/50.0).to_i;i2=((i*0.8+510)*(
j*0.9+1060)/51.0).to_i;$s[i][j]=x[(i1*i2)%255].clone}};$a=(0..25).
inject([]){|a,i|a<<(V..3).inject([]){|r,j|r<<$c[i*4+j]}};u.call;end
I=LocalJumpError;def run*a,&b;return if a.size==V;if a[V]==666;$b=b
elsif$b;$b.call;end;end;def main s,&u;$m=V;u.call rescue I;end
def rb_eval_string(*a);end     # you promised not to look here
def ruby_init;q=2.0;l=((X**q)*A+(Y**q)*A)**A;V.upto(Y-4){|s|V.
upto(X-4){|q|d=((q-X/A)**q+(s-Y/A)**q)**A;e=(cos(d*PI/(l/q))/q
+A)*3.0+1.0;v=2;f=v/e;a,p,b=$s[s],$s[s+1],$s[s+v];r=a[q][V]*e+
p[q][V]+a[q+1][V]+b[q][V]+a[q+v][V]+b[q+v/v][V]+p[q+v][V]+b[q+
v][V]*f;g=[a[q][V],b[q][V],a[q+v][V],b[q+v][V]];h=(g.max-g.min
)*f;$s[s][q][V]=[[(r/(e+f+6.0)+A+(h*0.4)).to_i,255].min,V].max
}};File.open("res.ppm","w+"){|f|f.write(# secret.greetings :-)
"P3\n# res.ppm\n#{X} #{Y}\n255\n"+$s.map{|a|a.map{|b|b.join' '
}.join(' ')+"\n"}.join)};end;def switch i,&b;b.call;return unless 
defined?($m);b=(X*0.01).to_i;d=1.0/40.0;e=0.09;c=(Y*0.01).to_i
a=$a.map{|(f,g,h,j)|[f*d,g*e,h*d,j*e]};a.each{|(k,l,m,n)|u($s,(k*X
).to_i+b+i,(l*Y).to_i+c+i,(m*X).to_i+b+i,(n*Y).to_i+c+i,[Z]*3)}
a.each{|(o,q,r,s)|u($s,(o*(X-Z)).to_i+i,(q*(Y-Z)).to_i+i,(r*(X-
Z)).to_i+i,(s*(Y-Z)).to_i+i,[(1<<8)-1]*3)};end;Q=Object;class
Regexp;def []=(v,is);is.each{|s|Q.send(:remove_const,s)if Q.
const_defined? s;Q.const_set(s,v)};end;end;def int*ptr;666
end;class O;def []=(a,b=nil);$c=a;end;end;alias:void:goto
#endif // pretend as if you havn't seen anything

Let's start by jumping into the code and having a look at the few initial lines.

2
3
4
5
6
       /sLaSh        *
  oBfUsCaTeD  RuBy   *   
   cOpYrIgHt 2005    *   
bY SiMoN StRaNdGaArD *
 #{X=320;Y=200;Z=20}  */

This is a regexp literal that does not much by itself. However, there is a few constant definitions inside the interpolated expression.

14
15
16
17
argc,argv,char=eval(\
"#{DATA.read}\n[3,2,1]"\
);sun=O.new\
if(0)

This sets argc to 3, argv to 2 and char to 1. It also evaluates the code from the DATA pseudo file handle which contains the stuff after __END__ at the end of the file. It sets sun to a new instance of the O class. (0 is a true value in Ruby!)

What's in his nasssty DATAsesss?

It's not a ring, that we know for sure. Let's have a look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def goto*s
  $s = [];
  Y.times {
    s = [];
    X.times {
      s << [0] * 3
    };
    $s << s
  }
end;

A = 0.5

include Math;

def u g,h,i,j,k,l;
  f, *m = ((j - h).abs > (k - i).abs) ? [
    proc { |n, o| g[o][n] = l },
    [h, i],
    [j, k]
  ] : [
    proc { |p, q| g[p][q] = l },
    [i, h],
    [k, j]
  ];

  b, a = m.sort
  c, d = a[1] - b[1], a[0] - b[0];

  d.times { |e|
    f.call(e + b[0], c * e / d + b[1])
  };
end;

V = 0;

def bo&u
  $u ||= V;
  $u += 1 + V;;
  return u.call if $u > 1;

  q = 128.0;
  x = (V .. 255).map {|y|
    f1, z = sin(y.to_f * PI / q), sin((y.to_f + 200) * PI / (q));
    [
      (f1 * 30.0 + 110.0).to_i,
      ((f1 + z) * 10.0 + 40.0).to_i,
      (z * 20.0 + 120.0).to_i
    ]
  };

  Y.times { |i|
    X.times { |j|
      i1 = ((i * 0.3 + 150) * (j * 1.1 + 50) / 50.0).to_i;
      i2 = ((i * 0.8 + 510) * (j * 0.9 + 1060) / 51.0).to_i;
      $s[i][j] = x[(i1 * i2) % 255].clone
    }
  };

  $a = (0 .. 25).inject([]) { |a, i|
    a << (V .. 3).inject([]) { |r, j|
      r << $c[i * 4 + j]
    }
  };

  u.call;
end

I = LocalJumpError;

def run*a,&b;
  return if a.size == V;

  if a[V] == 666;
    $b = b
  elsif $b;
    $b.call;
  end;
end;

def main s,&u;
  $m = V;
  u.call rescue I;
end

def rb_eval_string(*a); end   # you promised not to look here

def ruby_init;
  q = 2.0;
  l = ((X ** q) * A + (Y ** q) * A) ** A;

  V.upto(Y - 4) { |s|
    V.upto(X - 4) { |q|
      d = ((q - X / A) ** q + (s - Y / A) ** q) ** A;
      e = (cos(d * PI / (l / q)) / q + A) * 3.0 + 1.0;
      v = 2;
      f = v / e;
      a, p, b = $s[s], $s[s + 1], $s[s + v];
      r = a[q][V] * e + p[q][V] + a[q + 1][V] + b[q][V] + a[q + v][V] +
        b[q + v / v][V] + p[q + v][V] + b[q + v][V] * f;
      g = [
        a[q][V],
        b[q][V],
        a[q + v][V],
        b[q + v][V]
      ];
      h = (g.max - g.min) * f;
      $s[s][q][V] = [
        [
          (r / (e + f + 6.0) + A + (h * 0.4)).to_i,
          255
        ].min,
        V
      ].max
    }
  };

  File.open("res.ppm", "w+") { |f|
    f.write( # secret.greetings :-)
      "P3\n# res.ppm\n#{X} #{Y}\n255\n" + $s.map { |a|
        a.map { |b| b.join ' ' }.join(' ') + "\n"
      }.join
    )
  };
end;

def switch i,&b;
  b.call;
  return unless defined?($m);

  b = (X * 0.01).to_i;
  d = 1.0 / 40.0;
  e = 0.09;
  c = (Y * 0.01).to_i
  a = $a.map { |(f, g, h, j)|
    [f * d, g * e, h * d, j * e]
  };
  a.each { |(k, l, m, n)|
    u($s,
      (k * X).to_i + b + i,
      (l * Y).to_i + c + i,
      (m * X).to_i + b + i,
      (n * Y).to_i + c + i,
      [Z] * 3
    )
  }
  a.each { |(o, q, r, s)|
    u($s,
      (o * (X - Z)).to_i + i,
      (q * (Y - Z)).to_i + i,
      (r * (X - Z)).to_i + i,
      (s * (Y - Z)).to_i + i,
      [(1 << 8) - 1] * 3
    )
  };
end;

Q = Object;

class Regexp;
  def []=(v, is);
    is.each { |s|
      Q.send(:remove_const, s) if Q.const_defined? s;
      Q.const_set(s, v)
    };
  end;
end;

def int*ptr;
  666
end;

class O;
  def []=(a, b = nil);
    $c = a;
  end;
end;

alias :void :goto
#endif // pretend as if you havn't seen anything

Florian Groß