BMP Steganography Hider
Hide text or files inside BMP images using pixel LSB, palette, or reserved field methods. Optional AES-256 encryption. Original, encoded and difference views after encoding.
Try the demo instantly
Loads a 50×50 gradient BMP — no upload needed
Drop a BMP here or click to browse
Uncompressed BMP (8, 24 or 32-bit) · Max 50 MB
Processed in your browser
Your BMP is read and changed in this browser tab. The file itself is never sent to a server.
How to Hide a Message in a BMP (5 steps)
- Upload an uncompressed BMP (8, 24 or 32-bit)
- Enter the secret text or upload a file to hide
- Choose a method: Pixel LSB, Palette LSB or Reserved Bytes
- Adjust the bit depth: more bits hold more data but change the image more
- Click Encode and download your stego BMP, then give the Extractor the same settings if they are not the defaults
BMP Steganography Methods — Algorithm Comparison
| Method | Technique | Capacity | Best For |
|---|---|---|---|
| Pixel LSB | Overwrites the lowest 1 to 8 bits of the selected colour bytes (R, G, B and, on 32-bit files, alpha), in file order or a seeded random order | width × height × channels × bits ÷ 8 bytes before headers: 180,000 bytes for 800 × 600, 24-bit, 1 bit, R+G+B | 24 or 32-bit BMPs and larger payloads |
| Palette LSB | Overwrites the lowest 1 to 4 bits of the blue, green and red bytes of each colour-table entry; pixels are not touched | Raw capacity = colours × 3 × bits ÷ 8: 96 bytes for 256 colours at 1 bit. Usable payload after the 4-byte length and 7-byte header: 85 bytes, or 37 with a password (48-byte AES overhead) | 8-bit indexed BMPs and short messages |
| Reserved Bytes | Stores a length byte and up to 3 data bytes in the two reserved fields of the 14-byte file header | 3 bytes, no encryption | Tiny payloads and CTF-style hints |
What Hiding Data in a BMP Actually Changes
A BMP stores an image almost raw: a 14-byte file header, a DIB header, an optional colour table (the palette) and then the pixel bytes, row after row. There is no compression and no lossy step, so every byte you write stays exactly as written until someone re-saves the file. That is why BMP is a convenient carrier for least-significant-bit (LSB) steganography: changing the lowest bit of a colour byte moves that colour value by at most 1 out of 255.
This tool offers three places to put data. Pixel LSB rewrites the lowest bits of the colour bytes. Palette LSB rewrites the lowest bits of the colour table instead and leaves every pixel alone. Reserved Bytes uses four unused bytes in the file header. The output is always a .bmp file, and only this tool's Extractor reads the format it writes.
Which Method Should You Choose?
Use Pixel LSB for 24 or 32-bit BMPs. It holds by far the most data. On an 8-bit BMP it works differently: each pixel is one palette index, so flipping low bits of an index points the pixel at a different palette entry, and that can change the colour a lot if neighbouring entries are not similar. For 8-bit files, Palette LSB is usually the gentler choice if the message is short.
Use Palette LSB when the BMP has a colour table of at least 16 entries (an 8-bit BMP, typically 256). It cannot hold much (see the capacity table below). Use Reserved Bytes only for three-byte hints; it is visible in any hex editor.
Capacity: What Fits in a BMP
Capacity depends on the image's dimensions and your settings, not on the file size in megabytes. For Pixel LSB the raw capacity is the number of pixels × the number of channels used × the bit depth ÷ 8, rounded down: an 800 × 600 24-bit image at 1 bit on R, G and B is 800 × 600 × 3 × 1 ÷ 8 = 180,000 bytes. The tool then subtracts what it has to add. The stream that is written is a 4-byte length, a header (13 bytes for Pixel LSB, 7 for Palette LSB, 9 and 3 in the headerless plausible-deniability mode), and your payload. A password adds 48 bytes, and a Reed-Solomon level (RS-10, RS-20 or RS-30) adds a 16-byte header plus 10, 20 or 30 parity bytes per 223-byte block. Reserved Bytes is a separate layout with none of this (see the section on the two layouts).
The capacity bar and the Encode button use the same function the encoder enforces, so a payload that fits the bar is a payload that is written in full. The check is covered by a boundary test for every combination of method, bit depth, password and deniability setting. Sizes on this page are in bytes. Some capacity displays in the tool divide by 1,024 and label the result KB.
What Gets Written: Two Different Layouts
Pixel LSB and Palette LSB share one layout: a 4-byte little-endian length, then a header that starts with the signature STGB (53 54 47 42), then your payload. The header records the method, the bit depth, whether the payload is encrypted and, for Pixel LSB, which channels, which pixel order and which seed were used.
Reserved Bytes does not follow that layout at all. It has no 4-byte length, no STGB signature, no header, no encryption and no Reed-Solomon, so it can be read by anyone who knows where to look. The BMP file header has four reserved bytes at positions 6 to 9: byte 6 holds the length (1 to 3) and bytes 7, 8 and 9 hold the data, which is why the limit is 3 bytes. Because it has no signature, the Extractor cannot tell a Reserved Bytes message from a BMP whose reserved bytes were set for another reason.
Byte Order: Where the Bits Go
BMP pixel data is physically stored in B, G, R (and A) byte order, one pixel after another, row by row, and a bottom-up BMP stores its bottom row first. This tool visits pixels in the order you chose (see below). Inside each pixel it takes the bits from the channels you selected in a fixed order, R then G then B then Alpha, so the Hider and the Extractor agree however you ticked the boxes. Bits are written lowest-first: the payload's first bit goes into the lowest bit of the first byte it uses.
Palette LSB is different: it walks the colour table entry by entry and uses each entry's stored bytes in file order, blue, then green, then red (the fourth byte of each entry is left alone). The 8-bit "Pixel LSB" variant has no channels at all: it changes the low bits of each pixel's single palette-index byte.
Bit Depth, Channels and Pixel Order
Bit depth is how many low bits of each colour byte are overwritten: 1, 2, 3, 4, 6 or 8 for Pixel LSB and 1 to 4 for Palette LSB. Each extra bit doubles the capacity and raises the maximum change per colour value (1 bit: up to 1 out of 255, 2 bits: up to 3, 4 bits: up to 15, 8 bits: the whole byte). Channels (Standard and Forensic mode) choose which of R, G, B and, on 32-bit files, Alpha receive bits. Pixel order is Sequential, which fills the image from the first pixel in file order, or Random (seeded), which visits pixels in a shuffled order driven by a seed number.
Important for extraction: the Extractor cannot discover these settings from the file. Its Auto-Detect tries R+G+B in sequential order at 1, 2, 3 and 4 bits for Pixel LSB. Data hidden at 6 or 8 bits, on a different channel selection, or in random order needs the exact same bit depth, channels, pixel order and seed entered in the Extractor's Forensic mode. The seed is stored in the file's header, but the header can only be read once the bits are found, so it cannot be recovered from the file. Write the seed down. The "Smallest change" quick preset uses 1 bit with a random seed, so it always needs this.
How Strong Is the Encryption?
If you set a password, the payload is encrypted before it is hidden, in your browser, with the Web Crypto API. The cipher is AES-256 in GCM mode. The 256-bit key is derived from your password with PBKDF2 using HMAC-SHA-256 and 100,000 iterations. Every encryption generates a fresh random 16-byte salt and a fresh random 12-byte IV with the browser's cryptographically secure random generator (crypto.getRandomValues), so encrypting the same message twice gives different bytes. The stored layout is a 4-byte length, the salt, the IV, then the ciphertext followed by a 16-byte authentication tag — 48 bytes added in total. The tag means a wrong password, or any change to the ciphertext, makes decryption fail instead of returning garbage.
These are standard, widely used primitives, but this implementation has not had an independent audit, and using them does not make the system unbreakable. Their limits matter. Anyone who finds the hidden data can copy the ciphertext and try passwords against it offline, so your password is the real defence: use a long random passphrase, not a word or a date. The key derivation uses 100,000 PBKDF2-HMAC-SHA-256 iterations. This is below OWASP's commonly cited 600,000-iteration recommendation for password storage. That recommendation is not a direct requirement for this file-encryption format, but the lower count makes a strong, unique password especially important. The iteration count is fixed in the code and is not written into the file, so raising it requires a new, versioned format so that files already made can still be opened.
Encryption hides what the data says, not that it exists, and it does not hide the length: AES-GCM ciphertext is the same length as the plaintext, and the encrypted layout adds a fixed 48 bytes of overhead: its own 4-byte length field, the salt, the IV and the authentication tag, separate from any length or framing the tool puts around it. No padding is added. There is no key file and no way to recover a forgotten password. We have not measured key-derivation time on phones or older devices, so very large batches or slow devices may take noticeably longer per file. The tool is intended for educational and personal use; do not rely on it as the only protection for sensitive information, or against a well-resourced adversary.
Reed-Solomon Error Correction (RS-10, RS-20, RS-30)
Forensic mode offers three Reed-Solomon levels. The payload is split into blocks of up to 223 bytes and each block gets 10, 20 or 30 parity bytes, which lets the Extractor repair up to 5, 10 or 15 changed bytes per block, wherever they are. This is a real Reed-Solomon code over GF(2^8). The cost is 16 bytes of header plus the parity: for example a 1,000-byte payload grows by 16 + 5 × 10 = 66 bytes at RS-10.
Know its limits. It repairs a few scattered changed bytes, for example after someone edits a handful of pixels. It does not rescue a BMP that was re-encoded (JPEG changes almost every pixel), and damage beyond the limit in a block is reported as uncorrectable. Damage beyond the limit is usually reported as uncorrectable (in our test with 9 changed bytes against a limit of 5, at least 97% of trials were), but it can occasionally look like a different valid block and yield wrong bytes; a password protects against that, because the AES tag then fails. The 16-byte header is stored with the length twice but is not itself error-corrected.
Plausible Deniability: Two Different Options
Forensic mode has two separate features with similar names. The "Plausible deniability" switch only leaves out the 4-byte STGB signature from the header, so the Extractor's Auto-Detect cannot find the payload; you must use the Extractor's headerless option with the exact method and settings. It does not hide the change from statistical tests, and it should be used with a password.
"Plausible Deniability: Decoy + Real Payload" is a different, self-contained mode: a decoy message goes in the red channel's lowest bits with no password, and your real payload goes in the green channel's lowest bits, encrypted with AES-256-GCM. Anyone who opens the file without the passphrase can read the decoy only; with the passphrase, the Extractor also shows the real payload. It needs a 24 or 32-bit uncompressed BMP and rejects 8-bit files. Both layers are plain 1-bit substitution, so neither damages the other, and neither is hidden from steganalysis.
Batch Watermarking
After encoding, the result screen can generate one watermarked copy of your original BMP per recipient. Each copy gets a unique ID derived from a random master secret, the recipient and a hash of the document, written with 1-bit Pixel LSB on R, G, B in sequential order (the Extractor's Auto-Detect finds it). You download a ZIP of the copies and, separately, watermark_keys.json. Keep that file: it is the only link between an ID and a recipient. The Extractor's Watermark Attribution panel compares an extracted ID with it. This identifies the copy; it does not stop the BMP being re-saved, which removes the ID.
Capacity Examples (computed from the tool's own formulas)
Raw capacity is the number of bytes the carrier can hold at that setting. Usable is the largest payload that fits after the 4-byte length and the header (Pixel LSB 13 bytes, Palette LSB 7 bytes). Password adds 48 bytes. Computed with the same functions the encoder uses; no Reed-Solomon level, standard (non-deniability) header.
| Carrier and setting | Raw capacity | Usable, no password | Usable, with password |
|---|---|---|---|
| 800 × 600, 24-bit, Pixel LSB, 1 bit, R+G+B | 180,000 B | 179,983 B | 179,935 B |
| 800 × 600, 24-bit, Pixel LSB, 2 bits, R+G+B | 360,000 B | 359,983 B | 359,935 B |
| 800 × 600, 24-bit, Pixel LSB, 4 bits, R+G+B | 720,000 B | 719,983 B | 719,935 B |
| 1920 × 1080, 24-bit, Pixel LSB, 1 bit, R+G+B | 777,600 B | 777,583 B | 777,535 B |
| 800 × 600, 32-bit, Pixel LSB, 1 bit, R+G+B+A | 240,000 B | 239,983 B | 239,935 B |
| 256 × 256, 8-bit, Pixel LSB, 1 bit (palette indices) | 8,192 B | 8,175 B | 8,127 B |
| 256-colour palette, Palette LSB, 1 bit | 96 B | 85 B | 37 B |
| 256-colour palette, Palette LSB, 2 bits | 192 B | 181 B | 133 B |
| 256-colour palette, Palette LSB, 4 bits | 384 B | 373 B | 325 B |
| Reserved Bytes (any BMP) | 3 B | 3 B | not available |
Examples
Input: 800 × 600 24-bit BMP + the 10-character text FLAG{demo}, Pixel LSB, 1 bit, R+G+B, sequential, no password
Result: 4-byte length + 13-byte header + 10 bytes of text = 27 bytes = 216 bits, written 3 per pixel to the first 72 pixels of 480,000.
Meaning: The rest of the image is untouched and the Extractor's Auto-Detect reads it back. Source: arithmetic from the stream layout in bmpPipeline.ts and the 3-channels-per-pixel rule in bmpCore.ts.
Input: The same image and message with a password
Result: 10 bytes of text + 48 bytes of AES-256-GCM overhead = 58 bytes, plus 4 + 13 = 75 bytes = 600 bits, written to the first 200 pixels.
Meaning: The extracted bytes are random-looking until the correct password is supplied. Source: ENCRYPTION_OVERHEAD_BYTES (48) in lib/encryption.ts.
Input: A 256-colour 8-bit BMP and a 40-byte message with a password, Palette LSB at 1 bit
Result: Refused: the largest payload that fits is 37 bytes (96 − 4 − 7 − 48). The same message without a password fits (limit 85 bytes).
Meaning: Palette LSB is the smallest-capacity method that still encrypts. Raise the bit depth to 2 (limit 133 bytes with a password) or use Pixel LSB. Source: calculateBmpCapacity and maxBmpPayloadBytes, covered by the capacity-boundary unit tests.
Input: A 4-byte message with Reserved Bytes
Result: Refused with the message: "Reserved bytes method can only store up to 3 bytes".
Meaning: The method holds 3 data bytes because one of the four reserved bytes stores the length. Use Pixel LSB or Palette LSB. Source: the exact text is from bmpEncode.ts.
Input: The "Smallest change" quick preset: 1 bit, R+G+B, random order with seed 987654, then Auto-Detect in the Extractor
Result: Auto-Detect finds nothing. Entering Pixel LSB, 1 bit, Random (seeded) and seed 987654 in Forensic mode recovers the message; seed 987655 or Sequential order does not.
Meaning: Random order cannot be auto-detected, so keep the seed. Source: unit tests on a 64 × 64 test image that decode with the same seed, a neighbouring seed and sequential order; a Blue-only file likewise needs channels set to Blue.
Input: A 64 × 64 pseudo-random test image with a 19-byte message at 1, 2, 3 and 4 bits
Result: Auto-Detect recovered the message at every one of these depths. At 6 bits, Auto-Detect found nothing and a manual 6-bit decode recovered it.
Meaning: Auto-Detect covers 1 to 4 bits only. Source: the same unit-test fixture; this is a small synthetic image, so it checks the logic, not how a photo will look.
How the Hider Works
1. Parse
The 14-byte file header and the DIB header are read for the size, bit depth, compression, colour count and where the pixel data starts. Only uncompressed (BI_RGB) BMPs are accepted; a 32-bit BMP saved with colour masks (BI_BITFIELDS) is rejected.
2. Prepare the payload
Your text or file is optionally encrypted with AES-256-GCM, optionally wrapped in Reed-Solomon parity, and given a header (signature STGB, version, method, bit depth and flags) plus a 4-byte length.
3. Embed
Pixel LSB writes the bits into the chosen channel bytes of each pixel in file order or a seeded order. Palette LSB writes them into the colour-table entries. Reserved Bytes writes up to 3 bytes into the file header.
4. Measure
For Pixel LSB the tool compares the output with your original pixel by pixel and reports PSNR, a measured distortion figure. The result also has an Original / Encoded / Difference view.
5. Verify and download
Optional "Verify" decodes the output through the same worker the Extractor uses, with your channel, order and seed settings, and reports how many bytes came back. Then you download the .bmp.
What the results mean
| Result | What it means | What to do |
|---|---|---|
| PSNR: excellent (50 dB or more) | The output differs from the original by a very small amount | No action. Typical for short messages at 1 or 2 bits |
| PSNR: good (40 to 50 dB) | A small but measurable difference | Fine for most uses; check the difference view if it matters |
| PSNR: visible (under 40 dB) | The change is large enough to see | Lower the bit depth, shorten the message or use a larger image |
| Capacity used | Share of the carrier's raw capacity the stream occupies, including its headers | Above about 50% many more pixel values change; use a larger image if you can |
| Pixel order seed | Shown for random pixel order | Copy it. The Extractor needs the same seed in Forensic mode |
| Verify: bytes read back | The output decoded with the same settings; for an encrypted payload it only confirms the ciphertext is readable | Safe to download. A failure means the settings used to verify differ from those used to encode |
| Refused: exceeds capacity | The stream (length + header + payload + any password or Reed-Solomon parity) is bigger than the carrier can hold | Raise bit depth, add channels, use a bigger image, or shorten the payload |
Limitations
- Only uncompressed BMPs are accepted. RLE-compressed files and 32-bit BMPs saved with colour masks (BI_BITFIELDS) are rejected, and the message says the BMP is compressed. Re-save as a plain 24-bit BMP. Maximum file size is 50 MB.
- Re-encoding destroys Pixel LSB data. Converting to JPEG rewrites pixel values. Saving as PNG keeps them, but the Extractor only opens .bmp files. Services that recompress images may also change them; we have not tested individual services, so send the .bmp file itself.
- Auto-Detect in the Extractor is limited: Pixel LSB at 1 to 4 bits on R+G+B in sequential order, Palette LSB at 1 bit, and Reserved Bytes. Everything else needs matching settings in Forensic mode, and the Extractor reads only the format this tool writes (it does not read other tools' output).
- On 8-bit BMPs, Pixel LSB changes palette indices, which can shift a pixel to a visibly different colour. Palette LSB changes the table instead and is gentler for short messages. Palette LSB finds the colour table right after the DIB header, so it works with the standard 40-byte header and the longer V4 header (both are covered by tests); V5 and OS/2-style headers were not tested.
- LSB embedding is detectable by statistical tests (chi-square, RS analysis, sample pairs). A random pixel order and a low bit depth make it harder for a simple test, not impossible. PSNR measures how far the pixels moved; it is not a measure of how hard the file is to detect. Use the Steganography Detector to see what such tests report on your own file.
- Reed-Solomon repairs a few scattered changed bytes per block, not a re-encoded image. See the section above.
- How visible the change is depends on your image. The maximum change per colour value follows from the bit depth, but we have not published distortion measurements on real photographs; the result screen measures PSNR and shows a difference view for your own file.
- Steganography hides that a message exists; it is not a replacement for encryption. Without a password the payload is stored in plain form.
Privacy
Your BMP and payload are processed in your browser, in a Web Worker, and are not sent to a StegoToolkit upload server: the BMP code makes no network requests, and the encoded file is created on your device and downloaded from it. Page views are counted by Vercel Analytics, which is cookieless. Tool events (the options you choose such as the method, bit depth, channels, pixel order or error-correction level; the file's type and a size range; and how long processing took) are sent to Google Analytics only if the site has it configured and you accepted analytics cookies. The code does not pass the image, the payload, a password, the seed number or the filename to analytics, and a filter drops fields with names such as password, payload, data or filename before anything is sent. If you accept cookies, the site may also load advertising and other analytics scripts that are outside this tool's code. Your display mode preference is kept in this browser's localStorage.
Which Tool Next?
- BMP Steganography Extractor — Read back what you just hid
- LSB Embedder — LSB steganography on PNG and other images
- Palette Steganography Hider — Palette-based hiding for GIF, indexed PNG and 8-bit BMP
- PNG Steganography Hider — Hide data using PNG's own chunk structure
- Steganography Detector — Test whether an image shows signs of hidden data
Frequently Asked Questions
What's the difference between Pixel LSB and Palette LSB?
Pixel LSB changes the lowest bits of the colour bytes in the image data and holds the most. Palette LSB changes the lowest bits of the colour table (indexed BMPs only) and leaves every pixel alone, but holds little: 96 bytes of stream for a 256-colour palette at 1 bit.
Why can't I use Palette LSB with my image?
Palette LSB needs a colour table of at least 16 entries, which only 8-bit (and lower) BMPs have. 24-bit and 32-bit BMPs store colours directly in each pixel and have no palette.
What is bit depth and how does it affect capacity?
Bit depth is how many low bits of each colour byte are overwritten. Capacity grows in proportion: 2 bits holds twice as much as 1 bit. The maximum change to a colour value grows too: up to 1 at 1 bit, 3 at 2 bits, 15 at 4 bits. Pixel LSB offers 1, 2, 3, 4, 6 and 8; Palette LSB 1 to 4.
Will my image look different after encoding?
The change to each colour value is small at low bit depths (at most 1 at 1 bit, 3 at 2 bits, 15 at 4 bits), but whether you can see it depends on the image, and we have not measured it across a set of real photographs. The tool reports the measured PSNR and a difference view for your own file, so check those. Higher bit depths, longer payloads and flat-colour images show it more. On 8-bit BMPs, Pixel LSB changes palette indices and can change a colour noticeably.
Can this survive image compression or conversion?
No for JPEG: it rewrites pixel values. Saving as PNG keeps the pixel values, but the Extractor only opens .bmp files. We have not tested individual messaging services, so send the BMP as a file.
What's the Reserved Bytes method for?
It stores up to 3 bytes in the two reserved fields of the BMP file header (one more byte is spent on a length). It does not touch the image, but it cannot be encrypted and shows in any hex dump of the first 14 bytes. Good for CTF hints and tiny secrets.
Can the Extractor read every file the Hider makes?
Auto-Detect reads Pixel LSB at 1 to 4 bits on R+G+B in sequential order, Palette LSB at 1 bit and Reserved Bytes. If you used 6 or 8 bits, different channels, a random pixel order or a 2 to 4 bit Palette LSB, enter the same settings in the Extractor's Forensic mode. Keep the seed number if you used random order.
Does the Reed-Solomon option repair a damaged BMP?
Only lightly damaged data: RS-10, RS-20 and RS-30 repair up to 5, 10 or 15 changed bytes in each 223-byte block of the hidden payload. They do not survive converting the BMP to JPEG or any other re-encode, which changes almost every pixel.
Related Tools
BMP Steganography Extractor
Extract hidden data from BMP files made by the BMP Steganography Hider. Auto-detect covers pixel LSB (1 to 4 bits), palette LSB and reserved bytes; Forensic mode takes exact settings.
Image Steganography Hider
Hide secret messages or files inside PNG, BMP, WebP or JPEG images; the result is saved as PNG. Optional AES-256 encryption, processed in your browser.
Steganography Capacity Calculator
Calculate exact hidden-payload capacity for PNG, BMP, WebP, WAV, FLAC, AIFF and more — plus PSNR and detection risk, all client-side. Links to the right specialist hider/extractor for your file type.