1 /* radare2 - LGPL - Copyright 2017-2018 - rkx1209 */
2 
3 #include <r_types.h>
4 #include <r_util.h>
5 #include <r_lib.h>
6 #include <r_bin.h>
7 #include <r_io.h>
8 #include <r_cons.h>
9 #include "nxo/nxo.h"
10 #ifdef R_MESON_VERSION
11 #include <lz4.h>
12 #else
13 #include "../../../shlr/lz4/lz4.c"
14 #endif
15 
16 #define NSO_OFF(x) r_offsetof (NSOHeader, x)
17 #define NSO_OFFSET_MODMEMOFF r_offsetof (NXOStart, mod_memoffset)
18 
19 // starting at 0
20 typedef struct {
21 	ut32 magic;	// NSO0
22 	ut32 pad0;	// 4
23 	ut32 pad1;	// 8
24 	ut32 pad2;	// 12
25 	ut32 text_memoffset;	// 16
26 	ut32 text_loc;	// 20
27 	ut32 text_size;	// 24
28 	ut32 pad3;	// 28
29 	ut32 ro_memoffset;	// 32
30 	ut32 ro_loc;	// 36
31 	ut32 ro_size;	// 40
32 	ut32 pad4;	// 44
33 	ut32 data_memoffset;	// 48
34 	ut32 data_loc;	// 52
35 	ut32 data_size;	// 56
36 	ut32 bss_size;	// 60
37 } NSOHeader;
38 
decompress(const ut8 * cbuf,ut8 * obuf,int32_t csize,int32_t usize)39 static uint32_t decompress(const ut8 *cbuf, ut8 *obuf, int32_t csize, int32_t usize) {
40 	if (csize < 0 || usize < 0 || !cbuf || !obuf) {
41 		return -1;
42 	}
43 	return LZ4_decompress_safe ((const char*)cbuf, (char*)obuf, (uint32_t) csize, (uint32_t) usize);
44 }
45 
baddr(RBinFile * bf)46 static ut64 baddr(RBinFile *bf) {
47 	return 0x8000000;
48 }
49 
check_buffer(RBuffer * b)50 static bool check_buffer(RBuffer *b) {
51 	if (r_buf_size (b) >= 0x20) {
52 		ut8 magic[4];
53 		if (r_buf_read_at (b, 0, magic, sizeof (magic)) != 4) {
54 			return false;
55 		}
56 		return fileType (magic) != NULL;
57 	}
58 	return false;
59 }
60 
nso_new(void)61 static RBinNXOObj *nso_new(void) {
62 	RBinNXOObj *bin = R_NEW0 (RBinNXOObj);
63 	if (bin) {
64 		bin->methods_list = r_list_newf ((RListFree)free);
65 		bin->imports_list = r_list_newf ((RListFree)free);
66 		bin->classes_list = r_list_newf ((RListFree)free);
67 	}
68 	return bin;
69 }
70 
load_bytes(RBinFile * bf,void ** bin_obj,const ut8 * buf,ut64 sz,ut64 loadaddr,Sdb * sdb)71 static bool load_bytes(RBinFile *bf, void **bin_obj, const ut8 *buf, ut64 sz, ut64 loadaddr, Sdb *sdb) {
72 	eprintf ("load_bytes in bin.nso must die\n");
73 	RBin *rbin = bf->rbin;
74 	ut32 toff = r_buf_read_le32_at (bf->buf, NSO_OFF (text_memoffset));
75 	ut32 tsize = r_buf_read_le32_at (bf->buf, NSO_OFF (text_size));
76 	ut32 rooff = r_buf_read_le32_at (bf->buf, NSO_OFF (ro_memoffset));
77 	ut32 rosize = r_buf_read_le32_at (bf->buf, NSO_OFF (ro_size));
78 	ut32 doff = r_buf_read_le32_at (bf->buf, NSO_OFF (data_memoffset));
79 	ut32 dsize = r_buf_read_le32_at (bf->buf, NSO_OFF (data_size));
80 	ut64 total_size = tsize + rosize + dsize;
81 	RBuffer *newbuf = r_buf_new_empty (total_size);
82 	ut64 ba = baddr (bf);
83 	ut8 *tmp = NULL;
84 
85 	if (rbin->iob.io && !(rbin->iob.io->cached & R_PERM_W)) {
86 		eprintf ("Please add \'-e io.cache=true\' option to r2 command. This is required to decompress the code.\n");
87 		goto fail;
88 	}
89 	/* Decompress each sections */
90 	tmp = R_NEWS (ut8, tsize);
91 	if (!tmp) {
92 		goto fail;
93 	}
94 	if (decompress (buf + toff, tmp, rooff - toff, tsize) != tsize) {
95 		eprintf ("decompression failure\n");
96 		goto fail;
97 	}
98 	r_buf_write_at (newbuf, 0, tmp, tsize);
99 	R_FREE (tmp);
100 
101 	tmp = R_NEWS (ut8, rosize);
102 	if (!tmp) {
103 		goto fail;
104 	}
105 	if (decompress (buf + rooff, tmp, doff - rooff, rosize) != rosize) {
106 		eprintf ("decompression2 failure\n");
107 		goto fail;
108 	}
109 	r_buf_write_at (newbuf, tsize, tmp, rosize);
110 	R_FREE (tmp);
111 
112 	tmp = R_NEWS (ut8, dsize);
113 	if (!tmp) {
114 		goto fail;
115 	}
116 	if (decompress (buf + doff, tmp, r_buf_size (bf->buf) - doff, dsize) != dsize) {
117 		eprintf ("decompression3 failure\n");
118 		goto fail;
119 	}
120 	r_buf_write_at (newbuf, tsize + rosize, tmp, dsize);
121 	R_FREE (tmp);
122 
123 	/* Load unpacked binary */
124 	const ut8 *tmpbuf = r_buf_data (newbuf, &total_size);
125 	r_io_write_at (rbin->iob.io, ba, tmpbuf, total_size);
126 	ut32 modoff = r_buf_read_le32_at (newbuf, NSO_OFFSET_MODMEMOFF);
127 	RBinNXOObj *bin = nso_new ();
128 	eprintf ("MOD Offset = 0x%"PFMT64x"\n", (ut64)modoff);
129 	parseMod (newbuf, bin, modoff, ba);
130 	r_buf_free (newbuf);
131 	*bin_obj = bin;
132 	return true;
133 fail:
134 	free (tmp);
135 	r_buf_free (newbuf);
136 	*bin_obj = NULL;
137 	return false;
138 }
139 
load_buffer(RBinFile * bf,void ** bin_obj,RBuffer * buf,ut64 loadaddr,Sdb * sdb)140 static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *buf, ut64 loadaddr, Sdb *sdb) {
141 	r_return_val_if_fail (bf && buf, NULL);
142 	const ut64 la = bf->loadaddr;
143 	ut64 sz = 0;
144 	const ut8 *bytes = r_buf_data (buf, &sz);
145 	return load_bytes (bf, bin_obj, bytes, sz, la, bf->sdb);
146 }
147 
binsym(RBinFile * bf,int type)148 static RBinAddr *binsym(RBinFile *bf, int type) {
149 	return NULL; // TODO
150 }
151 
entries(RBinFile * bf)152 static RList *entries(RBinFile *bf) {
153 	RList *ret;
154 	RBinAddr *ptr = NULL;
155 	RBuffer *b = bf->buf;
156 	if (!(ret = r_list_new ())) {
157 		return NULL;
158 	}
159 	ret->free = free;
160 	if ((ptr = R_NEW0 (RBinAddr))) {
161 		ptr->paddr = r_buf_read_le32_at (b, NSO_OFF (text_memoffset));
162 		ptr->vaddr = r_buf_read_le32_at (b, NSO_OFF (text_loc)) + baddr (bf);
163 		r_list_append (ret, ptr);
164 	}
165 	return ret;
166 }
167 
get_sdb(RBinFile * bf)168 static Sdb *get_sdb(RBinFile *bf) {
169 	Sdb *kv = sdb_new0 ();
170 	sdb_num_set (kv, "nso_start.offset", 0, 0);
171 	sdb_num_set (kv, "nso_start.size", 16, 0);
172 	sdb_set (kv, "nso_start.format", "xxq unused mod_memoffset padding", 0);
173 	sdb_num_set (kv, "nso_header.offset", 0, 0);
174 	sdb_num_set (kv, "nso_header.size", 0x40, 0);
175 	sdb_set (kv, "nso_header.format", "xxxxxxxxxxxx magic unk size unk2 text_offset text_size ro_offset ro_size data_offset data_size bss_size unk3", 0);
176 	sdb_ns_set (bf->sdb, "info", kv);
177 	return kv;
178 }
179 
sections(RBinFile * bf)180 static RList *sections(RBinFile *bf) {
181 	RList *ret = NULL;
182 	RBinSection *ptr = NULL;
183 	RBuffer *b = bf->buf;
184 	if (!bf->o->info) {
185 		return NULL;
186 	}
187 	if (!(ret = r_list_new ())) {
188 		return NULL;
189 	}
190 	ret->free = free;
191 
192 	ut64 ba = baddr (bf);
193 
194 	if (!(ptr = R_NEW0 (RBinSection))) {
195 		return ret;
196 	}
197 	ptr->name = strdup ("header");
198 	ptr->size = r_buf_read_le32_at (b, NSO_OFF (text_memoffset));
199 	ptr->vsize = r_buf_read_le32_at (b, NSO_OFF (text_memoffset));
200 	ptr->paddr = 0;
201 	ptr->vaddr = 0;
202 	ptr->perm = R_PERM_R;
203 	ptr->add = false;
204 	r_list_append (ret, ptr);
205 
206 	// add text segment
207 	if (!(ptr = R_NEW0 (RBinSection))) {
208 		return ret;
209 	}
210 	ptr->name = strdup ("text");
211 	ptr->vsize = r_buf_read_le32_at (b, NSO_OFF (text_size));
212 	ptr->size = ptr->vsize;
213 	ptr->paddr = r_buf_read_le32_at (b, NSO_OFF (text_memoffset));
214 	ptr->vaddr = r_buf_read_le32_at (b, NSO_OFF (text_loc)) + ba;
215 	ptr->perm = R_PERM_RX;	// r-x
216 	ptr->add = true;
217 	r_list_append (ret, ptr);
218 
219 	// add ro segment
220 	if (!(ptr = R_NEW0 (RBinSection))) {
221 		return ret;
222 	}
223 	ptr->name = strdup ("ro");
224 	ptr->vsize = r_buf_read_le32_at (b, NSO_OFF (ro_size));
225 	ptr->size = ptr->vsize;
226 	ptr->paddr = r_buf_read_le32_at (b, NSO_OFF (ro_memoffset));
227 	ptr->vaddr = r_buf_read_le32_at (b, NSO_OFF (ro_loc)) + ba;
228 	ptr->perm = R_PERM_R;	// r--
229 	ptr->add = true;
230 	r_list_append (ret, ptr);
231 
232 	// add data segment
233 	if (!(ptr = R_NEW0 (RBinSection))) {
234 		return ret;
235 	}
236 	ptr->name = strdup ("data");
237 	ptr->vsize = r_buf_read_le32_at (b, NSO_OFF (data_size));
238 	ptr->size = ptr->vsize;
239 	ptr->paddr = r_buf_read_le32_at (b, NSO_OFF (data_memoffset));
240 	ptr->vaddr = r_buf_read_le32_at (b, NSO_OFF (data_loc)) + ba;
241 	ptr->perm = R_PERM_RW;
242 	ptr->add = true;
243 	eprintf ("BSS Size 0x%08"PFMT64x "\n", (ut64)
244 		r_buf_read_le32_at (bf->buf, NSO_OFF (bss_size)));
245 	r_list_append (ret, ptr);
246 	return ret;
247 }
248 
info(RBinFile * bf)249 static RBinInfo *info(RBinFile *bf) {
250 	RBinInfo *ret = R_NEW0 (RBinInfo);
251 	if (!ret) {
252 		return NULL;
253 	}
254 	ut8 magic[4];
255 	if (r_buf_read_at (bf->buf, NSO_OFF (magic), magic, sizeof (magic)) != sizeof (magic)) {
256 		free (ret);
257 		return NULL;
258 	}
259 
260 	const char *ft = fileType (magic);
261 	if (!ft) {
262 		ft = "nso";
263 	}
264 	ret->file = strdup (bf->file);
265 	ret->rclass = strdup (ft);
266 	ret->os = strdup ("switch");
267 	ret->arch = strdup ("arm");
268 	ret->machine = strdup ("Nintendo Switch");
269 	ret->subsystem = strdup (ft);
270 	ret->bclass = strdup ("program");
271 	ret->type = strdup ("EXEC (executable file)");
272 	ret->bits = 64;
273 	ret->has_va = true;
274 	ret->has_lit = true;
275 	ret->big_endian = false;
276 	ret->dbg_info = 0;
277 	return ret;
278 }
279 
280 #if !R_BIN_NSO
281 
282 RBinPlugin r_bin_plugin_nso = {
283 	.name = "nso",
284 	.desc = "Nintendo Switch NSO0 binaries",
285 	.license = "MIT",
286 	.load_buffer = &load_buffer,
287 	.check_buffer = &check_buffer,
288 	.baddr = &baddr,
289 	.binsym = &binsym,
290 	.entries = &entries,
291 	.sections = &sections,
292 	.get_sdb = &get_sdb,
293 	.info = &info,
294 };
295 
296 #ifndef R2_PLUGIN_INCORE
297 R_API RLibStruct radare_plugin = {
298 	.type = R_LIB_TYPE_BIN,
299 	.data = &r_bin_plugin_nso,
300 	.version = R2_VERSION
301 };
302 #endif
303 #endif
304