-
Notifications
You must be signed in to change notification settings - Fork 52
/
ROMFS.bt
executable file
·278 lines (230 loc) · 7.59 KB
/
ROMFS.bt
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
//--------------------------------------
//--- 010 Editor v5.0beta1 Binary Template
//
// File: ROMFS.bt
// Author: Jordan Milne <[email protected]>
// Revision: v0.2
// Purpose: Reverse engineering romfs images used in embedded devices
//--------------------------------------
//test suite in hex format at https://github.com/chexum/genromfs/tree/master/selftest
//format documentation at https://www.kernel.org/doc/Documentation/filesystems/romfs.txt
RequiresVersion(4, 0);
//RomFS uses big endian throughout
BigEndian();
//Color correct checksums green, bad checksums red
//Print warnings about correctness issues
#define STRICT_CHECKS 1
//Max number of bytes to read for the superblock checksum
//Defaults to 512 as per the spec.
#define MAX_SB_CHECKSUM_BYTES 512
//Magic bytes that identify a romfs image
#define MAGIC_BYTES_LEN 8
local char MAGIC_BYTES[] = "-rom1fs-";
//round up to the nearest multiple of 16
int pad16(int val)
{
return ((val & 0xf) ? (val + 16) & ~0xf : val);
}
//returns 0 for correct magic bytes, anything else is failure
int RomFSCheckMagicBytes()
{
local char found_bytes[MAGIC_BYTES_LEN];
ReadBytes(found_bytes, 0, MAGIC_BYTES_LEN);
return (Memcmp(MAGIC_BYTES, found_bytes, MAGIC_BYTES_LEN));
}
//returns 0 if checksum field for the superblock is correct and generate is 0
//returns the generated checksum if generate is 1
int32 RomFSSBChecksum(int generate)
{
local int64 skip_pos = -1;
local int32 sum;
//skip the checksum field when calculating the checksum
if(generate)
skip_pos = 3;
sum = RomFSChecksum(0, MAX_SB_CHECKSUM_BYTES, skip_pos);
//negate sum so checksum will work out to 0 when put it's put in the checksum field
//and generate is 0
if(generate)
sum = -sum;
return sum;
}
//alternative to ChecksumAlgStr CHECKSUM_INT_BE that can skip a byte
int32 RomFSChecksum(int64 pos, int64 num_bytes, int64 skip_pos)
{
local int64 i;
local int32 sum = 0;
local int64 file_size = FileSize();
if(num_bytes > file_size)
num_bytes = file_size;
//convert the number of bytes to number of ints
local int64 len = num_bytes >> 2;
for(i = 0; i < len; ++i)
{
if(i != skip_pos)
sum += ReadInt(pos + (i * 4));
}
return sum;
}
//get the length of the RomFS string at pos
int RomFSReadStringLength(int64 pos)
{
return pad16(ReadStringLength(pos));
}
//ROMFS strings are null-terminated and padded to 16 byte boundaries.
typedef struct {
string val;
//length (including terminating null)
local int len = Strlen(val) + 1;
local int pad_len = 16 - (len % 16);
//take up the rest of the space to reach the 16 byte boundary
if(pad_len)
char padding[pad_len];
} RomFSString <read=ReadRomFSString, write=WriteRomFSString>;
string ReadRomFSString(RomFSString& v)
{
string s;
SPrintf( s, "%s", v.val );
return s;
}
//Beware strings shorter than than old_len % 16 and longer than old_len + pad,
//they'll invalidate all of the positions (next_header, etc) in the entire image. Oh dear!
void WriteRomFSString(RomFSString& orig_str, string s)
{
local int start = startof(orig_str);
//Delete the old string + padding
DeleteBytes(start, RomFSReadStringLength(start));
//Make way for the new string
InsertBytes(start, pad16(Strlen(s) + 1));
//write in the new string
WriteString(start, s);
}
typedef struct {
#ifdef STRICT_CHECKS
if(RomFSCheckMagicBytes())
{
SetBackColor(cLtRed);
Printf("Incorrect magic bytes at %xh, expected \"%s\"\n", FTell(), MAGIC_BYTES);
}
else
SetBackColor(cLtGreen);
#endif
char magic_bytes[MAGIC_BYTES_LEN] <comment="Magic bytes">;
SetBackColor(cNone);
uint32 fs_size <comment="Filesystem size (in bytes)">;
#ifdef STRICT_CHECKS
//verify the checksum of the superblock (image)
if(RomFSSBChecksum(0))
{
SetBackColor(cLtRed);
Printf("Checksum Mismatch on superblock at %xh\n", FTell());
}
else
SetBackColor(cLtGreen);
#endif
int32 checksum <comment="Sum of the first 512 (or all, if the file is smaller) bytes of the file">;
SetBackColor(cNone);
RomFSString vol_name;
} RomFSHeader;
typedef enum rom_fs_entry_type {
HARD_LINK = 0,
DIRECTORY = 1,
FILE = 2,
SYMLINK = 3,
BLOCK_DEV = 4,
CHAR_DEV = 5,
SOCKET = 6,
FIFO = 7
} RomFSEntryType;
typedef int32 RomFSNextHeader <read=ReadNextHeader, write=WriteNextHeader>;
typedef struct {
RomFSNextHeader next_header : 28 <name="next_header", comment="Position of the next entry in the directory">;
int executable : 1 <name="executable", comment="Whether or not the entry is executable">;
RomFSEntryType type : 3 <name="type", comment="Type of entry">;
} RomFSObjectHdr <optimize=false, read=ReadRomFSObjectHdr>;
string ReadRomFSObjectHdr(RomFSObjectHdr& hdr)
{
string s;
SPrintf(s, "Exec: %d, Next: %s", hdr.executable, ReadNextHeader(hdr.next_header));
return s;
}
string ReadNextHeader(int32 next_header)
{
string s;
SPrintf(s, "%d", next_header << 4);
return s;
}
void WriteNextHeader(RomFSNextHeader& v, string s)
{
SScanf(s, "%d", v);
v >>= 4;
}
//some people generate invalid dir entries for . and ..
//and mess up parsing. ignore them.
int ValidDirName(string name)
{
return (name != "." && name != "..");
}
typedef struct {
SetBackColor(cLtPurple);
RomFSObjectHdr header;
SetBackColor(0xB9E2FF);
local RomFSEntryType type = header.type;
if(type == DIRECTORY)
int32 special_info <comment="position of the first file entry">;
else if(type == HARD_LINK)
int32 special_info <comment="link destination">;
else if(type == BLOCK_DEV)
int32 special_info <comment="16/16 major/minor number">;
else
int32 special_info <comment="Must be zero">;
int32 data_size <comment="Size of data in bytes (should be 0 except for files and symlinks">;
local int64 struct_start = startof(this);
#ifdef STRICT_CHECKS
//get the length of the struct (with the filename)
local int64 struct_len = 16 + RomFSReadStringLength(struct_start + 16);
if(RomFSChecksum(struct_start, struct_len, -1))
{
Printf("Checksum Mismatch on entry at %xh\n", FTell());
SetBackColor(cRed);
}
else
SetBackColor(cGreen);
#endif
int32 checksum <comment="Checksum for the entire entry excluding data field">;
SetBackColor(0xE2B9FF);
RomFSString name;
if(data_size)
{
SetBackColor(0xFFE2B9);
ubyte data[data_size] <comment="File contents or symlink destination">;
}
} RomFSObject <read=ReadRomFSObject, optimize=false>;
//Try to create entries for the subdirectory's children (if it's a directory and it has children)
void RomFSTryRecurse(RomFSObject& obj)
{
//don't recurse down self-referential or invalid directories
if(obj.header.type == DIRECTORY && ValidDirName(obj.name.val) && obj.special_info != startof(obj))
Recurse(obj.special_info);
}
string ReadRomFSObject(RomFSObject& v)
{
string s;
string type = EnumToString(v.header.type);
SPrintf(s, "%s: %s", type, v.name.val);
return s;
}
RomFSHeader header<optimize=false, comment="Filesystem header">;
void Recurse(int64 pos)
{
//while there are still files in the directory
while(pos)
{
FSeek(pos);
RomFSObject entry<comment="Filesystem entry">;
//Get the position of the next entry in the current directory
pos = entry.header.next_header << 4;
//Parse all of the children of this entry if it's valid directory
RomFSTryRecurse(entry);
}
}
Recurse(FTell());