1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Proxmark3 RDV40 Flash memory commands
17 //-----------------------------------------------------------------------------
18 #include "cmdflashmemspiffs.h"
21 #include "cmdparser.h" // command_t
23 #include "fileutils.h" //saveFile
24 #include "comms.h" //getfromdevice
25 #include "cliparser.h"
27 static int CmdHelp(const char *Cmd
);
29 int flashmem_spiffs_load(const char *destfn
, const uint8_t *data
, size_t datalen
) {
31 int ret_val
= PM3_SUCCESS
;
33 // We want to mount before multiple operation so the lazy writes/append will not
34 // trigger a mount + umount each loop iteration (lazy ops device side)
35 SendCommandNG(CMD_SPIFFS_MOUNT
, NULL
, 0);
38 uint32_t bytes_sent
= 0;
39 uint32_t bytes_remaining
= datalen
;
42 g_conn
.block_after_ACK
= true;
44 while (bytes_remaining
> 0) {
46 uint32_t bytes_in_packet
= MIN(FLASH_MEM_BLOCK_SIZE
, bytes_remaining
);
48 flashmem_write_t
*payload
= calloc(1, sizeof(flashmem_write_t
) + bytes_in_packet
);
50 payload
->append
= (bytes_sent
> 0);
52 uint8_t fnlen
= MIN(sizeof(payload
->fn
), strlen(destfn
));
54 payload
->fnlen
= fnlen
;
55 memcpy(payload
->fn
, destfn
, fnlen
);
57 payload
->bytes_in_packet
= bytes_in_packet
;
58 memset(payload
->data
, 0, bytes_in_packet
);
59 memcpy(payload
->data
, data
+ bytes_sent
, bytes_in_packet
);
61 PacketResponseNG resp
;
63 SendCommandNG(CMD_SPIFFS_WRITE
, (uint8_t *)payload
, sizeof(flashmem_write_t
) + bytes_in_packet
);
67 bytes_remaining
-= bytes_in_packet
;
68 bytes_sent
+= bytes_in_packet
;
71 while (WaitForResponseTimeout(CMD_SPIFFS_WRITE
, &resp
, 2000) == false) {
72 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
75 ret_val
= PM3_ETIMEOUT
;
84 // turn off fast push mode
85 g_conn
.block_after_ACK
= false;
87 // We want to unmount after these to set things back to normal but more than this
88 // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory
89 SendCommandNG(CMD_SPIFFS_UNMOUNT
, NULL
, 0);
93 int flashmem_spiffs_download(char *fn
, uint8_t fnlen
, void **pdest
, size_t *destlen
) {
94 // get size from spiffs itself !
96 SendCommandNG(CMD_SPIFFS_STAT
, (uint8_t *)fn
, fnlen
);
97 PacketResponseNG resp
;
98 if (WaitForResponseTimeout(CMD_SPIFFS_STAT
, &resp
, 2000) == false) {
99 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
103 uint32_t len
= resp
.data
.asDwords
[0];
105 PrintAndLogEx(ERR
, "error, failed to retrieve file stats on SPIFFSS");
109 *pdest
= calloc(len
, sizeof(uint8_t));
110 if (*pdest
== false) {
111 PrintAndLogEx(ERR
, "error, cannot allocate memory ");
115 uint32_t start_index
= 0;
116 PrintAndLogEx(INFO
, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len
, fn
);
118 if (GetFromDevice(SPIFFS
, *pdest
, len
, start_index
, (uint8_t *)fn
, fnlen
, NULL
, -1, true) == 0) {
119 PrintAndLogEx(FAILED
, "error, downloading from spiffs");
128 static int CmdFlashMemSpiFFSMount(const char *Cmd
) {
129 CLIParserContext
*ctx
;
130 CLIParserInit(&ctx
, "mem spiffs mount",
131 "Mount the SPIFFS file system if not already mounted",
138 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
140 clearCommandBuffer();
141 SendCommandNG(CMD_SPIFFS_MOUNT
, NULL
, 0);
145 static int CmdFlashMemSpiFFSUnmount(const char *Cmd
) {
146 CLIParserContext
*ctx
;
147 CLIParserInit(&ctx
, "mem spiffs unmount",
148 "Un-mount the SPIFFS file system",
149 "mem spiffs unmount");
155 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
157 clearCommandBuffer();
158 SendCommandNG(CMD_SPIFFS_UNMOUNT
, NULL
, 0);
162 static int CmdFlashMemSpiFFSTest(const char *Cmd
) {
163 CLIParserContext
*ctx
;
164 CLIParserInit(&ctx
, "mem spiffs test",
165 "Test SPIFFS Operations, require wiping pages 0 and 1",
172 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
174 clearCommandBuffer();
175 SendCommandNG(CMD_SPIFFS_TEST
, NULL
, 0);
179 static int CmdFlashMemSpiFFSCheck(const char *Cmd
) {
180 CLIParserContext
*ctx
;
181 CLIParserInit(&ctx
, "mem spiffs check",
182 "Check/try to defrag faulty/fragmented SPIFFS file system",
189 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
191 clearCommandBuffer();
192 SendCommandNG(CMD_SPIFFS_CHECK
, NULL
, 0);
196 static int CmdFlashMemSpiFFSTree(const char *Cmd
) {
197 CLIParserContext
*ctx
;
198 CLIParserInit(&ctx
, "mem spiffs tree",
199 "Print the Flash memory file system tree",
206 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
209 PrintAndLogEx(INFO
, "--- " _CYAN_("Flash Memory tree (SPIFFS)") " -----------------");
210 clearCommandBuffer();
211 SendCommandNG(CMD_SPIFFS_PRINT_TREE
, NULL
, 0);
215 static int CmdFlashMemSpiFFSInfo(const char *Cmd
) {
216 CLIParserContext
*ctx
;
217 CLIParserInit(&ctx
, "mem spiffs info",
218 "Print file system info and usage statistics",
225 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
228 PrintAndLogEx(INFO
, "--- " _CYAN_("Flash Memory info (SPIFFS)") " -----------------");
229 clearCommandBuffer();
230 SendCommandNG(CMD_SPIFFS_PRINT_FSINFO
, NULL
, 0);
234 static int CmdFlashMemSpiFFSRemove(const char *Cmd
) {
235 CLIParserContext
*ctx
;
236 CLIParserInit(&ctx
, "mem spiffs remove",
237 "Remove a file from SPIFFS filesystem",
238 "mem spiffs remove -f lasttag.bin"
243 arg_str1("f", "file", "<fn>", "file to remove"),
246 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
249 char filename
[32] = {0};
250 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, 32, &fnlen
);
253 PrintAndLogEx(DEBUG
, "Removing `" _YELLOW_("%s") "`", filename
);
259 memcpy(payload
.fn
, filename
, fnlen
);
261 PacketResponseNG resp
;
262 clearCommandBuffer();
263 SendCommandNG(CMD_SPIFFS_REMOVE
, (uint8_t *)&payload
, sizeof(payload
));
264 WaitForResponse(CMD_SPIFFS_REMOVE
, &resp
);
265 if (resp
.status
== PM3_SUCCESS
) {
266 PrintAndLogEx(INFO
, "Done!");
271 static int CmdFlashMemSpiFFSRename(const char *Cmd
) {
272 CLIParserContext
*ctx
;
273 CLIParserInit(&ctx
, "mem spiffs rename",
274 "Rename/move a file from SPIFFS filesystem.",
275 "mem spiffs rename -s aaa.bin -d bbb.bin"
280 arg_str1("s", "src", "<fn>", "source file name"),
281 arg_str1("d", "dest", "<fn>", "destination file name"),
284 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
288 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)src
, 32, &slen
);
292 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)dest
, 32, &dlen
);
295 PrintAndLogEx(DEBUG
, "Rename from `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src
, dest
);
306 memcpy(payload
.src
, src
, slen
);
307 memcpy(payload
.dest
, dest
, dlen
);
309 PacketResponseNG resp
;
310 clearCommandBuffer();
311 SendCommandNG(CMD_SPIFFS_RENAME
, (uint8_t *)&payload
, sizeof(payload
));
312 WaitForResponse(CMD_SPIFFS_RENAME
, &resp
);
313 if (resp
.status
== PM3_SUCCESS
) {
314 PrintAndLogEx(INFO
, "Done!");
317 PrintAndLogEx(HINT
, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
321 static int CmdFlashMemSpiFFSCopy(const char *Cmd
) {
323 CLIParserContext
*ctx
;
324 CLIParserInit(&ctx
, "mem spiffs copy",
325 "Copy a file to another (destructively) in SPIFFS file system",
326 "mem spiffs copy -s aaa.bin -d aaa_cpy.bin"
331 arg_str1("s", "src", "<fn>", "source file name"),
332 arg_str1("d", "dest", "<fn>", "destination file name"),
335 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
339 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)src
, 32, &slen
);
343 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)dest
, 32, &dlen
);
355 memcpy(payload
.src
, src
, slen
);
356 memcpy(payload
.dest
, dest
, dlen
);
358 PacketResponseNG resp
;
359 clearCommandBuffer();
360 SendCommandNG(CMD_SPIFFS_COPY
, (uint8_t *)&payload
, sizeof(payload
));
361 WaitForResponse(CMD_SPIFFS_COPY
, &resp
);
362 if (resp
.status
== PM3_SUCCESS
) {
363 PrintAndLogEx(INFO
, "Done!");
366 PrintAndLogEx(HINT
, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
370 static int CmdFlashMemSpiFFSDump(const char *Cmd
) {
371 CLIParserContext
*ctx
;
372 CLIParserInit(&ctx
, "mem spiffs dump",
373 "Dumps device SPIFFS file to a local file\n"
374 "Size is handled by first sending a STAT command against file to verify existence",
375 "mem spiffs dump -s tag.bin --> download binary file from device, saved as `tag.bin`\n"
376 "mem spiffs dump -s tag.bin -d a001 --> download tag.bin, save as `a001.bin`\n"
377 "mem spiffs dump -s tag.bin -t --> download tag.bin into trace buffer"
382 arg_str1("s", "src", "<fn>", "SPIFFS file to save"),
383 arg_str0("d", "dest", "<fn>", "file name to save to <w/o .bin>"),
384 arg_lit0("t", "trace", "download into trace buffer"),
387 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
390 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)src
, 32, &slen
);
393 char dest
[FILE_PATH_SIZE
] = {0};
394 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)dest
, FILE_PATH_SIZE
, &dlen
);
396 bool to_trace
= arg_get_lit(ctx
, 3);
399 // get size from spiffs itself !
400 clearCommandBuffer();
401 SendCommandNG(CMD_SPIFFS_STAT
, (uint8_t *)src
, slen
);
402 PacketResponseNG resp
;
403 if (WaitForResponseTimeout(CMD_SPIFFS_STAT
, &resp
, 2000) == false) {
404 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
408 uint32_t len
= resp
.data
.asDwords
[0];
409 uint8_t *dump
= calloc(len
, sizeof(uint8_t));
411 PrintAndLogEx(ERR
, "error, cannot allocate memory ");
415 // download from device
416 uint32_t start_index
= 0;
417 PrintAndLogEx(INFO
, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len
, src
);
418 if (GetFromDevice(SPIFFS
, dump
, len
, start_index
, (uint8_t *)src
, slen
, NULL
, -1, true) == false) {
419 PrintAndLogEx(FAILED
, "error, downloading from spiffs");
425 // copy to client trace buffer
426 if (ImportTraceBuffer(dump
, len
) == false) {
427 PrintAndLogEx(FAILED
, "error, copying to trace buffer");
431 PrintAndLogEx(HINT
, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save");
438 char fn
[FILE_PATH_SIZE
] = {0};
443 strncpy(fn
, dest
, dlen
);
445 strncpy(fn
, src
, slen
);
448 // set file extension
449 const char *suffix
= strchr(fn
, '.');
451 saveFile(fn
, suffix
, dump
, len
);
453 saveFile(fn
, ".bin", dump
, len
);
460 static int CmdFlashMemSpiFFSWipe(const char *Cmd
) {
461 CLIParserContext
*ctx
;
462 CLIParserInit(&ctx
, "mem spiffs wipe",
463 _RED_("* * * Warning * * *") " \n"
464 _CYAN_("This command wipes all files on the device SPIFFS file system"),
471 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
474 PrintAndLogEx(INFO
, "Wiping all files from SPIFFS file system");
475 PacketResponseNG resp
;
476 clearCommandBuffer();
477 SendCommandNG(CMD_SPIFFS_WIPE
, NULL
, 0);
478 WaitForResponse(CMD_SPIFFS_WIPE
, &resp
);
479 if (resp
.status
== PM3_SUCCESS
) {
480 PrintAndLogEx(INFO
, "Done!");
483 PrintAndLogEx(HINT
, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
487 static int CmdFlashMemSpiFFSUpload(const char *Cmd
) {
488 CLIParserContext
*ctx
;
489 CLIParserInit(&ctx
, "mem spiffs upload",
490 "Uploads binary-wise file into device file system\n"
491 "Warning: mem area to be written must have been wiped first.\n"
492 "This is already taken care when loading dictionaries.\n"
493 "File names can only be 32 bytes long on device SPIFFS",
494 "mem spiffs upload -s local.bin -d dest.bin"
499 arg_str1("s", "src", "<fn>", "source file name"),
500 arg_str1("d", "dest", "<fn>", "destination file name"),
503 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
506 char src
[FILE_PATH_SIZE
] = {0};
507 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)src
, FILE_PATH_SIZE
, &slen
);
511 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)dest
, 32, &dlen
);
514 PrintAndLogEx(DEBUG
, "Upload `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src
, dest
);
517 uint8_t *data
= NULL
;
519 int res
= loadFile_safe(src
, "", (void **)&data
, &datalen
);
520 if (res
!= PM3_SUCCESS
) {
525 res
= flashmem_spiffs_load(dest
, data
, datalen
);
528 if (res
== PM3_SUCCESS
)
529 PrintAndLogEx(SUCCESS
, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen
, dest
);
531 PrintAndLogEx(HINT
, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
535 static int CmdFlashMemSpiFFSView(const char *Cmd
) {
537 CLIParserContext
*ctx
;
538 CLIParserInit(&ctx
, "mem spiffs view",
539 "View a file on flash memory on device in console",
540 "mem spiffs view -f tag.bin"
545 arg_str1("f", "file", "<fn>", "SPIFFS file to view"),
546 arg_int0("c", "cols", "<dec>", "column breaks (def 16)"),
549 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
553 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)fn
, 32, &fnlen
);
555 int breaks
= arg_get_int_def(ctx
, 2, 16);
558 uint8_t *dump
= NULL
;
560 int res
= flashmem_spiffs_download(fn
, fnlen
, (void **)&dump
, &dumplen
);
561 if (res
!= PM3_SUCCESS
) {
565 PrintAndLogEx(NORMAL
, "");
566 print_hex_break(dump
, dumplen
, breaks
);
567 PrintAndLogEx(NORMAL
, "");
572 static command_t CommandTable
[] = {
573 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
574 {"copy", CmdFlashMemSpiFFSCopy
, IfPm3Flash
, "Copy a file to another (destructively) in SPIFFS file system"},
575 {"check", CmdFlashMemSpiFFSCheck
, IfPm3Flash
, "Check/try to defrag faulty/fragmented file system"},
576 {"dump", CmdFlashMemSpiFFSDump
, IfPm3Flash
, "Dump a file from SPIFFS file system"},
577 {"info", CmdFlashMemSpiFFSInfo
, IfPm3Flash
, "Print file system info and usage statistics"},
578 {"mount", CmdFlashMemSpiFFSMount
, IfPm3Flash
, "Mount the SPIFFS file system if not already mounted"},
579 {"remove", CmdFlashMemSpiFFSRemove
, IfPm3Flash
, "Remove a file from SPIFFS file system"},
580 {"rename", CmdFlashMemSpiFFSRename
, IfPm3Flash
, "Rename/move a file in SPIFFS file system"},
581 {"test", CmdFlashMemSpiFFSTest
, IfPm3Flash
, "Test SPIFFS Operations"},
582 {"tree", CmdFlashMemSpiFFSTree
, IfPm3Flash
, "Print the Flash memory file system tree"},
583 {"unmount", CmdFlashMemSpiFFSUnmount
, IfPm3Flash
, "Un-mount the SPIFFS file system"},
584 {"upload", CmdFlashMemSpiFFSUpload
, IfPm3Flash
, "Upload file into SPIFFS file system"},
585 {"view", CmdFlashMemSpiFFSView
, IfPm3Flash
, "View file on SPIFFS file system"},
586 {"wipe", CmdFlashMemSpiFFSWipe
, IfPm3Flash
, "Wipe all files from SPIFFS file system * " _RED_("dangerous") " *" },
587 {NULL
, NULL
, NULL
, NULL
}
590 static int CmdHelp(const char *Cmd
) {
591 (void)Cmd
; // Cmd is not used so far
592 CmdsHelp(CommandTable
);
596 int CmdFlashMemSpiFFS(const char *Cmd
) {
597 clearCommandBuffer();
598 return CmdsParse(CommandTable
, Cmd
);