Changelogs
Encoding & Obfuscation Fixes
- Schema Size & Alignment Issue:
- Bug: The schema size calculation incorrectly divided the payload length by the architecture size (e.g., 8 bytes for x64). However,
SchemaCipheralways operates in 4-byte (32-bit) chunks regardless of architecture. If a payload wasn't perfectly aligned to a 4-byte multiple, the decoding loop would experience out-of-bounds reads/writes or partial decoding. - Fix: Hardcoded the divisor to 4 for all architectures and added padding (
0x90NOPs) to ensure the payload perfectly aligns with the required schema boundaries.
- Bug: The schema size calculation incorrectly divided the payload length by the architecture size (e.g., 8 bytes for x64). However,
- ADD/SUB Cipher Arithmetic:
- Bug: The ADD/SUB ciphers used
% 0xFFFFFFFFto simulate 32-bit wrapping. This is mathematically incorrect because the max value ofuint32is0xFFFFFFFF, soval % 0xFFFFFFFFevaluates to0instead of wrapping correctly. - Fix: Removed the modulo completely. In Go,
uint32natively wraps around, so standard addition and subtraction inherently provide the correct behavior.
- Bug: The ADD/SUB ciphers used
- Unsafe Garbage Instructions (
JMP 2):- Bug:
JMP 2was included in theSafeGarbageInstructionslist. This is a relative jump that skips exactly two bytes. If it executes in the middle of a larger instruction block or without two bytes of trailing instructions, it corrupts the instruction pointer and crashes the payload. - Fix: Removed
JMP 2from the safe instructions list to prevent execution crashes.
- Bug:
- Obfuscation Limit Ignored:
- Bug: The encoder previously continued to generate garbage instructions even if the obfuscation limit was explicitly set to 0.
- Fix: Added an explicit check (
if encoder.ObfuscationLimit <= 0) to bypass garbage assembly entirely when disabled.
Register Selection & Stack Management
- Incorrect Register Definitions:
- Bug: The 32-bit definitions for
ESIandEDIincorrectly assigned their 8-bitLowrepresentations toALandBL. This meant if the encoder substituted an 8-bit instruction targeting ESI, it would clobber AL/BL instead. Additionally, the 64-bitRDIregister incorrectly had itsHighrepresentation mapped toDX(which belongs to RDX). - Fix: Removed the 8-bit
Lowrepresentations for ESI and EDI, and corrected RDI'sHighmapping toDI.
- Bug: The 32-bit definitions for
- Stack Pointer (RSP) Clobbering:
- Bug: The x64 register save/restore stubs pushed and popped the
RSPregister (PUSH RSP/POP RSP). Pushing and popping the stack pointer manually inside a decoder stub breaks stack alignment and offset calculations, resulting in immediate segmentation faults. - Fix: Removed RSP from the save/restore prefixes and suffixes.
- Bug: The x64 register save/restore stubs pushed and popped the
- Random Register Selection Panic/Errors:
- Bug: The
GetRandomRegisterlogic blindly picked a random register and asked for a specific size representation (like.Lowor.High). Since some registers don't have these representations (like ESI 8-bit), it returned empty strings, causing the Keystone assembler to fail. - Fix: Rewrote the random selection logic to filter and build an array of only valid register representations for the requested size before picking one randomly.
- Bug: The
CLI & Core Logic
- Broken Payload Selection Constraints:
- Bug: In the main CLI loop, the condition to validate a generated payload used an
||(OR) operator for ASCII and bad-byte checks. This meant a payload would be accepted if it was ASCII (but contained bad bytes) OR had no bad bytes (but failed the ASCII constraint). - Fix: Refactored the checks to strictly enforce
&&(AND), ensuring payloads pass both constraints simultaneously before being saved. Option application was also moved inside the loop.
- Bug: In the main CLI loop, the condition to validate a generated payload used an
- File Overwrite Truncation:
- Bug: When saving a payload over an existing file, the file wasn't truncated. If the new payload was smaller than the old one, the generated file would contain trailing garbage data. The file descriptor was also never explicitly closed.
- Fix: Added the
os.O_TRUNCflag when opening the output file and addeddefer out.Close().
- Example Import Casing:
- Bug:
encode_x64_binary.goused a lowercase import pathgithub.com/egebalci/sgn/pkg, which fails to compile on strict module resolution systems because the actual repository isEgeBalci. - Fix: Corrected the import path to
github.com/EgeBalci/sgn/pkg.
- Bug:
Performance & Testing
- String Search Optimization:
- Optimization: Replaced a manual array loop and
bytes.Containscheck in theContainsByteshelper with the much fasterbytes.IndexByte.
- Optimization: Replaced a manual array loop and
- Execution Stress Test:
- Testing: Added
TestIntegrationExecutionon Windows, which generates, allocates, and executes dummy payloads 1000 times to continuously verify that none of the encoder/decoder logic introduces crashes.
- Testing: Added
Many thanks to @Drewsif
cf6209e4bcdd99938bafdcc8e99988fd69d0b7ef7bf9898123edce89b63fb903 sgn_linux_amd64_2.0.2.zip
de8418cc4cb087baa8df80677cfcd2d29f107a1d5f95b6009ca31f73b0680be3 sgn_windows_386_2.0.2.zip
ebb5129979c8f71af1940e92e53ceacd5343891d57bc4ead47dc4a10996ea5a9 sgn_windows_amd64_2.0.2.zip