You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copyright (c) 2021 (12022 Holocene), Simon Pinfold
2
+
3
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Run python from a single exe (without needing to extract anything to disk).
4
+
5
+
This project uses reflective dll loading and nim's `staticRead` to load the python runtime from the executable itself.
6
+
Custom (python) import hooks are installed to support loading modules (both native python (.pyc) and extension modules (.pyd)) from the embedded standard library.
7
+
8
+
## HOWTO
9
+
10
+
0. Set up nim
11
+
1. Download `python-3.10.1-embed-amd64.zip` to the project directory
12
+
2.`nimble build`
13
+
3. Run `onefile_python.exe`
14
+
15
+
## TODO
16
+
17
+
Currently the exe just drops to an interactive loop. Modification to run a script or embed a file/module should be trivial.
18
+
19
+
-[ ] Support `onefile_python.exe script.py`
20
+
-[ ] Build option for embedding a python file/module and running that on launch
21
+
-[ ] Support other versions of python than `3.10.1` (autodetect?)
Both projects are better suited for bundling an application (and all its dependencies) to end users. They both support some form of dependency resolution so modules not required by the bundled don't get installed, while this project includes the entire standard library.
29
+
30
+
PyInstaller supports single exe mode, but this just extracts the runtime to a temporary directory.
31
+
py2exe supports a diskless/"bundle" mode.
32
+
33
+
Both these projects are *much* more complex than this one and support lots of extra features, but sometimes you don't need that...
34
+
35
+
This project is (maybe) better if you want a single exe that can run any python script [or will be once that is supported], or just want an exe that gives a python REPL.
36
+
Being simpler, this project should be easier to hack on or learn from.
37
+
38
+
39
+
## How it works
40
+
41
+
There's not much to it...
42
+
43
+
0. Use nim's [staticRead](https://nim-lang.org/docs/system.html#staticRead%2Cstring) to include `python-*-embedded.zip` and `bootstrap.py` inside compiled exe itself.
44
+
1. Use [zippy](https://github.com/guzba/zippy) to access the contents of the archive at runtime.
45
+
2. Use [memlib](https://github.com/khchen/memlib) to perform reflective dll loading of the embedded `python*.dll`. Reflective dll loading allows for loading the dll from memory rather than from disk. Hook `LdrLoadDll` and `K32EnumProcessModules` so other code using the dll can find it.
46
+
3. Call various functions in the (reflectively) loaded dll to partially initialize python. Configure python to not try to load anything from disk (not absolutely required, but prevents conflicts and means the exe doesn't run any code in the current directory)
47
+
4. Use [nimpy](https://github.com/yglukhov/nimpy) to initialize a python extension exporting some nim functions that can read data out of the `python*.zip` standard library (contained within the `...-embedded.zip`).
48
+
5. Run the embedded `bootsrap.py` code to install an import hook. This import hook uses the functions from (4) to support importing python modules. If a `.pyc` can be found that matches an import, a loader that returns the unmarshalled `.pyc` is provided. If a `.pyd` can be found, the returned loader reflectively loads the `.pyd` and calls the module's initialization routine.
49
+
6. Now that python's standard library can be imported, finish initializing python.
50
+
6. Reflectively load other `.dlls` inside the `...-embedded.zip`. This is required so extension modules that depend on these dlls work e.g. `ctypes` needs `_ctypes.pyd` which requires `libffi.dll`.
51
+
7. Run python code / REPL
52
+
53
+
## Is this a virus
54
+
55
+
No.
56
+
57
+
It uses reflective DLL loading, which is a technique some malware uses so that might upset particularly sensitive AVs.
58
+
Like python itself, it could be used to run a malicious script.
0 commit comments