Vulnerability Development mailing list archives

windows scripting encoder


From: Stephanie Wehner <_ () R4K NET>
Date: Tue, 7 Nov 2000 14:37:13 +0100

Hi,

Since a friend of mine told me they use the windows scripting encoder
to obfuscate their asp code before giving it to customers, I decided
to take a look at it. It turned out to be extremely trivial.

Of course this is just encoding and is, as ms says themselves, not
a real protection of asp code. However I find it a bit misleading to
present this as an option to 'protect your sourcecode from prying eyes',
since it really is totally simple. I think it creates a false sense of
'security' by hiding your source code that just encourages sloppy coding
practises. I'd be curious to know if anyone is actually using this or
ever looked at it.

There's some info at:
http://msdn.microsoft.com/library/periodic/period99/scriptengine.htm

You can get the encoder from:
http://msdn.microsoft.com/scripting/vbscript/download/x86/sce10en.exe

Anyway, this is how it works:
The encoder will translate each character in the input, to one of three
possible other characters depending on the position in the input. There
is basically a pattern which determines which of the three characters will
replace the input character at a certain position. This pattern repeats
after 64 chars. This pattern is the same for all characters. There's some
exceptions to this rule, namely \n and \r will be encoded to 2 characters
each. Futhermore this encoding is not dependant on the machine it is done
on.

That's all there is to it. So in order to write a decoder one needs to
find the pattern and the character combinations. In order to do this,
I just made a sample file like:

<%
'**Start Encode**
aaa
%>

If one encoded this, one basically knows the 3 letters a maps to. Therefore
it is easy to just to this for all ascii characters (after installing a nice
shell on windows :) ) and make a little script which will create some perl
hash, which will give me the outcome of each letter seen in the encoded string,
cause the original character is known. I couldn't find any obvious connection
between the the input character and the output, hence the table.

Next step is to extract the pattern. This is also very simple by encoding the
same file as above with a lot of a's, and then have a script dump this as an
array to include in the perl code.

With this information one can write a perl script, like the one below,
which will decode the asp file. For example the encoded file:

%@ LANGUAGE = VBScript.Encode %>
<%
'**Start Encode**#@~^JQAAAA==@#@&K4b/,k/,L!dY,/G:^?PdO!wk9~Y^?/Y@#@&FQsAAA==^#~@%>

The output of the decoder will be:
<script language="VBScript">
<%
This is just some stupid test
%>
</script>

I've tried this on some real asp code as well.

I assume that in some dll, there has to be the code to do this for you,
since IIS will run this script and I suppose it has to be decoded first.
But I'm not really a windows person. I searched the ms site, but couldn't
find anything.

I'm not really sure how serious this is. I suppose no sane person would
use this scripting encoder and assume noone could read the code on their
website or that customers can't read the code you wrote for them. However
I'm kind of wondering why this functionality is offered at all then.

bye,
Stephanie
----------------------------<> _ () r4k net <>-----------------<> FreeBSD <>---
<hegemoOn> jx : yeah im a net-monk
<hegemoOn> and i pray every day for the soul of internet    - #unix,07/08/00


#!/usr/bin/perl
# Windows Scripting Decoder
#
# 16/11/2000, Stephanie Wehner, _ () r4k net
#
# Note that this does not attempt to be a nice decoder and produce
# nicely formatted output. It's just a demo, your milage may vary.
#
# Usage: cat file | ./dec.pl

use strict;

# pattern, auto generated by make_pat
my @pat = 
(1,2,0,1,2,0,2,0,0,2,0,2,1,0,2,0,1,0,2,0,1,1,2,0,0,2,1,0,2,0,0,2,1,1,0,2,0,2,0,1,0,1,1,2,0,1,0,2,1,0,2,0,1,1,2,0,0,1,1,2,0,1,0,2);

# list auto generated by make_list, some of this probably newer occurs
# but oh well
my %code = (
0 => [0,0,0],
1 => [1,1,1],
2 => [2,2,2],
3 => [3,3,3],
4 => [4,4,4],
5 => [5,5,5],
6 => [6,6,6],
7 => [7,7,7],
8 => [8,8,8],
9 => [123,87,110],
10 => [0,0,0],
11 => [11,11,11],
12 => [12,12,12],
13 => [0,0,0],
14 => [14,14,14],
15 => [15,15,15],
16 => [16,16,16],
17 => [17,17,17],
18 => [18,18,18],
19 => [19,19,19],
20 => [20,20,20],
21 => [21,21,21],
22 => [22,22,22],
23 => [23,23,23],
24 => [24,24,24],
25 => [25,25,25],
26 => [26,26,26],
27 => [27,27,27],
28 => [28,28,28],
29 => [29,29,29],
30 => [30,30,30],
31 => [31,31,31],
32 => [50,46,45],
33 => [48,71,117],
34 => [33,122,82],
35 => [41,86,96],
36 => [91,66,113],
37 => [56,106,94],
38 => [51,47,73],
39 => [61,38,92],
40 => [88,73,98],
41 => [58,65,125],
42 => [53,52,41],
43 => [101,50,54],
44 => [57,91,32],
45 => [92,118,124],
46 => [86,114,122],
47 => [115,67,0],
48 => [102,56,107],
49 => [78,57,99],
50 => [69,112,51],
51 => [107,69,43],
52 => [98,104,104],
53 => [89,113,81],
54 => [120,79,102],
55 => [94,9,118],
56 => [125,98,49],
57 => [74,68,100],
58 => [109,35,84],
59 => [113,117,67],
60 => [0,0,0],
61 => [96,126,58],
62 => [0,0,0],
63 => [83,94,126],
64 => [0,0,0],
65 => [66,119,69],
66 => [39,74,44],
67 => [72,97,42],
68 => [114,93,116],
69 => [117,34,39],
70 => [49,75,55],
71 => [55,111,68],
72 => [77,78,121],
73 => [82,59,89],
74 => [34,76,47],
75 => [84,80,111],
76 => [106,103,38],
77 => [71,42,114],
78 => [100,125,106],
79 => [45,116,57],
80 => [32,84,123],
81 => [0,43,63],
82 => [46,45,56],
83 => [76,44,119],
84 => [93,48,103],
85 => [126,110,83],
86 => [108,107,71],
87 => [111,102,52],
88 => [121,53,120],
89 => [116,37,93],
90 => [67,33,48],
91 => [38,100,35],
92 => [118,77,90],
93 => [37,82,91],
94 => [36,99,108],
95 => [43,63,72],
96 => [40,123,85],
97 => [35,120,112],
98 => [65,41,105],
99 => [52,40,46],
100 => [9,115,76],
101 => [42,89,33],
102 => [68,51,36],
103 => [63,0,78],
104 => [119,109,80],
105 => [59,85,9],
106 => [85,83,86],
107 => [105,124,115],
108 => [97,58,53],
109 => [99,95,97],
110 => [80,101,75],
111 => [103,70,88],
112 => [81,88,59],
113 => [73,49,87],
114 => [79,105,34],
115 => [70,108,109],
116 => [104,90,77],
117 => [124,72,37],
118 => [54,39,40],
119 => [112,92,70],
120 => [110,61,74],
121 => [122,36,50],
122 => [47,121,65],
123 => [95,55,61],
124 => [75,96,95],
125 => [90,81,79],
126 => [44,32,66],
127 => [87,54,101]
);


my $encode = 0;
my($l);

while(<>) {

        # if only <% on line, don't look further
        if(/^\s*<%\s*$/) {
                next;
        }

        # Check if this line contains things that need decoding
        if(($encode == 1) && ((/<%/) || (/Start Encode/))){

                # Cut out all actual encoded parts
                $l = $_;

                while($l =~ /^(.*?)(<%)*('\*\*Start Encode\*\*)*(=*)#@~\^[0-9a-zA-Z]+==(.*?).{3}AAA==\^#~@%>(.*)$/) {
                        print $1  . "<%" . $4;
                        decode_string($5);      
                        print "%>";
                        $l = $6;
                }
                print $l . "\n";

        } elsif((/<script\s*language\s*=\s*(\S+)\.encode\s*>/i) ||
           (/<%@*\s*language\s*=\s*(\S+)\.encode\s*%>/i)) {

                # encoding header found, start to look for encoded parts
                $encode = 1;

                # make a new header
                print "<script language=\"" . $1 . "\">\n";
        } else {

                # if this line doesn't contain encoded things, just print it
                print $_;
        }
}
print "</script>\n";

# This function does the actual decoding. For each char found, determine
# which char it needs to be decoded to, depending on the current position
# in the pattern

sub
decode_string
{
        my($in) = @_;
        my($i, $c, $pos, $mod);
        my $length = length($in);
        my $s = 0;

        if($in eq "") {
                print "Regexp Failed\n";
                return;
        }

        for($i = 0;$i < $length;$i++) {
                
                $c = substr($in,$i,1);

                if($s == 0) {

                        # encountering @ is a special situation
                        # cause it's the start of a two letter
                        # encoding (for \r and \n)
                        
                        if ($c eq "@") {
                                $s = 1;
                        } else {
                                $mod = $pos % 64;
                                print chr($code{ord($c)}[$pat[$mod]]);
                                $pos++;
                        }

                } elsif ($s == 1) {

                        if($c eq "#") {
                                print "\r";
                        } elsif($c eq "&") {
                                print "\n";
                        }
                        # other @ not of interest
                        $s = 0;
                        $pos++;
                                
                }
        }
}


Current thread: