Two Dev.to posts describe an x86_64 Linux ICMP sniffer implemented without standard libraries, using direct system calls and custom packet parsing. The ICMP sniffer creates a raw socket configured for IPv4 with protocol filtering for ICMP, receiving packets via a syscall-based recvfrom into a buffer. Because raw sockets deliver the packet with protocol headers intact, the code manually accounts for the IPv4 header (20 bytes) and ICMP header (8 bytes), skipping a combined 28 bytes to access the ICMP-related data it wants to inspect. The accompanying work focuses on converting IP address bytes extracted from packet headers into the correct dotted-decimal string format. It explains that IP addresses are stored in network byte order (big-endian) while x86/x64 registers are little-endian, so naive printing can produce reversed octets. To address this, the authors implement a single-pass conversion algorithm that reads IP bytes in reverse order and writes characters into an output buffer backward, inserting dot separators between octets while avoiding a separate reverse pass. Both posts provide source code availability on a linked GitHub repository and include educational/research legal disclaimers.
x64 Assembly ICMP sniffer demonstrates Linux raw sockets and manual IP-to-ASCII handling
Two Dev.to posts describe an x86_64 Linux ICMP sniffer implemented without standard libraries, using direct system calls and custom packet parsing. The ICMP sniffer creates a raw socket configured for...
- The projects use Linux raw sockets to capture IPv4 ICMP packets via direct syscalls, avoiding standard libraries.
- Packets are received with headers included; code skips 20-byte IPv4 and 8-byte ICMP headers (28 bytes total) to reach the relevant data.
- IP addresses from packet headers are in network byte order (big-endian) but x86/x64 use little-endian, leading to reversed output without conversion.
- A custom IP-to-dotted-decimal ASCII conversion is implemented in assembly using division to extract base-10 digits and manual dot separator insertion.
- A single-pass method builds the dotted-decimal string by reading IP bytes backward and writing into an output buffer from the end.
Research Context In the realm of network security and packet analysis, tools like Python (Scapy) or C are the usual go-tos. However, when we want to strip away all abstraction layers from the OS network stack and talk directly to the processor, resources become incredibly scarce. Finding modern, zero-dependency networking tools written in x64 Assembly on the internet is almost impossible today. In this post, we will explore the architecture and design decisions behind my x64 Assembly-based ICMP Sniffer project, completely rejecting standard C libraries (libc) and relying purely on direct Linux system calls (syscalls). The Concept: Why Assembly? Our goal isn't just to catch ICMP (ping) packets on the network. We want to manually manage memory, register allocations, and data type conversions (integer-to-string) at the CPU cycle level. This approach provides a flawless foundation for understanding how hardware behaves during System security auditing and low-level software analysis. How Does It Work? (Technical Deep Dive) The architecture of the tool is divided into three main phases: The Raw Socket Foundation To capture raw, unprocessed packets passing through the network interface card (NIC), the application uses sys_socket (syscall 41) with AF_INET and SOCK_RAW parameters. Our target here is strictly the IPPROTO_ICMP protocol. This tells the operating system to filter out all TCP/UDP traffic and hand us only the ICMP packets. Packet Observation and Header Stripping Incoming packets are read into a memory buffer using sys_recvfrom. Since we are using Raw Sockets, the data arrives in its absolute raw form. To reach the actual payload, we must manually bypass the protocol headers: IPv4 Header: 20 Bytes ICMP Header: 8 Bytes Therefore, by utilizing the lea rsi, [sniffed_data + 28] instruction in our Assembly code, we strip away this 28-byte "noise" and dive straight into the heart of the data. The Custom Integer-to-ASCII Engine This is the most complex and educational part of the project. The captured IP address (e.g., 192.168.1.29) resides in memory as raw binary (hexadecimal). To print this to the terminal, we must convert it into a human-readable ASCII string. Since we aren't using any external printf or itoa functions, I designed the engine as follows: Each octet (8-bit IP segment) fetched from the network address is divided by 10 using the div instruction. We mathematically add 48 (0x30) to the remainders to convert them into ASCII characters. These converted characters are written into a 16-byte memory buffer in reverse order (from end to start). Using logical brakes via conditional jumps (je, jg), dot (.) characters are strategically inserted only between the octets to prevent malformed strings. Conclusion and Source Code This tool proves how we can filter not just the "existence" of ICMP packets, but the actual payloads hidden inside them (like Non-standard data structures or remote management signals) at the kernel level. Writing our own string conversion engine using nothing but Linux Syscalls, without relying on any external libraries, has been a fantastic exercise in pushing the limits of low-level system programming. For security researchers, Blue Team members, and exploit development enthusiasts who want to test the tool or review the code, the full source is available on my GitHub profile: 🔗 GitHub Repo: JM00NJ/asm-icmp-sniffer ⚠️ Legal Disclaimer This project is created for educational purposes and security research only. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool. Operating this tool on networks you do not own is strictly prohibited. Related Defying Python: Building a Bare-Metal HTTP Server in x86_64 Assembly Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm Network Fingerprinting: Analyzing Default ICMP Structures and Payload Mimicry
3 hours agoResearch Context When doing low-level network programming in Assembly, you experience firsthand the immense chaos running behind the scenes of operations we solve with a single line in high-level languages (Python, C, etc.). While developing the Nested-ICMP-Communication Analysis project, specifically an Encapsulated ICMP framework, I hit exactly this kind of wall: extracting an IP address from a packet header and printing it to the screen in the correct format. Sounds simple, right? However, when x86 architecture and network protocols are involved, seeing 5.1.168.192 instead of 192.168.1.5 on your terminal is extremely common. So why does this happen, and what kind of algorithm did I develop to overcome this issue during the debugging process? Let's dive into the background. The Endianness Problem in Network Headers When you capture a packet coming over the network and read the source/destination IP address inside the sockaddr_in structure, the data arrives in Network Byte Order (Big-Endian) format. This means the most significant byte is stored at the lowest memory address. However, the x86/x64 processor architectures we use rely on Little-Endian (Host Byte Order). When the processor pulls this 4-byte IP data into a register, the reading direction is effectively reversed for our purposes. The result? A packet that arrives as 192.168.1.5 appears scrambled if we try to naively print it from memory. The inet_ntoa() function in high-level languages handles this conversion in the background. But if you are writing a custom sniffer in pure Assembly, you must do this conversion byte by byte yourself. Debugging Hell: The Problems Encountered While writing this conversion, I encountered a few critical issues that cost me hours in GDB (GNU Debugger): Register Clashes: While separating each octet (byte) of the IP address and converting it to an ASCII character (string), you must use the AX register for division operations (DIV). If you don't carefully manage your remainders (AH) and quotients (AL), the numbers of the IP address get completely corrupted. The Dot (.) Separator: It's not enough to just convert the numbers; a . (0x2E / 46 in decimal) character must be inserted exactly between each octet, but not at the very end. Performance Loss (The Reversing Trap): In standard logic, you parse the IP, convert it to a string, and realize the string is backwards. Then, you write a second loop to reverse that string. This creates unnecessary memory read/write cycles and bloats the code. The Solution: A Single-Pass Backward Build Algorithm To solve the problem, instead of creating the string and then reversing it, I designed a more optimized algorithm. The logic is simple but highly effective: Read the IP bytes backwards, and write the ASCII string backwards. By starting at the end of the IP address within the sockaddr_in structure (offset 7 down to 4) and writing from the end of a 15-byte output buffer (addr_ip) down to index 0, the string naturally formats itself correctly from left to right. Here is the exact critical loop from my engine: ASSEMBLY CODE: ; IP ADDRESS TO STRING ALGORITHM (EXTRACT REVERSE BYTE-BY-BYTE AND CONVERT TO ASCII) xor rdx, rdx ; Clear rdx xor rbx, rbx ; Clear rbx mov rcx, 7 ; Start index for reading IP from sockaddr_in (sin_addr offset) mov rdi, 15 ; Start index for writing to the addr_ip buffer (backwards) _loopforip: mov bl, 10 ; Divisor for base-10 conversion movzx ax, [incoming_addr+rcx] ; Fetch one octet from IP address _divloop: div bl ; Divide AX by 10; AL = quotient, AH = remainder add ah, 48 ; Convert remainder to ASCII character mov [addr_ip+rdi], ah ; Store ASCII character in the output buffer dec rdi ; Move buffer pointer backward xor ah, ah ; Clear AH for the next division cycle cmp al, 0 ; Check if quotient is zero jg _divloop ; If not zero, continue extracting digits cmp rcx, 4 ; Check if this is the last octet (first IP block) je _contiune ; If last octet, skip adding the dot separator mov byte [addr_ip+rdi], 46 ; Insert '.' (dot) character _contiune: dec rdi ; Move buffer pointer backward for the next octet dec rcx ; Move to the next IP octet in sockaddr_in cmp rcx, 3 ; Check if all 4 octets have been processed jg _loopforip This single-pass method successfully converts the raw network bytes into a human-readable ASCII string using minimal CPU cycles, entirely avoiding an extra "string reversing" loop. Conclusion and Open Source Network programming in Assembly might seem tedious at first, but it is a unique experience for understanding the true mechanics underlying these systems. Especially when working on tunneling architectures aimed at Evaluating IDS detection resilience, having this level of byte-control is absolutely vital. You can find this algorithm in action, along with the complete source code of the asm-icmp-sniffer, on my GitHub profile: JM00NJ/asm-icmp-sniffer Disclaimer: This article and the associated source code are intended for educational purposes and authorized security research only. Understanding low-level network protocols is essential for building better defense mechanisms. Related Building a Low-Level ICMP Sniffer in x64 Assembly Linux x64 Assembly: Syscalls, Registers, and the .bss Segment Linux Process Evasion: ptrace & prctl
1 day ago
Several BMW M models from 1995–2025 are listed online, including M3, M2, M5, and M6 variants
Across multiple listings, Bring a Trailer presents several BMW performance models spanning three decades, each described...
US raises concerns with ASML over possible China access to top EUV chip tool
The Trump administration, through U.S. Commerce Secretary Howard Lutnick, raises concerns with Dutch chip-equipment make...
Reports: M5 Ultra Mac Studio in 2026, M7 Ultra model with cooling changes in 2028
Multiple reports cite Bloomberg’s Mark Gurman in suggesting Apple’s Mac Studio lineup will receive future updates center...