flat assembler
Message board for the users of flat assembler.

Index > Windows > Basic Steganography

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
pal



Joined: 26 Aug 2008
Posts: 227
pal 10 Apr 2009, 22:39
Well, I am trying to make a basic steganography program. I have the C code down, but I cant get the ASM code correct.

Basically what it is is I have two bitmap files: one big one, one small one. The small one is going to be inserted into the big one. The program has to loop through the small picture file (from 0x37 as the header of a bitmap file is 0x36 bytes long) and add one bit from each byte to the lowest significant byte (using big endian bit storage) of a byte in the big picture file, if that makes sense. So like:

Bytes in big picture: [10011001] * 8
Byte in small picture: 01100110

So each bit from the small picture will be added to the next byte in the big picture, so the first byte in the big picture would end up being:

10011000

So yeah, thats what it is basically doing. So far I have this code (it probably makes no sense by the way and I think I have fucked up the looping badly):

Code:
             lea             esi,[pMemOrig]
              lea             ebx,[pMemAdd+0x36]
          
            addImageLoop:
                   cmp             ecx,0x36
                    jg              imageData
                   mov             eax,[esi+ecx]
                       mov             [pMemOut+ecx],eax
                   inc             ecx
                 jmp             addImageLoop
                        imageData:
                              push    ecx
                         xor             eax,eax
                             mov             eax,ecx
                             xor             ecx,ecx
                             
                            shiftLoop:
                                      mov             edx,[ebx]
                                   shl             edx,cl
                                      shr             edx,30
                                      mov             [pMemOut+eax+ecx],edx
                                       xor             edx,edx
                                     add             esi,ecx
                                     add             esi,eax
                                     mov             edx,[esi]
                                   sub             esi,eax
                                     sub             esi,ecx
                                     or              [pMemOut+eax+ecx],edx
                                       inc             ecx
                                 cmp             ecx,7
                                       jle             shiftLoop
                           xor             ecx,ecx
                             xor             eax,eax
                             pop             ecx
                         add             ecx,8
                               cmp             ecx,lOrigSize
                               jl              addImageLoop
    


Assume all registers are nulled before and pMemOut and pMemOrig is a pointer to a block of memory allocated by GlobalLock. lOrigSize is the size of the original image, and therefore the output image.

I think I may be looping an infinite amount of times as the file crashes upon loading.

Cheers for any help, Unknown.
Post 10 Apr 2009, 22:39
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4156
Location: vpcmpistri
bitRAKE 11 Apr 2009, 01:24
DWORDs are being loaded/stored, but the pointers are only being updated by byte increments - it doesn't make any sense. Why clear EAX only to store ECX into EAX afterward - doesn't make sense. Why clear ECX and then POP ECX? Please, fit those things and repost. Also, you'll need to explain the image format - what are you adding? RGB components, or what?
Post 11 Apr 2009, 01:24
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4156
Location: vpcmpistri
bitRAKE 11 Apr 2009, 06:49
I think the SHR EDX,30 instruction had me going in the wrong direction; and the goal is to load/store bytes - just to capture a single bit value (ouch). The BT instruction was designed for this.

Reading over your other posts makes it clear that you're translating quite literally - that is where all the XOR REG,REG 's are coming from. Can you post the C code?
Post 11 Apr 2009, 06:49
View user's profile Send private message Visit poster's website Reply with quote
Madis731



Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
Madis731 11 Apr 2009, 09:18
Sometimes its easier for me too to see the C code Smile Then I can see what the "black box" needs to be between the IN and the OUT Wink Thanks!
Post 11 Apr 2009, 09:18
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 11 Apr 2009, 11:31
Hmm good point about the dword and about the counting, I could have kept ecx and just made eax the sub-counter. By the way I am not porting the code directly, I am trying to just do it in ASM without the C code if that makes sense.

This is the full C code (it works but probably isn't that good, and it doesn't return anything I know but meh):

Code:
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    AddBitmap("C:\\Users\\Unknown\\in.bmp","C:\\Users\\Unknown\\temp.bmp","C:\\Users\\Unknown\\out.bmp");
}

int AddBitmap(char* szOriginal, char* szAdd, char* szOutput)
{
    FILE *pOriginal = fopen(szOriginal,"rb"); if(pOriginal == 0) return 0;
    FILE *pAdd = fopen(szAdd,"rb"); if(pAdd == 0) goto CleanUp;

    long lOrigSize, lAddSize;
    fseek(pOriginal,0,SEEK_END); fseek(pAdd,0,SEEK_END);
    lOrigSize = ftell(pOriginal); lAddSize = ftell(pAdd);
    rewind(pOriginal); rewind(pAdd);

    if((lOrigSize - 0x36)<(8 * (lAddSize - 0x36))) goto CleanUp2;

    char *szOrigCont = (char*)malloc(lOrigSize); if(szOrigCont == 0) goto CleanUp2;
    char *szAddCont = (char*)malloc(lAddSize); if(szAddCont == 0) goto CleanUp3;

    if(fread(szOrigCont,1,lOrigSize,pOriginal)!=lOrigSize) goto CleanUp4;
    if(fread(szAddCont,1,lAddSize,pAdd)!=lAddSize) goto CleanUp4;

    FILE *pOutput = fopen(szOutput,"wb"); if(pOutput == 0) goto CleanUp4;
    char *szOutCont = (char*)malloc(lOrigSize); if(szOutCont == 0) goto CleanUp5;
    memcpy(szOutCont,szOrigCont,0x36);

    long i = 0x37, x = 0x37, lShift;
    for( ; i<=lOrigSize && x<=lAddSize && (lShift=7); x++)
        while((lShift--)>=0) szOutCont[i] = (szOrigCont[i++] & ~1)|((szAddCont[x] >> lShift) & 1);

    fwrite(szOutCont,1,lOrigSize,pOutput);

    free(szOutCont);
    CleanUp5: fclose(pOutput);
    CleanUp4: free(szAddCont);
    CleanUp3: free(szOrigCont);
    CleanUp2: fclose(pAdd);
    CleanUp: fclose(pOriginal);
    return 0;
}
    


Quote:

Reading over your other posts makes it clear that you're translating quite literally - that is where all the XOR REG,REG 's are coming from. Can you post the C code?


Sorry what do you mean by that? Also when you move a value into a register should it xor automatically (i.e. if I moved al into byte [ebx], all of ebx but bl would remain as it is yeah? So I have to xor before to null the values.).

With bt, to get the CF value, should I null al and use setc to get the value? Do I actually need to null it or does CF set it as 0 if the value is 0?
Post 11 Apr 2009, 11:31
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4156
Location: vpcmpistri
bitRAKE 11 Apr 2009, 15:03
A store overwrites whatever was in a register. So, putting a zero in a register prior to overwriting is superfluous. Yet, a partial store might require zeroing the register if the upper bits need to be zero. An example is in order:
Code:
xor eax,eax ; not needed
mov eax,[edi] ; overwrites full 32-bits of EAX

; partial store example
xor eax,eax ; clear all bits
mov al,[edi] ; just set low 8-bits of EAX
add esi,eax ; use full register EAX

; preferred over partial store
movzx eax,byte[edi]
add esi,eax    
...in the partial store example the upper bits of EAX are cleared because the move into AL only effects the least significant byte. (Partial stores are generally bad in terms of performance - use MOVZX.)

If you read the description of SETC it should make clear that either a 00 or 01 is stored - making prior contents of byte history.
Post 11 Apr 2009, 15:03
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 11 Apr 2009, 16:23
Ahh, thank you I learnt quite a bit from that! Will update if I have anything further done with the code.

When you say bad in terms of performance, do you mean because:

Code:
xor eax,eax
mov al,byte [edi]
    


Takes up one more byte than:

Code:
movzx eax,byte [edi]    


Or not?
Post 11 Apr 2009, 16:23
View user's profile Send private message Reply with quote
Madis731



Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
Madis731 11 Apr 2009, 18:04
Its just that movzx does the same as the xor/mov-pair, but with less time. Its a problem with implementation of x86, where writing to a small part of a register will stall the execution of any other instruction that executes on the full register. I think the delay was 1 or 2 clocks.

Its not much, but they can surprise you in critical loops. Btw, we have a Vista user (pal) here Very Happy I recognized the Users folder, because I have one too on my Server 2008.
Post 11 Apr 2009, 18:04
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 11 Apr 2009, 19:34
Ahh, interesting. Has that problem been fixed with x64 or is it still present? I'll be sure to use movzx from now on, cheers. Was ironic, 10 minutes after bitRAKE told me about that I looked at someones assembly code and corrected them for doing a xor then a partial move Razz

Yeah I run Vista, I'm assuming not many people here do? I'm getting into Linux more and more though, I have it on dual boot, its just me a Linux seriously don't go well together.
Post 11 Apr 2009, 19:34
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4156
Location: vpcmpistri
bitRAKE 12 Apr 2009, 01:15
pal wrote:
Has that problem been fixed with x64 or is it still present?
It isn't so much a problem as a feature of the architecture. It is better to know the limitations because it isn't always bad. For example, Madis731 says it is a cycle or two stall - there might be a situation where we can both delay the use and shorten the instruction stream by using the byte size store - this could potentially be faster (although an extremely marginal solution).

Goggling Steganography just now gives me a better idea of the goal. Kind of wondering how much data could be hidden out there in plain sight...

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 12 Apr 2009, 01:15
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Apr 2009, 08:44
Yeah I see.

Basically steganography is the art of hiding something within something else. For example you can hide textual information within a BMP header by changing the values of the data offset (it will just be skipped) or in TAR headers (there are 100 bytes of data space to specify the name of an archive; null bytes pad it, so if we find the first null byte, we can start from that +1 and add our data, and again it will be skipped).

In this case I'm trying to hide an image in another image as already said. As for the quantity, it depends how you are hiding it. If you are hiding it like I am for example, the maximum I am going to be able to hide is:

Size of raw data of original image / 8 (as one bit is added to each byte).

For the extraction, we assume that the person knows the size of the image and the number of bits per pixel.

On the other hand if we are hiding an image in the TAR files of a Linux distrobution, say there are 1000 files within it. This means there is going to be 1000 * 100 bytes of data for the name, all null padded. So if on average each file name takes up 15 bytes of data, then we have (1000 * 100) - (15 * 1000) = 85,000 bytes of space. We could hide a file which has been compressed in there e.g. a JPG or a PNG, but we couldn't hide a BMP file very easily because they have large file sizes. We could though hide text in this easily, but then we have the problem of it being plaintext etc.

Hope that helps? Unless I misunderstood you and you were implying that at me, then my whole text was useless -.-
Post 12 Apr 2009, 08:44
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20516
Location: In your JS exploiting you and your system
revolution 12 Apr 2009, 09:00
If you are worried about the plaintext aspect then you should encrypt the data before inserting it into the BMP/TAR file.

Also, no need to limit yourself to storing images in the BMP, you can store any arbitrary binary data in the lowest bit.

Also, note that there are now some very good algorithms that can detect the presence of hidden data within images. So perhaps they are not hidden as well as you might have imagined. Be careful about what you put there!
Post 12 Apr 2009, 09:00
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Apr 2009, 10:45
Yeah I know, this is kind of a POC code more than anything, maybe a little utility to hide somethings.

What kind of algorithms are there? I'm assuming they don't just extract possible data, but they recognise encryption algorithms etc?
Post 12 Apr 2009, 10:45
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20516
Location: In your JS exploiting you and your system
revolution 12 Apr 2009, 11:09
You can consider a normal BMP file as an 8-8-8 data stream. But for a BMP file with "hidden" data the format is 7-7-7 with an extra 1-1-1 data stream.

One simple method is if the 1-1-1 data is some type of unencrypted file then often it is quite obvious it is not part of the image because of particular things, like an .exe file will have a MZ/PE header, or a text file will only have 7-bit data, etc. For this reason some type of scrambling (like encryption) is usually done to avoid this simple detection method and force people to move to more sophisticated methods like the one below.

Another, more complex method, is to look at the 7-7-7 portion and compare it to the 1-1-1 portion. With a normal image the patterns match in certain ways, but with a steganographic image the patterns strongly diverge in almost all ways. Hence the detection of something "not normal".

And a third method is very simply the fact that you have chosen not to use the more usual compressed format that the average person likes to use.

Mr investigator: "So, pal, why did you use a BMP format for your simple holidays shots when everyone else would use JPG? Are you hiding something?"
Post 12 Apr 2009, 11:09
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Apr 2009, 11:33
:p Cool, thats a good idea about the headers etc. The good thing about storing certain types of file (e.g. BMP) is you don't have to store the header, you can get all the required data from the user (height, width, bits per pixel) and then fill the rest in yourself.

I may have to try to implement that algorithm sometime (the checking one), seems quite interesting. What do you mean by a text file will only have seven bit data though?
Post 12 Apr 2009, 11:33
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20516
Location: In your JS exploiting you and your system
revolution 12 Apr 2009, 12:00
pal wrote:
What do you mean by a text file will only have seven bit data though?
ASCII data is only 7-bit. Have a look at your .asm file, no high bits (bit 7) are set.
Post 12 Apr 2009, 12:00
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Apr 2009, 12:13
Ahh yeah, that was a retarded question -.- Cheers.

For using bt, the offset (when I am using a byte for the bitbase), for big endian bit numbering does that start at 7?

I.e. We have: 01101001 in ah.

Would: bt ah,7 Get the most significat bit or the second most?
Post 12 Apr 2009, 12:13
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20516
Location: In your JS exploiting you and your system
revolution 12 Apr 2009, 12:22
pal wrote:
Would: bt ah,7 Get the most significat bit or the second most?
Everything in x86 is zero based. bit-7 is the highest bit in an 8 bit register. And also x86 is 100% little endian.
Post 12 Apr 2009, 12:22
View user's profile Send private message Visit poster's website Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Apr 2009, 12:27
Oops, I get the two endians mixed up, need to read up on them more, cheers though, think I nearly have this program done.

By the way, does this site have an IRC channel?
Post 12 Apr 2009, 12:27
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4156
Location: vpcmpistri
bitRAKE 12 Apr 2009, 15:39
The best thing about BT* is that it works with memory addresses and a 32-bit index into the bit array - hurray! So, you want bits 7, 15, 23, 31, 39, ... from the same address. I imagine they would all be cleared in the target (BTR) and then just set the bits that aren't zero in the source (BTS).

[I wonder if any espionage virus tries to send it's data in this fashion? User thinks they are uploading a picture and their personal info gets tacked on for free.]
Post 12 Apr 2009, 15:39
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< 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-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.