![]() |
|
#1
|
|||
|
|||
|
EXE to JPEG obfuscator
This is my MSVC source code that has never been posted anywhere else before.
It is a C code for a binary to JPEG file converter/obfuscator. I find it useful to smuggle binary files (especially executable files) to various AI LLMs that do not allow binary file uploads but have no problem with uploads of JPEG files. This is also useful for smuggling executable files past restrictive email attachment scanners. After an executable file is obfuscated as a valid JPEG file and uploaded, it can get past the filters implemented by the control-freaks, tyrants and paranoid admins. Next, the AI LLM can be instructed to convert it back into an executable file, for e.g.: binary analysis and disassembly, which some AI LLMs are quite adept at. Code:
#include stdio.h //missing angle brackets (because exetools strips them)
#include stdlib.h //missing angle brackets (because exetools strips them)
#include string.h //missing angle brackets (because exetools strips them)
#include math.h //missing angle brackets (because exetools strips them)
#include tchar.h //missing angle brackets (because exetools strips them)
#include windows.h //missing angle brackets (because exetools strips them)
#define JPEG_SOI 0xFFD8
#define JPEG_APP0 0xFFE0
#define JPEG_DQT 0xFFDB
#define JPEG_SOF0 0xFFC0
#define JPEG_DHT 0xFFC4
#define JPEG_SOS 0xFFDA
#define JPEG_EOI 0xFFD9
// Hidden flag values in JFIF thumbnail width
#define FLAG_STANDARD 0 // Byte stuffing applied
#define FLAG_RAW 1 // Raw mode, no byte stuffing
typedef struct {
HANDLE hFile;
DWORD dwPos;
} FileContext;
BOOL WriteBytes(FileContext* ctx, const void* data, DWORD size) {
DWORD written;
if (!WriteFile(ctx->hFile, data, size, &written, NULL) || written != size) {
return FALSE;
}
ctx->dwPos += written;
return TRUE;
}
BOOL WriteByte(FileContext* ctx, unsigned char value) {
return WriteBytes(ctx, &value, 1);
}
BOOL WriteBE16(FileContext* ctx, unsigned short value) {
unsigned char bytes[2] = { (value >> 8) & 0xFF, value & 0xFF };
return WriteBytes(ctx, bytes, 2);
}
BOOL WriteJFIFHeader(FileContext* ctx, int mode_flag) {
if (!WriteBE16(ctx, JPEG_APP0)) return FALSE;
if (!WriteBE16(ctx, 16)) return FALSE;
if (!WriteBytes(ctx, "JFIF\0", 5)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE; // Version 1
if (!WriteByte(ctx, 1)) return FALSE; // Version 1
if (!WriteByte(ctx, 1)) return FALSE; // Density units (dots per inch)
if (!WriteBE16(ctx, 96)) return FALSE; // X density 96 DPI
if (!WriteBE16(ctx, 96)) return FALSE; // Y density 96 DPI
if (!WriteByte(ctx, mode_flag)) return FALSE; // Thumbnail width - HIDDEN FLAG
if (!WriteByte(ctx, 0)) return FALSE; // Thumbnail height
return TRUE;
}
BOOL WriteDQT(FileContext* ctx) {
unsigned char lum_table[64] = {
2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5,
3, 3, 3, 3, 3, 6, 4, 4, 3, 5, 7, 6, 7, 7, 7, 6,
7, 7, 8, 9, 11, 9, 8, 8, 10, 8, 7, 7, 10, 13, 10, 10,
11, 12, 12, 12, 12, 7, 9, 14, 15, 13, 12, 14, 11, 12, 12, 12
};
unsigned char chr_table[64] = {
2, 2, 2, 3, 3, 3, 6, 3, 3, 6, 12, 8, 7, 8, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
};
// Luminance
if (!WriteBE16(ctx, JPEG_DQT)) return FALSE;
if (!WriteBE16(ctx, 67)) return FALSE;
if (!WriteByte(ctx, 0)) return FALSE;
if (!WriteBytes(ctx, lum_table, 64)) return FALSE;
// Chrominance
if (!WriteBE16(ctx, JPEG_DQT)) return FALSE;
if (!WriteBE16(ctx, 67)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE;
if (!WriteBytes(ctx, chr_table, 64)) return FALSE;
return TRUE;
}
BOOL WriteSOF0(FileContext* ctx, int width, int height) {
if (!WriteBE16(ctx, JPEG_SOF0)) return FALSE;
if (!WriteBE16(ctx, 17)) return FALSE;
if (!WriteByte(ctx, 8)) return FALSE;
if (!WriteBE16(ctx, height)) return FALSE;
if (!WriteBE16(ctx, width)) return FALSE;
if (!WriteByte(ctx, 3)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE;
if (!WriteByte(ctx, 0x22)) return FALSE;
if (!WriteByte(ctx, 0)) return FALSE; // Y: 2x2 subsampling
if (!WriteByte(ctx, 2)) return FALSE;
if (!WriteByte(ctx, 0x11)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE; // Cb: 1x1 subsampling
if (!WriteByte(ctx, 3)) return FALSE;
if (!WriteByte(ctx, 0x11)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE; // Cr: 1x1 subsampling
return TRUE;
}
BOOL WriteDHT_DC(FileContext* ctx, int table_id) {
unsigned char bits[16] = { 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
unsigned char vals[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
if (!WriteBE16(ctx, JPEG_DHT)) return FALSE;
if (!WriteBE16(ctx, 31)) return FALSE;
if (!WriteByte(ctx, table_id)) return FALSE;
if (!WriteBytes(ctx, bits, 16)) return FALSE;
if (!WriteBytes(ctx, vals, 12)) return FALSE;
return TRUE;
}
BOOL WriteDHT_AC_Lum(FileContext* ctx) {
unsigned char bits[16] = { 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
unsigned char vals[] = {
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
if (!WriteBE16(ctx, JPEG_DHT)) return FALSE;
if (!WriteBE16(ctx, 181)) return FALSE;
if (!WriteByte(ctx, 0x10)) return FALSE; // AC table 0
if (!WriteBytes(ctx, bits, 16)) return FALSE;
if (!WriteBytes(ctx, vals, 162)) return FALSE;
return TRUE;
}
BOOL WriteDHT_AC_Chr(FileContext* ctx) {
unsigned char bits[16] = { 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
unsigned char vals[] = {
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa
};
if (!WriteBE16(ctx, JPEG_DHT)) return FALSE;
if (!WriteBE16(ctx, 181)) return FALSE;
if (!WriteByte(ctx, 0x11)) return FALSE; // AC table 1
if (!WriteBytes(ctx, bits, 16)) return FALSE;
if (!WriteBytes(ctx, vals, 162)) return FALSE;
return TRUE;
}
BOOL WriteSOS(FileContext* ctx) {
if (!WriteBE16(ctx, JPEG_SOS)) return FALSE;
if (!WriteBE16(ctx, 12)) return FALSE;
if (!WriteByte(ctx, 3)) return FALSE;
if (!WriteByte(ctx, 1)) return FALSE;
if (!WriteByte(ctx, 0x00)) return FALSE;
if (!WriteByte(ctx, 2)) return FALSE;
if (!WriteByte(ctx, 0x11)) return FALSE;
if (!WriteByte(ctx, 3)) return FALSE;
if (!WriteByte(ctx, 0x11)) return FALSE;
if (!WriteByte(ctx, 0)) return FALSE;
if (!WriteByte(ctx, 63)) return FALSE;
if (!WriteByte(ctx, 0)) return FALSE;
return TRUE;
}
DWORD WriteStuffedData(FileContext* ctx, unsigned char* data, DWORD size) {
DWORD stuffed_size = 0;
for (DWORD i = 0; i < size; i++) {
if (!WriteByte(ctx, data[i])) return 0;
stuffed_size++;
if (data[i] == 0xFF) {
if (!WriteByte(ctx, 0x00)) return 0;
stuffed_size++;
}
}
return stuffed_size;
}
void ReverseBuffer(unsigned char* buf, DWORD len) {
unsigned char* left = buf;
unsigned char* right = buf + len - 1;
while (left < right) {
*left ^= *right;
*right ^= *left;
*left ^= *right;
left++;
right--;
}
}
BOOL ReadBE16(HANDLE hFile, unsigned short* value) {
unsigned char bytes[2];
DWORD read;
if (!ReadFile(hFile, bytes, 2, &read, NULL) || read != 2) {
return FALSE;
}
*value = (bytes[0] << 8) | bytes[1];
return TRUE;
}
BOOL FindJFIFFlag(HANDLE hFile, int* flag, int force_raw) {
LARGE_INTEGER pos;
pos.QuadPart = 0;
SetFilePointerEx(hFile, pos, NULL, FILE_BEGIN);
unsigned short marker;
if (!ReadBE16(hFile, &marker) || marker != JPEG_SOI) {
return FALSE;
}
if (!ReadBE16(hFile, &marker) || marker != JPEG_APP0) {
return FALSE;
}
unsigned short length;
if (!ReadBE16(hFile, &length)) {
return FALSE;
}
char jfif[5];
DWORD read;
if (!ReadFile(hFile, jfif, 5, &read, NULL) || read != 5) {
return FALSE;
}
if (memcmp(jfif, "JFIF\0", 5) != 0) {
return FALSE;
}
// Skip version (2 bytes), units (1 byte), densities (4 bytes)
pos.QuadPart = 7;
SetFilePointerEx(hFile, pos, NULL, FILE_CURRENT);
unsigned char thumbnail_width;
if (!ReadFile(hFile, &thumbnail_width, 1, &read, NULL) || read != 1) {
return FALSE;
}
if (force_raw) {
*flag = FLAG_RAW;
}
else {
*flag = (thumbnail_width == FLAG_RAW) ? FLAG_RAW : FLAG_STANDARD;
}
return TRUE;
}
BOOL FindSOSOffset(HANDLE hFile, DWORD* sos_data_offset) {
LARGE_INTEGER pos;
pos.QuadPart = 2; // Skip SOI
SetFilePointerEx(hFile, pos, NULL, FILE_BEGIN);
unsigned short marker, length;
while (ReadBE16(hFile, &marker)) {
if (marker == JPEG_SOS) {
if (!ReadBE16(hFile, &length)) return FALSE;
// Skip SOS header
pos.QuadPart = length - 2;
SetFilePointerEx(hFile, pos, NULL, FILE_CURRENT);
// Current position is start of scan data
pos.QuadPart = 0;
SetFilePointerEx(hFile, pos, &pos, FILE_CURRENT);
*sos_data_offset = (DWORD)pos.QuadPart;
return TRUE;
}
if (marker == JPEG_EOI || marker < 0xFF00) {
return FALSE;
}
if (!ReadBE16(hFile, &length)) return FALSE;
pos.QuadPart = length - 2;
SetFilePointerEx(hFile, pos, NULL, FILE_CURRENT);
}
return FALSE;
}
BOOL FindEOIOffset(HANDLE hFile, DWORD* eoi_offset) {
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize == INVALID_FILE_SIZE) return FALSE;
if (fileSize < 2) return FALSE;
LARGE_INTEGER pos;
pos.QuadPart = fileSize - 2;
SetFilePointerEx(hFile, pos, NULL, FILE_BEGIN);
unsigned short marker;
if (!ReadBE16(hFile, &marker) || marker != JPEG_EOI) {
return FALSE;
}
*eoi_offset = fileSize - 2;
return TRUE;
}
DWORD UnstuffData(unsigned char* stuffed, DWORD stuffed_size, unsigned char** unstuffed) {
*unstuffed = (unsigned char*)malloc(stuffed_size);
if (*unstuffed == NULL) return 0;
DWORD out_pos = 0;
for (DWORD i = 0; i < stuffed_size; i++) {
(*unstuffed)[out_pos++] = stuffed[i];
if (stuffed[i] == 0xFF && i + 1 < stuffed_size && stuffed[i + 1] == 0x00) {
i++; // Skip the 0x00
}
}
return out_pos;
}
int UnpackJPEG(LPCTSTR input_file, LPCTSTR output_file, int force_raw) {
HANDLE hInput = CreateFile(input_file, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hInput == INVALID_HANDLE_VALUE) {
_tprintf(_T("Error: Could not open input file '%s'\n"), input_file);
return 1;
}
int mode_flag;
if (!FindJFIFFlag(hInput, &mode_flag, force_raw)) {
_tprintf(_T("Error: Invalid JPEG file or missing JFIF header\n"));
CloseHandle(hInput);
return 1;
}
DWORD sos_offset, eoi_offset;
if (!FindSOSOffset(hInput, &sos_offset)) {
_tprintf(_T("Error: Could not find SOS marker\n"));
CloseHandle(hInput);
return 1;
}
if (!FindEOIOffset(hInput, &eoi_offset)) {
_tprintf(_T("Error: Could not find EOI marker\n"));
CloseHandle(hInput);
return 1;
}
DWORD data_size = eoi_offset - sos_offset;
if (data_size == 0) {
_tprintf(_T("Error: No data found between SOS and EOI\n"));
CloseHandle(hInput);
return 1;
}
unsigned char* data_buffer = (unsigned char*)malloc(data_size);
if (data_buffer == NULL) {
_tprintf(_T("Error: Memory allocation failed\n"));
CloseHandle(hInput);
return 1;
}
LARGE_INTEGER pos;
pos.QuadPart = sos_offset;
SetFilePointerEx(hInput, pos, NULL, FILE_BEGIN);
DWORD bytes_read;
if (!ReadFile(hInput, data_buffer, data_size, &bytes_read, NULL) || bytes_read != data_size) {
_tprintf(_T("Error: Failed to read scan data\n"));
free(data_buffer);
CloseHandle(hInput);
return 1;
}
CloseHandle(hInput);
unsigned char* final_data;
DWORD final_size;
if (mode_flag == FLAG_RAW) {
final_data = data_buffer;
final_size = data_size;
_tprintf(_T("Mode detected: RAW (no byte stuffing)\n"));
}
else {
_tprintf(_T("Mode detected: STANDARD (with byte stuffing)\n"));
final_size = UnstuffData(data_buffer, data_size, &final_data);
free(data_buffer);
if (final_size == 0) {
_tprintf(_T("Error: Unstuffing failed\n"));
return 1;
}
}
// Decrypt
for (DWORD i = 0; i < final_size; i++) {
final_data[i] -= 0x55;
}
// Reverse
ReverseBuffer(final_data, final_size);
HANDLE hOutput = CreateFile(output_file, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOutput == INVALID_HANDLE_VALUE) {
_tprintf(_T("Error: Could not create output file '%s'\n"), output_file);
free(final_data);
return 1;
}
DWORD written;
if (!WriteFile(hOutput, final_data, final_size, &written, NULL) || written != final_size) {
_tprintf(_T("Error: Failed to write output file\n"));
CloseHandle(hOutput);
free(final_data);
return 1;
}
CloseHandle(hOutput);
free(final_data);
_tprintf(_T("Successfully extracted binary data to: %s\n"), output_file);
_tprintf(_T("Extracted data size: %lu bytes\n"), final_size);
_tprintf(_T("SOS data offset: 0x%08lX (%lu)\n"), sos_offset, sos_offset);
_tprintf(_T("EOI marker offset: 0x%08lX (%lu)\n"), eoi_offset, eoi_offset);
return 0;
}
int PackToJPEG(LPCTSTR input_file, LPCTSTR output_file, int use_raw_mode) {
HANDLE hInput = CreateFile(input_file, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hInput == INVALID_HANDLE_VALUE) {
_tprintf(_T("Error: Could not open input file '%s'\n"), input_file);
return 1;
}
DWORD input_size = GetFileSize(hInput, NULL);
if (input_size == INVALID_FILE_SIZE || input_size == 0) {
_tprintf(_T("Error: Invalid input file size\n"));
CloseHandle(hInput);
return 1;
}
unsigned char* buffer = (unsigned char*)malloc(input_size);
if (buffer == NULL) {
_tprintf(_T("Error: Memory allocation failed\n"));
CloseHandle(hInput);
return 1;
}
DWORD bytes_read;
if (!ReadFile(hInput, buffer, input_size, &bytes_read, NULL) || bytes_read != input_size) {
_tprintf(_T("Error: Failed to read input file\n"));
free(buffer);
CloseHandle(hInput);
return 1;
}
CloseHandle(hInput);
ReverseBuffer(buffer, bytes_read);
for (DWORD i = 0; i < input_size; i++) {
buffer[i] += 0x55; // Encrypt
}
int pixels = input_size * 2;
int width = (int)sqrt((double)pixels);
if (width < 64) width = 64;
width = (width + 15) & ~15;
int height = (pixels / width) + 16;
if (height < 64) height = 64;
height = (height + 15) & ~15;
HANDLE hOutput = CreateFile(output_file, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOutput == INVALID_HANDLE_VALUE) {
_tprintf(_T("Error: Could not create output file '%s'\n"), output_file);
free(buffer);
return 1;
}
FileContext ctx = { hOutput, 0 };
int mode_flag = use_raw_mode ? FLAG_RAW : FLAG_STANDARD;
if (!WriteBE16(&ctx, JPEG_SOI) ||
!WriteJFIFHeader(&ctx, mode_flag) ||
!WriteDQT(&ctx) ||
!WriteSOF0(&ctx, width, height) ||
!WriteDHT_DC(&ctx, 0) ||
!WriteDHT_AC_Lum(&ctx) ||
!WriteDHT_DC(&ctx, 1) ||
!WriteDHT_AC_Chr(&ctx) ||
!WriteSOS(&ctx)) {
_tprintf(_T("Error: Failed to write JPEG headers\n"));
CloseHandle(hOutput);
free(buffer);
return 1;
}
DWORD data_start = ctx.dwPos;
DWORD stuffed_size;
if (use_raw_mode) {
if (!WriteBytes(&ctx, buffer, input_size)) {
_tprintf(_T("Error: Failed to write raw data\n"));
CloseHandle(hOutput);
free(buffer);
return 1;
}
stuffed_size = input_size;
}
else {
stuffed_size = WriteStuffedData(&ctx, buffer, input_size);
if (stuffed_size == 0) {
_tprintf(_T("Error: Failed to write stuffed data\n"));
CloseHandle(hOutput);
free(buffer);
return 1;
}
}
DWORD data_end = ctx.dwPos;
if (!WriteBE16(&ctx, JPEG_EOI)) {
_tprintf(_T("Error: Failed to write EOI marker\n"));
CloseHandle(hOutput);
free(buffer);
return 1;
}
CloseHandle(hOutput);
free(buffer);
_tprintf(_T("Successfully created obfuscated JPEG file: %s\n"), output_file);
_tprintf(_T("Format: YCbCr 4:2:0 (matching MS Paint)\n"));
_tprintf(_T("Mode: %s\n"), use_raw_mode ? _T("RAW (no byte stuffing)") : _T("STANDARD (with byte stuffing)"));
_tprintf(_T("Hidden flag: %d (in JFIF thumbnail width)\n"), mode_flag);
_tprintf(_T("Image dimensions: %dx%d pixels\n"), width, height);
_tprintf(_T("Original data size: %lu bytes\n"), input_size);
_tprintf(_T("Written data size: %lu bytes\n"), stuffed_size);
_tprintf(_T("Binary data starts at offset: 0x%08lX (%lu)\n"), data_start, data_start);
_tprintf(_T("Last byte of binary data at offset: 0x%08lX (%lu)\n"), data_end, data_end);
}
void PrintUsage(LPCTSTR progName) {
_tprintf(_T("Usage: %s [-raw] [-unpack]
It obfuscates arbitrary binary data to look like a JPEG picture. The binary data is put where the image data normally would be. To defeat overeager signature scanners, the binary data is first rudimentarly encrypted by reversing the order of binary bytes and adding 0x55 to every byte (with wrap-around arithmetic). When the "-raw" switch is not specified on the command line, the binary data is also subjected to "byte stuffing" because in JPEG file format any 0xFF byte in the image data must be followed by 0x00 to distinguish it from JPEG markers (which start with 0xFF). At the end, this code outputs to the console the file offsets where the binary data starts and ends. The generated JPEG file is openable by MS-Paint and the currently generated structure of JPEG headers (and the values within them) deceives MS-Paint successfully. This relates to MS-Paint specific JPEG attributes, such as: JFIF version and density, quantization tables, chrominance subsampling, Huffman table structure, etc.. This code implements a hidden binary flag in the JPEG headers that does not perturb the validity of the JPEG file nor make it look unusual. This flag clandestinely denotes whether the binary data was encoded inside the JPEG file with "byte stuffing" or in the raw fashion. The unpack code performs the reverse operation, i.e.: it extracts the binary data embedded in such JPEG file. This extractor code is executed when an "-unpack" command line switch is found. This extractor code uses the hidden flag in JPEG headers to automatically determine whether the binary data has been encoded with "byte stuffing" or not, unless overridden by the "-raw" switch. The usage command line processing allows the "-raw" and "-unpack" switches to appear together at the beginning of the command line. The application uses WinAPI file processing functions like CreateFile(), ReadFile(), Writefile(), GetFileSize(), SetFilePointerEx(), etc... so Linux user must change them to Linux-specific file handling functions. A sample obfuscated JPEG file is attached. bbb.jpg Because most AI LLMs which can write code well, can also execute Python code in a sandbox, I have also created the following Python code that can deobfuscate the JPEG file and create an EXE file in the AI's sandbox file system ...which the AI can then read without restrictions. The Python code listed below is the full Python port of the -unpack path, including the -raw override switch. Reads the JFIF thumbnail-width byte at offset 18 to auto-detect mode, walks markers to find SOS scan data, slices to the trailing EOI, optionally reverses the FF 00 → FF stuffing, then runs the decrypt-and-reverse step. Code:
import struct
import sys
# JPEG markers
JPEG_SOI = 0xFFD8
JPEG_APP0 = 0xFFE0
JPEG_SOS = 0xFFDA
JPEG_EOI = 0xFFD9
# Hidden flag values stored in the JFIF thumbnail-width byte
FLAG_STANDARD = 0 # byte stuffing applied
FLAG_RAW = 1 # raw mode, no byte stuffing
# Decryption is a constant byte subtraction: build a 256-entry translation
# table once so .translate() can apply it in C-speed in a single pass.
_DECRYPT_TABLE = bytes((i - 0x55) & 0xFF for i in range(256))
def find_jfif_flag(data: bytes, force_raw: bool):
"""
Validate the SOI + APP0/JFIF header and return the mode flag stored in
the thumbnail-width byte. Returns None if the header is malformed.
JFIF layout (offsets from start of file):
0..1 SOI (FF D8)
2..3 APP0 (FF E0)
4..5 length
6..10 "JFIF\\0"
11..12 version
13 density units
14..15 X density
16..17 Y density
18 thumbnail width <-- hidden flag lives here
19 thumbnail height
"""
if len(data) < 20:
return None
if struct.unpack(">H", data[0:2])[0] != JPEG_SOI:
return None
if struct.unpack(">H", data[2:4])[0] != JPEG_APP0:
return None
if data[6:11] != b"JFIF\0":
return None
thumbnail_width = data[18]
if force_raw:
return FLAG_RAW
return FLAG_RAW if thumbnail_width == FLAG_RAW else FLAG_STANDARD
def find_sos_offset(data: bytes):
"""
Walk the marker chain starting just after SOI and return the byte offset
of the first byte of entropy-coded scan data (i.e. just past the SOS
segment header).
Each segment after a marker has a 2-byte big-endian length that includes
its own size, so advancing `pos` by `length` skips the entire segment.
"""
pos = 2 # skip SOI
n = len(data)
while pos + 4 <= n:
marker = struct.unpack(">H", data[pos:pos + 2])[0]
pos += 2
if marker == JPEG_SOS:
length = struct.unpack(">H", data[pos:pos + 2])[0]
pos += length # length includes its own 2 bytes
return pos
# Any non-marker (top byte != 0xFF) or a stray EOI here is malformed.
if marker == JPEG_EOI or marker < 0xFF00:
return None
if pos + 2 > n:
return None
length = struct.unpack(">H", data[pos:pos + 2])[0]
pos += length
return None
def find_eoi_offset(data: bytes):
"""
The packer writes EOI as the very last 2 bytes of the file, so we just
confirm that and return its offset.
"""
if len(data) < 2:
return None
if struct.unpack(">H", data[-2:])[0] != JPEG_EOI:
return None
return len(data) - 2
def unstuff_data(stuffed: bytes) -> bytes:
"""
Reverse JPEG byte-stuffing: every 0xFF in the original stream had a
0x00 inserted after it during packing, so collapse `FF 00` back to `FF`.
"""
out = bytearray()
i = 0
n = len(stuffed)
while i < n:
b = stuffed[i]
out.append(b)
if b == 0xFF and i + 1 < n and stuffed[i + 1] == 0x00:
i += 2 # consume the stuffed 0x00
else:
i += 1
return bytes(out)
def unpack_jpeg(input_path: str, output_path: str, force_raw: bool = False) -> int:
try:
with open(input_path, "rb") as f:
data = f.read()
except OSError as e:
print(f"Error: Could not open input file '{input_path}': {e}")
return 1
mode_flag = find_jfif_flag(data, force_raw)
if mode_flag is None:
print("Error: Invalid JPEG file or missing JFIF header")
return 1
sos_offset = find_sos_offset(data)
if sos_offset is None:
print("Error: Could not find SOS marker")
return 1
eoi_offset = find_eoi_offset(data)
if eoi_offset is None:
print("Error: Could not find EOI marker")
return 1
if eoi_offset <= sos_offset:
print("Error: No data found between SOS and EOI")
return 1
scan_data = data[sos_offset:eoi_offset]
if mode_flag == FLAG_RAW:
print("Mode detected: RAW (no byte stuffing)")
payload = scan_data
else:
print("Mode detected: STANDARD (with byte stuffing)")
payload = unstuff_data(scan_data)
# Decrypt (subtract 0x55 mod 256) then reverse the whole buffer.
payload = payload.translate(_DECRYPT_TABLE)[::-1]
try:
with open(output_path, "wb") as f:
f.write(payload)
except OSError as e:
print(f"Error: Could not create output file '{output_path}': {e}")
return 1
print(f"Successfully extracted binary data to: {output_path}")
print(f"Extracted data size: {len(payload)} bytes")
print(f"SOS data offset: 0x{sos_offset:08X} ({sos_offset})")
print(f"EOI marker offset: 0x{eoi_offset:08X} ({eoi_offset})")
return 0
def print_usage(prog: str) -> None:
print(f"Usage: {prog} [-raw]
Below is the standalone Python port for -unpack -raw options together. Since force_raw=True makes the thumbnail-width flag irrelevant and UnstuffData() is never called, both have been removed; only the JFIF header sanity-check, marker walk, decrypt, and reverse remain. This makes the "raw" version smaller. Code:
import struct
import sys
# JPEG markers
JPEG_SOI = 0xFFD8
JPEG_APP0 = 0xFFE0
JPEG_SOS = 0xFFDA
JPEG_EOI = 0xFFD9
# Decryption table: subtract 0x55 from each byte (mod 256) in one .translate() pass.
_DECRYPT_TABLE = bytes((i - 0x55) & 0xFF for i in range(256))
def validate_jfif(data: bytes) -> bool:
"""
Confirm the file starts with SOI + APP0/JFIF, matching what the C
FindJFIFFlag() function checks before returning. We don't read the
thumbnail-width flag because force_raw makes its value irrelevant.
"""
if len(data) < 20:
return False
if struct.unpack(">H", data[0:2])[0] != JPEG_SOI:
return False
if struct.unpack(">H", data[2:4])[0] != JPEG_APP0:
return False
if data[6:11] != b"JFIF\0":
return False
return True
def find_sos_offset(data: bytes):
"""
Walk markers from just after SOI until SOS is found and return the
offset of the first scan-data byte (i.e. just past the SOS header).
Each segment's 2-byte big-endian length includes its own size, so
`pos += length` skips the whole segment in one step.
"""
pos = 2
n = len(data)
while pos + 4 <= n:
marker = struct.unpack(">H", data[pos:pos + 2])[0]
pos += 2
if marker == JPEG_SOS:
length = struct.unpack(">H", data[pos:pos + 2])[0]
pos += length
return pos
if marker == JPEG_EOI or marker < 0xFF00:
return None
if pos + 2 > n:
return None
length = struct.unpack(">H", data[pos:pos + 2])[0]
pos += length
return None
def find_eoi_offset(data: bytes):
"""The packer always places EOI as the trailing 2 bytes of the file."""
if len(data) < 2:
return None
if struct.unpack(">H", data[-2:])[0] != JPEG_EOI:
return None
return len(data) - 2
def unpack_jpeg_raw(input_path: str, output_path: str) -> int:
try:
with open(input_path, "rb") as f:
data = f.read()
except OSError as e:
print(f"Error: Could not open input file '{input_path}': {e}")
return 1
if not validate_jfif(data):
print("Error: Invalid JPEG file or missing JFIF header")
return 1
sos_offset = find_sos_offset(data)
if sos_offset is None:
print("Error: Could not find SOS marker")
return 1
eoi_offset = find_eoi_offset(data)
if eoi_offset is None:
print("Error: Could not find EOI marker")
return 1
if eoi_offset <= sos_offset:
print("Error: No data found between SOS and EOI")
return 1
# In raw mode the scan-data slice is the encrypted payload directly —
# no FF 00 unstuffing, since the packer skipped that step too.
scan_data = data[sos_offset:eoi_offset]
print("Mode detected: RAW (no byte stuffing)")
# Decrypt then reverse, matching the order in UnpackJPEG().
payload = scan_data.translate(_DECRYPT_TABLE)[::-1]
try:
with open(output_path, "wb") as f:
f.write(payload)
except OSError as e:
print(f"Error: Could not create output file '{output_path}': {e}")
return 1
print(f"Successfully extracted binary data to: {output_path}")
print(f"Extracted data size: {len(payload)} bytes")
print(f"SOS data offset: 0x{sos_offset:08X} ({sos_offset})")
print(f"EOI marker offset: 0x{eoi_offset:08X} ({eoi_offset})")
return 0
def main(argv) -> int:
if len(argv) != 3:
print(f"Usage: {argv[0]}
Last edited by HarrySpoofer; 05-03-2026 at 08:30. |
| The Following 2 Users Gave Reputation+1 to HarrySpoofer For This Useful Post: | ||
Fyyre (05-23-2026), wx69wx2023 (05-02-2026) | ||
|
#2
|
|||
|
|||
|
Nice idea.
Any examples of LLMs you got this working with and what sort of useful information they gave you on the binaries/prompts to get that info? |
|
#3
|
|||
|
|||
|
Quote:
Grok was able to actually make a keygen for Offline Explorer and change the painting function in a silly Bubbles screensaver (the one built into Windows). Last edited by HarrySpoofer; 05-02-2026 at 18:51. |
| The Following User Says Thank You to HarrySpoofer For This Useful Post: | ||
niculaita (05-02-2026) | ||
|
#4
|
||||
|
||||
|
Quote:
Kinda offtopic.
__________________
EnJoy! |
|
#5
|
|||
|
|||
|
This can be applied to all types of applications, regardless of the operating system (Windows, OS, Mac), including translation, reverse engineering, or open search, as well as banking and government tools.
analysis LLM <> GPT |
|
#6
|
|||
|
|||
|
Oh, this was for a silly reason.
I wanted to modify this screen saver so it draws bubbles to a Window Device Context instead of the Desktop Device Context. Think about it as a screen saver for just one window. I needed it for a toy game for kids (if they win then they get bubbles as a reward- I was too lazy to render the bubbles myself so I repurposed the screensaver). Since then I also used this Binary to JPEG obfuscator to smuggle binaries into my employer's private corporate network because I needed some Sysinternals utilities that my IT Dept. just refused to install. The corporate email attachment filter just let these JPEG files right through. It's been several months now and nobody has noticed anything, probably because these utilities are signed by Microsoft. Last edited by HarrySpoofer; 05-03-2026 at 08:27. |
![]() |
| Thread Tools | |
| Display Modes | |
|
|