ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[readme.txt]ÄÄ
Reed-Solomon error correction code for virus
Error correction code (ECC) is a code used often in telecommunications, to
recover data loss in noisy connections. Several ways to do this are know,
and here i shown one of these way, specialy tailored for virus coding. The
routine presented here is a implementation of the Reed-Solomon algorithm,
that is able to correct till to 4 bytes in a 249 bytes long buffer (1.6%).
Dont seens good, but keep in mind that this code can correct bytes overwrited
from your virus code! This mean that AVs cant patch anymore your code with
a simple jump to disinfect it in mem. If your ECC routine code hooked, lets
said, int8, it will correct the patched code 1/18 secs after! You can hook
also int1c or even int9, correcting the virus code each time that the user
touch the keyboard.
The given routine encode a 249 buffer in a 256 code lenght, and vice-versa.
This mean that each 249 bytes of virus code need 256 of memory space. You
can do this by careful programming, putting a jmp $+8 / db 6 dup (0) after
each 247 bytes of code, or can store the in memory or even unused spaces of
the harddrive. In this case, each time the ECC is called, it read 256 bytes
from memory or disk, and correct 249 bytes of virus code.
The best way, anyway, is only store the 6 correction bytes in a buffer, and
have a routine to calculate the range of the requested offset in the ECC
table. Something like this:
int GetECCTablePosition(c,t)
{
return (((c/249)*6)+t);
}
Where c is the code offset what we want to access the ECC value, and t is the
offset of the ECC table.
Of course, in the virus code, you need add some lines like these ones:
extrn rsencode : near
extrn rsdecode : near
To compile the C routines, i used Turbo C++ 3.0, a good'n'old C compiler. The
code is, more or less, ANSI based, so, you will have not problem using other
compiler, like DJGPP or Watcom. I use this params to compile:
tcc -Z -d -mt -2 -O -a -c -p gflib.c
tcc -Z -d -mt -2 -O -a -c -p rslib.c
These option means model tiny, 286 opcodes, compile without link, optimize
jumps and registers reload, and, specialy, Pascal callings. I use Pascal
callings because they make the routine smaller, make easier to call from
assembler, and clean the stack. Fell free to modify this is you want.
In the virus code, to call the encode routine, make something like this:
push offset BufferToProcess
push offset DestinationBuffer
call rsencode
The return buffer should contain the reversed entry buffer and the 6 ECC
codes. You can save all it, of just the ECC codes. To fix a block, the code
is the following:
push offset BufferAndECCCode
push offset DestinationBuffer
push offset NumberOfBytesChanged
call rsencode
The first buffer is the reversed virus code together with the 6 ECC codes.
The destination buffer will have the corrected virus code, and the memory
word pointed for the last parameter is the number of errors detected. Remember
that is this word is above 4, the code wasnt corrected, because too much
errors where detected. Take the apropriated action them, like format the HD
or, more likely, reboot. Users will blame the AV for this. :->
Thanks to the Pascal calling convention, the ECC routines clean the stack, so
the pushed params dont mess the virus. If you want the ASM code of the C
routines, just add a -S command switch to the compiler. The code produced
can be optimized by hand, saving much bytes (well, at least in my old compiler
that dont have a good optimization).
To finish this article, the most common approach will be a virus using these
routines. I'm very new in virus community, and cant code a real good virus to
show these technics. So,i put a little command line utility to test these
routines. But you can see these technics in real virus, as they're present
in some virus, like Yankee Doddle, XPEH and RDA.Fighter, altought the routines
present in RDA are only a hack of the Yankee Doddle code.
The utility have 3 possible parameters. For encoding, use /e. For decoding,
use /d. And to show the version of the prog, use /v. Get a text file, lets
said, readme.txt, and do the following
ecc -e encode.ecc
The result file will look as the input file with some garbage inserted. These
garbage are the ECC codes. Get a text editor and change some bytes. Make a
'A' to 'Z', this kind of stuff, but dont change too much bytes together. Then
run the utility to decode:
ecc -d result.ecc
As you will see, all the errors introduced where corrected!!!
I wish to thanks Vecna, for translating this stuff from portuguese to english,
and (try) to teach me how code a virus, and for all 29A, for accepting this
contribution. The 29A zines are the best ones ever!
Kala-Marai
PS: Do you dont know what a Kala-Marai is?? Ask Q, or watch the StartTrek
"Deja Q" episody. :->
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[readme.txt]ÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[rslib.c]ÄÄ
/* rslib.c
Library of Reed - Solomon Routines
This file contains the actual routines to implement a Reed - Solomon
(255,249,7) code. The encoder uses a feedback shift register
generator, which systematically encodes 249 bytes into a 255 byte
block. The decoder is a classic Peterson algorithm.
*/
#include "ecc.h"
/* Reed - Solomon Encoder. The Encoder uses a shift register algorithm,
as detailed in _Applied Modern Algebra_ by Dornhoff and Hohn (p.446).
Note that the message is reversed in the code array; this was done to
allow for (emergency) recovery of the message directly from the
data stream.
*/
rsencode (m, c)
unsigned char m[249], c[255];
{
unsigned char r[6], rtmp;
int i, j;
for (i = 0; i < 6; i++)
r[i] = 0;
for (i = 0; i < 249; i++)
{
c[254 - i] = m[i];
rtmp = gfadd (m[i], r[5]);
for (j = 5; j > 0; j--)
{
r[j] = gfadd (gfmul (rtmp, g[j]), r[j - 1]);
}
r[0] = gfmul (rtmp, g[0]);
}
for (i = 0; i < 6; i++)
{
c[i] = r[i];
}
}
/* Polynomial Evaluator, used to determine the Syndrome Vector. This is
relatively straightforward, and there are faster algorithms.
*/
unsigned char
evalpoly (p, x)
unsigned char p[255], x;
{
unsigned char y;
int i;
y = 0;
for (i = 0; i < 255; i++)
{
y = gfadd (y, gfmul (p[i], gfexp (x, i)));
}
return (y);
}
/* Determine the Syndrome Vector. Note that in s[0] we return the OR of
all of the syndromes; this allows for an easy check for the no - error
condition.
*/
syndrome (c, s)
unsigned char c[255], s[7];
{
extern unsigned char e2v[256];
int i;
s[0] = 0;
for (i = 1; i < 7; i++)
{
s[i] = evalpoly (c, e2v[i]);
s[0] = s[0] | s[i];
}
}
/* Determine the number of errors in a block. Since we have to find the
determinant of the S[] matrix in order to determine singularity, we
also return the determinant to be used by the Cramer's Rule correction
algorithm.
*/
errnum (s, det, errs)
unsigned char s[7], *det;
int *errs;
{
*det = gfmul (s[2], gfmul (s[4], s[6]));
*det = gfadd (*det, gfmul (s[2], gfmul (s[5], s[5])));
*det = gfadd (*det, gfmul (s[6], gfmul (s[3], s[3])));
*det = gfadd (*det, gfmul (s[4], gfmul (s[4], s[4])));
*errs = 3;
if (*det != 0)
return;
*det = gfadd (gfmul (s[2], s[4]), gfexp (s[3], 2));
*errs = 2;
if (*det != 0)
return;
*det = s[1];
*errs = 1;
if (*det != 0)
return;
*errs = 4;
}
/* Full impementation of the three error correcting Peterson decoder. For
t<6, it is faster than Massey - Berlekamp. It is also somewhat more
intuitive.
*/
rsdecode (code, mesg, errcode)
unsigned char code[255], mesg[249];
int *errcode;
{
extern unsigned char v2e[256];
unsigned char syn[7], deter, z[4], e0, e1, e2, n0, n1, n2, w0, w1, w2,
x0, x[3];
int i, sols;
*errcode = 0;
/* First, get the message out of the code, so that even if we can't correct
it, we return an estimate.
*/
for (i = 0; i < 249; i++)
mesg[i] = code[254 - i];
syndrome (code, syn);
if (syn[0] == 0)
return;
/* We now know we have at least one error. If there are no errors detected,
we assume that something funny is going on, and so return with errcode 4,
else pass the number of errors back via errcode.
*/
errnum (syn, &deter, errcode);
if (*errcode == 4)
return;
/* Having obtained the syndrome, the number of errors, and the determinant,
we now proceed to correct the block. If we do not find exactly the
number of solutions equal to the number of errors, we have exceeded our
error capacity, and return with the block uncorrected, and errcode 4.
*/
switch (*errcode)
{
case 1:
x0 = gfmul (syn[2], gfinv (syn[1]));
w0 = gfmul (gfexp (syn[1], 2), gfinv (syn[2]));
if (v2e[x0] > 5)
mesg[254 - v2e[x0]] = gfadd (mesg[254 - v2e[x0]], w0);
return;
case 2:
z[0] = gfmul (gfadd (gfmul (syn[1], syn[3]), gfexp (syn[2], 2)), gfinv (deter));
z[1] = gfmul (gfadd (gfmul (syn[2], syn[3]), gfmul (syn[1], syn[4])), gfinv (deter));
z[2] = 1;
z[3] = 0;
polysolve (z, x, &sols);
if (sols != 2)
{
*errcode = 4;
return;
}
w0 = gfmul (z[0], syn[1]);
w1 = gfadd (gfmul (z[0], syn[2]), gfmul (z[1], syn[1]));
n0 = 254 - v2e[gfinv (x[0])];
n1 = 254 - v2e[gfinv (x[1])];
e0 = gfmul (gfadd (w0, gfmul (w1, x[0])), gfinv (z[1]));
e1 = gfmul (gfadd (w0, gfmul (w1, x[1])), gfinv (z[1]));
if (n0 < 249)
mesg[n0] = gfadd (mesg[n0], e0);
if (n1 < 249)
mesg[n1] = gfadd (mesg[n1], e1);
return;
case 3:
z[3] = 1;
z[2] = gfmul (syn[1], gfmul (syn[4], syn[6]));
z[2] = gfadd (z[2], gfmul (syn[1], gfmul (syn[5], syn[5])));
z[2] = gfadd (z[2], gfmul (syn[5], gfmul (syn[3], syn[3])));
z[2] = gfadd (z[2], gfmul (syn[3], gfmul (syn[4], syn[4])));
z[2] = gfadd (z[2], gfmul (syn[2], gfmul (syn[5], syn[4])));
z[2] = gfadd (z[2], gfmul (syn[2], gfmul (syn[3], syn[6])));
z[2] = gfmul (z[2], gfinv (deter));
z[1] = gfmul (syn[1], gfmul (syn[3], syn[6]));
z[1] = gfadd (z[1], gfmul (syn[1], gfmul (syn[5], syn[4])));
z[1] = gfadd (z[1], gfmul (syn[4], gfmul (syn[3], syn[3])));
z[1] = gfadd (z[1], gfmul (syn[2], gfmul (syn[4], syn[4])));
z[1] = gfadd (z[1], gfmul (syn[2], gfmul (syn[3], syn[5])));
z[1] = gfadd (z[1], gfmul (syn[2], gfmul (syn[2], syn[6])));
z[1] = gfmul (z[1], gfinv (deter));
z[0] = gfmul (syn[2], gfmul (syn[3], syn[4]));
z[0] = gfadd (z[0], gfmul (syn[3], gfmul (syn[2], syn[4])));
z[0] = gfadd (z[0], gfmul (syn[3], gfmul (syn[5], syn[1])));
z[0] = gfadd (z[0], gfmul (syn[4], gfmul (syn[4], syn[1])));
z[0] = gfadd (z[0], gfmul (syn[3], gfmul (syn[3], syn[3])));
z[0] = gfadd (z[0], gfmul (syn[2], gfmul (syn[2], syn[5])));
z[0] = gfmul (z[0], gfinv (deter));
polysolve (z, x, &sols);
if (sols != 3)
{
*errcode = 4;
return;
}
w0 = gfmul (z[0], syn[1]);
w1 = gfadd (gfmul (z[0], syn[2]), gfmul (z[1], syn[1]));
w2 = gfadd (gfmul (z[0], syn[3]), gfadd (gfmul (z[1], syn[2]), gfmul (z[2], syn[1])));
n0 = 254 - v2e[gfinv (x[0])];
n1 = 254 - v2e[gfinv (x[1])];
n2 = 254 - v2e[gfinv (x[2])];
e0 = gfadd (w0, gfadd (gfmul (w1, x[0]), gfmul (w2, gfexp (x[0], 2))));
e0 = gfmul (e0, gfinv (gfadd (z[1], gfexp (x[0], 2))));
e1 = gfadd (w0, gfadd (gfmul (w1, x[1]), gfmul (w2, gfexp (x[1], 2))));
e1 = gfmul (e1, gfinv (gfadd (z[1], gfexp (x[1], 2))));
e2 = gfadd (w0, gfadd (gfmul (w1, x[2]), gfmul (w2, gfexp (x[2], 2))));
e2 = gfmul (e2, gfinv (gfadd (z[1], gfexp (x[2], 2))));
if (n0 < 249)
mesg[n0] = gfadd (mesg[n0], e0);
if (n1 < 249)
mesg[n1] = gfadd (mesg[n1], e1);
if (n2 < 249)
mesg[n2] = gfadd (mesg[n2], e2);
return;
default:
*errcode = 4;
return;
}
}
/* Polynomial Solver. Simple exhaustive search, as solving polynomials is
generally NP - Complete anyway.
*/
polysolve (polynom, roots, numsol)
unsigned char polynom[4], roots[3];
int *numsol;
{
extern unsigned char e2v[256];
int i, j;
unsigned char y;
*numsol = 0;
for (i = 0; i < 255; i++)
{
y = 0;
for (j = 0; j < 4; j++)
y = gfadd (y, gfmul (polynom[j], gfexp (e2v[i], j)));
if (y == 0)
{
roots[*numsol] = e2v[i];
*numsol = *numsol + 1;
}
}
}
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[rslib.c]ÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[ecc.h]ÄÄ
/* ecc.h
Generator Polynomial Coefficients
This header file contains an array which defines the generator
polynomial for the Reed - Solomon Code (255,249,7). The polynomial
was hand generated, using a set of calculator programs.
*/
static unsigned char g[6] =
{
117, 49, 58, 158, 4, 126};
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[ecc.h]ÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[gflib.c]ÄÄ
/* gflib.c
Math Library for GF[256]
This file contains a number of mathematical functions for GF[256].
Entry and result are always assumed to be in vector notation, since
said notation allows for the zero element. Attempting to reciprocate
the zero element results in process exit 42.
*/
#include "gf.h"
/* Multiply two field elements */
unsigned char
gfmul (mul1, mul2)
unsigned char mul1, mul2;
{
unsigned char mul3;
if (mul1 == 0 || mul2 == 0)
mul3 = 0;
else
mul3 = e2v[(v2e[mul1] + v2e[mul2]) % 255];
return (mul3);
}
/* Add two field elements. Subtraction and addition are equivalent */
unsigned char
gfadd (add1, add2)
unsigned char add1, add2;
{
unsigned char add3;
add3 = add1 ^ add2;
return (add3);
}
/* Invert a field element, for division */
unsigned char
gfinv (ivt)
unsigned char ivt;
{
unsigned char ivtd;
/* if (ivt == 0)
exit (42);*/
ivtd = e2v[255 - v2e[ivt]];
return (ivtd);
}
/* Exponentiation. Convert to exponential notation, mod 255 */
unsigned char
gfexp (mant, powr)
unsigned char mant, powr;
{
unsigned char expt;
if (mant == 0)
expt = 0;
else
expt = e2v[(v2e[mant] * powr) % 255];
return (expt);
}
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[gflib.c]ÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[gf.h]ÄÄ
/* gf.h
Galois Field [256] Elements
This header file contains two arrays which transform field elements
from exponential notation to vector (e2v) and vice versa (v2e).
Note that there is no exponential notation for the zero vector,
and that a^255 = a^0 = 1.
*/
unsigned char e2v[256] =
{
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38,
76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192,
157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35,
70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161,
95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226,
217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206,
129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204,
133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84,
168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255,
227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65,
130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166,
81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9,
18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1};
unsigned char v2e[256] =
{
255, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175};
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[gf.h]ÄÄ