I have developed a small Java program to perform Chi-Square attack and LSB Enhancement attack on 24-bit BMP images.
What does LSB Enhancement mean?
Well, it is the process of manipulating the least-significant-bit(LSB) of image data in order to produce an output to discover the embedded data.
Well, it is the process of manipulating the least-significant-bit(LSB) of image data in order to produce an output to discover the embedded data.
How it works?
First of all we need to read the image data in Java. I used an already implemented Java class which was based on the code from authors Abdul Bezrati and Pepijn Van Eeckhoudt. The class is called BitmapLoader and it can read both 8-bit and 24-bit BMP images. The method loadBitmap(String file) returns a BufferedImage of type BufferedImage.TYPE_4BYTE_ABGR which means that when a pixel value is read using image.getRGB(x, y) it will return a 32-bit integer value of which the left most 8 bits will represent the ALPHA component, next 8 bits BLUE component, next 8 bits GREEN component and the least significant 8 bits will represent the RED component of the pixel. We can read these components individually by performing bitwise shift operations on the RGB value as
First of all we need to read the image data in Java. I used an already implemented Java class which was based on the code from authors Abdul Bezrati and Pepijn Van Eeckhoudt. The class is called BitmapLoader and it can read both 8-bit and 24-bit BMP images. The method loadBitmap(String file) returns a BufferedImage of type BufferedImage.TYPE_4BYTE_ABGR which means that when a pixel value is read using image.getRGB(x, y) it will return a 32-bit integer value of which the left most 8 bits will represent the ALPHA component, next 8 bits BLUE component, next 8 bits GREEN component and the least significant 8 bits will represent the RED component of the pixel. We can read these components individually by performing bitwise shift operations on the RGB value as
int pRGB = image.getRGB(x, y);
int alpha = (pRGB >> 24) & 0xFF;
int blue = (pRGB >> 16) & 0xFF;
int green = (pRGB >> 8) & 0xFF;
int red = pRGB & 0xFF;
LSB enhancement means setting the component value to 255 if the least-significant-bit is 1 or leave as it is if 0. As a result of this operation, the image will have some flashy colours in the parts where the LSBs were 1s.
How it works in java? First we need to set the alpha component to 255 or the background of the image will be transparent. Then we need to check the red, green and blue components if the LSBs are 1, if they are, we will set these values to 255. It is performed with the following lines of code in Java:
int pRGB = 255 << 24; //set the alpha component
int temp = 0;
if ((red & 0x01) == 1) {//check if the LSB is 1
pRGB += 255;
}
if ((green & 0x01) == 1) {//check if the LSB is 1
temp = pRGB;
pRGB = 255 << 8;
pRGB += temp;
}
if ((blue & 0x01) == 1) {//check if the LSB is 1
temp = pRGB;
pRGB = 255 << 16;
pRGB += temp;
}
lsbImage.setRGB(x, y, pRGB);//set the RGB value of the image.
When the above operation is performed on all pixels of the image, the resulting lsbImage which is of type BufferedImage will reveal the parts of the image with the hidden data.
How Chi-Square attack works?
This attack was developed by Andreas Westfeld and Andreas Pfitzmann in 2000 (Andreas Westfeld and Andreas Pfitzmann, Attacks on Steganographic Systems. Breaking the Steganographic Utilities EzStego, Jsteg,Steganos, and S-Toolsand Some Lessons Learned, 2000).
First of all, we need to know what is Chi-Square analysis? It is a statistical test to measure if a given set of observed data and an expected set of data are similar or not. This is the main idea, you can read about it in more details in any statistics book.
The idea of this attack is to compare Pair-of-Values' observed frequencies with their expected frequencies and calculate p value which will represent the probability of having some embedded data in an image.
What do we mean by Pair-of-Values? According to Westfeld & Pfitzmann, when some data like encrypted text is embedded into an image the LSB values of the original data change in a way that the number of these pairs become nearly equal while they differ so much when there is no embedding. We can see pairs of values as the following bit sequences in our case where we have 8-bit components. As a result we have 128 pairs of values like 0-1, 2-3, 4-5, ..., 254-255. What we do with these values? We will count the occurrences of the individual colour values in the image and we will come up with the observed frequencies data set which will be used in the chi-square test. But we also need an expected data set which is explained by Westfeld & Pfitzmann as the average of the occurrences of the values in a pair.
00000000
00000001
00000010
00000011
00000100
00000101
........
11111110
11111111
For the observed data set we just need to take the frequency of one of the pairs. It will not make any difference because the expected value is the average of both values in a pair which means that both of the values are in the same distance to the average. For example, we counted 800 occurrences for value 0 and 200 occurrences for value 1. The expected value will be the average of these two values which will result in 500. Both of the observed values are at the same distance to the average which is 300.
How is it implemented in Java?
I have a class named PoV which has only the following attribute
I have a class named PoV which has only the following attribute
private long[] pov = new long[256];
and three methods as
public double[] getExpected() { double[] result = new double[pov.length / 2];
for (int i = 0; i < result.length; i++) {
double avg = (pov[2 * i] + pov[2 * i + 1]) / 2;
result[i] = avg;
}
return result;
}
public void incPov(int i) {
pov[i]++;
}
public long[] getPov() {
long[] result = new long[pov.length / 2];
for (int i = 0; i < result.length; i++) {
result[i] = pov[2 * i + 1];
}
return result;
How do we perform the Chi-square test on the image?
The performed test is the iterated version of the above described attack and it is performed after reading a predefined amount of data(chunk size) is read. I chose chunk-size as 128 bytes which means after reading 128 bytes of image data, a test is performed and its result is stored. The total number of tests/chunks in the image is
numOfChunks=(int)(Math.floor((width*height*3)/chunkSize)+1);
which will also be the width in pixels of the output generated at the end. Sample outputs of the program are given below.
Download available at: https://sourceforge.net/projects/chi-square/
for (int i = 0; i < result.length; i++) {
double avg = (pov[2 * i] + pov[2 * i + 1]) / 2;
result[i] = avg;
}
return result;
}
public void incPov(int i) {
pov[i]++;
}
public long[] getPov() {
long[] result = new long[pov.length / 2];
for (int i = 0; i < result.length; i++) {
result[i] = pov[2 * i + 1];
}
return result;
How do we perform the Chi-square test on the image?
The performed test is the iterated version of the above described attack and it is performed after reading a predefined amount of data(chunk size) is read. I chose chunk-size as 128 bytes which means after reading 128 bytes of image data, a test is performed and its result is stored. The total number of tests/chunks in the image is
numOfChunks=(int)(Math.floor((width*height*3)/chunkSize)+1);
which will also be the width in pixels of the output generated at the end. Sample outputs of the program are given below.
![]() |
| 480x360 embedded with 6 KB data |
![]() |
| LSB Enhancement output |
![]() |
| Chi-square attack output |
Download available at: https://sourceforge.net/projects/chi-square/


