안녕하세요.
달빛연구자 입니다.
이번 화에서는 그림 파일에 비밀 메시지를 숨겨 넣는 프로그램을 만들어 보도록 할께요.
프로그램의 원리는 다음과 같습니다.
메시지를 숨기는 프로그램
1. 그림파일을 연다. 텍스트 파일도 연다.
2. 그림의 색깔을 미묘~하게 조절해서 비밀 메시지를 숨겨 넣는다.
3. 그림파일을 닫는다. 텍스트 파일도 닫는다.
4. 인터넷에 올린다. 끝~
메시지를 꺼내는 프로그램
1. 인터넷에서 그림파일을 받는다.
2. 그림파일을 연다.
3. 그림의 미묘~한 색상 변화를 읽어서 비밀메시지를 복원한다.
4. 복원한 비밀메시지를 텍스트파일로 출력한다.
5. 그림파일을 닫는다.
단순하죠? :)
소스코드는 가장 아랫쪽에 첨부해 두었습니다.
프로그램의 동작은 다음과 같아요...
1. hide_messge.wlua
프로그램을 동작시키면, 첫번째 열기창이 나오는데요. 여러분이 메시지를 담을 이미지 파일을 선택해 줍니다.
그림파일의 선택을 하고나면, 두번째 열기 창이 나오는데요. 여기서 담고자 하는 메시지가 들어있는 텍스트 파일을 선택해 줍니다.
이때 주의할 점은 텍스트 파일의 크기인데요. 대략 픽셀 3개당 1byte 씩 넣을 수 있고 텍스트파일이 너무 크면 중간에 잘릴 수 있으니,
픽셀 개수의 1/3 byte 크기의 파일을 선택해 주어야 합니다. (그래도 꽤 많이 들어가요 ㄷㄷ)
두 파일을 선택한 뒤 아래와 같이 out.png파일이 생성되고 Complete 라는 창이 나오면 메세지 삽입이 완료된 것 입니다.
out.png라는 출력파일이 생성됩니다.
2. get_message.wlua
두둥!!!!! 망했습니다. ㅜㅠ
본래 만들어지는 out.png는 250kb 의 크기를 가지고 있는데요.
오유 게시판에 올리고나서 받으니, 그림크기가 50kb가 되어 있네요.. 오유게시판 내부적으로 png를 jpg로 압축해서 올리는 로직이 있는 듯 합니다..
아아.. 운영자님이시여 저에게 어찌하여 이런 시련을 내리시나이까... ㅠㅠ
쓰읍... 뭐.. 안되는건 어쩔 수 없으니... 여기 가서 이미지를 받아옵니다.
두번째 스크립트를 get_message.wlua 로 저장하고 실행하면 다음과 같은 열기창이 나옵니다.
방금 받은 png파일을 선택해 줍니다.
역시 Complete 가 나오면 끝입니다. Out.txt 파일이 생성됩니다.
생성된 out.txt 를 열어보면 아까 이미지에 넣엇던 메시지가 복원되었음을 알 수 있습니다.
자... 여기까지 따라오신 분들이 계시다면 아마 묻겠지요...
이거 어디에 쓰냐고 (...)
네.. 쓸데 없습니다;;
루아이야기 6편 쓰다가 갑자기 아이디어가 떠올라서 작성한 코드인데요..
딱히 쓸모는 없는데, 동작하기는 하지;; 버리기는 아깝고.. ㅜㅜ;;
자.. 이 프로그램 과연 어디에 쓰면 좋을까요? ㄷㄷㄷ
소스코드 입니다.
1. hide_message.wlua
require("iuplua")
require("imlua")
--image file open
iup.Message("Message","메세지를 넣고자 하는 png 파일을 선택해 주세요.")
filename = iup.GetFile("*.png")
local image = im.FileImageLoad(filename)
--txt file open
iup.Message("Message","메시지가 들어있는 txt 파일을 선택해 주세요.")
filename = iup.GetFile("*.txt")
msfile = io.open(filename)
local message = msfile:read("*all")
msfile:close()
bytechar = {}
bitfield ={}
local bitidx = 1
--message break to bit
for idx = 1,string.len(message) do
bytechar[idx] = string.byte(message,idx)
for tx = 1,8 do
bitfield[bitidx] = (bytechar[idx] % (2^tx))/2^(tx-1)
bytechar[idx] = bytechar[idx] - (bytechar[idx] % (2^tx))
bitidx = bitidx + 1
end
end
local r = image[0]
local g = image[1]
local b = image[2]
bitidx = 1
--break pixel just 1 bit
for row = 0, image:Height() - 1, 1 do
for column = 0, image:Width() - 1, 1 do
r[row][column] = r[row][column] - (r[row][column] % 2)
g[row][column] = g[row][column] - (g[row][column] % 2)
b[row][column] = b[row][column] - (b[row][column] % 2)
--insert message
if(bitfield[bitidx] ~= nil) then
r[row][column] = r[row][column] + bitfield[bitidx]
bitidx = bitidx + 1
end
if(bitfield[bitidx] ~= nil) then
g[row][column] = g[row][column] + bitfield[bitidx]
bitidx = bitidx + 1
end
if(bitfield[bitidx] ~= nil) then
b[row][column] = b[row][column] + bitfield[bitidx]
bitidx = bitidx + 1
end
end
end
print (bitidx-1)
image:Save("out.png", "PNG")
iup.Message("Message","Complete.")
-----------------------------------------------------------------
2. get_message.wlua
require("iuplua")
require("imlua")
iup.Message("Message","메세지를 꺼내고자 하는 png 파일을 선택해 주세요.")
local filename = iup.GetFile("*.*")
local image = im.FileImageLoad(filename)
local r = image[0]
local g = image[1]
local b = image[2]
bitidx = 1
local bitfield ={}
--get message bit from image
for row = 0, image:Height() - 1, 1 do
for column = 0, image:Width() - 1, 1 do
bitfield[bitidx] = r[row][column] % 2
bitidx = bitidx + 1
bitfield[bitidx] = g[row][column] % 2
bitidx = bitidx + 1
bitfield[bitidx] = b[row][column] % 2
bitidx = bitidx + 1
end
end
--recover message
local asciibox = {}
local asciiidx = 1
local strbox = {}
local stridx = 1
for idx = 1, #bitfield do
asciibox[asciiidx] = bitfield[idx]
if asciiidx == 8 then
asciiidx = 0
strbox[stridx] = asciibox[1] + asciibox[2] * 2 +asciibox[3] * 4 +asciibox[4] * 8
+asciibox[5]* 16 +asciibox[6] * 32 +asciibox[7] * 64 +asciibox[8] * 128
if strbox[stridx] == 0 then
break;
end
stridx = stridx + 1
end
asciiidx = asciiidx + 1;
end
local outfilename = "out.txt"
local outfile = io.open(outfilename,"w")
local iidx = 1
for iidx = 1 , #strbox -1 do
outfile:write(string.char(strbox[iidx]))
end
outfile:close()
iup.Message("Message","Complete")
-----------------------------------------------------------------
네.. 각각 60라인 , 50라인 밖에 안되는(?) 간단한 소스 코드 입니다. (퍽!)