If you’ve ever crafted a perfect shellcode and ROP chain only to have your exploit immediately crash with a SIGSEGV(a signal sent by the operating system to a program when it attempts to access a protected or invalid memory location) or EIP(a 32-bit CPU register in the x86 architecture that holds the memory address of the next machine instruction to be executed) pointing to garbage, you’ve likely met the silent killer of beginners: Endianness.
Endianness is fundamental; it defines the order in which a multi-byte value (like a 4-byte integer or an 8-byte memory address) is stored in a machine’s memory. For us in the pwn game, this translates directly to whether the addresses we pack into our exploit payloads are interpreted correctly by the target program. A single misplaced byte means the difference between a shell and a stack smash(stack buffer overflow).
There are two primary byte orders
Little-Endian (LE): The Least Significant Byte (LSB) is stored first, at the lowest memory address. This is the byte order used by the vast majority of personal computers, including all modern Intel/AMD (x86/x64) architectures.
Big-Endian (BE): The Most Significant Byte (MSB) is stored first, at the lowest memory address. This is often called Network Byte Order (NBO) and is common in network protocols, as well as some older or embedded architectures like PowerPC, MIPS, and SPARC.
If you’re attacking an embedded device, router, or IoT target, it might be Big-Endian. Always confirm the target architecture and ABI. Common mistakes when cross-compiling or reusing gadgets:
Using x86/x86_64 gadgets and packers on a MIPS/PowerPC target (different endianness and instruction set) will not work.
pwntools and other frameworks allow you to set context.arch and context.endian So packers and disassemblers behave correctly.
Run the small C program above on the target if you have code execution. Use readelf, file, or objdump On binaries from the target filesystem, some formats indicate endianness.
In GDB, examine the memory representation of a known constant or pointer. Put a known value in a register or memory, and inspect the byte order with x/4xb address.
(gdb) p/x 0x41424344 $1 = 0x41424344 (gdb) set {int}0x601000 = 0x41424344 (gdb) x/4xb 0x601000 0x601000: 0x44 0x43 0x42 0x41 # little-endian: ‘DCBA’Print the raw payload bytes you are sending, compare with what the target receives (packet capture or logging), and what the target’s memory shows in a debugger.
Verify pointer size. Are you packing 32-bit or 64-bit addresses?
Check alignment and padding. Are there extra bytes inserted by the protocol or scanf/fgets behavior?
Confirm endianness on the target; don’t assume x86 for embedded devices.
Use tool packing (struct.pack, pwntools) instead of hand-reversing where possible.
If attacking over the network, confirm whether fields are consumed in network byte order.
Confirm target architecture (x86, x86_64, MIPS, ARM, PowerPC).
Confirm endianness (LE or BE).
Use correct pointer width (p32 vs p64) and pack accordingly.
Use library packing (struct, pwntools) to avoid manual reversal mistakes.
For network services, encode multi-byte protocol fields in network (big-endian) byte order unless the protocol documentation says otherwise.
Check struct padding and alignment on the target.
Inspect the target’s memory with a debugger to verify the actual byte layout.
Endianness is simple in concept but vital in practice. For most modern exploit work, you’ll be on x86_64 (Little-Endian), so make it a habit to pack addresses in reverse byte order relative to how you read them in a disassembler. Use pwntools, struct, or equivalent to do the packing for you, but keep in mind why bytes are reversed; that understanding is what will let you debug the next crash quickly and confidently.
Recommended reading:
https://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/
https://www.geeksforgeeks.org/dsa/little-and-big-endian-mystery/
Disclaimer: This article was written with AI assistance, for a bit of brainstorming and proofreading.
.png)

