信息隐藏实验

**

信息隐藏实验

**
本编程在 VirtualBox 虚拟机下安装 Ubuntu 16.04 x64 系统,在 Ubuntu 下使用 Golang 1.8 进行编程实践,运用 Golang 基本语句完成多项任务的编程,结合实践来巩固学生对于本书的知识掌握,从直观上了解和认知计算机思维。


实验目标

实验目标:

  1. 实现信息隐藏:把一个字符串(一篇文章)隐藏到一张图片中。
  2. 实现信息显隐:从隐藏了字符串的图片中还原出该字符串。

提示:以下是本篇文章正文内容,下面案例可供参考

实验方法

1) 了解 little-endian 对 4 字节整数的存储方法,实现 _4byte2int 函数(此函数在解析BMPINFO头中长和宽的像素时使用),使4字节的little-endian的数据能够转化为对应的整数。
func _4byte2int(bs []byte) int { }
2) 学习 BMP文件的格式,实现 GetPartsOfBmp 函数(需要调用ReadAllFromFile函数来读取BMP文件的所有数据,在隐藏文本和显示文本的功能中都需要调用此函数),使其将BMP文件拆成三个部分:文件头、BMPINFO头和像素阵列。
func GetPartsOfBmp(img_path string) ([]byte, []byte, []byte) { }
3) 学习本实验中信息隐藏的算法,实现 HideText 函数和 ShowText 函数
func HideText(hide_data []byte, pixel_array []byte) []byte { }
func ShowText(pixel_array []byte) []byte { }
4) 测试实验:在线上实验平台下载测试图片和文本文件,分别保存为ucas.bmp和Richard_Karp.txt。
在hide.go代码编译成功后,同学在命令行中输入:
./hide hide ucas.bmp Richard_Karp.txt ucas_with_info.bmp 完成隐藏;
./hide show ucas_with_info.bmp info.txt 完成显隐;
diff Richard_Karp.txt info.txt 检查隐藏再显隐后文本是否没有变化(没有输出即可);
display ucas_with_info.bmp 肉眼检查隐藏效果是否比较好。
5) 提交完整的 hide.go 代码

代码


```go
package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

const (
	// all in byte
	FILE_HEADER_SIZE    = 14 // standard size of file header
	BMPINFO_HEADER_SIZE = 40 // standard size of bmpinfo header
	LENGTH_FIELD_SIZE   = 16 // size of occupancy in bmp for the length of hidden data
	INFO_UNIT_SIZE      = 4  // size of occupancy in bmp for a byte of hidden data
)

// Read all bytes from a file
func ReadAllFromFile(path string) []byte {
	if all, err := ioutil.ReadFile(path); err != nil {
		fmt.Fprintf(os.Stderr, "%v", err)
		os.Exit(1)
		return []byte{}
	} else {
		return all
	}
}

// Write all data to a file.
func WriteAllToFile(data []byte, path string) {
	if err := ioutil.WriteFile(path, data, 0666); err != nil {
		fmt.Fprintf(os.Stderr, "%v", err)
		os.Exit(1)
		return
	}
}

// Output the bmp file through the indepensible three parts.
// @param imp_path. Output path for the bmp image.
// @param fh, bh, pixel_array. File header, bmpinfo header, pixel array.
// @return possible errors for output
func ProduceImg(img_path string, fh []byte, bh []byte, pixel_array []byte) error {
	if f, err := os.OpenFile(img_path, os.O_RDWR|os.O_CREATE, 0660); err != nil {
		return err
	} else {
		f.Write(fh)
		f.Write(bh)
		f.Write(pixel_array)
		if err := f.Close(); err != nil {
			return err
		} else {
			return nil
		}
	}
}

// Transform bytes to an integer in a little-endian way
// @param bs. byte slice
// @return. The integer value transformed by the slice
func _4byte2int(bs []byte) int {
	// TODO Your code here
      var in int = int(bs[0])+int(bs[1])*256+int(bs[2])*256*256+int(bs[3])*256*256*256
        return in  
}

// Retrieve three parts of the bmp file: file header, bmpinfo header and pixel
// array. Note the bmp file may contain other parts after the pixel array.
// @param imp_path. The bmp file path.
// @return file_header. File heder of 14 bytes.
// @return bmpinfo_header. Bmpinfo header of 40 bytes.
// @return pixel_array. Pixel array of bytes.
func GetPartsOfBmp(img_path string) ([]byte, []byte, []byte) {
	var file_header, bmpinfo_header, pixel_array []byte
	// TODO Your code here
filedata := ReadAllFromFile(img_path)
        width := _4byte2int(filedata[18:22])
        height := _4byte2int(filedata[22:26])
        file_header = filedata[0:14]
        bmpinfo_header = filedata[14:54]
        pixel_array = filedata[54:54+width*height*3]
	return file_header, bmpinfo_header, pixel_array
}

// Hide information into the pixel array
// @param hide_data. The data to be hidden
// @param pixel_array. The original pixel array
// @return the modified pixel data, which hides info.
func HideInfo(hide_data []byte, pixel_array []byte) []byte {
	// TODO Your code here
   length := len(hide_data)
        insert_data(length, pixel_array[0:16], 16)
        for i:= 0; i <=length-1; i++ {
            var v int= int (hide_data[i])
            offset := 16+i*4
            insert_data(v, pixel_array[offset:offset+4],4)
            }
        return pixel_array
}
func insert_data(data int, byte_slice []byte, n int)[]byte{
        for i:= 0; i<=n-1; i++ {
        _2bit := data & 0x3
        o := byte(_2bit)
        byte_slice[i] = byte_slice[i] & 0xFC
        byte_slice[i] = byte_slice[i] | o
        data = data >> 2
        }
        return byte_slice
}

// Restore the hidden data from the pixel array.
// @param pixel_array. Pixel array in bmp file.
// @return. The hidden data in byte array.
func ShowInfo(pixel_array []byte) []byte {
	// TODO Your code here
    var length int = restore_data(pixel_array[0:16], 16)
        var content []byte= make([]byte,length)
        for i:= 0; i<= length-1; i++ {
        offset := 16+i*4
        content[i] = byte(restore_data(pixel_array[offset: offset+4],4))
        }
        return content
} 
func restore_data(byte_slice []byte, n int) int {
        data :=0
        for i:=n-1; i >= 0; i-- {
        _2bit := byte_slice[i] & 0x3
        var g int = int(_2bit)
        data = data << 2
        data = data | g
        } 
        return data
}

func HideProcedure(src_img_path string, hide_file_path string, dest_img_path string) {
	fmt.Printf("Hide %v into %v -> %v\n", hide_file_path, src_img_path, dest_img_path)
	file_header, bmpinfo_header, pixel_array := GetPartsOfBmp(src_img_path)
	hide_data := ReadAllFromFile(hide_file_path)
	new_pixel_array := HideInfo(hide_data, pixel_array)
	ProduceImg(dest_img_path, file_header, bmpinfo_header, new_pixel_array)
}

func ShowProcedure(src_img_path string, data_path string) {
	fmt.Printf("Show hidden info from %v, then write it to %v\n",
		src_img_path, data_path)
	_, _, pixel_array := GetPartsOfBmp(src_img_path)
	info := ShowInfo(pixel_array)
	WriteAllToFile(info, data_path)

}

func _print_usage() {
	fmt.Fprintf(os.Stderr, "* hide args: hide <src_img_path> <hide_file_path> "+
		"<dest_img_path>\n")
	fmt.Fprintf(os.Stderr, "* show args: show <img_path> <data_file>\n")
}

func main() {
	// please do not change any of the following code,
	// or do anything to subvert it.
	if len(os.Args) < 2 {
		_print_usage()
		return
	} else {
		action := os.Args[1]
		switch action {
		case "hide":
			{
				if len(os.Args) < 5 {
					_print_usage()
				} else {
					HideProcedure(os.Args[2], os.Args[3], os.Args[4])
				}
			}
		case "show":
			{
				if len(os.Args) < 4 {
					_print_usage()
				} else {
					ShowProcedure(os.Args[2], os.Args[3])
				}
			}
		default:
			_print_usage()
		}
	}
}

原图片和隐藏后的图片并无明显差别,将文件复原后也与原文件没有差别


总结

常见问题

问题:某个变量显示“used as value”

解决方式:检查对变量的声明,检查声明时调用的函数是否有误,导致该变量成为常数。
反思:某步骤出现错误,未必是这一步骤的语法错误等,可能来源于与其相关的其他步骤,比如声明时调用的函数出错。因为go build只可以检测出语法错误,若函数写错导致函数的值成为常数,并不会报错。在debug时,一直显示该行出错,修改多次毫无变化,后来从头到尾重新审视所有代码,发现是调用的函数出现错误。

问题:hide.go可以顺利编译,但隐藏后的图片无法打开

解决方式:检查隐藏的_4byte2int函数,看是否获取错误。
反思:在_4byte2int中,使用了错误的数据类型byte,当数值较大时可能会溢出导致获取错误的数值,每一步计算都应该为int型计算,

byte(bs[0]+bs[1]256+bs[2]256256+bs[3]256256256)

int(bs[0]+bs[1]256+bs[2]256256+bs[3]256256256)

int(bs[0])+int(bs[1]256)+int(bs[2]256256)+int(bs[3]256256256)

这三种写法都是错误的,都有可能因为数据溢出导致整个函数出错,正确的写法应该为
int(bs[0])+int(bs[1])256+int(bs[2])256256+int(bs[3])256256256

问题:将常数输入错误,如256写成255

解决方式:逐行检查修改

问题:变量取值范围输入错误

解决方式:单独考虑临界值,单独考虑是否可以取等号

对待数据类型要谨慎,考虑清楚需要的是byte还是int,注意进行类型转换,以及使用byte时可能有数据溢出。显示某行错误未必就是该行的错误,在debug时不可拘泥于该行,应该以该行为中心,检查所有与其密切相关的地方。编译正确不代表程序可以得到正确的运行结果,只能代表没有语法错误,最终率还需要从头逐行进行debug,检查每个函数的意义。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值