forked from yanncam/UnSHc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
unshc-v0.3.sh
609 lines (547 loc) · 18.3 KB
/
unshc-v0.3.sh
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
#!/bin/bash
###################
# Author: Luiz Otavio Duarte a.k.a. (LOD)
# 11/03/08 - v.0.1
# Updated: Yann CAM v0.2 - [email protected] | www.asafety.fr
# 06/27/13 - v.0.2
# -- Adding new objdump format (2.22) to retrieve data (especially on Ubuntu distribution)
# -- Patch few regex with sorted address list
# Updated: Yann CAM v0.3 - [email protected] | www.asafety.fr
# 18/11/15 - v.0.3
# -- Adapt script for new architecture
# -- Clean and optimize functions
# -- Add an (unsigned long) cast in shc C source code
###################
# Tested on :
# Ubuntu 14.04.3 LTS x86_64
# Linux server 3.13.0-61-generic #100-Ubuntu SMP Wed Jul 29 11:21:34 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
# Linux version 3.13.0-61-generic (buildd@lgw01-50) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #100-Ubuntu SMP Wed Jul 29 11:21:34 UTC 2015
#
# CentOS release 6.6 (Final) x86_64
# Linux server 2.6.32-504.23.4.el6.x86_64 #1 SMP Tue Jun 9 20:57:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
# Linux version 2.6.32-504.23.4.el6.x86_64 ([email protected]) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-11) (GCC) ) #1 SMP Tue Jun 9 20:57:37 UTC 2015
#
# Debian 7.8 i686
# Linux server 3.2.0-4-686-pae #1 SMP Debian 3.2.68-1+deb7u2 i686 GNU/Linux
# Linux version 3.2.0-4-686-pae ([email protected]) (gcc version 4.6.3 (Debian 4.6.3-14) ) #1 SMP Debian 3.2.68-1+deb7u2
###################
VERSION="0.3"
OBJDUMP=`which objdump`
GREP=`which grep`
CUT=`which cut`
SHRED=`which shred`
UNIQ=`which uniq`
SORT=`which sort`
GCC=`which gcc`
WC=`which wc`
AWK=`which awk`
SED=`which sed`
TR=`which tr`
HEAD=`which head`
BINARY=$1
TMPBINARY=$(mktemp /tmp/XXXXXX)
OBJFILE=$(mktemp /tmp/XXXXXX)
STRINGFILE=$(mktemp /tmp/XXXXXX)
CALLFILE=$(mktemp /tmp/XXXXXX)
CALLADDRFILE=$(mktemp /tmp/XXXXXX)
CALLSIZEFILE=$(mktemp /tmp/XXXXXX)
declare -A LISTOFCALL
# Variable to know the index of variables.
# This var is to loop on each 14 arc4() call with ordered args.
j=0
# Simple usage help / man
function usage(){
echo "[*] usage : $0 <file.sh.x>"
echo "[*] e.g : $0 script.sh.x"
}
# Clean all temp file created for this script
function clean(){
$SHRED -zu -n 1 $OBJFILE $CALLFILE $CALLADDRFILE $CALLSIZEFILE $STRINGFILE $TMPBINARY ${TMPBINARY}.c
}
# Clean error exit function after cleaning temp file
function exit_error(){
clean
exit 1;
}
# Check the availability of basic commands usefull for this script
function check_binaries() {
if [ ! -x ${OBJDUMP} ]; then
echo "[-] Error, cannot execute or find objdump binary"
exit_error
fi
if [ ! -x ${GREP} ]; then
echo "[-] Error, cannot execute or find grep binary"
exit_error
fi
if [ ! -x ${CUT} ]; then
echo "[-] Error, cannot execute or find cut binary"
exit_error
fi
if [ ! -x ${SHRED} ]; then
echo "[-] Error, cannot execute or find shred binary"
exit_error
fi
if [ ! -x ${UNIQ} ]; then
echo "[-] Error, cannot execute or find uniq binary"
exit_error
fi
if [ ! -x ${SORT} ]; then
echo "[-] Error, cannot execute or find sort binary"
exit_error
fi
if [ ! -x ${GCC} ]; then
echo "[-] Error, cannot execute or find gcc binary"
exit_error
fi
if [ ! -x ${WC} ]; then
echo "[-] Error, cannot execute or find wc binary"
exit_error
fi
}
# Create dump files of encrypted script
function generate_dump() {
# OBJFILE dump to retrive arc4 address, address and size of each arc4 arguments and pwd
$OBJDUMP -D $BINARY > $OBJFILE
# STRINGFILE dump to retrieve pwd and arc4 argument
$OBJDUMP -s $BINARY > $STRINGFILE
}
# Find out the most called function. This function is arc4() and there are 14 calls.
# Update 27/06/2013 : Regexps updated to match new objdump format and retrieve the $CALLADDR from his number of call (bug initial with "sort")
# Update 16/11/2015 : Adding new architecture support
function extract_arc4_call_addr(){
CALLADDR=$($GREP -Eo "call.*[0-9a-f]{6,}" $OBJFILE | $GREP -Eo "[0-9a-f]{6,}" | $SORT | $UNIQ -c | $SORT | $GREP -Eo "(14).*[0-9a-f]{6,}" | $GREP -Eo "[0-9a-f]{6,}")
if [ -z "$CALLADDR" ]; then
echo "[-] Unable to define arc4() call address..."
exit_error
fi
echo "[+] ARC4 address call defined : [0x$CALLADDR]"
}
# Extract each args values of arc4 calls
function extract_variables_from_binary(){
echo "[*] Extracting each args address and size for the 14 arc4() calls..."
# Initialize the number of line before CALLADDR to looking for addresses of args
i=2
# Retrieve ordered list of address var and put it to $CALLADDRFILE
while [[ $($WC -l < $CALLADDRFILE) -ne 14 ]]; do
$GREP -B $i "call.*$CALLADDR" $OBJFILE | $GREP -v "$CALLADDR" | $GREP -Eo "(0x[0-9a-f]{6,})" > $CALLADDRFILE
i=$(($i + 1))
if [ $i -eq 10 ]; then
echo "[-] Unable to extract addresses of 14 arc4 args..."
exit_error
fi
done
# Initialize the number of line before CALLADDR to looking for sizes of args
i=3
# Retrieve ordered list of size var and append it to $CALLSIZEFILE
while [[ $($WC -l < $CALLSIZEFILE) -ne 14 ]]; do
$GREP -B $i "call.*$CALLADDR" $OBJFILE | $GREP -v "$CALLADDR" | $GREP -Eo "(0x[0-9a-f]+,)" | $GREP -Eo "(0x[0-9a-f]+)" | $GREP -Ev "0x[0-9a-f]{6,}" > $CALLSIZEFILE
i=$(($i + 1))
if [ $i -eq 10 ]; then
echo "[-] Unable to extract sizes of 14 arc4 args..."
exit_error
fi
done
#cat $CALLADDRFILE
#cat $CALLSIZEFILE
# For each full address in $CALLADDRFILE and corresponding size in $CALLSIZEFILE
IFS=$'\n' read -d '' -r -a LISTOFADDR < $CALLADDRFILE
IFS=$'\n' read -d '' -r -a LISTOFSIZE < $CALLSIZEFILE
for (( x = 0; x < ${#LISTOFADDR[*]}; x = x+1 ))
do
i=${LISTOFADDR[$x]}
NBYTES=${LISTOFSIZE[$x]}
echo -e "\t[$x] Working with var address at offset [$i] ($NBYTES bytes)"
# Some diferences in assembly.
# We can have:
# mov <adr>,%eax
# push 0x<hex>
# push %eax
# call $CALLADDR
#
# or
#
# push 0x<hex>
# push 0x<adr>
# call $CALLADDR
#
# UPDATE 27/06/2013 :
# Adding support of objdump format
# New format supported (Debian 7 x86) :
#
# movl $0x<hex>,0x4(%esp)
#
# movl $0x<adr>,(%esp)
# call $CALLADDR
#
# UPDATE 18/11/2015 :
# Adding support of objdump format
# Ubuntu 14.04 LTS x86_64
#
# mov $0x<hex>,%esi
# mov $0x<adr>,%edi
# callq $CALLADDR <fork@plt+0x23b>
#
# Key is the address with the variable content.
KEY=$(echo $i | $CUT -d 'x' -f 2)
# A 2 bytes variable (NBYTES > 0) can be found like this: (in STRINGFILE)
# ---------------X
# X---------------
#
# So we need 2 lines from STRINGFILE to make it all correct. So:
NLINES=$(( ($NBYTES / 16) +2 ))
# All line in STRINGFILE starts from 0 to f. So LASTBIT tells me the index in the line to start recording.
let LASTBYTE="0x${KEY:$((${#KEY}-1))}"
# Grep all lines needed from STRINGFILE, merge lines.
STRING=$( $GREP -A $(($NLINES-1)) -E "^ ${KEY:0:$((${#KEY}-1))}0 " $STRINGFILE | $AWK '{ print $2$3$4$5}' | $TR '\n' 'T' | $SED -e "s:T::g")
# Change string to begin in the line index.
STRING=${STRING:$((2*$LASTBYTE))}
# Cut the string to the number off bytes of the variable.
STRING=${STRING:0:$(($NBYTES * 2))}
# We need to convert to a \x??\x?? structure so:
FINALSTRING=""
for ((i = 0; i < $((${#STRING} /2 )); i++)); do
FINALSTRING="${FINALSTRING}\x${STRING:$(($i * 2)):2}"
done
define_variable
done
}
# arc4 function is called 14 times in the C code.
# Each call is done with the same args sequence even if their declaration is randomized :
# msg1, date, shll, inlo, xecc, lsto, tst1, chk1, msg2, rlax, opts, text, tst2 and chk2.
function define_variable() {
case "$j" in
0) VAR_MSG1=$FINALSTRING
VAR_MSG1_Z=$NBYTES;;
1) VAR_DATE=$FINALSTRING
VAR_DATE_Z=$NBYTES;;
2) VAR_SHLL=$FINALSTRING
VAR_SHLL_Z=$NBYTES;;
3) VAR_INLO=$FINALSTRING
VAR_INLO_Z=$NBYTES;;
4) VAR_XECC=$FINALSTRING
VAR_XECC_Z=$NBYTES;;
5) VAR_LSTO=$FINALSTRING
VAR_LSTO_Z=$NBYTES;;
6) VAR_TST1=$FINALSTRING
VAR_TST1_Z=$NBYTES;;
7) VAR_CHK1=$FINALSTRING
VAR_CHK1_Z=$NBYTES;;
8) VAR_MSG2=$FINALSTRING
VAR_MSG2_Z=$NBYTES;;
9) VAR_RLAX=$FINALSTRING
VAR_RLAX_Z=$NBYTES;;
10) VAR_OPTS=$FINALSTRING
VAR_OPTS_Z=$NBYTES;;
11) VAR_TEXT=$FINALSTRING
VAR_TEXT_Z=$NBYTES;;
12) VAR_TST2=$FINALSTRING
VAR_TST2_Z=$NBYTES;;
13) VAR_CHK2=$FINALSTRING
VAR_CHK2_Z=$NBYTES;;
esac
j=$(($j + 1))
}
# The password is used in the key function right before first call to arc4.
# So we need the previous call just before the first "call ARC4_CALLADDR" and its args.
# Update 27/06/2013 : Add new objdump format
# Update 18/11/2015 : Simplify extraction
function extract_password_from_binary(){
echo "[*] Extracting password..."
KEY_ADDR=""
KEY_SIZE=""
# Initialize the number of line before CALLADDR to watch
i=5
while [[ ( -z "$KEY_ADDR" ) || ( -z "$KEY_SIZE" ) ]]; do
$GREP -B $i -m 1 "call.*$CALLADDR" $OBJFILE | $GREP -v $CALLADDR > $CALLFILE
#cat $CALLFILE
# Adjust these two next line to grep right addr & size value (depending on your architecture)
KEY_ADDR=$($GREP -B 3 -m 1 "call" $CALLFILE | $GREP mov | $GREP -oE "0x[0-9a-z]{6,}+" | $HEAD -n 1)
KEY_SIZE=$($GREP -B 3 -m 1 "call" $CALLFILE | $GREP mov | $GREP -v $KEY_ADDR | $GREP -oE "0x[0-9a-z]+" | $HEAD -n 1)
i=$(($i + 1))
if [ $i -eq 10 ]; then
echo "[-] Error, function call previous first call of arc4() hasn't been identified..."
exit_error
fi
done
echo -e "\t[+] PWD address found : [$KEY_ADDR]"
echo -e "\t[+] PWD size found : [$KEY_SIZE]"
# Defining the address without 0x.
KEY=$(echo $KEY_ADDR | $CUT -d 'x' -f 2)
# Like the other NLINES
NLINES=$(( ($KEY_SIZE / 16) +2 ))
# Like the other LASTBYTE
LASTBYTE="0x${KEY:$((${#KEY}-1))}"
# Extract PWD from STRINGFILE
STRING=$( $GREP -A $(($NLINES-1)) -E "^ ${KEY:0:$((${#KEY}-1))}0 " $STRINGFILE | $AWK '{ print $2$3$4$5}' | $TR '\n' 'T' | $SED -e "s:T::g")
STRING=${STRING:$((2*$LASTBYTE))}
STRING=${STRING:0:$(($KEY_SIZE * 2))}
# Encode / rewrite PWD in the \x??\x?? format
FINALSTRING=""
for ((i=0;i<$((${#STRING} /2 ));i++)); do
FINALSTRING="${FINALSTRING}\x${STRING:$(($i * 2)):2}"
done
VAR_PSWD=$FINALSTRING
}
# This function append a generic engine for decrypt from shc project. With out own new variables extracted.
# Rather than execute the source code decrypted, it's printed in stdout.
function generic_file(){
cat > ${TMPBINARY}.c << EOF
#define msg1_z $VAR_MSG1_Z
#define date_z $VAR_DATE_Z
#define shll_z $VAR_SHLL_Z
#define inlo_z $VAR_INLO_Z
#define xecc_z $VAR_XECC_Z
#define lsto_z $VAR_LSTO_Z
#define tst1_z $VAR_TST1_Z
#define chk1_z $VAR_CHK1_Z
#define msg2_z $VAR_MSG2_Z
#define rlax_z $VAR_RLAX_Z
#define opts_z $VAR_OPTS_Z
#define text_z $VAR_TEXT_Z
#define tst2_z $VAR_TST2_Z
#define chk2_z $VAR_CHK2_Z
#define pswd_z $KEY_SIZE
static char msg1 [] = "$VAR_MSG1";
static char date [] = "$VAR_DATE";
static char shll [] = "$VAR_SHLL";
static char inlo [] = "$VAR_INLO";
static char xecc [] = "$VAR_XECC";
static char lsto [] = "$VAR_LSTO";
static char tst1 [] = "$VAR_TST1";
static char chk1 [] = "$VAR_CHK1";
static char msg2 [] = "$VAR_MSG2";
static char rlax [] = "$VAR_RLAX";
static char opts [] = "$VAR_OPTS";
static char text [] = "$VAR_TEXT";
static char tst2 [] = "$VAR_TST2";
static char chk2 [] = "$VAR_CHK2";
static char pswd [] = "$VAR_PSWD";
#define hide_z 4096
/* rtc.c */
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* 'Alleged RC4' */
static unsigned char stte[256], indx, jndx, kndx;
/*
* Reset arc4 stte.
*/
void stte_0(void)
{
indx = jndx = kndx = 0;
do {
stte[indx] = indx;
} while (++indx);
}
/*
* Set key. Can be used more than once.
*/
void key(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
do {
tmp = stte[indx];
kndx += tmp;
kndx += ptr[(int)indx % len];
stte[indx] = stte[kndx];
stte[kndx] = tmp;
} while (++indx);
ptr += 256;
len -= 256;
}
}
/*
* Crypt data.
*/
void arc4(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
indx++;
tmp = stte[indx];
jndx += tmp;
stte[indx] = stte[jndx];
stte[jndx] = tmp;
tmp += stte[indx];
*ptr ^= stte[tmp];
ptr++;
len--;
}
}
/* End of ARC4 */
/*
* Key with file invariants.
*/
int key_with_file(char * file)
{
struct stat statf[1];
struct stat control[1];
if (stat(file, statf) < 0)
return -1;
/* Turn on stable fields */
memset(control, 0, sizeof(control));
control->st_ino = statf->st_ino;
control->st_dev = statf->st_dev;
control->st_rdev = statf->st_rdev;
control->st_uid = statf->st_uid;
control->st_gid = statf->st_gid;
control->st_size = statf->st_size;
control->st_mtime = statf->st_mtime;
control->st_ctime = statf->st_ctime;
key(control, sizeof(control));
return 0;
}
void rmarg(char ** argv, char * arg)
{
for (; argv && *argv && *argv != arg; argv++);
for (; argv && *argv; argv++)
*argv = argv[1];
}
// Update 18/11/2015 : Update "mask" casting from "unsigned" to "unsigned long".
int chkenv(int argc)
{
char buff[512];
unsigned mask, m;
int l, a, c;
char * string;
extern char ** environ;
mask = (unsigned long)chkenv;
mask ^= (unsigned)getpid() * ~mask;
sprintf(buff, "x%x", mask);
string = getenv(buff);
l = strlen(buff);
if (!string) {
/* 1st */
sprintf(&buff[l], "=%u %d", mask, argc);
putenv(strdup(buff));
return 0;
}
c = sscanf(string, "%u %d%c", &m, &a, buff);
if (c == 2 && m == mask) {
/* 3rd */
rmarg(environ, &string[-l - 1]);
return 1 + (argc - a);
}
return -1;
}
char * xsh(int argc, char ** argv)
{
char * scrpt;
int ret, i, j;
char ** varg;
stte_0();
key(pswd, pswd_z);
arc4(msg1, msg1_z);
arc4(date, date_z);
if (date[0] && date[0]<time(NULL))
return msg1;
arc4(shll, shll_z);
arc4(inlo, inlo_z);
arc4(xecc, xecc_z);
arc4(lsto, lsto_z);
arc4(tst1, tst1_z);
key(tst1, tst1_z);
arc4(chk1, chk1_z);
if ((chk1_z != tst1_z) || memcmp(tst1, chk1, tst1_z))
return tst1;
ret = chkenv(argc);
arc4(msg2, msg2_z);
if (ret < 0)
return msg2;
varg = (char **)calloc(argc + 10, sizeof(char *));
if (!varg)
return 0;
if (ret) {
arc4(rlax, rlax_z);
if (!rlax[0] && key_with_file(shll))
return shll;
arc4(opts, opts_z);
arc4(text, text_z);
printf("%s",text);
return 0;
/*arc4(tst2, tst2_z);
key(tst2, tst2_z);
arc4(chk2, chk2_z);
if ((chk2_z != tst2_z) || memcmp(tst2, chk2, tst2_z))
return tst2;
if (text_z < hide_z) {
scrpt = malloc(hide_z);
if (!scrpt)
return 0;
memset(scrpt, (int) ' ', hide_z);
memcpy(&scrpt[hide_z - text_z], text, text_z);
} else {
scrpt = text;
}*/
} else {
if (*xecc) {
scrpt = malloc(512);
if (!scrpt)
return 0;
sprintf(scrpt, xecc, argv[0]);
} else {
scrpt = argv[0];
}
}
j = 0;
varg[j++] = argv[0]; /* My own name at execution */
if (ret && *opts)
varg[j++] = opts; /* Options on 1st line of code */
if (*inlo)
varg[j++] = inlo; /* Option introducing inline code */
varg[j++] = scrpt; /* The script itself */
if (*lsto)
varg[j++] = lsto; /* Option meaning last option */
i = (ret > 1) ? ret : 0; /* Args numbering correction */
while (i < argc)
varg[j++] = argv[i++]; /* Main run-time arguments */
varg[j] = 0; /* NULL terminated array */
execvp(shll, varg);
return shll;
}
int main(int argc, char ** argv)
{
argv[1] = xsh(argc, argv);
return 1;
}
EOF
}
##########################################
## Starting
echo "[*] UnSHc - The shc decrypter."
echo "[*] Version: $VERSION"
if [ $# -lt 1 ]; then
usage
clean
exit 0
fi
if [ ! -e $1 ]; then
echo "[-] Error, File $1 not found."
exit_error
fi
# Check the availability of each command needed in this script.
check_binaries
# Create OBJFILE and STRINGFILE from the *.sh.x encrypted script
generate_dump
# Find out the most called function. This function is arc4() and there are 14 calls.
extract_arc4_call_addr
# Retrieve the data used in each CALLADDR call
extract_variables_from_binary
# Retrieve PWD from function call just before the first CALLADDR call
extract_password_from_binary
# Create a C source code to decrypt *.sh.x file with previously extracted data
generic_file
# Compile C source code to decrypt *.sh.x file
$GCC -o $TMPBINARY ${TMPBINARY}.c
echo "[*] Executing $TMPBINARY to decrypt [${BINARY}]"
echo "[*] Retrieving initial source code in [${BINARY%.x}]"
$TMPBINARY > ${BINARY%.x}
echo "[*] All done!"
clean
exit 0