flat assembler
Message board for the users of flat assembler.

Index > Windows > Basic Steganography

Goto page Previous  1, 2
Thread Post new topic Reply to topic

Joined: 26 Aug 2008
Posts: 227
Right, I think I have a code which will work now, but I am having another problem. In my file I declared a structure which is the equivilant of BITMAPFILEHEADER and BITMAPINFOHEADER (as these are the headers I need). I need to fill this structure with the first 0x36 bytes of data, so I do the following:

              mov     ecx,0x36 ; sizeof(BITMAPINFOHEADER)
                     movzx   eax,byte [pMemOrig]
                 mov             byte [bmpOriginal],al
                       movzx   eax,byte [pMemAdd]
                  mov     byte [bmpAdd],al
                    loop    headerFill

I'm probably making some stupid mistake I know... I realise I could also change it to move more than one byte at once, I could do two (but not four easily as 54 (0x36) / 4 = 13.5).

There are not any viruses that I have heard of that are like that. The problem is as already discussed, the data would have to be encrypted with something like AES before the adding occurs. It is a good way to hide information if the person is not expecting it too.

I have also redone my adding bit (thanks to bitRAKE's method ^^):

              mov             ebx,[bmpOriginal.bfOffBits]
         mov             edx,[bmpAdd.bfOffBits]
              mov             ecx,[bmpAdd.biSizeImage]
            mov             eax,0
               shl             eax,3
               sub             eax,7
                   bt              [pMemAdd+edx],eax ; Bit at (pMemAdd + ebx) + eax = 1 ? 
                     setc    bl ; bl = CF
                        shl             eax,3 ; eax *= 8
                    sub             eax,7 ; eax -= 7
                    bts             [pMemOrig+ebx],eax
                  cmp             bl,0 
                       je              addImageBitClear
                               btr             [pMemOrig+ebx],eax
                          inc     eax
                               add             eax,7 ; eax += 7
                            shr             eax,3 ; eax /= 8
                            inc             eax
                         loop    addImageLoop

I dunno if it will work though; I hope it will as I cant see anything wrong with it!
Post 12 Apr 2009, 19:10
View user's profile Send private message Reply with quote

Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
Yeah, I assembled a "decryptor" to the sample image in Wiki Smile
Just pass it the original image and it will output the other in a few seconds:
format binary as "bmp"
file 'StenographyOriginal.bmp':0,54
repeat 120000
    virtual at 0
        file 'StenographyOriginal.bmp':%+53,1
        load char from 0
    end virtual
    char=(char and 011b)*64
    db char
end repeat 

Razz Maybe too simple, because another advancement would be automatic filesize.

You have a bug in the first code. You don't use ECX anywhere!
Here, I've fixed it for you:
;The stall only happens when you do this:
; mov al,12h
; mov eax,1234h
;but not when you access *only* al!
;The simplest code would be:
mov     ecx,0x36 ; sizeof(BITMAPINFOHEADER)
        mov     al,[pMemOrig+ecx]
        mov     [bmpOriginal+ecx],al
        mov     al,[pMemAdd+ecx]
        mov     [bmpAdd+ecx],al
        loop    headerFill

;Another try
mov     ecx,0x36 and 0xFC ; sizeof(BITMAPINFOHEADER)
        sub     ecx,4
        jc      .loop_tail
        mov     eax,[pMemOrig+ecx]
        mov     [bmpOriginal+ecx],eax
        mov     eax,[pMemAdd+ecx]
        mov     [bmpAdd+ecx],eax
        jmp     headerFill
        mov     ecx,0x34
        mov     ax,[pMemOrig+ecx]
        mov     [bmpOriginal+ecx],ax
        mov     ax,[pMemAdd+ecx]
        mov     [bmpAdd+ecx],ax

And hey, look what my compiler did Smile
Btw, this is your (54-byte) header-copying part if you didn't recognize
$B2$24:                         ; Preds $B2$23
        mov       rdx, QWORD PTR [rsi]                          ;25.5
        mov       QWORD PTR [rcx], rdx                          ;25.5
        mov       r8, QWORD PTR [rsi+8]                         ;25.5
        mov       QWORD PTR [rcx+8], r8                         ;25.5
        mov       r9, QWORD PTR [rsi+16]                        ;25.5
        mov       QWORD PTR [rcx+16], r9                        ;25.5
        mov       r10, QWORD PTR [rsi+24]                       ;25.5
        mov       QWORD PTR [rcx+24], r10                       ;25.5
        mov       r11, QWORD PTR [rsi+32]                       ;25.5
        mov       QWORD PTR [rcx+32], r11                       ;25.5
        mov       rdx, QWORD PTR [rsi+40]                       ;25.5
        mov       QWORD PTR [rcx+40], rdx                       ;25.5
        mov       edx, DWORD PTR [rsi+48]                       ;25.5
        mov       DWORD PTR [rcx+48], edx                       ;25.5
        movzx     edx, WORD PTR [rsi+52]                        ;25.5
        mov       WORD PTR [rcx+52], dx                         ;25.5

I would argue that this isn't the most optimal, but it is the fastest Smile

Last edited by Madis731 on 13 Apr 2009, 09:38; edited 2 times in total
Post 13 Apr 2009, 07:46
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote

Joined: 26 Aug 2008
Posts: 227
Thats just annoying that my code is hella long compared to yours <_< (I use all Windows APIs etc.). Nice example though.

Any ideas about the structure copying thing?
Post 13 Apr 2009, 08:35
View user's profile Send private message Reply with quote

Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
(Now that I've understood Stenograhy) I just realized that your algorithm loop should be something like:
for i=0 to size step4
  DWORD dst_img[i] = (src_img[i] & 0xFEFEFEFE) | (add_img[i] & 0x01010101)

You shouldn't need anything fancier Smile Some attempt to translate this pseudo to asm:
  mov ecx,[size]
  mov eax,[esi]
  and eax,0xFEFEFEFE
  mov ebx,[ebp]
  and ebx,0x01010101
  or  eax,ebx
  mov [edi],eax
  sub ecx,4
  jnc @b

...and we're done Smile

I know I shouldn't optimize it that much, but I love it so here's a smaller and faster variant:
mov eax,[esi]
mov ebx,[ebp]
xor ebx,eax ;Find differences
and ebx,0x01010101 ;take only one bit per byte
xor eax,ebx ; update the bytes
mov [edi],eax

Try this site for even wilder algorithms: http://www.jjj.de/fxt/ It was renamed from "Algorithms for Programmers" to "Matters Computational" some time ago. There's a huge (5MB) pdf there. Samples are in C
Post 13 Apr 2009, 09:08
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote

Joined: 26 Aug 2008
Posts: 227
Cheers for all the help man! I aint got it working, but I can see that your code will work. I just cant get the header bit working (the code works, but I think its something to do with reading the file earlier on as it ends up that in the header copy loop pMemAdd is only like 4 more than pMemOrig, which is obviously incorrect).

I think I need to work a bit more in ASM if I cant do simple tasks like this -.- Somone ort to slap me. (How long was it before you were good at ASM?)

Actually, just looking at the pseudo-code, that is incorrect as that will mean each bit will be stored in the corresponding bit on the original image, whilst it should be the corresponding byte.

I looked at that link. Damn that is really useful, nice codes and the PDF. Tyvm.
Post 13 Apr 2009, 15:33
View user's profile Send private message Reply with quote

Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
Maybe I did things differently and I didn't understand your bit-wizardry Smile, so I will try to explain.
I took my algorithm from the Wikipedia where each byte corresponds to another byte and the images are of same size. What I can see from your code posted here, you are trying to fit all the significant bits in the larger image and if you use only one bit, then you need to have the "hidden" image be 8 times smaller. Smile
I had this "cat-image" stored as low two significant bits, so it was really easy to "and" and "or" these two pictures. In your case I think I'll need to revisit this topic later.
Sorry if I have mislead you.

Btw, I've done ASM about 5 years now and still learning. There might be some wonder-persons who got a grasp on ASM after a few weeks, but for me it took at least a year or so - maybe because I'm lazy. You can be thankful that the FASM-community has been ever-increasing so the learning-curve is not so bad!
Post 14 Apr 2009, 06:07
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote

Joined: 26 Aug 2008
Posts: 227
Ahh I see yeah. Thats why steganography is good, there are a wide range of methods that can be used. Yeah I can see how your code works now. I think using bt* will work in my case, I just need to get the header filling in working properly, but yeah more reading first! Its cool about misleading me Razz I still learnt so its all good.

I am thinking of doing what the UK did in WWI (or WWII <- think it was that one). The Germans were intercepting UK-USA communication, so we added white noise to the recordings. Apparently without pretty much an identical white noise sound, you cannot extract the original recording.

Once my exams are over its gonna be ASM and C pretty much all the way, need to get the languages down. By the way, here is my full bitmap steganographier:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define    BMAGIC 0x4d42

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;

typedef struct {
    word         biType;
    dword        biFileSize;
    word         biReserved1;
    word         biReserved2;
    dword        biDataOffset;
    dword        biInfoSize;
    dword        biWidth;
    dword        biHeight;
    word         biPlanes;
    word         biBitCount;
    dword        biCompression;
    dword        biSizeImage;
    dword        biXPelsPerMeter;
    dword        biYPelsPerMeter;
    dword        biClrUsed;
    dword        biClrImportant;
} __attribute__ ((packed)) BITMAPHEADERINFO;

int ExtractBitmap(char* szBitmap, char* szOutput, long lWidth, long lHeight, long lBitsPerPix);
int AddBitmap(char* szOriginal, char* szAdd, char* szOutput);

int ExtractBitmap(char* szBitmap, char* szOutput, long lWidth, long lHeight, long lBitsPerPix)
    FILE *pBitmap = fopen(szBitmap,"rb"); if(pBitmap == NULL) return 0;
    long lRes = 0;
    long lBitSize = ftell(pBitmap);
    char *szBitCont = (char*)malloc(lBitSize);
    if(szBitCont != NULL)
        if(fread(szBitCont,1,lBitSize,pBitmap) == lBitSize)
            BITMAPHEADERINFO bmpBitmap; memset(&bmpBitmap,0,sizeof(BITMAPHEADERINFO));
            long lOutSize = (lWidth * lHeight * (lBitsPerPix / 8));
            if((lBitSize - bmpBitmap.biDataOffset) >= lOutSize)
                FILE *pOutput = fopen(szOutput,"wb");
                if(pOutput != NULL)
                    BITMAPHEADERINFO bmpOutput; memset(&bmpOutput,0,sizeof(BITMAPHEADERINFO));
                    bmpOutput.biType = BMAGIC;
                    bmpOutput.biFileSize = (lWidth * lHeight * (lBitsPerPix / 3)) + sizeof(BITMAPHEADERINFO);
                    bmpOutput.biDataOffset = sizeof(BITMAPHEADERINFO);
                    bmpOutput.biInfoSize = 40;
                    bmpOutput.biWidth = lWidth;
                    bmpOutput.biHeight = lHeight;
                    bmpOutput.biPlanes = 1;
                    bmpOutput.biBitCount = lBitsPerPix;
                    bmpOutput.biSizeImage = lOutSize;

                    char *szOutCont = (char*)calloc(lOutSize,1);
                    if(szOutCont != NULL)
                        unsigned long i = bmpBitmap.biDataOffset, x = 0, lShift;
                        for(; i < lBitSize && x < lOutSize && (lShift = Cool; x++)
                            while(lShift--) szOutCont[x] |= ((szBitCont[i++] & 1) << lShift);
                        if(fwrite(szOutCont,1,lOutSize,pOutput) == lOutSize) lRes = 1;
    return lRes;

int AddBitmap(char* szOriginal, char* szAdd, char* szOutput)
    FILE *pOriginal = fopen(szOriginal,"rb"); if(pOriginal == NULL) return 0;
    long lRes = 0;
    FILE *pAdd = fopen(szAdd,"rb");
    if(pAdd != NULL)
        long lOrigSize, lAddSize;
        fseek(pOriginal,0,SEEK_END); fseek(pAdd,0,SEEK_END);
        lOrigSize = ftell(pOriginal); lAddSize = ftell(pAdd);
        fseek(pOriginal,0,0); fseek(pAdd,0,0); // 0 = SEEK_SET
        char *szOrigCont = (char*)malloc(lOrigSize);
        if(szOrigCont != NULL)
            char *szAddCont = (char*)malloc(lAddSize);
            if(szAddCont != NULL)
                    BITMAPHEADERINFO bmpOriginal, bmpAdd;
                    memset(&bmpOriginal,0,sizeof(BITMAPHEADERINFO)); memset(&bmpAdd,0,sizeof(BITMAPHEADERINFO));
                    memcpy(&bmpOriginal,szOrigCont,sizeof(BITMAPHEADERINFO)); memcpy(&bmpAdd,szAddCont,sizeof(BITMAPHEADERINFO));
                    if((lOrigSize - bmpOriginal.biDataOffset) >= (8 * (lAddSize - bmpOriginal.biDataOffset)))
                        FILE *pOutput = fopen(szOutput,"wb");
                        if(pOutput != NULL)
                            char *szOutCont = (char*)calloc(lOrigSize,1);
                            if(szOutCont != 0)

                                long i = bmpOriginal.biDataOffset, x = bmpAdd.biDataOffset, lShift;
                                for(; i<=lOrigSize && x<=lAddSize && (lShift=Cool; x++)
                                    while(lShift--) szOutCont[i] = (szOrigCont[i++] & ~1)|((szAddCont[x] >> lShift) & 1);
                                if(fwrite(szOutCont,1,lOrigSize,pOutput)==lOrigSize) lRes = 1;
    return lRes;

If you are interested... ^^
Post 14 Apr 2009, 08:54
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page Previous  1, 2

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum

Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.