Endianness
Source: pwnable.kr: walkthrough notes from their
collisionchallenge. You don’t need an account or to follow along to read this; it’s just context for where the problem came from.
This post will get you familiar with endianness, memory layout, and integer constraints.
We are given this C program running on a 32-bit architecture:
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res = 0;
for(i = 0; i < 5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
if(argc < 2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
if(hashcode == check_password(argv[1])){
setregid(getegid(), getegid());
system("/bin/cat flag");
return 0;
} else {
printf("wrong passcode.\n");
}
return 0;
}
Understanding the program
The first thing to notice is this line in check_password:
int* ip = (int*)p;
This doesn’t create arrays for each character like you might think. It just reinterprets the same 20 bytes as 5 integers, each 4 bytes.
So the 20 bytes are grouped like this:
| Bytes | Integer |
|---|---|
b0 b1 b2 b3 |
ip[0] |
b4 b5 b6 b7 |
ip[1] |
b8 b9 b10 b11 |
ip[2] |
b12 b13 b14 b15 |
ip[3] |
b16 b17 b18 b19 |
ip[4] |
Then the loop sums them:
res += ip[i]; // adds all 5 integers
Basically, the program is just adding 5 numbers that come from your input bytes.
The main function also has this check:
if(strlen(argv[1]) != 20)
Which means:
- You must input exactly 20 bytes
- No
\x00allowed, becausestrlenstops early
This is the length/integer constraint we need to respect.
Strategy to pass the check
We need:
ip[0] + ip[1] + ip[2] + ip[3] + ip[4] = 0x21DD09EC
A simple way to do it:
- Pick 4 integers to be something simple, for example:
A = B = C = D = 0x01010101 - Compute the 5th integer:
E = 0x21DD09EC - (A + B + C + D)
Endianness
The system is 32-bit little-endian, so the last integer E must be reversed in memory.
For example, if:
E = 0x1DD905E8
Memory layout (little-endian) becomes:
E8 05 D9 1D
So the full input must be:
\x01\x01\x01\x01
\x01\x01\x01\x01
\x01\x01\x01\x01
\x01\x01\x01\x01
\xE8\x05\xD9\x1D
Total = 20 bytes.
Key takeaways
- Input is raw bytes, not printable characters
- The program reinterprets bytes as integers
- Endianness determines byte order
- This challenge is about memory layout + arithmetic, not cryptography
Check yourself
If someone inputs:
\x1D\xD9\x05\xE8
The program will read it as:
0xE805D91D
Because of little-endian ordering.