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 = §ions,
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