1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
~ This file is a commented hexadecimal representation of a compiled
~ program, the output of Evocation's hex transform. The comments are
~ intended to allow a human reader to audit it. You can convert it to a
~ runnable executable by piping it through the program named 'hex' and
~ chmodding the output 755. The difference between a source and a binary
~ is comments! Enjoy. :)
~ This particular program is 'hex' itself. You of course already need to
~ have a binary copy of it to use this file, but you may find it helpful
~ to have this hex dump anyway, to aid in auditing your binary.
~ ELF file header
~ * denotes mandatory fields according to breadbox
7f 45 4c 46 ~ *magic number
02 ~ 64-bit
01 ~ little-endian
01 ~ ELF header format v1
00 ~ System-V ABI
00 00 00 00 00 00 00 00 ~ (padding)
02 00 ~ *executable
3e 00 ~ *Intel x86-64
01 00 00 00 ~ ELF format version
78 00 00 08 00 00 00 00 ~ *entry point
40 00 00 00 00 00 00 00 ~ *program header offset
00 00 00 00 00 00 00 00 ~ section header offset
00 00 00 00 ~ processor flags
40 00 ~ ELF header size
38 00 ~ *program header entry size
01 00 ~ *number of program header entries
00 00 ~ section header entry size
00 00 ~ number of section header entries
00 00 ~ section name string table index
~ ELF program header
01 00 00 00 ~ *"loadable" segment type
07 00 00 00 ~ *read+write+execute permission
00 00 00 00 00 00 00 00 ~ *offset in file
00 00 00 08 00 00 00 00 ~ *virtual address
00 00 00 00 00 00 00 00 ~ physical address (ignored)
e0 01 00 00 00 00 00 00 ~ *size in file
e0 01 00 00 00 00 00 00 ~ *size in memory
00 00 00 00 00 00 00 00 ~ segment alignment
~ This is the start routine, the first thing that runs when the ELF loads.
~ The basic registers preserved across syscalls are rbx, rsp, rbp.
~ To avoid redundant moves, we store the buffer pointer in rbx just once,
~ and keep it there. We've made sure our load origin fits in 32 bits, so
~ we can use imm32 for that. We're going to want to do an indirect load
~ from it, so we can't use rbp for this.
48 c7 c3 d8 01 00 08 ~ 0x80001d8 :rbx mov-reg64-imm32
e8 88 00 00 00 ~ 0x88 call-rel-imm32
~ If the length is 0, we got EOF. If it's less than zero, we got a read
~ error. Either way, we exit. This is a signed comparison, as it needs to
~ be.
48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8
74 72 ~ 0x72 :cc-equal jmp-cc-rel-imm8
7c ee ~ 0xee :cc-less jmp-cc-rel-imm8
~ Now that the length is handled, retrieve the input byte.
48 8b 03 ~ :rbx :rax mov-reg64-indirect-reg64
~ If it's space or linefeed, skip it (go back to the loop start).
48 83 f8 20 ~ 0x20 :rax cmp-reg64-imm8
~ ASCII space
74 ea ~ 0xffffffffffffffea :cc-equal jmp-cc-rel-imm8
48 83 f8 0a ~ 0xa :rax cmp-reg64-imm8
~ ASCII linefeed
74 e4 ~ 0xffffffffffffffe4 :cc-equal jmp-cc-rel-imm8
~ If it's a comment, skip the whole thing.
48 83 f8 7e ~ 0x7e :rax cmp-reg64-imm8
~ ASCII tilde
74 43 ~ 0x43 :cc-equal jmp-cc-rel-imm8
~ Decode the value, or exit with an error.
e8 79 00 00 00 ~ 0x79 call-rel-imm32
~ We use rbp as a place to stash the high nibble.
48 89 c5 ~ :rax :rbp mov-reg64-reg64
48 c1 c5 04 ~ 0x4 :rbp rol-reg64-imm8
~ Now we read another byte.
e8 5a 00 00 00 ~ 0x5a call-rel-imm32
~ Handle the length. A second hex digit is required here.
48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8
74 ac ~ 0xac :cc-equal jmp-cc-rel-imm8
7c c0 ~ 0xc0 :cc-less jmp-cc-rel-imm8
~ Now that the length is handled, retrieve the input byte.
48 8b 03 ~ :rbx :rax mov-reg64-indirect-reg64
~ Decode the value, or exit with an error.
e8 5d 00 00 00 ~ 0x5d call-rel-imm32
~ We OR in the low nibble.
48 0b e8 ~ :rax :rbp or-reg64-reg64
~ Output the byte. We reuse the buffer as a place to store it.
48 89 2b ~ :rbp :rbx mov-indirect-reg64-reg64
48 89 de ~ :rbx :rsi mov-reg64-reg64
~ buffer pointer
48 c7 c2 01 00 00 00 ~ 0x1 :rdx mov-reg64-imm32
~ buffer length
48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32
~ syscall number for sys-write
48 c7 c7 01 00 00 00 ~ 0x1 :rdi mov-reg64-imm32
~ file descriptor 1 is stdout
0f 05 ~ syscall
~ Back to the start of the loop.
eb 9b ~ 0xffffffffffffff9b jmp-rel-imm8
~ Read a byte for the comment.
e8 23 00 00 00 ~ 0x23 call-rel-imm32
~ Handle the length. We're allowed to end in a comment.
48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8
74 0d ~ 0xd :cc-equal jmp-cc-rel-imm8
7c 89 ~ 0x89 :cc-less jmp-cc-rel-imm8
~ Now that the length is handled, retrieve the input byte.
48 8b 03 ~ :rbx :rax mov-reg64-indirect-reg64
~ If it's linefeed, the comment is over.
48 83 f8 0a ~ 0xa :rax cmp-reg64-imm8
~ ASCII linefeed
74 85 ~ 0xffffffffffffff85 :cc-equal jmp-cc-rel-imm8
~ We're still in the comment, keep handling it.
eb e8 ~ 0xffffffffffffffe8 jmp-rel-imm8
~ This is the routine named "exit".
48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32
~ syscall number for sys-exit
48 c7 c7 00 00 00 00 ~ 0x0 :rdi mov-reg64-imm32
~ exit code
0f 05 ~ syscall
~ This is the routine named "read-byte".
~ We use self-xor as a concise way to set registers to zero.
48 33 c0 ~ :rax :rax xor-reg64-reg64
~ syscall number for sys-read
48 33 ff ~ :rdi :rdi xor-reg64-reg64
~ file descriptor 0 is stdin
48 89 de ~ :rbx :rsi mov-reg64-reg64
~ buffer pointer
~ We read one byte at a time, because it makes the loop structure simple.
48 c7 c2 01 00 00 00 ~ 0x1 :rdx mov-reg64-imm32
~ buffer length
0f 05 ~ syscall
c3 ~ ret
~ This is the routine named "decode-nibble".
48 83 e8 30 ~ 0x30 :rax sub-reg64-imm8
~ ASCII zero
7c 29 ~ 0x29 :cc-less jmp-cc-rel-imm8
48 83 f8 0a ~ 0xa :rax cmp-reg64-imm8
72 22 ~ 0x22 :cc-below jmp-cc-rel-imm8
48 83 e8 11 ~ 0x11 :rax sub-reg64-imm8
~ ASCII capital A
7c 1d ~ 0x1d :cc-less jmp-cc-rel-imm8
48 83 c0 0a ~ 0xa :rax add-reg64-imm8
48 83 f8 10 ~ 0x10 :rax cmp-reg64-imm8
72 12 ~ 0x12 :cc-below jmp-cc-rel-imm8
48 83 e8 2a ~ 0x2a :rax sub-reg64-imm8
~ ASCII lowercase a
7c 0d ~ 0xd :cc-less jmp-cc-rel-imm8
48 83 c0 0a ~ 0xa :rax add-reg64-imm8
48 83 f8 10 ~ 0x10 :rax cmp-reg64-imm8
72 02 ~ 0x2 :cc-below jmp-cc-rel-imm8
eb 01 ~ 0x1 jmp-rel-imm8
c3 ~ ret
~ These are the error handler routines.
48 be ae 01 00 08 00 00 00 00 ~ 0x80001ae :rsi mov-reg64-imm64
48 ba 0e 00 00 00 00 00 00 00 ~ 0xe :rdx mov-reg64-imm64
eb 2a ~ 0x2a jmp-rel-imm8
48 be c8 01 00 08 00 00 00 00 ~ 0x80001c8 :rsi mov-reg64-imm64
48 ba 10 00 00 00 00 00 00 00 ~ 0x10 :rdx mov-reg64-imm64
eb 14 ~ 0x14 jmp-rel-imm8
48 be bc 01 00 08 00 00 00 00 ~ 0x80001bc :rsi mov-reg64-imm64
48 ba 0c 00 00 00 00 00 00 00 ~ 0xc :rdx mov-reg64-imm64
48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32
~ syscall number for sys-write
48 c7 c7 02 00 00 00 ~ 0x2 :rdi mov-reg64-imm32
~ file descriptor 2 is stderr
0f 05 ~ syscall
48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32
~ syscall number for sys-exit
48 c7 c7 01 00 00 00 ~ 0x1 :rdi mov-reg64-imm32
~ exit code
0f 05 ~ syscall
~ These are the message strings.
~ String literal with null terminator: "Invalid byte."
49 6e 76 61 6c 69 64 20 62 79 74 65 2e 00
~ String literal with null terminator: "Read error."
52 65 61 64 20 65 72 72 6f 72 2e 00
~ String literal with null terminator: "Unexpected EOF."
55 6e 65 78 70 65 63 74 65 64 20 45 4f 46 2e 00
~ This is a buffer that's zero in the ELF, but is written to at runtime
~ and used as a variable.
00 00 00 00 00 00 00 00
~ This is the end of the hex dump.
|