─── Before we start ─────────────────────────────────────────────────────────── A hex constant in a write-up. A signed comparison that went the wrong way. Four bytes from a debugger. Usually that means opening another tab or a REPL. Try selecting -1 here, then open [inspect]. The decimal value is still -1. The hex is 0xffffffff. Change BITS to 16: now it is 0xffff. Same idea, less window shuffling. The examples below are meant to be used. Select the input itself, without the answer beside it. For a comparison, select the whole expression. A result stays put until you select something else, scroll away, or press Esc. [ Contents ] 01 Bases & permissions 06 Floating point 02 Signed meets unsigned 07 Numbers versus bits 03 A pocket C evaluator 08 Bytes & byte order 04 Widths & decompiler types 09 Reading a dump 05 Masks & partial values 10 Text hiding in bytes C expressions use Linux x86-64: signed char, 16-bit short, 32-bit int, 64-bit long and long long. Signed integer casts follow GCC. There is no process attached; all operands have to be in the selected text. ─── 1. Bases & permissions ──────────────────────────────────────────────────── The four base rows describe one bit pattern. HEX, OCT and BIN use the selected width, so a negative integer has a two's-complement representation. Input DEC HEX ──────────────────────────── ─────────────────── ────────────────── 4096 4096 0x1000 0X1000 4096 0x1000 0b1001 9 0x9 0o755 493 0x1ed 0755 493 0x1ed 755 755 0x2f3 9007199254740993 9007199254740993 0x20000000000001 That last row is already past JavaScript's safe integer range. It stays exact. /// mode bits ///////////////////////////////////////////////////////////////// Bare 755 is decimal. C's 0755 and the explicit 0o755 are octal. A chmod mode is also octal, even without the leading zero. Select the command and its mode: chmod 755 rwxr-xr-x chmod 4755 rwsr-xr-x mode 2644 rw-r-Sr-- 01777 rwxrwxrwt 07000 --S--S--T Switch VIEW to Permissions to split owner, group and other. The uppercase S or T means the special bit is set but the corresponding execute bit is not. This view describes permission bits; it does not run chmod. ─── 2. Signed meets unsigned ────────────────────────────────────────────────── Here is a table, with each row written as a complete expression. Every comparison below is true. The interesting part is the COMPARE row: it shows the type used for the operands, followed by their converted values. Expression Operands compared as ──────────────────────────────────────── ───────────────────── 0 == 0U unsigned int / 32 -1 < 0 int / 32 -1 > 0U unsigned int / 32 2147483647 > -2147483647-1 int / 32 2147483647U < -2147483647-1 unsigned int / 32 -1 > -2 int / 32 (unsigned)-1 > -2 unsigned int / 32 2147483647 < 2147483648U unsigned int / 32 2147483647 > (int)2147483648U int / 32 Now try these two together: -1 < 0U false LEFT becomes 4294967295 -1L < 0U true a signed long can hold every unsigned int "Unsigned always wins" misses that second case. Width and rank both matter. The comparison result itself is an int: 0 or 1. Literal spelling matters too: 2147483648 long / 64 0x80000000 unsigned int / 32 (int)2147483648U -2147483648 -2147483647-1 int / 32 -2147483648 long / 64 The minus sign is an operator. In that last row, 2147483648 gets its type before it is negated. ─── 3. A pocket C evaluator ─────────────────────────────────────────────────── Offsets, alignment, a flag check: small expressions save the most trips. Input Result ───────────────────────────────────── ──────────────────────── 0x400123 - 0x400000 0x123 (0x1234 + 0xfff) & ~0xfff 0x2000 2 + 3 * 4 14 (2 + 3) * 4 20 -7 / 3 -2 -7 % 3 -1 0xff ^ 0x0f 0xf0 0x80 | 0x11 0x91 (0x91 & 0x80) != 0 true 1U << 31 0x80000000 -8 >> 2 -2 !(0 | 0) 1 3 >= 2 && 3 <= 4 1 0 && 1/0 0 1 || 1/0 1 1 ? 3 : 1/0 3 0 ? 0U : -1 4294967295 The unevaluated side of &&, || or ?: stays unevaluated. Both branches of ?: still contribute to the result type. Try the last two rows to see the split. Supported operators: unary + - ~ ! arithmetic + - * / % shifts << >> bitwise & ^ | comparison == != < <= > >= logical && || conditional ? : /// wrapping is not always an answer ////////////////////////////////////////// 0xffffffffU + 1 0 unsigned arithmetic wraps 2147483647 + 1 UNDEFINED: signed overflow 1 << 31 UNDEFINED: signed shift overflow 1U << 32 UNDEFINED: count reaches the width -1 << 1 UNDEFINED: negative signed operand 1 / 0 UNDEFINED: integer division by zero (-2147483647-1) % -1 UNDEFINED: signed division overflow (int)1e40 UNDEFINED: conversion out of range The inspector shows the reason for these cases instead of inventing a result. ─── 4. Widths & decompiler types ────────────────────────────────────────────── BITS changes the displayed interpretation. A cast changes the expression. This distinction matters when the next operator performs integer promotion. Input Result ──────────────────────────── ─────────────────────────────────── (uint8_t)0x1234 0x34 (int8_t)0xff -1 (int16_t)0x8000 -32768 (uint32_t)-1 4294967295 (int64_t)(int8_t)0x80 -128 / 0xffffffffffffff80 (uint64_t)(uint8_t)0x80 128 / 0x80 ~ (uint8_t)0 -1, because uint8_t promotes to int (uint8_t)~0 255 (bool)256 1 (int)-3.9 -3 Select 511 and change BITS to 8. The low byte is ff; its signed view is -1. The note keeps the original value visible. Widen (int8_t)0x80 to 64 bits for sign extension, then do the same with (uint8_t)0x80 for zero extension. Signed and unsigned decimal alternatives appear when they differ. To read both interpretations of raw memory, choose Integer in a byte selection. /// spellings from C, asm and IDA ///////////////////////////////////////////// 42U 42UL 42ULL 42L 42LL 18h 0FFh 0LL 42i64 42ui64 U, L and LL suffixes work in either case and in either legal order. IDA's i64/ui64 suffixes name 64-bit values. An h suffix marks an assembler hex constant; 18h is 24, not eighteen. (_BYTE)0x1234 0x34 (_WORD)0x123456 0x3456 (_DWORD)-1 0xffffffff (_QWORD)-1 0xffffffffffffffff (__int64)-1 -1 (unsigned __int64)-1 18446744073709551615 (size_t)-1 18446744073709551615 (ptrdiff_t)0x100 - 0x120 -32 'A' 65 '\n' 10 '\x41' 65 '\101' 65 C type names include signed/unsigned char, short, int, long and long long; float, double and _Bool; int8_t through uint64_t; size_t, ssize_t, intptr_t, uintptr_t and ptrdiff_t. IDA's __int8/16/32/64 and _BYTE/_WORD/_DWORD/_QWORD aliases have their stated widths. Character constants cover single ASCII bytes and the usual C escapes. /// larger than a machine word //////////////////////////////////////////////// A standalone integer can be inspected up to 256 bits: 0xffffffffffffffffffffffffffffffff 0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0 These get an exact-integer view, with 128 or 256 available under BITS. They are not silently introduced as extra types in C expressions. ─── 5. Masks & partial values ───────────────────────────────────────────────── Select 0x91, choose Bits, then set BITS to 8. bit 7 6 5 4 3 2 1 0 │ │ │ │ │ │ │ │ 1───0───0───1───0───0───0───1 │ │ │ └───────────┴───────────────┘ three set bits SET BITS counts from zero at the least significant end. POPCNT is 3. LOW 4 is 0x1; HIGH 4 is 0x9. Wider selections split into wider halves. IDA helpers are useful when the text already names the part you need: LOBYTE(0x12345678) 0x78 HIBYTE(0x12345678) 0x12 BYTE1(0x12345678) 0x56 LOWORD(0x12345678) 0x5678 HIWORD(0x12345678) 0x1234 WORD1(0x12345678) 0x1234 LODWORD(0x123456789abcdef0ULL) 0x9abcdef0 HIDWORD(0x123456789abcdef0ULL) 0x12345678 DWORD1(0x123456789abcdef0ULL) 0x12345678 SLOBYTE(0xff) -1 SHIBYTE((uint16_t)0x8000) -128 The S-prefixed forms read a signed part. BYTE0..7, WORD0..3 and DWORD0..1 also work, provided the operand is wide enough. These helpers follow the little-endian target. HIBYTE means its highest byte: 0x12 above. For the high byte of a 16-bit word, make the word explicit: HIBYTE((uint16_t)0x1234). __PAIR16__(0x12, 0x34) 0x1234 __PAIR32__(0x1234, 0x5678) 0x12345678 __PAIR64__(0x12345678, 0x9abcdef0) 0x123456789abcdef0 __ROL1__(0x81, 1) 0x03 __ROL2__(0x8001, 1) 0x0003 __ROR4__(0x12345678, 8) 0x78123456 __ROR8__(1ULL, 1) 0x8000000000000000 PAIR joins high and low halves. ROL/ROR rotate; their suffix is the operand size in bytes: 1, 2, 4 or 8. Counts wrap around that width. ─── 6. Floating point ───────────────────────────────────────────────────────── An f suffix selects float32. Without it, a floating literal is float64. The VALUE row shows the stored number. HEX and the sign/exponent/fraction rows show why it looks that way. Input Stored value IEEE hex ──────────────────── ────────────────────────── ────────────────── 0.1f 0.10000000149011612 0x3dcccccd 11.28125f 11.28125 0x41348000 1.25e-3 0.00125 0x3f547ae147ae147b 0x1.8p+1 3 0x4008000000000000 -0.0f -0 0x80000000 1e-45f 1.401298464324817e-45 0x00000001 0x1p-150f 0 0x00000000 In a hex float, p scales by a power of two. The significand is still hex. Here 0x1.8 is 1.5, multiplied by 2 to get 3. Change VIEW from Float64 to Float32 on 0.1 to see the narrower rounding. Change ORDER to see the same stored number laid out in LE or BE bytes. 1.0f + 0.5 1.5, with a float64 result 0x1.000001p0f halfway: rounds to 1, ties to even 1.000000059604644775390626f just above halfway: rounds upward Decimal literals round directly to the requested format. That last example would lose the distinction if it went through float64 before float32. CLASS distinguishes normal, subnormal, zero, infinity and NaN. Try: INFINITY -INFINITY NAN inf nan 1e400 1e-400 0.0 / 0.0 Uppercase NAN/INFINITY use float32, like the C macros. Lowercase spellings use float64. Arithmetic uses IEEE round-to-nearest, ties-to-even; there is no alternate rounding-mode or floating-exception environment here. ─── 7. Numbers versus bits ──────────────────────────────────────────────────── These two lines have very different jobs: (float)0x3f800000 1065353216 COERCE_FLOAT(0x3f800000) 1 The cast converts the integer's value to float. COERCE reads the existing bits as a float. You can get that second view by selecting 0x3f800000 and choosing Float32 bits, without writing a helper. COERCE_DOUBLE(0x3ff0000000000000ULL) 1 0x7f800001 Float32 bits: NaN 0x80000000 Float32 bits: negative zero The bit view keeps the original NaN payload in HEX. Integer inputs are truncated or sign/zero extended to 32 or 64 bits, as stated in the note. COERCE helpers require a source of that width; use a cast first if the selected constant has a different C type. ─── 8. Bytes & byte order ───────────────────────────────────────────────────── Select a whole byte run: 00 02 00 00 The default view keeps the bytes in their original order. UINT LE is 512; UINT BE is 131072. Neither number is more correct without the format around it. Switch to Integer for signed and unsigned readings at a chosen width. ff 7f Integer / 16 / LE 32767 ff 7f Integer / 16 / BE -129 91 00 Bits / 8 set bits 0, 4, 7 00 80 34 41 Float32 / LE 11.28125 3f f0 00 00 00 00 00 00 Float64 / BE 1 The integer and float views read from the start of the selection. BYTES shows exactly what was used; the note gives the byte count. To inspect a later field, select that field in the article. The following three selections contain the same bytes: 41 42 00 0x41 0x42 0x00 \x41\x42\x00 Quoted escaped bytes work too: "\x41\x42\x00". Whitespace between plain or prefixed pairs may include tabs and newlines. A lone 41 is an integer; \x41 is unambiguously one byte. Up to 32 bytes fit in a selection. This run exercises the full buffer: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ─── 9. Reading a dump ───────────────────────────────────────────────────────── Select one of the complete rows below, including its address. The address gets its own row in the inspector; the ASCII gutter is left out of the data. /// xxd /////////////////////////////////////////////////////////////////////// 00000000: 7f45 4c46 0201 0100 .ELF.... 00000008: 0000 0000 0000 0000 ........ Those two rows can also be selected together. Addresses must be contiguous; a gap is not filled with guessed bytes. /// hexdump -C //////////////////////////////////////////////////////////////// 00000000 7f 45 4c 46 02 01 01 00 |.ELF....| /// GDB x/bx ////////////////////////////////////////////////////////////////// 0x400000 <header>: 0x7f 0x45 0x4c 0x46 /// pwndbg hexdump //////////////////////////////////////////////////////////// +0000 0x400000 7f 45 4c 46 02 01 01 00 │.ELF....│ A byte dump records byte order. A debugger word such as 0x41424344 is already a number. Select that value on its own; the inspector does not pretend the whole debugger row is a byte dump. ─── 10. Text hiding in bytes ────────────────────────────────────────────────── ASCII is always available in the byte view. A dot stands in for a byte outside printable ASCII. Choose Text when the data might be UTF-8 or UTF-16. Bytes View Result ──────────────────────────── ──────────────────── ───────────────── 45 58 53 32 37 ASCII EXS27 20 7e 7f 00 ASCII space, ~, ., . c3 a9 Text / UTF-8 U+00E9 41 00 42 00 Text / UTF-16 / LE AB 00 41 00 42 Text / UTF-16 / BE AB 41 0a 42 00 Text / UTF-8 "A\nB\u0000" c3 28 Text / UTF-8 invalid encoding 41 00 42 Text / UTF-16 incomplete unit Decoded text is quoted, with control characters escaped. An invalid sequence is reported instead of being quietly replaced. ORDER applies to UTF-16; UTF-8 has no byte-order switch. ─── Where it stops ──────────────────────────────────────────────────────────── 1.0L unsupported: long double needs 80 bits (long double)1 same limit BYTE7(1) unsupported: operand is only 32 bits These are recognized, but need a representation the current view cannot give. Other text simply does not open an inspector: $rsp + 8 needs a register value [rbp-18h] needs a register and a memory read *(int*)0x400000 needs memory symbol + 4 needs a symbol table Select 18h, an actual address, or a numeric difference from such a line. The evaluator accepts constants, casts and the helpers listed above. It does not execute arbitrary functions. Expressions are bounded to 512 characters and 128 tokens, with at most 32 nested levels. Dump selections may use up to 2048 characters, but still at most 32 data bytes. Click any result to select its value. Ctrl+C uses the browser's normal Copy; Esc returns to the selected article text. That is the whole interaction. ─── Reading material ────────────────────────────────────────────────────────── C11 draft, integer constants and arithmetic conversions: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf GCC's implementation-defined integer behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html Hex-Rays on decompiler helpers: https://hex-rays.com/blog/igors-tip-of-the-week-67-decompiler-helpers ImHex's data inspector: https://docs.werwolv.net/imhex/views/data-inspector ─────────────────────────────────────────────────────────────────── end / 0x0a










