1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2013 m h swende <martin at swende.se>
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // Some lua scripting glue to proxmark core.
9 //-----------------------------------------------------------------------------
10 // 2020, added Python support (@iceman1001)
16 //#define PY_SSIZE_T_CLEAN
21 #include "cmdparser.h" // command_t
22 #include "scripting.h"
24 #include "cmdscript.h"
26 #include "pm3_binlib.h"
27 #include "pm3_bitlib.h"
30 #include "proxmark3.h"
32 #include "fileutils.h"
33 #include "cliparser.h" // cliparsing
36 extern int luaopen_pm3(lua_State
*L
);
40 #ifdef HAVE_PYTHON_SWIG
41 extern PyObject
*PyInit__pm3(void);
42 #endif // HAVE_PYTHON_SWIG
44 // Partly ripped from PyRun_SimpleFileExFlags
45 // but does not terminate client on sys.exit
46 // and print exit code only if != 0
47 static int Pm3PyRun_SimpleFileNoExit(FILE *fp
, const char *filename
) {
49 int set_file_name
= 0, ret
= -1;
50 m
= PyImport_AddModule("__main__");
54 d
= PyModule_GetDict(m
);
55 if (PyDict_GetItemString(d
, "__file__") == NULL
) {
57 f
= PyUnicode_DecodeFSDefault(filename
);
60 if (PyDict_SetItemString(d
, "__file__", f
) < 0) {
64 if (PyDict_SetItemString(d
, "__cached__", Py_None
) < 0) {
71 v
= PyRun_FileExFlags(fp
, filename
, Py_file_input
, d
, d
, 1, NULL
);
74 if (PyErr_ExceptionMatches(PyExc_SystemExit
)) {
75 // PyErr_Print() exists if SystemExit so we've to handle it ourselves
76 PyObject
*ty
= 0, *er
= 0, *tr
= 0;
77 PyErr_Fetch(&ty
, &er
, &tr
);
78 long err
= PyLong_AsLong(er
);
80 PrintAndLogEx(WARNING
, "\nScript terminated by " _YELLOW_("SystemExit %li"), err
);
97 if (set_file_name
&& PyDict_DelItemString(d
, "__file__"))
102 #endif // HAVE_PYTHON
113 static int CmdHelp(const char *Cmd
);
117 #define PYTHON_LIBRARIES_WILDCARD "?.py"
119 static int split(char *str
, char **arr
) {
124 while (isspace(str
[begin_index
])) {
127 if (str
[begin_index
] == '\0') {
130 int end_index
= begin_index
;
131 while (str
[end_index
] && !isspace(str
[end_index
])) {
134 int len
= end_index
- begin_index
;
135 char *tmp
= calloc(len
+ 1, sizeof(char));
136 memcpy(tmp
, &str
[begin_index
], len
);
137 arr
[word_cnt
++] = tmp
;
138 begin_index
= end_index
;
143 static void set_python_path(char *path
) {
144 PyObject
*syspath
= PySys_GetObject("path");
146 PrintAndLogEx(WARNING
, "Python failed to getobject");
149 PyObject
*pName
= PyUnicode_FromString(path
);
150 if (PyList_Insert(syspath
, 0, pName
)) {
151 PrintAndLogEx(WARNING
, "Error inserting extra path into sys.path list");
154 if (PySys_SetObject("path", syspath
)) {
155 PrintAndLogEx(WARNING
, "Error setting sys.path object");
159 static void set_python_paths(void) {
160 //--add to the LUA_PATH (package.path in lua)
161 // so we can load scripts from various places:
162 const char *exec_path
= get_my_executable_directory();
163 if (exec_path
!= NULL
) {
164 // from the ./luascripts/ directory
165 char scripts_path
[strlen(exec_path
) + strlen(PYTHON_SCRIPTS_SUBDIR
) + strlen(PYTHON_LIBRARIES_WILDCARD
) + 1];
166 strcpy(scripts_path
, exec_path
);
167 strcat(scripts_path
, PYTHON_SCRIPTS_SUBDIR
);
168 // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD);
169 set_python_path(scripts_path
);
172 const char *user_path
= get_my_user_directory();
173 if (user_path
!= NULL
) {
174 // from the $HOME/.proxmark3/luascripts/ directory
175 char scripts_path
[strlen(user_path
) + strlen(PM3_USER_DIRECTORY
) + strlen(PYTHON_SCRIPTS_SUBDIR
) + strlen(PYTHON_LIBRARIES_WILDCARD
) + 1];
176 strcpy(scripts_path
, user_path
);
177 strcat(scripts_path
, PM3_USER_DIRECTORY
);
178 strcat(scripts_path
, PYTHON_SCRIPTS_SUBDIR
);
179 // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD);
180 set_python_path(scripts_path
);
184 if (exec_path
!= NULL
) {
185 // from the $PREFIX/share/proxmark3/luascripts/ directory
186 char scripts_path
[strlen(exec_path
) + strlen(PM3_SHARE_RELPATH
) + strlen(PYTHON_SCRIPTS_SUBDIR
) + strlen(PYTHON_LIBRARIES_WILDCARD
) + 1];
187 strcpy(scripts_path
, exec_path
);
188 strcat(scripts_path
, PM3_SHARE_RELPATH
);
189 strcat(scripts_path
, PYTHON_SCRIPTS_SUBDIR
);
190 // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD);
191 set_python_path(scripts_path
);
197 * Generate a sorted list of available commands, what it does is
198 * generate a file listing of the script-directory for files
201 static int CmdScriptList(const char *Cmd
) {
202 CLIParserContext
*ctx
;
203 CLIParserInit(&ctx
, "script list",
204 "List available Lua, Cmd and Python scripts",
211 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
213 PrintAndLogEx(NORMAL
, "\n" _YELLOW_("[ Lua scripts ]"));
214 int ret
= searchAndList(LUA_SCRIPTS_SUBDIR
, ".lua");
215 if (ret
!= PM3_SUCCESS
)
218 PrintAndLogEx(NORMAL
, "\n" _YELLOW_("[ Cmd scripts ]"));
219 ret
= searchAndList(CMD_SCRIPTS_SUBDIR
, ".cmd");
220 if (ret
!= PM3_SUCCESS
)
223 PrintAndLogEx(NORMAL
, "\n" _YELLOW_("[ Python scripts ]"));
224 return searchAndList(PYTHON_SCRIPTS_SUBDIR
, ".py");
231 * @brief CmdScriptRun - executes a script file.
236 static int CmdScriptRun(const char *Cmd
) {
237 CLIParserContext
*ctx
;
238 CLIParserInit(&ctx
, "script run",
239 "Run a Lua, Cmd or Python script. "
240 "If no extension it will search for lua/cmd/py extensions\n"
241 "Use `script list` to see available scripts",
242 "script run my_script -h\n"
247 arg_file1(NULL
, NULL
, "<filename>", "name of script to run"),
248 arg_strx0(NULL
, NULL
, "<params>", "script parameters"),
253 char filename
[128] = {0};
255 char arguments
[256] = {0};
257 sscanf(Cmd
, "%127s%n %255[^\n\r]%n", filename
, &fnlen
, arguments
, &arg_len
);
260 // since we don't want to use "-f" for script filename,
261 // and be able to send in parameters into script meanwhile
262 // being able to "-h" here too.
263 if ((strlen(filename
) == 0) ||
264 (strcmp(filename
, "-h") == 0) ||
265 (strcmp(filename
, "--help") == 0)) {
266 ctx
->argtable
= argtable
;
267 ctx
->argtableLen
= arg_getsize(argtable
);
268 CLIParserPrintHelp(ctx
);
274 // try to detect a valid script file extention, case-insensitive
276 extension_chk
= str_dup(filename
);
277 str_lower(extension_chk
);
279 pm3_scriptfile_t ext
= PM3_UNSPECIFIED
;
280 if (str_endswith(extension_chk
, ".lua")) {
282 } else if (str_endswith(extension_chk
, ".cmd")) {
286 else if (str_endswith(extension_chk
, ".py")) {
292 static uint8_t luascriptfile_idx
= 0;
293 char *script_path
= NULL
;
294 if (((ext
== PM3_LUA
) || (ext
== PM3_UNSPECIFIED
)) && (searchFile(&script_path
, LUA_SCRIPTS_SUBDIR
, filename
, ".lua", true) == PM3_SUCCESS
)) {
296 if (luascriptfile_idx
== MAX_NESTED_LUASCRIPT
) {
297 PrintAndLogEx(ERR
, "too many nested scripts, skipping %s\n", script_path
);
301 PrintAndLogEx(SUCCESS
, "executing lua " _YELLOW_("%s"), script_path
);
302 PrintAndLogEx(SUCCESS
, "args " _YELLOW_("'%s'"), arguments
);
306 // create new Lua state
307 lua_State
*lua_state
;
308 lua_state
= luaL_newstate();
310 // load Lua libraries
311 luaL_openlibs(lua_state
);
313 //Sets the pm3 core libraries, that go a bit 'under the hood'
314 set_pm3_libraries(lua_state
);
316 //Add the 'bin' library
317 set_bin_library(lua_state
);
319 //Add the 'bit' library
320 set_bit_library(lua_state
);
322 luaL_requiref(lua_state
, "pm3", luaopen_pm3
, 1);
324 error
= luaL_loadfile(lua_state
, script_path
);
327 lua_pushstring(lua_state
, arguments
);
328 lua_setglobal(lua_state
, "args");
330 //Call it with 0 arguments
331 error
= lua_pcall(lua_state
, 0, LUA_MULTRET
, 0); // once again, returns non-0 on error,
333 if (error
) { // if non-0, then an error
334 // the top of the stack should be the error string
335 if (!lua_isstring(lua_state
, lua_gettop(lua_state
)))
336 PrintAndLogEx(FAILED
, "error - but no error (?!)");
338 // get the top of the stack as the error and pop it off
339 const char *str
= lua_tostring(lua_state
, lua_gettop(lua_state
));
340 lua_pop(lua_state
, 1);
341 PrintAndLogEx(FAILED
, _RED_("error") " - %s", str
);
344 //luaL_dofile(lua_state, buf);
345 // close the Lua state
346 lua_close(lua_state
);
348 PrintAndLogEx(SUCCESS
, "\nfinished " _YELLOW_("%s"), filename
);
352 if (((ext
== PM3_CMD
) || (ext
== PM3_UNSPECIFIED
)) && (searchFile(&script_path
, CMD_SCRIPTS_SUBDIR
, filename
, ".cmd", true) == PM3_SUCCESS
)) {
354 PrintAndLogEx(SUCCESS
, "executing Cmd " _YELLOW_("%s"), script_path
);
355 PrintAndLogEx(SUCCESS
, "args " _YELLOW_("'%s'"), arguments
);
357 int ret
= push_cmdscriptfile(script_path
, true);
358 if (ret
!= PM3_SUCCESS
)
359 PrintAndLogEx(ERR
, "could not open " _YELLOW_("%s") "...", script_path
);
365 For apt (Ubuntu, Debian...):
366 sudo apt-get install python3-dev # for python3.x installs
368 For yum (CentOS, RHEL...):
369 sudo yum install python3-devel # for python3.x installs
372 sudo dnf install python3-devel # for python3.x installs
374 For zypper (openSUSE...):
375 sudo zypper in python3-devel # for python3.x installs
379 # This is a departure from the normal Alpine naming
380 # scheme, which uses py2- and py3- prefixes
382 sudo apk add python3-dev # for python3.x installs
384 For apt-cyg (Cygwin...):
385 apt-cyg install python3-devel # for python3.x installs
390 if (((ext
== PM3_PY
) || (ext
== PM3_UNSPECIFIED
)) && (searchFile(&script_path
, PYTHON_SCRIPTS_SUBDIR
, filename
, ".py", true) == PM3_SUCCESS
)) {
392 PrintAndLogEx(SUCCESS
, "executing python " _YELLOW_("%s"), script_path
);
393 PrintAndLogEx(SUCCESS
, "args " _YELLOW_("'%s'"), arguments
);
395 wchar_t *program
= Py_DecodeLocale(filename
, NULL
);
396 if (program
== NULL
) {
397 PrintAndLogEx(ERR
, "could not decode " _YELLOW_("%s"), filename
);
402 // optional but recommended
403 Py_SetProgramName(program
);
404 #ifdef HAVE_PYTHON_SWIG
405 // hook Proxmark3 API
406 PyImport_AppendInittab("_pm3", PyInit__pm3
);
410 //int argc, char ** argv
412 int argc
= split(arguments
, argv
);
413 wchar_t *py_args
[argc
];
414 py_args
[0] = Py_DecodeLocale(filename
, NULL
);
415 for (int i
= 0; i
< argc
; i
++) {
416 py_args
[i
+ 1] = Py_DecodeLocale(argv
[i
], NULL
);
419 PySys_SetArgv(argc
+ 1, py_args
);
422 for (int i
= 0; i
< argc
; ++i
) {
426 // setup search paths.
429 FILE *f
= fopen(script_path
, "r");
431 PrintAndLogEx(ERR
, "Could open file " _YELLOW_("%s"), script_path
);
435 int ret
= Pm3PyRun_SimpleFileNoExit(f
, filename
);
437 PyMem_RawFree(program
);
440 PrintAndLogEx(WARNING
, "\nfinished " _YELLOW_("%s") " with exception", filename
);
443 PrintAndLogEx(SUCCESS
, "\nfinished " _YELLOW_("%s"), filename
);
449 // file not found, let's search again to display the error messages
450 int ret
= PM3_EUNDEF
;
452 ret
= searchFile(&script_path
, LUA_SCRIPTS_SUBDIR
, filename
, ".lua", false);
453 else if (ext
== PM3_CMD
)
454 ret
= searchFile(&script_path
, CMD_SCRIPTS_SUBDIR
, filename
, ".cmd", false);
456 else if (ext
== PM3_PY
)
457 ret
= searchFile(&script_path
, PYTHON_SCRIPTS_SUBDIR
, filename
, ".py", false);
458 else if (ext
== PM3_UNSPECIFIED
)
459 PrintAndLogEx(FAILED
, "Error - can't find %s.[lua|cmd|py]", filename
);
461 else if (ext
== PM3_UNSPECIFIED
)
462 PrintAndLogEx(FAILED
, "Error - can't find %s.[lua|cmd]", filename
);
468 static command_t CommandTable
[] = {
469 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
470 {"list", CmdScriptList
, AlwaysAvailable
, "List available scripts"},
471 {"run", CmdScriptRun
, AlwaysAvailable
, "<name> - execute a script"},
472 {NULL
, NULL
, NULL
, NULL
}
476 * Shows some basic help
481 static int CmdHelp(const char *Cmd
) {
482 (void)Cmd
; // Cmd is not used so far
484 PrintAndLogEx(NORMAL
, "This is a feature to run Lua/Cmd/Python scripts. You can place scripts within the luascripts/cmdscripts/pyscripts folders. ");
486 PrintAndLogEx(NORMAL
, "This is a feature to run Lua/Cmd scripts. You can place scripts within the luascripts/cmdscripts folders. ");
492 * Finds a matching script-file
497 int CmdScript(const char *Cmd
) {
498 clearCommandBuffer();
499 return CmdsParse(CommandTable
, Cmd
);