Sunday, April 30, 2017

Genetic algorithms


#!/usr/bin/env python

import numpy as np
import time, random

class GeneticALG():
    def __init__(self, chromosome_size, genetic_base, population_size, fitness_test, crossover=0.7, mutation=0.001, elite_pool=0.10):
        self.chromosome_size = chromosome_size
        self.genetic_base    = genetic_base
        self.crossover       = crossover
        self.mutation        = mutation
        self.elite_pool      = elite_pool
        self.population_size = population_size
        self.fitness_test    = fitness_test
        self.population      = list()
        self.generation      = 0
        np.random.seed([int(time.time() * 1e9) % 4294967296])
        random.seed(time.time())
        for i in xrange(population_size):
            self.population.append([
                np.random.choice([x for x in xrange(genetic_base)], size=(chromosome_size,)),
                0
             ])
    
    
    def test_fitness(self):
        # test fitness
        for c in self.population:
            c[1] = self.fitness_test(c[0])
    
    def breed(self):
        # choose two mates
        op = sorted(self.population, key=lambda x: x[1], reverse=True)
        for i in xrange(int(self.population_size * self.crossover)):
            male   = random.randint(0, self.population_size - 1)
            female = random.choice(op[:int(self.population_size * self.elite_pool)])
            splice = random.randint(0, self.chromosome_size - 1)
            
            for p in xrange(self.chromosome_size - splice):
                index = p + splice
                self.population[male][0][index] = female[0][index]
        
        # mutate
        for i in xrange(int(self.population_size * self.mutation)):
            cancer   = random.randint(0, self.chromosome_size - 1)
            victim   = random.randint(0, self.population_size - 1)
            self.population[victim][0][cancer] = random.randint(0, self.genetic_base)
                    
        # test fitness
        self.test_fitness()
        self.generation += 1

if __name__ == "__main__":
    from numpy import array_equal
    def fitness(x):
        r = 0
        if x[0] == 3:
            r += 1
        if x[1] == 2:
            r += 1
        if x[2] == 1:
            r += 1
        if x[3] == 1:
            r += 1
        if x[4] == 2:
            r += 1
        if x[5] == 3:
            r += 1
        if x[6] == 4:
            r += 1
        if x[7] == 5:
            r += 1
        if x[8] == 6:
            r += 1
        if x[9] == 7:
            r += 1
        
        
        if np.array_equal(x, np.array([3,2,1,1,2,3,4,5,6,7,8,9])):
            print "[!] YAY, only %i generations" % species.generation
            exit(0)
        return r
        
    species = GeneticALG(12, 10, 5000, fitness, elite_pool=0.60)
    
    print "[*] Starting population generated:"
    print "[*] Running until solution is found"
    
    while True:
        species.breed()




Friday, April 28, 2017

The beginnings to a neural network module for python, with a very simple POC

#!/usr/bin/env python
################################################################################
# cool resources:
#
# https://archive.ics.uci.edu/ml/datasets.html
# https://archive.ics.uci.edu/ml/datasets/Dow+Jones+Index
# http://cs229.stanford.edu/proj2013/DaiZhang-MachineLearningInStockPriceTrendForecasting.pdf
# http://www.cs.bris.ac.uk/~flach/mlbook/materials/mlbook-beamer.pdf

from numpy import exp, array, random, dot, mean, abs, atleast_1d
from datetime import datetime
import cPickle as pickle


class FFNeuralNetwork():
    # feed forward NN
    def __init__(self, inputs, outputs, layers, bias = 1):
        if layers < 3:
            print "Neural net must be >= 3"
            exit(0)
        # seed the generator, numpy requires this to work with time seed
        #random.seed([int(time.time() * 1e9) % 4294967296])
        random.seed(13) # debugging
        
        self.inputs   = inputs
        self.outputs  = outputs
        self.layers   = layers
        self.bias     = bias
        self.synapses = list()
        
        # number of synapses is one less than number of layers
        for s in xrange(self.layers - 1):
            if s == (self.layers - 2): # if this is the final synapse layer
                self.synapses.append(2 * random.random((self.inputs + 1, self.outputs)) - self.bias)
            elif s == 0: # if this is the input synapse layer
                self.synapses.append(2 * random.random((self.inputs, self.inputs + 1)) - self.bias)
            else:
                # this is a middle layer
                self.synapses.append(2 * random.random((self.inputs + 1, self.inputs + 1)) - self.bias)

    # sigmoid function, describes an S shaped curve
    # pass the weighted sum of the inputs to normalize between 1 and 0
    def __sigmoid(self, x, deriv=False):
        if deriv:
            # gradient of sigmoid curve
            return x * (1 - x)
            
            
        else:
            return 1 / (1 + exp(-x))
        
    # if there is enough divergence in the result, and the pattern of that
    # divergence matches the expected output, turn this on in think for faster training
    def __rectify(self, x):
        return (x + mean(x)) - 1
        
    def train(self, train_inputs, train_outputs, iterations):
        for j in xrange(iterations):
            # forward propogate layers
            layers = [train_inputs]
            for l in xrange(self.layers - 1):
                layers.append(self.__sigmoid(dot(layers[l], self.synapses[l])))

            # back propogate errors
            error  = train_outputs - layers[-1]
            deltas = [error * self.__sigmoid(layers[-1], deriv=True)]
            if (j % 10000) == 0:
                print "[+] %i Error: %s" % (j, mean(abs(error)))
            
            for s in xrange(len(self.synapses) - 1):
                index = (len(self.synapses) - 1) - s
                error = deltas[s].dot(self.synapses[index].T)
                deltas.append(error * self.__sigmoid(layers[index], deriv=True))
            
            # update synapses
            for s in reversed(xrange(len(self.synapses))):
                self.synapses[s] += layers[s].T.dot(deltas[(len(self.synapses) - 1) - s])
        return layers[-1]


    def think(self, x, rectify=False):
        # forward propogate and return a result
        layers = [x]
        for l in xrange(self.layers - 1):
            layers.append(self.__sigmoid(dot(layers[l], self.synapses[l])))
        return self.__rectify(layers[-1]) if rectify else layers[-1]
    
    def save_brain(self, s):
        pickle.dump(self.synapses, open(s, "wb" ))

    def restore_brain(self, s):
        self.synapses = pickle.load(open(s, "rb" ))

if __name__ == "__main__":
    import sys, os
    
    clock_flip = False
    total_start_clock = 0
    def timer():
        global clock_flip
        global total_start_clock
        if not clock_flip:
            clock_flip = True
            total_start_clock = datetime.now()
        else:
            clock_flip = False
            total_stop_clock = datetime.now()
            total_time = total_stop_clock - total_start_clock
            print "[*] Finished, time: %s" % total_time

    def draw_shape(x):
        x = x.tolist()
        for v in xrange(len(x)):
            sys.stdout.write("%s%s" % (u"o" if x[v] else " ", "\n" if (v + 1) % 3 == 0 else ""))
    
    inputs = array([
    # diagonal
    [1,0,0,
     0,1,0,
     0,0,1],
    [0,0,1,
     0,1,0,
     1,0,0],
    [0,0,0,
     0,1,0,
     0,0,1],
    [0,0,0,
     0,1,0,
     1,0,0],
    [0,1,0,
     0,0,1,
     0,0,0],
    [0,1,0,
     1,0,0,
     0,0,0],
    [0,0,0,
     0,1,0,
     1,0,0],
    [0,0,0,
     0,1,0,
     0,0,1],
    [0,0,0,
     1,0,0,
     0,1,0],
    [0,1,0,
     1,0,1,
     0,1,0],
    [1,1,0,
     0,1,1,
     0,0,1],
    [0,0,0,
     1,1,0,
     0,1,1],
    [1,0,0,
     1,1,0,
     0,1,1],
    [0,1,0,
     1,0,1,
     0,0,0],
    [0,0,0,
     0,1,1,
     1,1,0],

    #horizontal
    [0,0,0,
     1,1,1,
     0,0,0],
    [1,1,1,
     0,0,0,
     0,0,0],
    [0,0,0,
     0,0,0,
     1,1,1],
    
    #verticle
    [0,1,0,
     0,1,0,
     0,1,0],
    [0,0,1,
     0,0,1,
     0,0,1],
    [1,0,0,
     1,0,0,
     1,0,0],
    [0,0,0,
     0,0,1,
     0,0,1],
    [0,0,0,
     1,0,0,
     1,0,0],
    [0,0,0,
     1,0,1,
     1,0,1],
    
    # weird shapes
    [1,1,1,
     1,0,0,
     1,0,0],
    
    [1,1,1,
     1,0,1,
     1,1,1],
     
    [1,1,1,
     1,1,0,
     1,0,1],
    
    [1,1,1,
     1,1,1,
     1,1,1],
    
    [1,0,1,
     1,0,1,
     1,1,1],
    
    [0,0,1,
     0,0,1,
     1,1,1],
     
    [1,0,0,
     1,0,0,
     1,1,1],
    
    [0,0,0,
     0,0,1,
     1,1,1],
     
    [0,0,0,
     1,0,0,
     1,1,1],
    
    [1,1,1,
     0,0,0,
     1,1,1],
    
    [0,0,0,
     1,1,1,
     1,1,1],
    
    [1,0,1,
     1,0,1,
     1,0,1],
    
    [0,1,1,
     0,1,1,
     0,1,1],
    
    [0,1,0,
     1,1,1,
     0,0,0],
    
    [0,1,0,
     1,0,1,
     0,0,0],
    
    [0,0,0,
     0,1,0,
     1,0,1],
    
    [0,0,0,
     0,1,0,
     1,1,1],
    
    [0,1,0,
     1,0,1,
     1,1,1],
    ])

    outputs = array([
       # d h v
       #diagonal
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        [1,0,0],
        
       #horizontal
        [0,1,0],
        [0,1,0],
        [0,1,0],
        
       #verticle
        [0,0,1],
        [0,0,1],
        [0,0,1],
        [0,0,1],
        [0,0,1],
        [0,0,1],
       
       #shape
        [0,1,1],
        [0,1,1],
        [1,1,1],
        [1,1,1],
        [0,1,1],
        [0,1,1],
        [0,1,1],
        [0,1,1],
        [0,1,1],
        [0,1,0],
        [0,1,0],
        [0,0,1],
        [0,0,1],
        [1,1,0],
        [1,0,0],
        [1,0,0],
        [1,1,0],
        [1,1,1],
    ])

    # test matrix
    test = array([
        [0,1,0,
         1,0,1,
         1,1,1],
        
        [0,0,0,
         1,0,1,
         1,1,1],
        
        [1,1,1,
         1,0,1,
         1,1,1],
        
        [0,0,0,
         1,1,1,
         1,1,1],
        
        [0,0,0,
         0,1,1,
         1,1,0],
        
        [0,0,0,
         0,1,0,
         1,1,1],
        
        ])

    #initialize a neural network
    nn = FFNeuralNetwork(9, 3, 6, bias=1)
    
    # restore synapses
    if os.path.exists("shapes.pkl"):
        nn.restore_brain("shapes.pkl")
    
    #train this baby
    cycles = 5000000
    print "[*] Training for %i cycles" % cycles
    
    timer()
    nn.train(inputs, outputs, cycles)
    timer()
    
    # store synapses after training
    nn.save_brain("binary_counter.pkl")
    
    print "[*] Getting test result"
    timer()
    result = nn.think(test, rectify=True)
    timer()
    
    print "[+] Results"
    for i in xrange(len(result)):
        draw_shape(test[i])
        if result[i][0] > 0:
            sys.stdout.write("d")
        if result[i][1] > 0:
            sys.stdout.write("h")
        if result[i][2] > 0:
            sys.stdout.write("v")
        sys.stdout.write("\n\n")
            






Sunday, June 26, 2016

George Marsaglia's xorshift PRNG lightweight implementation

;////////////////////////////////////////////////////////////////////////////////
;// THE SCOTCH-WARE LICENSE (Revision 0):
;// <aaronryool/gmail.com> wrote this file. As long as you retain this notice you
;// can do whatever you want with this stuff. If we meet some day, and you think
;// this stuff is worth it, you can buy me a shot of scotch in return
;////////////////////////////////////////////////////////////////////////////////

; George Marsaglia's xorshift PRNG
; t = r10
prng_state:
w: dq 99999
x: dq 100
y: dq 35
z: dq 27365

prng_seed:
rdtsc
mov qword [x], rax
ret

prng:
; t = x
mov r10, qword [x]
; t ^= t << 11
mov rax, r10
shl rax, 11
xor r10, rax
; t ^= t >> 8
mov rax, r10
shr rax, 8
xor r10, rax
; x = y, y = z, z = w
push qword [w]
push qword [y]
push qword [z]
pop qword [y]
pop qword [x]
pop qword [z]
; w ^= w >> 19
mov rax, [w]
shr rax, 19
xor qword [w], rax
; w ^= t
xor qword [w], r10

; return w
mov rax, qword [w]
ret

Sunday, December 20, 2015

Better visualization of data formats using assembly POC's to better implement them in C

Are you tired of staring at hex dumps, white papers and manual pages? Want to feel like you truly know how a data type like a file format, or a disk partition LOOKS like. Well I can't help you with the manual pages and white papers, (https://staff.washington.edu/dittrich/misc/fatgen103.pdf, http://www.eit.lth.se/fileadmin/eit/courses/eitn50/Projekt1/FAT12Description.pdf) but here is a great example of implementing a fat12 floppy image in assembly.

;////////////////////////////////////////////////////////////////////////////////
;// THE SCOTCH-WARE LICENSE (Revision 0):
;// <aaronryool@gmail.com> wrote this file. As long as you retain this notice you
;// can do whatever you want with this stuff. If we meet some day, and you think
;// this stuff is worth it, you can buy me a shot of scotch in return
;////////////////////////////////////////////////////////////////////////////////
; This assembles to a floppy disk image
[bits 16]
[org 0x7C00]

;;;;;;;;;;;;;;;;;;;;;;;;
; BIOS Parameter Block
jmp short boot_code
nop
db "MadOSv01"          ; OEM identifier
dw 512                 ; Bytes per sector
db 1                   ; sectors per cluster
dw 1                   ; reserved sectors
db 1                   ; number of fats
dw 112                 ; directory entries
dw 1280                ; logical sectors
db 0xFB                ; media descriptor type
dw 1                   ; sectors per fat, fat12/fat16 only
dw 18                  ; sectors per track
dw 2                   ; number of heads / sides on media
dd 0                   ; number of hidden sectors
dd 0                   ; Large amount of sector on media. This field is set if there are more than 65535 sectors in the volume.

; Extended Boot Record
db 0                   ; drive number
db 0                   ; reserved / NT flags (maybe use for MadOS, More research needed)
db 0x29                ; signiture, 0x28 or 0x29 for fat12 /fat16
dd 0x1337              ; volume serial number
db "MadOS Disk "       ; volume label
db "FAT12   "          ; identifier string

; Boot code
boot_code:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, boot_code_end + (1024 * 4)

    lea si, [message]
    call print_string
    jmp $

print_string:
    lodsb
    or al, al
    jz done
    mov ah, 0x0E
    int 0x10
    jmp print_string
done:
   ret

message:
    db "MadOS disk not bootable...", 0xd, 0xa

;dw 0xAA55 ; bootable partition signiture, uncomment to make the floppy boot
boot_code_end:
times 450 - (boot_code_end - boot_code) db 0

;;;;;;;;;;;;;;;;;;;;;
; DATA AREA START

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FAT entries
; an entry is 3 nibbles:
;   000         -> unused
;   0xFF0-0xFF6 -> reserved cluster
;   0xFF7       -> bad cluster
;   0xFF8-0xFFF -> last cluster in file
;   ***         -> next data cluster

fat1:
    db 0xFB, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0x00, 0x00
    ;    0     1     2     3     4     5     6     7     8     9     10    11    12    13
    times 512 - ($ - fat1) db 0
fat2:
    times 512 - ($ - fat2) db 0
;;;;;;;;;;;;;;;;;;
; root directory
root:
    folder:
        db "folder     "
        db 0x20 | 0x10         ; attributes: a, d
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 3                   ; low order bits of first cluster address
        dd 64                  ; file size

    file:
        db "file       "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 2                   ; low order bits of first cluster address
        dd 40                  ; file size

; padd out the rest of the root directory
times (512 * 4) - ($ - root) db 0


times 1024 db 0     ; first two data blocks are reserved

; 2 file
file_data:
    db "This is a file in the root directory :D", 0xa
; cluster padding
times 512 - ($ - file_data) db 0

; 3 folder
folder_data:
    file2:
        db "file2      "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 4                   ; low order bits of first cluster address
        dd 34                  ; file size
    folder2:
        db "folder2    "
        db 0x20 | 0x10         ; attributes: a, d
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 5                   ; low order bits of first cluster address
        dd 32                  ; file size
; cluster padding
times 512 - ($ - file2) db 0

; 4 file2
file2_data:
    db "This is a file in a sub directory", 0xa
; cluster padding
times 512 - ($ - file2_data) db 0


; 5 folder2_data
folder2_data:
    file3:
        db "file3      "
        db 0x20                ; attributes: a
        dw 0                   ; reserved
        dw 0xA496              ; creation time (h,m,s)
        dw 0x4793              ; creation date
        dw 0x4793              ; access date
        dw 0                   ; high order bits of first cluster address, 0 on fat12
        dw 0xA496              ; write time (h,m,s)
        dw 0x4793              ; write date
        dw 6                   ; low order bits of first cluster address
        dd 1024                ; file size
; cluster padding
times 512 - ($ - folder2_data) db 0

; 6 file3_data
file3_data:
times 512 - ($ - file3_data) db 'A'

; 7 file3_data2
file3_data2:
times 512 - ($ - file3_data2) db 'B'




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; padd out the rest of the floppy
times ((1024 * 1024) + 461373) - ($ - $$) db 0

So from this exploration, we can construct the following header file as a start:

////////////////////////////////////////////////////////////////////////////////
// THE SCOTCH-WARE LICENSE (Revision 0):
//  wrote this file. As long as you retain this notice you
// can do whatever you want with this stuff. If we meet some day, and you think
// this stuff is worth it, you can buy me a shot of scotch in return
////////////////////////////////////////////////////////////////////////////////

#ifndef __FAT_H__
#define __FAT_H__

#include <stdint.h>
#include <machine.h>

typedef struct bpb {
    char jmp_code[3];
    char oem[8];
    uint16_t bytes_per_sector;
    uint8_t sectors_per_cluster;
    uint16_t reserved_sectors;
    uint8_t fats;
    uint16_t dir_entries;
    uint16_t logical_sectors;
    uint8_t media_type;
    uint16_t sectors_per_fat;
    uint16_t sectors_per_track;
    uint16_t heads;
    uint32_t hidden_sectors;
    uint32_t large_sectors;
} bpb_t;

typedef struct ebr {
    uint8_t drive;
    uint8_t rflags;
    uint8_t signiture;
    uint32_t serial;
    char label[11];
    char identifier[8];
    char boot_code[448];
    uint16_t boot_sign;
} ebr_t;

typedef struct fat12_partition_info {
// see fat12.s for an implementation
// in assembly for visualization :D

    bpb_t bpb;
    ebr_t ebr;
    char data[];
} fat12_partition_info_t;

typedef struct fat_dir_entry {
    char name[11];
    uint8_t attributes;
    uint16_t reserved;
    uint16_t creation_time;
    uint16_t creation_date;
    uint16_t access_date;
    uint16_t caddr_high;
    uint16_t write_time;
    uint16_t write_date;
    uint16_t caddr_low;
    uint32_t size;
} fat_dir_entry_t;

const uint8_t fat12_format_stub[122] = {
  0xEB, 0x3C, 0x90, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x76, 0x30,
  0x31, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x37,
  0x13, 0x00, 0x00, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x20, 0x44,
  0x69, 0x73, 0x6B, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20,
  0x20, 0x20, 0x31, 0xC0, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xD0,
  0xBC, 0x7A, 0x8C, 0x8D, 0x36, 0x5E, 0x7C, 0xE8, 0x02, 0x00,
  0xEB, 0xFE, 0xAC, 0x08, 0xC0, 0x74, 0x06, 0xB4, 0x0E, 0xCD,
  0x10, 0xEB, 0xF5, 0xC3, 0x4D, 0x61, 0x64, 0x4F, 0x53, 0x20,
  0x64, 0x69, 0x73, 0x6B, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x62,
  0x6F, 0x6F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x2E, 0x2E, 0x2E,
  0x0D, 0x0A
};


/////////////////////////////////////////////
// FAT12 entries                           //
/////////////////////////////////////////////
// an entry is 3 nibbles:                  //
//   000         -> unused                 //
//   0xFF0-0xFF6 -> reserved cluster       //
//   0xFF7       -> bad cluster            //
//   0xFF8-0xFFF -> last cluster in file   //
//   ***         -> next data cluster      //
/////////////////////////////////////////////

typedef char fat12_t[];
#define GET_FAT12_ENTRY(n, fat)\
    (n % 2 == 0 ? fat[((3 * n) / 2)] | (fat[1 + (3 * n) / 2] & 0x0F) << 8 : \
    (fat[(3 * n) / 2] & 0xF0) >> 4 | fat[1 + (3 * n) / 2] << 4)


#endif


Wednesday, December 16, 2015

Flat Memory Manager example


Saturday, November 28, 2015

Exploring cellular automata with conway's game of life as a foundation


Friday, August 14, 2015

simulating router firmware for live demonstration, more advanced RE, or just giggles

So I needed to put together a live demonstration for a presentation I'm doing soon on the attack surface of embedded devices, and why the industry seems to want their devices vulnerable. I have never needed to boot a firmware image in such a controlled manner before, and decided this would be something nice to share with others so they can just get this kind of thing up and running without hassle for their warped projects. So without further ado, lets get started.

Before you set this up, you will need to set up a basic virtual machine with the GNU/Linux distribution of your choice. You will also need to have already extracted the firmware image into a folder called squashfs-root unless you want to do the smart thing, and apply these instructions to your own personal setup.

First open the /etc/inittab for your firmware image. locate the line for sysinit and take note of what it has there.

::sysinit:/etc/rcS
::respawn:/sbin/getty 115200 ttyS1
::respawn:/bin/sh 
::restart:/sbin/init
::shutdown:/bin/umount -a -r

Ok, copy the squashfs-root folder to the root directory of the virtual machines disk image, then copy the following script into the root directory of the image as well after modifying it to meet the needs of your particular firmware image:

#!/bin/bash

export FIRMROOT="/squashfs-root"
export SYSINIT="/etc/rcS"

ifconfig eth0 10.0.3.10

# bind mount the important things so that this acts like a real linux system
mount --bind /tmp $FIRMROOT/tmp
mount --bind /sys $FIRMROOT/sys
mount --bind /proc $FIRMROOT/proc
mount --bind /dev $FIRMROOT/dev

# chroot into the firmware image, and launch the correct sysinit script for your firmware.
chroot $FIRMROOT /bin/sh -c $SYSINIT

And thats how its done. Here is a screenshot of the web login for this router running in VM:

And here is a screenshot of the router VM being exploited: