/*****************************************************
DESC : Program steganografi sederhana, hanya dapat menggunakan bitmap 24bit
sebagai data penyamar. Error-checking cukup ekstensif, namun fungsi pembaca
header kurang meyakinkan karena hanya bergantung pada analisa (tebakan) le-
wat hex-editor. Menggunakan sekaligus fungsi-fungsi C dan STL C++. Linear
demi keterbacaan oleh pemula (mengorbankan extendability - ubah strukturnya
bila Anda ingin mengembangkan).
VER : 0.8b
LAST MODIFIED : 26/02/2006 10:39:05
LICENSE TYPE : Domain publik
*****************************************************
*
* (c) copyright 2001, 2006 Cadmus Softworks.
*
****************************************************/
#define DEBUG
#include <iostream> // STL C++. Untuk I/O
#include <fstream> // STL C++. Spesialisasi I/O file.
#include <sstream> // STL C++. Spesialisasi I/O string; untuk bmpdst_str.
#include <string> // STL C++. string::npos.
#include <string.h> // C. memcpy() dan fungsi manipulasi string lainnya.
#include <ctype.h> // C. Dipakai untuk tolower()
#include <bitset> // STL C++. Dipakai untuk manipulasi bit-by-bit
#include <math> // C. min()
typedef unsigned int UINT;
#define BMPHDRCNT 54
#define BMPBUFSIZE 32768
#define DATBUFSIZE (BMPBUFSIZE/8)
#define FN(f) (subslc(f,'\\',1))
#define ERROR(m) {std::cout << std::endl << "ERROR: " << m; exit(1);}
#define INFO(m) {std::cout << std::endl << "INFO : " << m;}
// Kalau DEBUG, pernyataan dalam D() dicetak, ekspresi dalam ND() tidak berjalan
#ifdef DEBUG
std::ofstream cout = std::ofstream("log.txt",std::ios::binary);
# define D(m) {cout << std::endl << "DEBUG: " << m;}
# define ND(m) {}
#else
using std::cout;
using std::cin;
# define D(m) {}
# define ND(m) {m}
#endif // DEBUG
const char * subslc(const char * s,const char look_for, UINT before_or_after=0);
void ensteg(const char * fn_bmp, const char * fn_data);
void desteg(const char * fn_steg);
int main(const int argc, char** argv)
{
if (argc == 3)
ensteg(argv[1],argv[2]);
else if (argc == 2)
desteg(argv[1]);
else
{
std::cout << "Penggunaan: " << FN(argv[0])
<< " [namabitmap] [namadata]" << std::endl;
}
}
void ensteg(const char * fn_bmp, const char * fn_data)
{
std::ifstream bmpsrc = std::ifstream(fn_bmp,std::ios::binary);
std::ifstream datsrc = std::ifstream(fn_data,std::ios::binary);
if (!bmpsrc.is_open()) ERROR("Tidak dapat membuka bitmap " << fn_bmp);
if (!datsrc.is_open()) ERROR("Tidak dapat membuka data " << fn_data);
std::ostringstream bmpdst_str;
bmpdst_str << subslc(fn_bmp,'.') << ".stg.bmp";
std::ofstream bmpdst = std::ofstream( bmpdst_str.str().c_str(),
std::ios::binary);
if (!bmpdst.is_open()) ERROR("Tidak dapat membuka file bitmap hasil.");
try {
char bmpbuf[BMPBUFSIZE], datbuf[(BMPBUFSIZE/8)];
bmpsrc.read(bmpbuf, BMPHDRCNT);
if(bmpsrc.tellg()!=BMPHDRCNT) ERROR("File bitmap rusak.");
if(memcmp(bmpbuf,"BM",2) != 0) // Menebak...
ERROR("File bitmap harus dalam format bitmap Windows.");
if(memcmp(bmpbuf+10,"6",1) != 0) // Menebak lagi...
ERROR("Kedalaman warna bitmap harus 24bit.");
long bmpwdt_long; memcpy(&bmpwdt_long,bmpbuf+18,4); // TEBAK
long bmphgt_long; memcpy(&bmphgt_long,bmpbuf+22,4); // TERUSS!!
const long maxdat_long = // Berapa data yang bisa
((bmpwdt_long * bmphgt_long * 3)/8); // ditampung tuple byte?
datsrc.seekg(0,std::ios::end); // Pointer get ke ujung,
const long datlen_long = datsrc.tellg(); // untuk cari tahu pjg.
datsrc.seekg(0,std::ios::beg); // Kembalikan pointer.
INFO("Bitmap " << fn_bmp << " berdimensi "
<< bmpwdt_long << "x" << bmphgt_long << ",\n\t\t"
<< "yang berarti dapat menyamarkan " << maxdat_long
<< " byte data.\n\t"
<< "Panjang data dalam " << fn_data
<< " adalah " << datlen_long << " byte.\n\t"
<< "Semua byte berlebih akan diabaikan. Lanjutkan?[Y/T] ");
ND(if (tolower(cin.get()) != 'y') exit(0);); // Kl debug g nunggu.
memcpy(bmpbuf+50,&datlen_long,4); // Di ujung header, info ukuran
bmpdst.write(bmpbuf,BMPHDRCNT); // Tulis header.
bool bmpio = true, roofing = true;
long bmprdsz, datrdsz; UINT m_octet;
unsigned char curbit;
while(bmpio)
{
bmpsrc.read(bmpbuf,BMPBUFSIZE); bmprdsz = bmpsrc.gcount();
if (bmprdsz != BMPBUFSIZE) bmpio = false;//Kl g ambil ful,habis.
datsrc.read(datbuf,DATBUFSIZE); datrdsz = datsrc.gcount();
if(roofing)
{
m_octet = min((bmprdsz/8),datrdsz);
for(UINT octet = 0; octet < m_octet; ++octet)
for(UINT bit = 0; bit < 8; ++bit)
{
curbit = ((datbuf[octet])&(1 << bit));
if (bmpbuf[(octet*8)+bit] & 1)
bmpbuf[(octet*8)+bit] &= (bmpbuf[(octet*8)+bit] - 1);
bmpbuf[(octet*8)+bit] |= (curbit >> bit);
}
}
// Berhenti menyamarkan kalau bitmap atau data habis terbaca.
if (!bmpio || datrdsz != DATBUFSIZE) roofing = false;
bmpdst.write(bmpbuf,bmprdsz);
}
bmpsrc.close(); datsrc.close(); bmpdst.close();
}
catch (...) // Lazy catch.
{
bmpsrc.close(); datsrc.close(); bmpdst.close();
ERROR("Ada kesalahan yang tidak diketahui. Sori banget gitu...");
}
}
void desteg(const char * fn_steg)
{
std::ifstream bmpsrc = std::ifstream(fn_steg,std::ios::binary);
if (!bmpsrc.is_open()) ERROR("Tidak dapat membuka bitmap " << fn_steg);
std::ostringstream datdst_str;
datdst_str << subslc(fn_steg,'.') << ".txt";
std::ofstream datdst = std::ofstream( datdst_str.str().c_str(),
std::ios::binary);
if (!datdst.is_open()) ERROR("Tidak dapat membuka file bitmap hasil.");
try {
char srcbuf[BMPBUFSIZE], dstbuf[DATBUFSIZE];
memset(dstbuf,0,DATBUFSIZE);
bmpsrc.read(srcbuf, BMPHDRCNT);
if(bmpsrc.tellg()!=BMPHDRCNT) ERROR("File bitmap penyamar rusak.");
if(memcmp(srcbuf,"BM",2) != 0) // Menebak...
ERROR("File bitmap harus dalam format bitmap Windows.");
if(memcmp(srcbuf+10,"6",1) != 0) // Menebak lagi...
ERROR("Kedalaman warna bitmap harus 24bit.");
long bmpwdt_long;memcpy(&bmpwdt_long,srcbuf+18,4);
long bmphgt_long;memcpy(&bmphgt_long,srcbuf+22,4);
long spsdat_long;memcpy(&spsdat_long,srcbuf+50,4);memset(srcbuf+50,0,4);
const long maxdat_long = // Berapa data yang bisa
((bmpwdt_long * bmphgt_long * 3)/8); // ditampung tuple byte?
if (spsdat_long > maxdat_long)
INFO("Mengambil " << maxdat_long << " byte data tersembunyi, "
<< (spsdat_long - maxdat_long) << " byte selebihnya hilang.")
else
INFO("Mengambil " << spsdat_long << " byte data tersembunyi.")
const long datlen_long = min(spsdat_long, maxdat_long);
long bmprdsz;
UINT m_octet;
unsigned char curbit;
for(long dst_idx = 0; dst_idx < datlen_long; dst_idx += DATBUFSIZE)
{
bmpsrc.read(srcbuf,BMPBUFSIZE); bmprdsz = bmpsrc.gcount();
m_octet = min(bmprdsz/8,datlen_long-dst_idx);
// Untuk setiap byte data...
for(UINT octet = 0; octet < m_octet; ++octet)
{
for(UINT bit = 0; bit < 8; ++bit)
{
curbit = srcbuf[(octet*8) + bit] & 1;
dstbuf[octet] |= (curbit << bit);
}
cout << dstbuf[octet];
}
datdst.write(dstbuf,m_octet);
memset(dstbuf,0,DATBUFSIZE);
}
bmpsrc.close(); datdst.close();
}
catch (...) // Lazy catch.
{
bmpsrc.close(); datdst.close();
ERROR("Ada kesalahan yang tidak diketahui. Sori banget gitu...");
}
}
// Substring before or after last occurence of character.
const char * subslc(const char * s,const char c,UINT b)
{
std::string ss = std::string(s);
size_t last;
if ((last=ss.rfind(c)) == std::string::npos) return s;
if (b <= 0) // before occurence - default action
return ss.substr(0,last).c_str();
else
return ss.substr(last+b).c_str();
}