Académique Documents
Professionnel Documents
Culture Documents
A Thesis Presented to
Systems Technology Institute
Bacoor
in Partial Fulfillment
of the Requirements for the Degree of
Bachelor of Science in Computer Science
by:
March 2003
STI College – Bacoor
by:
_________________________________
Mr. Gerry A. Villanueva
Thesis Adviser
March 2003
STI College – Bacoor
_________________________________
Mr. Angelo Magdangal A. Maderal
Thesis Coordinator
_________________________________
Ms. Minerva R. Almalvez
Assistant Dean
March 2003
STI College – Bacoor
developed by:
_________________________________ _________________________________
Mr. Angelo Magdangal A. Maderal Mr. Ruben B. Muñoz
Panelist Panelist
March 2003 March 2003
_________________________________
Mr. Reynaldo P. Gozum
Lead Panelist
March 2003
TABLE OF CONTENTS
Title Page
Adviser’s Recommendation Sheet
Thesis Coordinator and Dean’s Acceptance Sheet
Panel’s Approval Sheet
List of Appendices
List of Tables
List of Figures
Acknowledgement
Abstract
Appendices
Bibliography
Curriculum Vitae
LIST OF APPENDICES
First of all, the author would like to thank his thesis adviser, Mr. Gerry
Villanueva, and the thesis coordinator, Mr. Angelo Magdangal Maderal. Their continued
The author would also like to express his appreciation for the well-needed support
and guidance extended by the faculty and staff of STI College Bacoor.
Sincere gratitude also goes to Mr. Conrado Vidal who provided valuable
The author also gives out thanks to his friends and classmates who have helped in
Lastly, the author extends out heartfelt appreciation to his parents and relatives
who have contributed a great deal of moral and material support not only for the
completion of this project but more importantly for the physical, mental, and spiritual
well-being of the author. This project would not have been possible without them.
ABSTRACT
In the electronic world, one of the most appropriate “hosts” for steganography are
digital images. Digital photographs are a commonly shared, sent, and exchanged
throughout the Internet in the form of email attachments or web postings. However,
current steganographic software available on the market have poor support for
distort or degrade the appearance of cover images and therefore exposes the
In this study, a new image steganography software was presented to answer the
need for a software that makes optimum use of hiding space in an image without creating
any visible distortions. Along with a highly secure method for randomized encoding,
techniques for adaptive encoding were incorporated with the design of the software.
diffusion.
The test results of comparing the performance of the presented software with
hidden data while preserving the quality and appearance of the image.
1.0 INTRODUCTION
security. With a history that can be traced back to the ancient Egyptians
some 4000 years ago, it still plays a crucial role in present-day diplomatic
have enough computing power to break the encryption and decipher the
frustrates their plans by putting them into solitary confinement every time
a way to conceal not only the content of the message but also the message
ancient times [KAHN1996]. One of these stories was that of a man named
Histaieus who wanted to inform his allies when to revolt against the
Medes and the Persians. To avoid detection, the message was tattooed on
the shaved head of a trusted slave whose hair was later allowed to grow
before being dispatched to Persia. The message was successfully sent and
spies made extensive use of invisible inks in order for their mail to pass
into the size of a printed period and avoid detection [KAHN1996]. At the
turn of the century, even the international terrorist leader Osama bin Laden
information hiding.
instance, practically any person who sees a piece of text written like
secretly host vital information. Modern approaches include the use of text
documents, digital pictures and even music files as carriers of hidden data.
For digital images, the most basic method for steganography is called least
significant bit encoding (LSB encoding). The simple and logical concept
formats, including popular standards like JPEG and GIF. As a result, most
popular in the information security industry. Among the most popular and
EyeMage IIE and The Third Eye. All these four steganographic software
transfer.
incorporated by these tools have certain weaknesses that may render the
of steganography.
(FBI) also stirred up human rights issues with its Carnivore software
felons. The online article “How Carnivore Works” explains that the
11, 2001 terrorist attack on the World Trade Center, Muslim extremist
groups that are linked to the attacks were already reported to be using
formidable security for data transmission over public channels such as the
to unauthorized personnel.
study are not suitable for other areas of information hiding since different
the true-color format offers a wider range of distinct colors. More colors in
each pixel before the human eye is capable of noticing the changes in
color.
bitmaps that utilize lossless compression store color values in exact detail.
This means that unlike in JPEG images, bitmaps remain exactly the same
in this study is not designed for any specific organization or individual. Its main
With this in mind, the generic paradigm was modified to suit the needs of the
project. The five phases of this modified paradigm is presented in Figure 2-1.
The analysis phase is concerned with defining the scope and functions of
the software being developed. In this phase, the specific attributes that need to be
incorporated to the stegosystem are assessed. Then follows the design phase,
where concepts and techniques are put together to form an abstract model of the
Analysis
Design
Coding
Testing
Enhancement
Figure 2-1.
Software Engineering Paradigm
prototype. After construction, next is the testing phase where the effectiveness of
Supplementary software features are also added in this phase, yielding a fully
for a specific use, not for a specific user. It is generic and does not need to adapt
data gathering operations, like interviews and surveys, are needed in this study.
The requirements defined for the proposed stegosystem are based entirely on the
Over the past few years, a growing interest in the field of information
Information Hiding” was held in Cambridge, UK. From then on, several areas of
the new field, like steganography and digital watermarking, started to get
attention from the research community. INSPEC reported that by 1998, the
journal articles that advertised the developing field. The Institute of Electrical and
the area of steganography. Neil Johnson and Sushil Jajodia’s article in the IEEE’s
[JOHN1998]. The article discussed basic techniques for image steganography and
along with Markus Kuhn, also wrote a summary of different areas of information
hiding and discussed several practical applications in the July 1999 issue of
“Techniques for Data Hiding” for the IBM Systems Journal [BEND1996]. This
the article also discussed low-bit coding, phase coding, echo data hiding, and
Bender et al. also explained semantic and syntactic methods for data
order to encode a series of bits. Semantic methods, on the other hand, encode data
presented in the article are the open space methods, otherwise known as white
space steganography. Bender presented such methods that encode data bits by
modulating the number of spaces between each pair of words. A simple approach
to such methods is to encode 0’s as single spaces and 1’s as double spaces.
set of spaces whose length is equal to the decimal value of the set of bits. Each set
of spaces is terminated with a tab character, which is also a white space. This
ensures that for each column of white spaces, exactly three bits of data are stored.
combination of seven spaces and a tab. The binary value 011 (decimal number 3)
Furthermore, these sets of white spaces are placed only at the end of every line of
“Applications for Data Hiding” in 2000 [BEND2000]. This next article, also
Well noted for his works on digital watermarking, Bender also supervised
the Master’s degree thesis of Fernando Paiz entitled “Tartan Threads: A Method
inkjet printers from printing copyrighted documents and even monetary bills.
also conducted studies on steganography. One of which was the project lead by
and imperceptibility. The Air Force Research Laboratory, on the other hand,
sponsored the work of Jiri Fridrich et al. on steganalysis techniques for detecting
LSB encoding in color images [FRID2000]. The Office of Naval Research also
The article explained how the concepts of information theory are inapplicable in
included discussions on both visual attacks and statistical attacks. Visual attacks
rely on a human observer to “look for suspicious artifacts using simple visual
inspection”. The paper pointed out that despite the simplicity of visual attacks, “it
Statistical tests, the other hand, look for discrepancies in certain expected
properties of an image. Neils Provos and Peter Honeyman explains that “some
tests are independent of the data format and just measure the entropy of the
redundant data” [PROV2001a]. This means that images with hidden data are
expected to have “higher entropy” than those without. Images without hidden
data, for example, tend to have an LSB plane that is correlated to 1 or 0. Jiri
Fridrich’s paper on steganalysis for the Air Force Research Laboratory discusses
a technique that is based on evaluating the relative frequencies of close color pairs
[FRID2000].
image steganography software called OutGuess which uses the JPEG format.
uses an average of 13% of the image file size, which means it is as space-efficient
the OutGuess” which explains how stego images created with OutGuess, F5, and
[FRID2002a]. The results presented in the paper was verified by Provos himself
[PROV2002].
hiding.
[BEND1996].
bit-rate, and payload, was defined by Eugene Lin and Edward Delp as
reduces the need for using larger cover-files. This in turn enhances
portability and transmission speed, which are both high priorities in covert
only small volumes of data like copyright information and thus capacity is
systems [LIN1999].
[FRID1998].
parameters cannot be maximized all at the same time. In the case of covert
Imperceptibility
Digital
Steganography Watermarking
Figure 4-1.
Data-Hiding Problem Space
significant bit (LSB) of each pixel in an image with the bits of the data to
lower the value, the closer the pixel is to the color black. Since the least
normally be imperceptible.
blue) are combined in varying intensities to define the color of each pixel.
the component’s intensity, ranging from 0 to 255. Since there are three
LSB’s in each pixel, the hiding capacity in terms of bits is three times the
These changes may exist in the form undesirable marks and artifacts or as
likely to create visible distortions even if only the first LSB’s are
modified.
Transform (DCT). The LSBs of the coefficients of this transform can then
robust as compared with standard LSB encoding, they also typically offer
image that are common or unique to that particular image format. This
an image [BEND1996].
A bit-plane is the plane formed by the bits of the same bit position
in each pixel. Figure 4-2 shows a grayscale image of the STI College logo
along with its eight bit-planes. Black areas in the bit-planes represent a bit
(a)
Figure 4-2.
A Grayscale STI College Logo and its Bit-planes
4-2i, on the other hand, is the least significant bit-plane of the original
image.
in Figure 4-2.
image.
The results of such methods for detection are not always reliable as they
image as a host for steganography. Moreover, Lee and Chen’s paper does
remains a visual attack and thus require a human observer to evaluate the
bit planes.
steganalysis explains that after a JPEG image is embedded with data, “the
cover image will become incompatible with the JPEG format in the sense
that it may be possible to prove that a particular 8x8 block of pixels could
one bit”. Such distinct properties therefore make JPEG images very
vulnerable to attacks.
bitmaps [FRID2000]. This technique called the RQP method (Raw Quick
Pair method) “is based on analyzing close pairs of colors created by LSB
embedding” [FRID2002].
already has been embedded with hidden data, the ratio between the
the image.
values of the other color is less than or equal to 1. This expression can be
expressed as:
number of unique colors in the cover image is less than 30% of the
size of the hiding space in each pixel must be dependent on the color
variation of the adjacent pixels. This prevents the embedding process from
hiding space also protects the stego image from bit-plane steganalysis and
A P E
(x-1,y) (x,y) (x+1,y)
H G F
(x-1,y+1) (x,y+1) (x+1,y+1)
Figure 4-3.
Neighbors of a Pixel
other visual attacks since the significant bits of each pixel remain
unchanged.
Lee and Chen presented a simple method for evaluating the hiding
P, the first step is to get the gray level variation in its top-left adjacent
Figure 4-3. The gray level variation is defined as the difference between
the maximum and the minimum gray values among the four pixels. The
as:
K = ë log2 V û
embedding function has already passed through these pixels. The other
four pixels on the bottom-right are still to undergo the embedding process,
visual system (HVS) that is crucial to capacity evaluation. They stated that
for the human eye, “the greater the gray-scale is, the more change of the
gray-scale could be tolerated” [LEE2000]. Simply put, this means that the
Considering this, an upper boundary for the capacity of a pixel can be set
based on its intensity. Lee and Chen gives the following condition:
the pixels must not be allowed to change so that the upper boundary may
condition for U.
in the proposed stegosystem. The idea behind MER is to adjust the bit that
minimal.
changed to 1111 (decimal number 15) because its three LSB’s were
replaced with embedded data, the difference from the original number is 7.
of 0, the binary number now becomes 0111 (decimal number 7) and the
color value, the maximum embedding error for that color value is 2K–1, or
the maximum value for a set of K bits. Since MER adjusts the bit next to
of 2(K–1) [LEE1999].
how MER works. A threshold of 5 was selected since embedding five bits
into an 8-bit value (a byte) using MER would mean that the sixth LSB
may also change. Since the only remaining bits that are protected from
modification are the two most significant bits (MSB), the value of a byte
when only these two bits are set to 1 is 192, i.e., 27 + 26. Whatever the
value of the six LSBs, the value of the byte will always be greater than
Chen analyzes only the four top-left adjacent pixels, distortions in the
the embedding error to the intensity or grayscale value of each of the four
pixels E, F, G, and H.
of the four adjacent pixels on its bottom-right sides are decreased. Lee and
Chen did not discuss exactly how such an operation helps in preventing
higher capacities for the succeeding pixels than what it would have
originally allocated if the intensity of the current pixel had not increased,
the current pixel affects the capacity evaluation for the succeeding pixels
that have not been processed yet. IGSC therefore compensates for the
Lee and Chen stated that it was based on an error-diffusion technique used
color images.
a given numerical value called the seed. Unlike real random number
seed will generate the exact same sequence of numbers every time it is
used.
by the PRNG. In such a setup, the seed of the PRNG can be the hash value
has been hashed, information about its original value has already been
lost.
operating system stores a list of valid passwords within the system for
to access the contents of such a list and break into the system.
are verified by calculating their hash values and comparing them with the
to the password list, the only way to know the actual passwords is to
calculate the hash value every possible character combination within the
maximum length for passwords and find the ones that matches the hash
values in the list. This would be an extremely exhaustive operation for the
attacker.
Because of this, data gathering for this study is focused on the modern researches
are conducted during the course of this study, as they are not necessary.
with most subjects, the Internet is evidently the best source for up-to-date and
technical reports, and journal articles gathered from the Internet provided the
technical information required for the designing and development of this study’s
Most of the papers referenced during the course of this study were
CiteSeer currently indexes over 500,000 research documents on the web and
formats.
Copies of the IBM Systems Journal downloaded from the IBM Research
website were also referenced in this study. The two issues of this journal
and technology showcase” from Fast Search & Transfer, Inc. [FAST2002].
Originally set up as a test site for new and experimental search features,
AlltheWeb grew into a fast and comprehensive search engine ideal for students
and researchers. At present, it “indexes over 2.1 billion web pages, 118 million
multimedia files, 132 million FTP files, two million MP3s, 15 million PDF files
only but allows data to be embedded in more significant bit planes, not
the name suggests, noise images are made up of random colors and form
obviously cryptic and does not provide a convincing illusion. Figure 6-1
Figure 6-1.
A Noise Image Created by EyeMage IIE
insertion is used. As the name suggests, the hidden data file is encoded as
unlimited hiding space and does not make any changes to the image itself,
such a technique is highly insecure. Since the data bits are not embedded
the size of the stego image and may attract suspicion since JPEG and PNG
“information security suite” because it can also be used exclusively for file
Sapphire II.
very easy to use as compared with other software. Figure 6-3 shows a
screenshot of this user interface. It has been observed in this study that
steganographic software.
95, Windows 98, Windows 2000, Windows ME, Windows NT, and
Blowfish.
Figure 6-4.
Screenshot of Steganos File Manager
that of the WinZip file compression software and is not as easy to use as
in Figure 6-4.
· Pentium processor
· 32 MB RAM
image formats:
The Third Eye also uses standard LSB encoding like Steganos and
system requirements:
is also quite easy to use since very few controls and menu options are on
the main window. Figure 6-5 shows a screenshot of this user interface.
Figure 6-5.
Screenshot of The Third Eye
7.1 Performance
7.2 Security
7.4 Functionality
· The software should provide a means for viewing the final appearance
of processed stego-image.
· The software should provide a means for opening the extracted data
file from within the software itself.
Cover
Image
Data File Encoding Stego Key Stego Key Decoding Data File
Module Module
Stego Stego
Transmission Channel
Image Image
Figure 8-1.
Framework of the Proposed Stegosystem
recipient. Decoding involves the extraction of the hidden data from the
stego image.
file, cover image, and stego key) and produces a single image (the
Encoding
1.0
Figure 8-2.
Hierarchical Input-Process-Output (HIPO) Chart of the Encoding Module
process. The MD5 checksum, on the other hand, is used for data
integrity verification.
true-color images have three color channels and each one is being
for this, the original formula for gray level variation is replaced
formed by pairs of adjacent pixels on the top and left sides of the
form:
the actual texture of the area being evaluated is not put into
tolerance to modifications.
8.1.2 Transmission
media. In the case of the Internet, the stego image can be posted on
bulletin boards and other public web sites. The advantage of using
such media is the fact that web pages have no specified recipients
8.1.3 Decoding
decoding at the receiving end. The decoding module uses the stego
image and the stego key for input and performs a reverse operation
Decoding
1.0
Figure 8-3.
Hierarchical Input-Process-Output (HIPO) Chart of the Decoding Module
first in order to determine the data file size and verify the stego key
embedding. If the stego key entered is correct, the data file will
that the original cover image cannot be regenerated out of the stego
embedding.
The following are abstract codes for the major components of the
the essential logic and fundamental structure of the actual program code.
Function ComputeCapacity()
K = Int(Log(V) / Log(2))
If K < U Then
Capacity = K
Else
Capacity = U
End If
End Function
Function PerformMER()
End Function
Function DiffuseError()
ErrorFraction = EmbeddingError / 4
End Function
Function Encode()
InitializePRNGWith(StegoKeyMD5Hash)
MetadataAddress = GetRandomNumber
CurrentAddress = 0
DataFileCounter = 0
Do Until AllPixelsEncoded
ComputeCapacity
PerformMER
DiffuseError
SelectRandomAddress
Loop
End Function
Function DecodeMetadata()
InitializePRNGWith(StegoKeyMD5Hash)
MetadataAddress = GetRandomNumber
CurrentAddress = 0
MetadataCounter = 0
ComputeCapacity
Extracted = PixelP And ((2 ^ Capacity) - 1)
AppendToMetadata(Extracted)
DataFileCounter = MetadataCounter + 1
SelectNextAddress
End If
Loop
End Function
Function DecodeDataFile()
InitializePRNGWith(StegoKeyMD5Hash)
MetadataAddress = GetRandomNumber
CurrentAddress = 0
DataFileCounter = 0
ComputeCapacity
AppendToDataFile(Extracted)
SelectRandomAddress
End If
Loop
End Function
red, green, and blue color channels. This allows for a randomized pattern
this study remains focused on 24-bit formats since 32-bit formats are
image format so as not to lose any of the embedded data bits. This
automatically eliminates the popular JPEG and GIF images from the list of
steganographic algorithm. Unlike algorithms that use JPEG and GIF, the
formats that do not apply transformations on the image. This means that
the encoding process does not leave any distinct signature on the created
stego image.
the characteristic structure of the JPEG image. Fridrich and Goljan explain
that when a JPEG image is embedded with data, “the cover image will
image to be incompatible with the JPEG format. Such irony may therefore
do not have distinct characteristic structures since every color pattern and
the species of reptiles that have the ability to adapt the color of their skin with
their surroundings. From hereon, the software developed in this study will
FreeImage written by Floris van den Berg was used for opening and
for Chameleon. This wrapper class also made use or several Windows API
pixel.
Ramirez Cobos is also used in encrypting data files with the very fast RC4
stream cipher. EzCryptoAPI also provided the MD5 and SHA-1 hash
removed from the disk’s file system but its contents are left intact. This
allows file recovery programs to “undelete” the file. For this reason,
data files when they are not needed in the computer further increases the
Chameleon:
· 64 MB RAM
taken from digital cameras. Using private and original photos instead of
Visual Basic. The sizes of the data files range from 10 kilobytes to 500
The seed value used for Visual Basic’s built-in PRNG is the
written on the data files were random enough in such a way that none of
since Steganos File Manager and The Third Eye does not include a data
Table 9-1 is a list of these digital photographs. The actual photos are
shown in Appendix D.
specifications:
· 64 MB RAM
Table 9-2.
Test Results for Cover Image “abandon.bmp”
Table 9-3.
Test Results for Cover Image “antelope.bmp”
The Third Eye 1.03 sec 96,789 bytes 90 KB failed to extract data
Table 9-5.
Test Results for Cover Image “death.bmp”
The Third Eye 1.08 sec 97,979 bytes 90 KB failed to extract data
Table 9-6.
Test Results for Cover Image “gardens.bmp”
Table 9-8.
Test Results for Cover Image “nap.bmp”
Table 9-9.
Test Results for Cover Image “passing.bmp”
Table 9-11.
Test Results for Cover Image “rain.bmp”
Table 9-12.
Test Results for Cover Image “ruins.bmp”
The Third Eye 1.16 sec 111,069 bytes 100 KB failed to extract data
EyeMage IIE 5.90 sec 367,904 bytes 350 KB highly severe distortions
The Third Eye 1.65 sec 111,579 bytes 100 KB failed to extract data
Table 9-14.
Test Results for Cover Image “style.bmp”
EyeMage IIE 3.07 sec 198,304 bytes 190 KB highly severe distortions
Table 9-15.
Test Results for Cover Image “subtle.bmp”
EyeMage IIE 5.46 sec 375,741 bytes 360 KB highly severe distortions
In all of the encoding tests, the password used was “test”. After
each encoding operation on an image, the created stego image was then
viewed to check for distortions or any visible differences from the cover
image. Finally, extraction is performed to verify that the data file has been
recorded on The Third Eye. In these four cases, The Third Eye displayed
error messages stating that no data is hidden within the stego images. The
regardless of the fact that The Third Eye performed the fastest in all tests,
the largest hiding capacity for all cover images. However, as the results
created on all images, even in images where it used the largest hiding
capacities. This proves that using more bits for embedding requires careful
analysis of the image. Invisible Secrets and Steganos File Manager also
did not produce any visible distortions on the images since both software
used only the first LSB plane of each image for embedding.
changes to an image.
compact and easy to install. The installation package was created with
Setup Factory 6.0 and is a single 3.41 MB self-extracting setup file. The
user interface of the setup program created by Setup Factory is very easy
to use.
Except for the Visual Basic runtime files, there are only seven
icons to help the user easily understand the function of each button.
first-time users by displaying the appropriate help topic at the press of the
steganography software listed in Chapter 1 have been solved. These problems and
include file encryption, file compression, file wiping, password verification, data
software more suitable for real-world covert communications and covert data
and are therefore inapplicable to other fields of information hiding such as digital
[LEE2002]. In this paper, the authors focused their work on the robustness of the
the embedding process, for example, steganographic systems may exploit the fact
[FRID2002].
cover-images or carriers are original photos taken from digital cameras. Also the
This generally prevents the recipient from being traced by potential attackers
cover medium
Also referred to as host or carrier, it is the input medium of the embedding
process that will host the embedded data. In image steganography, this
medium is called the cover-image.
embedded data
The input data hidden within the cover-medium.
embedding
The process of hiding data within a cover-medium. It may also be referred to
as encoding.
extraction
The process of retrieving the embedded data from a stego-medium. It may
also be referred to as decoding.
steganalysis
A deliberate and systematic attempt to detect the existence of hidden data in a
cover-medium through statistical or computer-based examinations. Broadly, it
is an attempt to defeat a stegosystem.
stego medium
The output medium of the embedding process that hosts the embedded data.
In image steganography, this medium is called the stego-image.
stegosystem
A complete design for the implementation of steganography, which defines
all the stages involved in the steganographic process as well as their
underlying concepts and techniques.
|x|
The absolute value of x.
ëxû
The highest integer less than or equal to x.
xy
The power of x raised to the exponent y.
logb x
The logarithm of x to the base b.
max {x, y, z}
The highest number between x, y, and z.
min {x, y, z}
The lowest number between x, y, and z.
round (x)
The value of x rounded to the nearest integer.
Requirements Analysis
Design
Algorithm Design
Interface Design
Coding
Prototype
Main Program
Help System
Testing
Error Testing
Performance Testing
Enhancement
Speed Optimization
Interface Enhancement
Figure D-1.
abandon.bmp
“Abandoned”
Copyright © 2002 Fred Miranda
“Antelope”
Copyright © 2002 Fred Miranda
“Badwater”
Copyright © 2002 Fred Miranda
“Death Valley”
Copyright © 2002 Fred Miranda
“Descanso Gardens”
Copyright © 2002 Fred Miranda
“French Kiss”
Copyright © 2002 Fred Miranda
“Nap”
Copyright © 2002 Fred Miranda
“Passing By”
Copyright © 2002 Fred Miranda
“Racetrack”
Copyright © 2002 Fred Miranda
“Rain”
Copyright © 2002 Fred Miranda
“Old Ruins”
Copyright © 2002 Fred Miranda
“Style”
Copyright © 2002 Fred Miranda
“Subtle Light”
Copyright © 2002 Fred Miranda
“Desert Sunrise”
Copyright © 2002 Fred Miranda
Figure E-1.
e_abandon_data350k.bmp
About Steganography
Steganography refers to the art and science of hiding the existence of information.
A digital picture can be used as a cover image or "carrier" by embedding the data
bits of a file or message across the pixels of the picture. Accordingly, the resulting
image wherein secret information is embedded is called a stego image.
However, since only the very least significant bits are used, only a small capacity for
hidden information is available. In some images, using more bits in the color values of
pixels still doesn't cause visible changes and thus hiding capacity can be increased.
Some images, on the other hand, are visibly degraded or even distorted if more bits
are changed. In order to obtain optimum hiding capacities, a more space-efficient
technique must be used.
About Chameleon
Chameleon is an image steganography software for true-color (24-bit) digital images.
It was developed by Mark David Gan as part of his Computer Science Thesis at the
Systems Technology Institute for the school year of 2002-2003.
In addition to these formats, Chameleon can also open images stored in the following
formats:
· JPEG (*.jpg)
· Kodak PhotoCD (*.pcd)
· PC Paintbrush (*.pcx)
· Adobe Photoshop (*.psd)
Main Window
At start-up, Chameleon displays the Main Window which offers the user four basic
options:
· Extract
(shortcut key: Alt+E)
Starts up an extraction task and opens the first page of the Extract
Wizard.
· Wipe
(shortcut key: Alt+W)
Wipes a file selected by the user from the Wipe Dialog.
· Help
(shortcut key: F1)
Opens up this help file.
Hide Wizard
The Hide Wizard is designed to help the user perform hiding tasks quickly and
easily. It guides the user step-by-step throughout the entire operation and features a
context-sensitive help system that provides useful information for first-time users.
Whenever asked to specify a file, the user may either type the complete address of a
file in the File Path box or press the Open button (shown below) to select a file
from a dialog window.
Before continuing to the next page, the selected file may first be previewed by
clicking the Preview button (shown below). In previewing the file, Chameleon
opens the file in the application program associated to it. Text files, for example,
would normally be opened in "Notepad".
To ensure that the desired password is specified correctly, the user is asked to re-
enter the same password in the Password Confirmation box.
During encoding, a progress bar is displayed to indicate how much of the stego image
has already been processed.
After encoding, a dialog window is displayed to ask the user for the filename and
image format to use in saving the processed stego image.
Finally, a report of the encoding results is displayed. From here, the user may preview
a comparison of the original cover image and the processed stego image by pressing
the Preview button.
The user may also save the stego image multiple times in different filenames and
image formats by pressing the Save button (shown below) which opens up again
the previously-discussed dialog window.
Extract Wizard
The Extract Wizard is designed to help the user perform extraction tasks quickly
and easily. It guides the user step-by-step throughout the entire operation and
features a context-sensitive help system that provides useful information for first-time
users.
Whenever asked to specify a file, the user may either type the complete address of a
file in the File Path box or press the Open button (shown below) to select a file
from a dialog window.
Before continuing to the next page, the selected image may first be previewed by
clicking the Preview button (shown below). In previewing the image, Chameleon
displays the selected image in the center of a blank screen. Chameleon generates
image previews by itself and no external programs are used.
Pressing the Next button initiates password verification. The wizard will continually
ask for the password until the correct one is given or when the Cancel button is
pressed.
During decoding, a progress bar is displayed to indicate how much of the data file has
already been processed.
Finally, a report of the decoding results is displayed. From here, the user may preview
the extracted data file by pressing the Preview button. In previewing the file,
Chameleon opens the file in the application program associated to it. Text files, for
example, would normally be opened in "Notepad". For this reason, previewing is only
possibly after the data file has been saved by the user.
Pressing the Close button closes the wizard and opens up the Main Window.
Wipe Dialog
The Wipe Dialog asks the user to select a file to be wiped off the disk.
Wiping a file means permanently destroying the contents of a file and then deleting
it from the disk. This prevents a file from ever being recovered from the disk after
deletion. Such a feature is particularly useful in cases wherein a file containing secret
information needs to be permanently removed from the computer without leaving any
trace of its existence.
To wipe a file, Chameleon begins by renaming the file so as not to leave any trace of
what the file contains. Next, Chameleon overwrites the file with a bit pattern of
alternating 0's and 1's. Then, the file is overwritten again but this time with a bit
pattern of alternating 1's and 0's. Finally, the file is overwritten with 0's and then
deleted from the disk.
Nevertheless, there are still certain steps that a user can take to further improve
security and reliability:
· Use original photos taken from digital cameras and avoid scanned
images. Scanned images are usually of low quality and may already
appear degraded.
· After hiding a data file in an image, wipe the original cover image to
prevent attackers from comparing the original cover image with the
created stego image.
· After hiding a data file in an image, wipe the original data file from
the computer to prevent unauthorized users from accessing its
contents.
'------------------------------------------------------------------------------'
' ' End Sub
' Chameleon Image Steganography v1.2 '
' '
' Cover Image Selection Form ' Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
' [frmCoverImage] '
' ' If Shift = vbCtrlMask Then
'------------------------------------------------------------------------------'
' ' Select Case KeyCode
' Copyright (C) 2003 Mark David Gan ' Case vbKeyO: btnBrowse.Press
' ' Case vbKeyP: btnView.Press
'------------------------------------------------------------------------------' End Select
'------------------------------------------------------------------------------' End If
' Public Procedures '
'------------------------------------------------------------------------------' End Sub
txtPath.SetFocus
Private Sub txtPath_MouseDown(Button As Integer, Shift As Integer, _
End Sub X As Single, Y As Single)
txtPath.Tag = "MouseDown"
End Sub
Private Sub btnCancel_Click()
frmMainMenu.Show
frmMainMenu.SetFocus Private Sub txtPath_MouseUp(Button As Integer, Shift As Integer, _
End Sub X As Single, Y As Single)
txtPath.Tag = ""
End Sub
Private Sub btnHelp_Click()
DisplayHelpFile "hide_wizard.html#Page2"
End Sub Private Sub txtPath_Validate(Cancel As Boolean)
If Len(txtPath.Text) = 0 Then
Private Sub btnNext_Click() MsgBox "No file has been specified." & vbCrLf & _
"Please specify an existing file.", vbExclamation
On Error Resume Next Cancel = True
Me.ValidateControls ElseIf Not FileExists(txtPath.Text) Then
MsgBox "The specified file cannot be found." & vbCrLf & _
If ActiveControl Is btnNext Then "Please specify an existing file.", vbExclamation
frmPassword1.Show Cancel = True
frmPassword1.SetFocus End If
End If
End Sub
End Sub
'------------------------------------------------------------------------------'
Private Sub Form_Deactivate() ' Public Procedures '
If Screen.ActiveForm.MDIChild Then Me.Hide '------------------------------------------------------------------------------'
End Sub
txtPath.SetFocus
End Sub
End With
Private Sub txtPath_MouseUp(Button As Integer, Shift As Integer, _
X As Single, Y As Single) End Function
txtPath.Tag = ""
End Sub
'------------------------------------------------------------------------------'
' Event Handlers '
Private Sub txtPath_Validate(Cancel As Boolean) '------------------------------------------------------------------------------'
If Len(txtPath.Text) = 0 Then
MsgBox "No file has been specified." & vbCrLf & _ Private Sub btnBack_Click()
End Sub
Private Sub btnCancel_Click()
End Sub
Private Sub btnView_Click()
If Len(m_DataFilePath) = 0 Then
Private Sub btnClose_Click() MsgBox "The extracted data file has not been saved yet." & vbCrLf & _
"Please save the data file first.", _
If fraSave.Visible Then vbExclamation
If Len(m_DataFilePath) = 0 Then btnSave.SetFocus
If MsgBox("Would you like to save the extracted data file first?", _ Else
vbQuestion + vbYesNo) = vbYes Then OpenFile m_DataFilePath
btnSave.Press End If
End If End Sub
End If
End If
Private Sub Form_Activate()
frmMainMenu.Show
frmMainMenu.SetFocus m_Cancel = False
m_StegoImagePath = frmStegoImage.txtPath.Text
End Sub m_TemporaryFilePath = GenerateTempFile
m_TemporaryDataFilePath = GenerateTempFile
m_DataFilePath = vbNullString
Private Sub btnHelp_Click()
DisplayHelpFile "extract_wizard.html#Page3" lblDataFileName1(1).Caption = frmMainMenu.StegoDecoder.DataFileName
End Sub lblDataFileName2(1).Caption = lblDataFileName1(1).Caption
lblDataFileDate(1).Caption = FormatDate(frmMainMenu.StegoDecoder.DataFileDate)
'[ display elapsed time ]' Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Tmr.Mark
lblStatus(1).Caption = "Processing Time: " & Tmr.ElapsedTimeInMinutes If Shift = vbCtrlMask Then
'------------------------------------------------------------------------------'
' ' '------------------------------------------------------------------------------'
' Chameleon Image Steganography v1.2 ' ' Event Handlers '
' ' '------------------------------------------------------------------------------'
' Encoding Process Form '
' [frmEncode] '
' ' Private Sub btnBack_Click()
'------------------------------------------------------------------------------' frmPassword1.Show
' ' frmPassword1.SetFocus
' Copyright (C) 2003 Mark David Gan ' End Sub
' '
'------------------------------------------------------------------------------'
Private Sub btnCancel_Click()
End Sub
Private Function PerformCompression() As Boolean
PerformCompression
Private Function PerformEmbedding() If m_Cancel Then Exit Sub
'--------------------------------------------------------------------------' End If
' '
' Special Note: ' End Sub
' For some reason, the last hash function executed by EzCryptoApi '
' affects the succeeding encryption/decryption operation. Because of '
' this, it must be made sure that last hash function called before ' Private Sub btnSave_Click()
' a decryption operation is the same as the last hash function called '
' before its corresponding encryption operation. ' On Error Resume Next
' ' dlgBrowse.ShowSave
'--------------------------------------------------------------------------'
GetHashValue m_Password, SHA If Err.Number = 0 Then
On Error Resume Next '[ set stego image file format ]'
Dim ImageFormat As FREE_IMAGE_FORMAT
With frmMainMenu.EzCrypto Select Case dlgBrowse.FilterIndex
.EncryptionAlgorithm = RC4 Case 1: ImageFormat = FIF_BMP
.Speed = [1KB] Case 2: ImageFormat = FIF_PNG
.Password = m_Password Case 3: ImageFormat = FIF_PPMRAW
.EncryptFile m_TemporaryFilePath Case 4: ImageFormat = FIF_TIFF
End With Case 5: ImageFormat = FIF_TARGA
End Select
End If
'------------------------------------------------------------------------------'
End Sub ' Windows API Function Declarations '
'------------------------------------------------------------------------------'
Me.Hide
Private Sub btnHide_Click()
'[ reset controls ]' CurrentTask = TASK_ENCODE
fraStatus.Visible = False frmDataFile.Show
fraProperties(0).Visible = True frmDataFile.SetFocus
fraProperties(1).Visible = False End Sub
fraHelp.Visible = True
fraSave.Visible = False
fraView.Visible = False Private Sub btnWipe_Click()
Me.Show
Private Sub ZlibDecompressor_Progress(ByVal percent_complete As Integer) Private Sub MDIForm_QueryUnload(Cancel As Integer, UnloadMode As Integer)
With frmDecode
.prgStatus.Value = percent_complete If (UnloadMode = vbAppTaskManager) Or (UnloadMode = vbFormControlMenu) Then
.lblStatus(1).Caption = percent_complete & "% complete" If Not frmMDI.ActiveForm Is frmMainMenu Then
End With
End Sub If MsgBox("Do you wish to cancel the current task and exit Chameleon?", _
vbQuestion + vbYesNo) = vbNo Then
Cancel = True
Exit Sub
End If
End If
'------------------------------------------------------------------------------' End If
' '
' Chameleon Image Steganography v1.2 ' Cancel = False
' '
' Multiple Document Interface (MDI) Form ' '[ hide mdi form ]'
' [frmMDI] ' frmMDI.WindowState = vbMinimized
' ' frmMDI.Hide
'------------------------------------------------------------------------------'
' ' CloseHelpFiles
' Copyright (C) 2003 Mark David Gan '
' ' Unload frmView
'------------------------------------------------------------------------------' Unload frmMDI
End Sub
Option Explicit
'------------------------------------------------------------------------------'
' Windows User Interface Constants '
'------------------------------------------------------------------------------'
'------------------------------------------------------------------------------'
' '
Private Const MF_BYCOMMAND = &H0& ' Chameleon Image Steganography v1.2 '
Private Const MF_BYPOSITION = &H400& ' '
' Password Specification Form '
Private Const SC_SIZE = &HF000& ' [frmPassword1] '
Private Const SC_MOVE = &HF010& ' '
Private Const SC_MINIMIZE = &HF020& '------------------------------------------------------------------------------'
Private Const SC_MAXIMIZE = &HF030& ' '
' Copyright (C) 2003 Mark David Gan '
Private Const WS_CAPTION = &HC00000 ' '
Private Const WS_MAXIMIZEBOX = &H10000 '------------------------------------------------------------------------------'
Private Const WS_MINIMIZEBOX = &H20000
Private Const WS_SYSMENU = &H80000
Private Const WS_THICKFRAME = &H40000 Option Explicit
If .LoadStegoImage(m_StegoImagePath) Then
Private Sub Form_Activate()
On Error Resume Next PasswordHashMD5 = GetHashValue(txtPassword.Text, MD5)
txtPassword(0).SetFocus PasswordHashSHA = GetHashValue(txtPassword.Text, SHA)
End Sub
lblStatus(0).Caption = "Verifying password..."
fraStatus.Visible = True
Private Sub Form_Deactivate()
If Screen.ActiveForm.MDIChild Then Me.Hide .DecodeMetadata PasswordHashMD5
End Sub
lblStatus(0).Caption = vbNullString
lblStatus(1).Caption = vbNullString
Private Sub Form_Load()
Set imgPageIcon(1).Picture = imgPageIcon(0).Picture If .StoredPassword = PasswordHashSHA Then
SetMargins txtPassword(0), 1 If m_Cancel Then Exit Sub
SetMargins txtPassword(1), 1 frmDecode.Show
End Sub frmDecode.SetFocus
Else
If m_Cancel Then Exit Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) lblStatus(0).Caption = "Invalid Password."
If KeyCode = vbKeyF1 Then btnHelp.Press MsgBox "The specified password is invalid." & vbCrLf & _
End Sub "Please specify the correct password.", vbExclamation
fraPage.Enabled = True
txtPassword.SetFocus
Private Sub txtPassword_GotFocus(Index As Integer) End If
End Sub
'------------------------------------------------------------------------------'
' Public Procedures ' Private Sub Form_Load()
'------------------------------------------------------------------------------' Set imgPageIcon(1).Picture = imgPageIcon(0).Picture
SetMargins txtPassword, 1
End Sub
Public Sub ResetControls()
txtPassword.Text = vbNullString
End Sub Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyF1 Then btnHelp.Press
End Sub
'------------------------------------------------------------------------------'
' Private Procedures '
'------------------------------------------------------------------------------' Private Sub txtPassword_GotFocus()
With txtPassword
End Sub
Private Sub Form_Deactivate()
If Screen.ActiveForm.MDIChild Then Me.Hide
Private Sub txtPassword_KeyDown(KeyCode As Integer, Shift As Integer) End Sub
If KeyCode = vbKeyReturn Then btnNext.SetFocus
End Sub
Private Sub Form_Load()
End Sub
'------------------------------------------------------------------------------'
' Public Procedures '
'------------------------------------------------------------------------------' Private Sub txtPath_GotFocus()
With txtPath
Public Sub ResetControls() If .Tag <> "MouseDown" Then
lblFileSize(1).Caption = vbNullString .SelStart = 0
lblFileDate(1).Caption = vbNullString .SelLength = Len(.Text)
txtPath.Text = vbNullString End If
End Sub End With
End Sub
'------------------------------------------------------------------------------'
' Form Event Handlers '
'------------------------------------------------------------------------------' Private Sub txtPath_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyReturn Then btnNext.SetFocus
End Sub
Private Sub btnBack_Click()
frmMainMenu.Show
frmMainMenu.SetFocus Private Sub txtPath_LostFocus()
End Sub txtPath.Text = GetAbsolutePath(Trim$(txtPath.Text))
lblFileSize(1).Caption = GetFileSize(txtPath.Text)
lblFileDate(1).Caption = GetFileDateTime(txtPath.Text)
Private Sub btnBrowse_Click() End Sub
Case vbKeyUp:
'------------------------------------------------------------------------------' If picCoverImage(0).Visible Then
' Public Procedures ' Call picCoverImage_MouseDown(1, vbLeftButton, 0, 0, 0)
'------------------------------------------------------------------------------' Call picCoverImage_MouseMove(1, vbLeftButton, 0, 0, 10)
Else
Call picStegoImage_MouseDown(1, vbLeftButton, 0, 0, 0)
Public Function DisplayByDIB( _ Call picStegoImage_MouseMove(1, vbLeftButton, 0, 0, 10)
Optional ByVal CoverImage As Long = 0, _ End If
Optional ByVal StegoImage As Long = 0 _
) As Boolean Case vbKeyEscape:
Call mnuClose_Click
DisplayByDIB = False
Case 93:
m_ImageCount = 0 Call Form_MouseDown(vbRightButton, 0, 0, 0)
m_CoverImageDIB = CoverImage
m_StegoImageDIB = StegoImage End Select
m_DIBsTemporary = False
End Sub
If PaintImage(picCoverImage(1)) Then m_ImageCount = m_ImageCount + 1
If PaintImage(picStegoImage(1)) Then m_ImageCount = m_ImageCount + 1
Private Sub Form_Load()
If m_ImageCount > 0 Then Set Imager = New FreeImageWrapper
Call Form_Resize End Sub
Me.Show
DisplayByDIB = True
End If Private Sub Form_MouseDown(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
End Function If Button = vbRightButton Then
PopupMenu mnuView
End If
Public Function DisplayByFilename( _ End Sub
Optional ByVal CoverImage As String = vbNullString, _
Optional ByVal StegoImage As String = vbNullString _
) As Boolean Private Sub Form_Resize()
End If
End If Public Const HH_DISPLAY_TOPIC As Long = &H0
Public Const HH_SET_WIN_TYPE As Long = &H4
End Sub Public Const HH_GET_WIN_TYPE As Long = &H5
Public Const HH_GET_WIN_HANDLE As Long = &H6
Public Const HH_DISPLAY_TEXT_POPUP As Long = &HE
Private Sub picStegoImage_MouseMove(Index As Integer, Button As Integer, _ Public Const HH_HELP_CONTEXT As Long = &HF
Shift As Integer, X As Single, Y As Single) Public Const HH_TP_HELP_CONTEXTMENU As Long = &H10
Public Const HH_TP_HELP_WM_HELP As Long = &H11
If (Index = 1) Then Public Const HH_CLOSE_ALL As Long = &H12
If (Button = vbLeftButton) Then
'------------------------------------------------------------------------------'
'------------------------------------------------------------------------------' ' HTML Help Function Declarations '
' ' '------------------------------------------------------------------------------'
' Chameleon Image Steganography v1.2 '
' '
' Miscellaneous Application Procedures Module ' Private Declare Function HtmlHelp _
' [modApplication] ' Lib "hhctrl.ocx" Alias "HtmlHelpA" ( _
' ' ByVal hwndCaller As Long, _
'------------------------------------------------------------------------------' ByVal pszFile As String, _
' ' ByVal uCommand As Long, _
' Copyright (C) 2003 Mark David Gan ' ByVal dwData As Long _
' ' ) As Long
'------------------------------------------------------------------------------'
'------------------------------------------------------------------------------'
Option Explicit ' Public Variables '
'------------------------------------------------------------------------------'
'------------------------------------------------------------------------------'
' Public Enumerated Data Types ' Public CurrentTask As appTaskConstants
'------------------------------------------------------------------------------'
'------------------------------------------------------------------------------'
Public Enum appTaskConstants ' Public Procedures '
TASK_NONE = 0 '------------------------------------------------------------------------------'
TASK_ENCODE = 1
TASK_DECODE = 2
End Enum Public Sub CloseHelpFiles()
Call HtmlHelp(0, "", HH_CLOSE_ALL, 0)
Dim Cap As String '[ constants for "SetWindowLong" function "nIndex" parameter ]'
Dim RC As RECT Private Const GWL_WNDPROC As Long = (-4)
With Label1 '[ constants for "TRACTMOUSEEVENTTYPE" structure "dwFlags" member ]'
Private Const TME_HOVER As Long = &H1
'[ compute boundary ]' Private Const TME_LEAVE As Long = &H2
RC.Right = frmView.ScaleX(.Width, vbTwips, vbPixels) Private Const TME_QUERY As Long = &H40000000
RC.Bottom = frmView.ScaleY(.Height, vbTwips, vbPixels) Private Const TME_CANCEL As Long = &H80000000
Dim RV As Long
Public Function NewWndProc(ByVal hWnd As Long, ByVal uMsg As Long, _
On Error Resume Next ByVal wParam As Long, ByVal lParam As Long) As Long
OpenFile = (ShellExecute(0, "", FileName, "", "", SW_SHOW) > 32)
Dim CButton1 As CustomButton
If Not OpenFile Then
On Error Resume Next
RV = ShellExecute(0, "open", FileName, "", "", SW_SHOW)
If Not m_ButtonCollection Is Nothing Then
If RV > 32 Then
Set CButton1 = m_ButtonCollection.Item("hWnd: " & hWnd)
OpenFile = True
'[ test for mouse leave event ]'
Else If CButton1.Enabled Then
Select Case uMsg
OpenFile = False
Case WM_MOUSELEAVE:
Select Case RV CButton1.State = cbtnStateNormal
Case SE_ERR_NOASSOC, SE_ERR_ASSOCINCOMPLETE: CButton1.RaiseMouseLeaveEvent
MsgBox "The file " & FileName & " cannot be opened." & vbCrLf & _
"The file may not be associated with any application.", _ End Select
vbExclamation End If
Case Else:
MsgBox "The file " & FileName & " cannot be opened." & vbCrLf & _ '[ call original window procedure ]'
"An error occured while accessing the file.", vbExclamation NewWndProc = CallWindowProc(CButton1.OldWndProc, hWnd, uMsg, wParam, lParam)
End Select
End If
End If
End Function
End If
If CButton1.OldWndProc = 0 Then
Public Sub SetMargins(ByRef Text1 As TextBox, ByVal Margin As Long)
SendMessageLong Text1.hWnd, EM_SETMARGINS, EC_LEFTMARGIN, Margin '[ create button collection ] '
SendMessageLong Text1.hWnd, EM_SETMARGINS, EC_RIGHTMARGIN, Margin * &H10000 If m_ButtonCollection Is Nothing Then
End Sub Set m_ButtonCollection = New Collection
End If
End Sub
'[ 64-bit time structure ]'
Private Type FILETIME
dwLowDateTime As Long Public Function FileExists(ByVal Path As String) As Boolean
dwHighDateTime As Long Dim FSO As Object
End Type Set FSO = CreateObject("Scripting.FileSystemObject")
FileExists = FSO.FileExists(Path)
'[ multiple-field time structure ]' End Function
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer Public Function FolderExists(ByVal Path As String) As Boolean
wDayOfWeek As Integer Dim FSO As Object
wDay As Integer Set FSO = CreateObject("Scripting.FileSystemObject")
wHour As Integer FolderExists = FSO.FolderExists(Path)
wMinute As Integer End Function
wSecond As Integer
wMilliseconds As Integer
End Type Public Function GenerateTempFile() As String
'[ constants for "CreateFile" function "dwDesiredAccess" parameter ]' '[ get path of temporary folder ]'
Private Const GENERIC_READ As Long = &H80000000 GenerateTempFile = FSO.GetSpecialFolder(GSF_TEMPORARYFOLDER) & "\" & _
Private Const GENERIC_WRITE As Long = &H40000000 UCase$(FSO.GetTempName)
'[ constants for "CreateFile" function "dwShareMode" parameter ]' End Function
Private Const FILE_SHARE_READ As Long = &H1
Private Const FILE_SHARE_WRITE As Long = &H2
Public Function GetAbsolutePath(ByVal Path As String) As String
'[ constants for "CreateFile" function "dwCreateDisposition" parameter ]'
Private Const CREATE_ALWAYS As Long = 2 If Len(Path) = 0 Then
Private Const CREATE_NEW As Long = 1 GetAbsolutePath = ""
Private Const OPEN_ALWAYS As Long = 4 Exit Function
Private Const OPEN_EXISTING As Long = 3 End If
Private Const TRUNCATE_EXISTING As Long = 5
Dim FSO As Object
'[ constants for "CreateFile" function "dwFlagsAndAttributes" parameter ]' Set FSO = CreateObject("Scripting.FileSystemObject")
Private Const FILE_ATTRIBUTE_ARCHIVE As Long = &H20
Private Const FILE_ATTRIBUTE_HIDDEN As Long = &H2 '[ replace illegal characters ]'
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80 Path = Replace$(Path, ">", "")
Private Const FILE_ATTRIBUTE_READONLY As Long = &H1 Path = Replace$(Path, "<", "")
Private Const FILE_ATTRIBUTE_SYSTEM As Long = &H4 Path = Replace$(Path, "*", "")
Private Const FILE_FLAG_DELETE_ON_CLOSE As Long = &H4000000 Path = Replace$(Path, "?", "")
Private Const FILE_FLAG_NO_BUFFERING As Long = &H20000000 Path = Replace$(Path, "|", "")
Private Const FILE_FLAG_OVERLAPPED As Long = &H40000000 Path = Replace$(Path, Chr(34), "")
Private Const FILE_FLAG_POSIX_SEMANTICS As Long = &H1000000
Private Const FILE_FLAG_RANDOM_ACCESS As Long = &H10000000 '[ ensure that the colon character is in place ]'
Private Const FILE_FLAG_SEQUENTIAL_SCAN As Long = &H8000000 If InStr(1, Path, ":") = 2 Then
Private Const FILE_FLAG_WRITE_THROUGH As Long = &H80000000 Path = Left$(Path, 2) & Replace$(Path, ":", "", 3)
Else
'[ constants for "SetFilePointer" function "dwMoveMethod" parameter ]' Path = Replace$(Path, ":", "")
Private Const FILE_BEGIN As Long = 0 End If
Private Const FILE_CURRENT As Long = 1
Private Const FILE_END As Long = 2 '[ set current drive to that of the windows folder ]'
ChDir Left$(FSO.GetSpecialFolder(GSF_WINDOWSFOLDER), 2) & "\"
'[ constants for "GetSpecialFolder" function "folderspec" parameter ]' Public Function GetFileDateTime(ByVal FileName As String) As String
Private Const GSF_WINDOWSFOLDER As Long = 0
Private Const GSF_SYSTEMFOLDER As Long = 1 If FileExists(FileName) Then
Private Const GSF_TEMPORARYFOLDER As Long = 2 GetFileDateTime = FormatDate(FileDateTime(FileName))
Else
GetFileDateTime = vbNullString
'------------------------------------------------------------------------------' End If
' Windows API Function Declarations '
'------------------------------------------------------------------------------' End Function
Private Declare Function CloseHandle _ Public Function GetFileSize(ByVal FileName As String) As String
Lib "kernel32.dll" (ByVal hObject As Long) As Long
If FileExists(FileName) Then
Private Declare Function CreateFile _ GetFileSize = FormatSize(FileLen(FileName))
Lib "kernel32.dll" Alias "CreateFileA" ( _ Else
ByVal lpFileName As String, _ GetFileSize = vbNullString
ByVal dwDesiredAccess As Long, _ End If
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _ End Function
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long _ Public Function GetPathFileName(ByVal Path As String) As String
) As Long Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Private Declare Function SetFilePointer _ GetPathFileName = FSO.GetFileName(Path)
Lib "kernel32.dll" ( _ End Function
ByVal iFileHandler As Long, _
ByVal lDistanceToMove As Long, _
ByRef lpDistanceToMoveHigh As Long, _ Public Function GetPathFolderName(ByVal Path As String) As String
ByVal dwMoveMethod As Long _ Dim FSO As Object
) As Long Set FSO = CreateObject("Scripting.FileSystemObject")
GetPathFolderName = FSO.GetParentFolderName(Path)
Public Function SetFileDate(ByVal FileName As String, _ Private m_Stream() As Byte '[ bit stream array ]'
ByVal FileDate As Date) As Boolean Private m_Length As Long '[ bit stream length ]'
End If
Public Sub InsertBitsAtEnd(ByVal Bits As Long, ByVal BitCount As Long)
FileError:
WipeFile = False If BitCount > 32 Then BitCount = 32
'------------------------------------------------------------------------------'
Public Sub InsertByteAtTop(ByVal Bits As Byte) ' Windows API Constants '
'------------------------------------------------------------------------------'
'[ resize stream ]'
ReDim Preserve m_Stream(1 To (m_Length + 8))
'[ constant for "CreateDIBitmap" function "BitFlags" parameter ]'
'[ insert bits in the stream ]' Private Const CBM_INIT As Long = &H4
m_Stream(m_Length + 8) = (Bits And &H80) \ &H80
m_Stream(m_Length + 7) = (Bits And &H40) \ &H40 '[ color representation ]'
m_Stream(m_Length + 6) = (Bits And &H20) \ &H20 Private Const DIB_RGB_COLORS As Long = 0 '[ colors as rgb components ]'
m_Stream(m_Length + 5) = (Bits And &H10) \ &H10 Private Const DIB_PAL_COLORS As Long = 1 '[ colors as palette indexes ]'
m_Stream(m_Length + 4) = (Bits And &H8) \ &H8
m_Stream(m_Length + 3) = (Bits And &H4) \ &H4
m_Stream(m_Length + 2) = (Bits And &H2) \ &H2 '------------------------------------------------------------------------------'
m_Stream(m_Length + 1) = Bits And &H1 ' FreeImage Constants '
'------------------------------------------------------------------------------'
'[ increment length ]'
m_Length = m_Length + 8
'[ default load/save flags ]'
End Sub Private Const BMP_DEFAULT As Long = 0
Private Const CUT_DEFAULT As Long = 0
Private Const ICO_DEFAULT As Long = 0
Public Sub InsertStringAtEnd(ByVal Bits As String) Private Const IFF_DEFAULT As Long = 0
Dim Ctr As Long Private Const JPEG_DEFAULT As Long = 0
For Ctr = 1 To Len(Bits) Private Const KOALA_DEFAULT As Long = 0
InsertByteAtEnd Asc(Mid$(Bits, Ctr, 1)) Private Const LBM_DEFAULT As Long = 0
Next Ctr Private Const MNG_DEFAULT As Long = 0
End Sub Private Const PCD_DEFAULT As Long = 0
Private Const PCX_DEFAULT As Long = 0
Private Const PNG_DEFAULT As Long = 0
Public Sub InsertStringAtTop(ByVal Bits As String) Private Const PNM_DEFAULT As Long = 0
Dim Ctr As Long Private Const PSD_DEFAULT As Long = 0
For Ctr = Len(Bits) To 1 Step -1 Private Const RAS_DEFAULT As Long = 0
InsertByteAtTop Asc(Mid$(Bits, Ctr, 1)) Private Const TARGA_DEFAULT As Long = 0
Next Ctr Private Const TIFF_DEFAULT As Long = 0
End Sub Private Const WBMP_DEFAULT As Long = 0
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long '------------------------------------------------------------------------------'
' FreeImage Library Filetype Function Declarations '
Private Declare Function GetDesktopWindow Lib "user32" () As Long '------------------------------------------------------------------------------'
Option Explicit
Public Function LoadDIB(ByVal FileName As String, _
Optional ByRef FIF As Long) As Long
'------------------------------------------------------------------------------'
Dim Flg As Long ' Windows Timer Function Declarations '
'------------------------------------------------------------------------------'
'[ get format ]'
FIF = GetFileType(FileName)
Private Declare Function QueryPerformanceCounter _
'[ set load flags based on format ]' Lib "kernel32" (lpPerformanceCount As Currency) As Long
Select Case FIF
Case FIF_JPEG: Flg = JPEG_ACCURATE Private Declare Function QueryPerformanceFrequency _
Case FIF_PCD: Flg = PCD_BASE Lib "kernel32" (lpFrequency As Currency) As Long
Case FIF_PNG: Flg = PNG_IGNOREGAMMA
Case Else: Flg = 0
End Select '------------------------------------------------------------------------------'
' Private Variables '
'[ if format recognized, then load image ]' '------------------------------------------------------------------------------'
If FIF = FIF_UNKNOWN Then
LoadDIB = 0
Else Private m_Elapsed As Currency
LoadDIB = FreeImage_Load(FIF, FileName, Flg) Private m_Frequency As Currency
End If Private m_Start As Currency
Private m_Stop As Currency
End Function Private m_Supported As Boolean
Public Function SaveDIB(ByVal hDIB As Long, ByVal FileName As String, _ Public Property Get ElapsedTimeInSeconds() As String
ByVal FIF As FREE_IMAGE_FORMAT) As Long ElapsedTimeInSeconds = Format$((m_Elapsed \ 1000) Mod 100, "#00.") & _
Format$(m_Elapsed Mod 1000, "000")
Dim Flg As Long End Property
'[ set save flags ]'
Select Case FIF '------------------------------------------------------------------------------'
Case FIF_JPEG: Flg = JPEG_QUALITYSUPERB ' Public Procedures '
Case FIF_PBM: Flg = PNM_SAVE_RAW '------------------------------------------------------------------------------'
Case FIF_PGM: Flg = PNM_SAVE_RAW
Case FIF_PPM: Flg = PNM_SAVE_RAW
Case Else: Flg = 0 Public Sub Reset()
End Select QueryPerformanceCounter m_Start
End Sub
SaveDIB = FreeImage_Save(FIF, hDIB, FileName, Flg)
'------------------------------------------------------------------------------' '[ starting address for metadata (color channel, horizontal, vertical) ]'
' ' Private m_MetadataPosC As Long
' Requires: FreeImageWrapper.cls ' Private m_MetadataPosX As Long
' FreeImage.dll ' Private m_MetadataPosY As Long
' '
'------------------------------------------------------------------------------' '[ current pixel position pointers (color channel, horizontal, vertical) ]'
Private m_PosC As Long
Private m_PosX(0 To 2) As Long
'------------------------------------------------------------------------------' Private m_PosY(0 To 2) As Long
' '
' Metadata Format: ' '[ abort process flag ]'
' Password SHA Hash Value - String - 20 Bytes ' Private m_Abort As Boolean
' Data File Name - String - 255 Bytes '
' Data File Date & Time - FILETIME - 8 Bytes '
' Data File Size - Long - 4 Bytes ' '------------------------------------------------------------------------------'
' Data File MD5 Checksum - String - 16 Bytes ' ' Public Properties '
' ' '------------------------------------------------------------------------------'
'------------------------------------------------------------------------------'
Public Property Get DataFileChecksum() As String
Option Explicit DataFileChecksum = m_DataFileChecksum
End Property
'------------------------------------------------------------------------------'
' Windows API Structure Data Types ' Public Property Get DataFileDate() As Date
'------------------------------------------------------------------------------' DataFileDate = m_DataFileDate
End Property
'[ multiple-field time structure ]' Public Property Get DataFileSize() As Long
Private Type SYSTEMTIME DataFileSize = m_DataFileSize
wYear As Integer End Property
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer Public Property Get StegoImageDIB() As Long
wHour As Integer StegoImageDIB = m_StegoImageDIB
wMinute As Integer End Property
wSecond As Integer
wMilliseconds As Integer
End Type Public Property Get StoredPassword() As String
StoredPassword = m_StoredPassword
End Property
'------------------------------------------------------------------------------'
' Private Constants '
'------------------------------------------------------------------------------' '------------------------------------------------------------------------------'
' Private Properties '
'------------------------------------------------------------------------------'
'[ metadata bit length constants ]'
Private Const STOREDPASSWORD_BITS As Long = 160
Private Const FILENAME_BITS As Long = 2040 Private Property Get Pixel0() As Long
Private Const FILEDATEHIGH_BITS As Long = 32 '[ color value of current pixel ]'
Private Const FILEDATELOW_BITS As Long = 32 Pixel0 = m_ImageBits(m_PosC, m_PosX(m_PosC), m_PosY(m_PosC))
Private Const FILESIZE_BITS As Long = 32 End Property
Private Const FILECHECKSUM_BITS As Long = 128
Private Const METADATA_BITS As Long = STOREDPASSWORD_BITS + _
FILENAME_BITS + _ Private Property Get Pixel1() As Long
FILEDATEHIGH_BITS + _ '[ color value of middle-left pixel ]'
FILEDATELOW_BITS + _ Pixel1 = m_ImageBits(m_PosC, m_PosX(m_PosC) - 1, m_PosY(m_PosC))
FILESIZE_BITS + _ End Property
FILECHECKSUM_BITS
'[ metadata byte length constants ]' Private Property Get Pixel2() As Long
Private Const STOREDPASSWORD_BYTES As Long = 20 '[ color value of top-left pixel ]'
Private Const FILENAME_BYTES As Long = 255 Pixel2 = m_ImageBits(m_PosC, m_PosX(m_PosC) - 1, m_PosY(m_PosC) - 1)
Private Const FILEDATEHIGH_BYTES As Long = 4 End Property
Private Const FILEDATELOW_BYTES As Long = 4
Private Const FILESIZE_BYTES As Long = 4
Private Const FILECHECKSUM_BYTES As Long = 16 Private Property Get Pixel3() As Long
Private Const METADATA_BYTES As Long = STOREDPASSWORD_BYTES + _ '[ color value of top-middle pixel ]'
FILENAME_BYTES + _ Pixel3 = m_ImageBits(m_PosC, m_PosX(m_PosC), m_PosY(m_PosC) - 1)
FILEDATEHIGH_BYTES + _ End Property
FILEDATELOW_BYTES + _
FILESIZE_BYTES + _
FILECHECKSUM_BYTES Private Property Get Pixel4() As Long
'[ color value of top-right pixel ]'
'[ metadata progress report interval ]' Pixel4 = m_ImageBits(m_PosC, m_PosX(m_PosC) + 1, m_PosY(m_PosC) - 1)
Private Const METADATA_INTERVAL As Long = METADATA_BITS \ 100 End Property
'------------------------------------------------------------------------------' '------------------------------------------------------------------------------'
' Windows API Function Declarations ' ' Public Procedures '
'------------------------------------------------------------------------------' '------------------------------------------------------------------------------'
'[ compute highest address in image where metadata can fit ]'
'------------------------------------------------------------------------------' m_MetadataMaxPosX = m_ImageWidth - METADATA_BITS
' Private Variables ' m_MetadataMaxPosY = m_ImageHeight - 1
'------------------------------------------------------------------------------'
Do While (m_MetadataMaxPosX < 0)
'[ create temporary bitmap from the stego image dib ]' Rnd -1
hBmp = Imager.CreateBitmapFromDIB(m_StegoImageDIB) Randomize AscB(Left$(Seed, 1))
'[ copy bitmap bits to pixel array ]' For Ctr = 2 To Len(Seed)
'[ if current pixel is not at topmost row, then compute capacity ]' End Sub
If (m_PosY(m_PosC) > 0) Then
Private Sub SelectPixelChannel() Private Declare Function GetInputState Lib "user32" () As Long
'[ select random color channel ]' Private Declare Function SystemTimeToFileTime _
m_PosC = Int(Rnd * 3) Lib "kernel32" ( _
lpSystemTime As SYSTEMTIME, _
'[ if selected color channel is full, then select next ]' lpFileTime As FILETIME _
If m_PosY(m_PosC) >= m_ImageHeight Then ) As Long
m_PosC = (m_PosC + 1) Mod 3
If m_PosY(m_PosC) >= m_ImageHeight Then
m_PosC = (m_PosC + 1) Mod 3 '------------------------------------------------------------------------------'
End If ' Public Events '
End If '------------------------------------------------------------------------------'
End Sub
Public Event Progress(ByVal Processed As Long, ByVal Total As Long)
Private Sub SelectPixelCoordinate()
'[ current pixel position pointers (color channel, horizontal, vertical) ]' Private Property Get Pixel8() As Long
Private m_PosC As Long '[ color value of bottom-left pixel ]'
Private m_PosX(0 To 2) As Long Pixel8 = m_ImageBits(m_PosC, m_PosX(m_PosC) - 1, m_PosY(m_PosC) + 1)
Private m_PosY(0 To 2) As Long End Property
'------------------------------------------------------------------------------'
Public Property Get DataFileDate() As Date ' Public Procedures '
DataFileDate = m_DataFileDate '------------------------------------------------------------------------------'
End Property
'------------------------------------------------------------------------------' '[ compute highest address in image where metadata can fit ]'
' Private Properties ' m_MetadataMaxPosX = m_ImageWidth - METADATA_BITS
'------------------------------------------------------------------------------' m_MetadataMaxPosY = m_ImageHeight - 1
'[ create temporary bitmap from the cover image dib ]'
Private Property Get Pixel1() As Long hBmp = Imager.CreateBitmapFromDIB(m_CoverImageDIB)
'[ color value of middle-left pixel ]'
Pixel1 = m_ImageBits(m_PosC, m_PosX(m_PosC) - 1, m_PosY(m_PosC)) '[ copy bitmap bits to pixel array ]'
End Property Imager.GetBitmapPixels hBmp, m_ImageBits
SelectPixelCoordinate '[ if current pixel is not at topmost row, then compute capacity ]'
If (m_PosY(m_PosC) > 0) Then
If (PixelCtr Mod Interval) = 0 Then
'[ report progress ]' Dim Dif1 As Long
RaiseEvent Progress(PixelCtr, m_ImagePixels) Dim Dif2 As Long
DoEvents Dim Dif3 As Long
ElseIf GetInputState() Then Dim Dif4 As Long
'[ allow other processes to execute ]' Dim Avg As Long
DoEvents
End If '[ get difference of adjacent pairs formed by pixel1, pixel2, & pixel3 ]'
If m_PosX(m_PosC) > 0 Then
Next PixelCtr Dif1 = Abs(Pixel3 - Pixel1)
Dif2 = Abs(Pixel1 - Pixel2)
'[ report completion ]' Dif3 = Abs(Pixel2 - Pixel3)
If (PixelCtr Mod Interval) > 0 Then Else
RaiseEvent Progress(PixelCtr, m_ImagePixels) Dif1 = 0
End If Dif2 = 0
Dif3 = 0
'[ update temporary bitmap with contents of pixel array ]' End If
Imager.SetBitmapPixels hBmp, m_ImageBits
'[ get difference of pixel3 and pixel4 ]'
'[ create stego dib from temporary bitmap ]' If m_PosX(m_PosC) < (m_ImageWidth - 1) Then
m_StegoImageDIB = Imager.CreateDIBFromBitmap(hBmp) Dif4 = Abs(Pixel3 - Pixel4)
Else
'[ compute net image capacity in bytes ]' Dif4 = 0
m_ImageCapacity = (EmbedCtr - METADATA_BITS) \ 8 End If
'[ return whether all data file bits have been embedded ]' '[ compute average difference in color intensity ]'
Encode = (m_ImageCapacity >= m_DataFileSize) Avg = Round((Dif1 + Dif2 + Dif3 + Dif4) / 4)
Cleanup: '[ if c would not be equal to 0, compute hiding capacity of pixel ]'
If Avg > 1 Then
'[ unload temporary bitmap ]'
Imager.DeleteBitmap hBmp Dim C As Long
Erase m_ImageBits Dim U As Long
End Function '[ compute capacity (base-2 logarithm of average difference) ]'
C = Int(Log(Avg) / Log(2))
'[ create new dib for cover image ]' '[ compute upper boundary ]'
m_CoverImageDIB = Imager.LoadDIB(FileName) If Pixel0 > 191 Then
U = 5
If m_CoverImageDIB = 0 Then Else
LoadCoverImage = False U = 4
Else End If
'[ get cover image dimensions ]'
m_ImageWidth = Imager.GetWidth(m_CoverImageDIB) '[ limit capacity by the upper boundary ]'
m_ImageHeight = Imager.GetHeight(m_CoverImageDIB) If C < U Then
m_ImagePixels = m_ImageWidth * m_ImageHeight * 3 Capacity = C
LoadCoverImage = (m_ImagePixels > METADATA_BITS) Else
End If Capacity = U
End If
m_ImageCapacity = 0
Else
End Function
Capacity = C
'[ get data file checksum ]' '[ if pixel in color channel of reserved area for metadata ]'
m_DataFileChecksum = Checksum If m_PosC = m_MetadataPosC Then
Capacity = 0
'------------------------------------------------------------------------------'
End If ' '
' Chameleon Image Steganography v1.2 '
End If ' '
' Custom Button User Control '
End Sub ' [CustomButton] '
' '
'------------------------------------------------------------------------------'
Private Sub PerformErrorDiffusion(ByVal EmbeddingError As Long) ' '
' Copyright (C) 2003 Mark David Gan '
If EmbeddingError > 3 Then ' '
'------------------------------------------------------------------------------'
Dim E As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
'------------------------------------------------------------------------------'
Private Declare Function DeleteObject _ ' Public Properties '
Lib "gdi32" (ByVal hObject As Long) As Long '------------------------------------------------------------------------------'
Friend Property Get State() As cbtnStateConstants '[ restore original font ]'
State = m_State SelectObject hDC, hOldFont
End Property DeleteObject hNewFont
End Sub
Friend Property Let State(ByVal New_State As cbtnStateConstants)
If m_State <> New_State Then
m_State = New_State Private Sub DrawFocusRectangle(ByVal hDC As Long, ByRef tRect As RECT)
PaintControl Dim tRect2 As RECT
End If tRect2.Left = tRect.Left + 3
End Property tRect2.top = tRect.top + 3
tRect2.Right = tRect.Right - 4
tRect2.Bottom = tRect.Bottom - 4
'------------------------------------------------------------------------------' SetTextColor hDC, vbBlack
' Public Procedures ' DrawFocusRect hDC, tRect2
'------------------------------------------------------------------------------' End Sub
'[ get mask color ]' Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
If m_MaskStyle = cbtnMaskStyleAuto Then
MaskClr = GetPixel(ByVal hDCTmp, 0, 0) If (KeyCode = vbKeyReturn) Then
Else '[ simulate click ]'
MaskClr = TranslateColor(UserControl.MaskColor) RaiseEvent Click
End If ElseIf (KeyCode = vbKeySpace) And (m_State <> cbtnStatePressed) Then
'[ simulate mouse down ]'
BackClr = TranslateColor(UserControl.BackColor) m_State = cbtnStatePressed
PaintControl
'[ clear transparent pixels of picture ]' ElseIf (KeyCode = vbKeyDown) Or (KeyCode = vbKeyRight) Then
For Ctr2 = 0 To PicHeight '[ select next control ]'
For Ctr1 = 0 To PicWidth SendKeys "{TAB}", True
If GetPixel(ByVal hDCTmp, Ctr1, Ctr2) = MaskClr Then ElseIf (KeyCode = vbKeyUp) Or (KeyCode = vbKeyLeft) Then
SetPixel hDCTmp, Ctr1, Ctr2, BackClr '[ select previous control ]'
End If SendKeys "+{TAB}", True
Next Ctr1 End If
Next Ctr2
RaiseEvent KeyDown(KeyCode, Shift)
End If
End Sub
'[ paint picture ]'
BitBlt hDC, X, Y, PicWidth, PicHeight, hDCTmp, 0, 0, vbSrcCopy
Private Sub UserControl_KeyUp(KeyCode As Integer, Shift As Integer)
'[ cleanup temporary dc ]'
DeleteDC hDCTmp If (KeyCode = vbKeySpace) And (m_State = cbtnStatePressed) Then
'[ simulate click ]'
End If m_State = cbtnStateNormal
PaintControl
End Sub RaiseEvent Click
End If
End With
End Sub
[FAST2002] Fast Search & Transfer, Inc. (2002). ‘About Us’, AlltheWeb.
http://www.alltheweb.com/about/index.html .
[KWAN2001] M Kwan (2001). ‘How SNOW Works’, The SNOW Home Page.
http://www.darkside.com.au/snow/description.html .
[LIN1999] E Lin & E Delp (1999). ‘A Review of Data Hiding in Digital Images’,
Video and Image Processing Laboratory, School of Electrical and
Computer Engineering, Purdue University.
[PAIZ1999] F J Paiz (1999). ‘Tartan Threads: A Method for the Real-time Digital
Recognition of Secure Documents in Ink Jet Printers’, Department of
Electrical Engineering and Computer Science, MIT.
[SCHM2001] G Schmid (2001). Report on the Existence of a Global System for the
Interception of Private and Commercial Communications, Session
Document: A5-0264/2001-Par1, Temporary Committee on the
ECHELON Interception System, European Parliament.
[STEG2002] Steganos GmbH (2002). ‘Steganos Security Suite 4’, Steganos GmbH
Home. http://www.steganos.com/en/sss/index.htm .
Awards 2002
2002
STI “INTO Programming” - Senior Level
Computer Programming Contest
· Third Place in National Competition
2001