⬆️ ⬇️

Implementing Koh-Zhao steganographic method on Ruby

This article will discuss the most preferred steganographic method of hiding information in images, both in terms of resistance to various types of attacks, and in terms of maintaining acceptable image quality.



The Koch-Zhao algorithm for embedding information uses the frequency domain of the container and consists in the relative replacement of the values ​​of the discrete cosine transform (DCT) coefficients. The image is divided into blocks of 8 × 8 pixels and DCT is applied to each block. Each block is suitable for recording one bit of information.



So the algorithm for hiding will be as follows:

')

- we iterate the image with a double array in increments of 8

- at each iteration we create a temporary array of 8x8 pixels, each element of which will be a set of three pixel colors.

- apply DCT to this array, and get an array of coefficients of size 8x8

- choose 2 coefficients and calculate their difference in absolute value

- if the difference is less than or equal to 25, then we assign the positive coefficient of the second + constant to the first coefficient, or the same but with a minus sign (this is called bit transmission)

- if the difference is less than or equal to -25, then the same actions only for the second coefficient.

- then the reverse DCT is applied to transfer our coefficients back to the spatial domain.

- then copy the new color values ​​into the image.



The implementation of the DCT and ODKP functions in the Ruby language is as follows:



def dct(dct,arr) s=0 (0..7).each do |i| (0..7).each do |j| temp = 0.0 (0..7).each do |x| (0..7).each do |y| temp = temp + $cos_t[i][x]*$cos_t[j][y]*arr[x][y][:blue] end end dct[i][j] = $e[i][j]*temp end end return dct end def idct(dct,arr) (0..7).each do |i| (0..7).each do |j| temp = 0 (0..7).each do |x| (0..7).each do |y| temp += dct[x][y]*$cos_t[x][i]*$cos_t[y][j]*$e[x][y] arr[i][j][:blue] = (temp > 255) ? 255 : (temp < 0 ) ? 0 : temp.round; end end end end return arr end 




$ cos_t and $ e are global array variables with values ​​already filled in.



It should be noted that the method is rather resistant to image distortion, even to its significant change, but is not applicable to hide large amounts of data.



The full code of the script that implements the hide \ read information:



 require 'RMagick' include Magick $cos_t = [ [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.9807853, 0.8314696, 0.5555702, 0.1950903, -0.1950903,-0.5555702,-0.8314696,-0.9807853], [0.9238795, 0.3826834,-0.3826834,-0.9238795, -0.9238795,-0.3826834, 0.3826834, 0.9238795], [0.8314696,-0.1950903,-0.9807853,-0.5555702, 0.5555702, 0.9807853, 0.1950903,-0.8314696], [0.7071068,-0.7071068,-0.7071068, 0.7071068, 0.7071068,-0.7071068,-0.7071068, 0.7071068], [0.5555702,-0.9807853, 0.1950903, 0.8314696, -0.8314696,-0.1950903, 0.9807853,-0.5555702], [0.3826834,-0.9238795, 0.9238795,-0.3826834, -0.3826834, 0.9238795,-0.9238795, 0.3826834], [0.1950903,-0.5555702, 0.8314696,-0.9807853, 0.9807853,-0.8314696, 0.5555702,-0.1950903] ] $e = [ [0.125, 0.176777777, 0.176777777, 0.176777777, 0.176777777, 0.176777777, 0.176777777, 0.176777777], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]]; def insert_message(image,text) i = image.copy dct = Array.new(8).map!{Array.new(8)} k = 0 l = 0 s=0 temp = Array.new(8).map!{Array.new(8)} (0..(image.columns - 1)).step(8) do |i| (0..(image.rows - 1)).step(8) do |j| break if l >= text.length*8 (0..7).each do |x| (0..7).each do |y| temp[x][y] = { :red=>image.pixel_color(j+x,i+y).red, :green=>image.pixel_color(j+x,i+y).green, :blue=>image.pixel_color(j+x,i+y).blue } end end dct = dct(dct,temp) k = dct[3][4].abs - dct[4][3].abs if get_bit(text,l) if k<=25 dct[3][4] = (dct[3][4]>=0) ? dct[4][3].abs + 150 : -1*(dct[4][3].abs + 150) end else if k>=-25 dct[4][3] = (dct[4][3]>=0) ? dct[3][4].abs + 150 : -1*(dct[3][4].abs + 150) end end xt = temp.clone temp = idct(dct,temp) (0..7).each do |x| (0..7).each do |y| pixel = Pixel.new(temp[x][y][:red],temp[x][y][:green],temp[x][y][:blue]) image.pixel_color(j+x,i+y,pixel) end end l=l+1 end break if l >= text.length end image.write('cr.bmp') return image end def read_message(image) k = 0 s=0 out = [] l=0 a='' p=0 b=0 dct = Array.new(8).map!{Array.new(8)} temp = Array.new(8).map!{Array.new(8)} (0..(image.columns - 1)).step(8) do |i| (0..(image.rows - 1)).step(8) do |j| (0..7).each do |x| (0..7).each do |y| temp[x][y] = { :red=>image.pixel_color(j+x,i+y).red, :green=>image.pixel_color(j+x,i+y).green, :blue=>image.pixel_color(j+x,i+y).blue } end end l=l+1 dct = dct(dct,temp) k = dct[3][4].abs-dct[4][3].abs if k>=25 a=1 elsif k<=-25 a=0 else a=-1 break end b |= a << p if p==7 out.push(b.chr) b=0 end p=(p<7) ? p+1 : 0 end if a==-1 break end end return out.join end def dct(dct,arr) s=0 (0..7).each do |i| (0..7).each do |j| temp = 0.0 (0..7).each do |x| (0..7).each do |y| temp = temp + $cos_t[i][x]*$cos_t[j][y]*arr[x][y][:blue] end end dct[i][j] = $e[i][j]*temp end end return dct end def idct(dct,arr) (0..7).each do |i| (0..7).each do |j| temp = 0 (0..7).each do |x| (0..7).each do |y| temp += dct[x][y]*$cos_t[x][i]*$cos_t[y][j]*$e[x][y] arr[i][j][:blue] = (temp > 255) ? 255 : (temp < 0 ) ? 0 : temp.round; end end end end return arr end def get_bit(str,pos) return true if str[pos/8].ord & (1 << pos % 8) > 0 return false if str[pos/8].ord & (1 << pos % 8) <= 0 end image = Magick::Image.read('src.bmp').first insert_message(image,"qweqwe") image = Magick::Image.read('dst.bmp').first puts read_message image 

Source: https://habr.com/ru/post/216207/



All Articles