;;; hw6.asm
;;; Amy Gray
;;; This program reads a bmp file into memory and decodes a secret
;;; message hidden in the pixels via steganography
;;;
%include "mytools2.inc" ; for basic printing to the screen
%include "fileio2.inc" ; for basic file operations
%assign SYS_EXIT 1
%assign MAXPIXEL 5000000
%assign RED 2
%assign GREEN 1
%assign BLUE 0
;;; ---------------------------------------------------------
;;; data segments
;;; ---------------------------------------------------------
section .data
errorMsg db "Error accessing file!",0x0A
errorLen equ $-errorMsg
message db 0 ;will be used to store decoded byte
section .bss
filename resb 255 ; reserve 255 chars for filename
handle resd 1 ; file descriptor
noRead resd 1 ; contains number of bytes read
lWidth resd 1 ; the line width rounded to the nearest
; dword boundary lWidth=(((width-1)/4)+1)*4
;;; ----------------------------------------------------------
;;; bmp buffer starts here
;;; ----------------------------------------------------------
bmpBuf equ $
type resw 1 ; header
size resd 1
resvd1 resw 1
resvd2 resw 1
offset resd 1
;;;
size2 resd 1 ; information header
width resd 1
height resd 1
planes resw 1
xbits resw 1
compr resd 1
imgsize resd 1
xres resd 1
yres resd 1
nocol resd 1
impcol resd 1
headLen equ $-bmpBuf ; length of header
pixelBuf resb MAXPIXEL*3 ; times 3 bytes (RGB)
;;; ---------------------------------------------------------
;;; code area
;;; ---------------------------------------------------------
section .text
global _start
_start:
;;; --- check that user entered more than 1 argument on command line ---
pop ebx ; get # of arguments on command line
cmp ebx,1
jg process ; if user forgot file name, exit
dumpStr "Syntax: progName filename"
jmp exit
;;; --- get filename from command line ---
process:
pop ebx ; pointer to argv[0] (name of program)
pop ebx ; pointer to argv[1]
mov edi,filename ; pointer to filename
copy: mov al,[ebx] ; copy name of file from argv[1] to
mov [edi],al ; filename string, until \0 is copied
inc ebx
inc edi
cmp al,0
jne copy
;;; --- open file ---
openFile filename, [handle], errorMsg, errorLen
;;; --- read file in buffer ---
readFile [handle], bmpBuf, MAXPIXEL, noRead
;;; --- compute length of a line of pixel:
;;; line length = (( byte width+3)/4)*4 where
;;; byte width = 3 * width, since we have 3 bytes per pixel
;;;
mov eax,[width]
add eax,[width]
add eax,[width] ; eax <-- 3 x width
dec eax ; eax
shr eax,2 ;
inc eax
shl eax,2
mov [lWidth],eax
;;; --------------------------------------------------------------
;;; Find the address of the first byte of the image and put
;;; it in ebx
;;; --------------------------------------------------------------
mov eax,0
push eax
push eax
call hw2index
;;; -------------------------------------------------------------
;;; mainloop: Extract message hidden in the image's pixels. Goes
;;; through eight bytes from the image and stores the
;;; least significant bit from in a new byte, which is
;;; a character from the message. The message ends with
;;; a zero, so each newly formed byte is checked to see
;;; if it is equal to zero. If yes, the program jumps
;;; to the end. If no, the program prints the character
;;; registers modified: eax, ebx, ecx, edx
;;; -------------------------------------------------------------
add ebx, 7 ;moves to eighth byte
;bits are stored backwards from 7 to 0
;; for some reason, when 'jmp mainloop' is called below, the program
;; was jumping back to 'add ebx, 7' and adding 7 to ebx (causing everything
;; to go wrong). 'add ebx, 0' is put in as a placeholder. The program
;; jumps back to here, does nothing and continues on to mainloop
add ebx, 0
mainloop:
call decode1byte
cmp byte[message], 0
je theEnd
push ebx ;ebx needs to be saved for accessing pixels
mov ecx, message
mov eax, SYS_WRITE
mov ebx, STDOUT
mov edx, 1
int 0x80
pop ebx ;restore ebx
jmp mainloop ;repeat
;; -----------------------------------
;; end of pixel loop area
;; -----------------------------------
theEnd:
;;; --- position file pointer back to beginning of file ---
seek0 [handle]
;;; --- write contents back to file ---
writeBuf [handle], bmpBuf, noRead
;;; --- close file ---
close [handle]
;;; --- exit() ---
exit:
mov eax,SYS_EXIT
mov ebx,0
int 0x80 ; final system call
;;; -------------------------------------------------------------
;;; hw2index: converts height and width to pixel index
;;; index returned in ebx. Note that computation is
;;; performed with lWidth which must be computed
;;; separately and is the number of bytes in one line
;;; of pixel, such that number of byte is a multiple of
;;; 4.
;;; registers modified: ebx
;;; -------------------------------------------------------------
hw2index:
push ebp
mov ebp,esp
push eax ; save eax and edx
push edx
mov eax,[lWidth]
xor edx,edx
mul dword[ebp+12] ; eax = lWidth * h
add eax,[ebp+8] ; eax += 3*w
add eax,[ebp+8]
add eax,[ebp+8]
mov ebx,eax ; pass result in ebx
pop edx
pop eax
pop ebp
ret 8
;;; --------------------------------------------------------
;;; decode1byte: runs through a series of eight bytes
;;; and extracts the last bit from each.
;;; Starts at the eighth bit and works backward
;;; Each extracted bit is stored in edx as
;;; a new byte. This byte is transferred into
;;; the variable 'message'
;;; registers modified: ebx
;;; -------------------------------------------------------
decode1byte:
push edx
push eax
push ecx
mov edx, 0 ;will hold secret byte
mov eax, 0 ;will hold byte to extract from
mov ecx, 7
;; the loop runs 7 times. At the end of each run, the
;; secret byte is shifted to the left to make room for
;; the next new bit. The eighth bit is then extracted
;; outside the loop, so that no shift happens. An
;; eighth shift would cause the first bit to be lost
;; and an extraneous zero to appear at the end.
forbyte:
mov al, byte[ebx+pixelBuf] ;byte to extract from
and al, 1 ;clear all but least significant bit
or dl, al ;add new bit to dl
shl dl, 1 ;make room for next bit
dec ebx ;move to next byte
loop forbyte
mov al, byte[ebx+pixelBuf] ;get 8th bit
and al, 1
or dl, al
mov byte[message], dl ;store decoded byte in 'message'
add ebx, 15 ;jumps to next set of eight bytes
pop ecx
pop eax
pop edx
ret