depyo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/depyo.js +213 -0
- package/lib/BinaryReader.js +153 -0
- package/lib/OpCode.js +90 -0
- package/lib/OpCodes.js +940 -0
- package/lib/PycDecompiler.js +2031 -0
- package/lib/PycDisassembler.js +55 -0
- package/lib/PycReader.js +905 -0
- package/lib/PycResult.js +82 -0
- package/lib/PythonObject.js +242 -0
- package/lib/Unpickle.js +173 -0
- package/lib/ast/ast_node.js +3442 -0
- package/lib/bytecode/python_1_0.js +116 -0
- package/lib/bytecode/python_1_1.js +116 -0
- package/lib/bytecode/python_1_3.js +119 -0
- package/lib/bytecode/python_1_4.js +121 -0
- package/lib/bytecode/python_1_5.js +120 -0
- package/lib/bytecode/python_1_6.js +124 -0
- package/lib/bytecode/python_2_0.js +137 -0
- package/lib/bytecode/python_2_1.js +142 -0
- package/lib/bytecode/python_2_2.js +147 -0
- package/lib/bytecode/python_2_3.js +145 -0
- package/lib/bytecode/python_2_4.js +147 -0
- package/lib/bytecode/python_2_5.js +147 -0
- package/lib/bytecode/python_2_6.js +147 -0
- package/lib/bytecode/python_2_7.js +151 -0
- package/lib/bytecode/python_3_0.js +132 -0
- package/lib/bytecode/python_3_1.js +135 -0
- package/lib/bytecode/python_3_10.js +312 -0
- package/lib/bytecode/python_3_11.js +284 -0
- package/lib/bytecode/python_3_12.js +327 -0
- package/lib/bytecode/python_3_13.js +173 -0
- package/lib/bytecode/python_3_14.js +177 -0
- package/lib/bytecode/python_3_2.js +136 -0
- package/lib/bytecode/python_3_3.js +136 -0
- package/lib/bytecode/python_3_4.js +137 -0
- package/lib/bytecode/python_3_5.js +149 -0
- package/lib/bytecode/python_3_6.js +153 -0
- package/lib/bytecode/python_3_7.js +292 -0
- package/lib/bytecode/python_3_8.js +294 -0
- package/lib/bytecode/python_3_9.js +296 -0
- package/lib/code_reader.js +146 -0
- package/lib/handlers/binary_ops.js +174 -0
- package/lib/handlers/collections_update.js +239 -0
- package/lib/handlers/comparisons.js +95 -0
- package/lib/handlers/context_managers.js +250 -0
- package/lib/handlers/control_flow_jumps.js +954 -0
- package/lib/handlers/exceptions_blocks.js +952 -0
- package/lib/handlers/formatting.js +31 -0
- package/lib/handlers/function_calls.js +496 -0
- package/lib/handlers/function_class_build.js +330 -0
- package/lib/handlers/generators_async.js +172 -0
- package/lib/handlers/imports.js +53 -0
- package/lib/handlers/load_store_names.js +711 -0
- package/lib/handlers/loop_iterator.js +318 -0
- package/lib/handlers/misc_other.js +1201 -0
- package/lib/handlers/pattern_matching.js +226 -0
- package/lib/handlers/stack_ops.js +280 -0
- package/lib/handlers/subscript_slice.js +394 -0
- package/lib/handlers/unary_ops.js +91 -0
- package/lib/handlers/unpack.js +141 -0
- package/lib/stack_history.js +63 -0
- package/lib/zip_reader.js +217 -0
- package/package.json +35 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const Reader = require('./code_reader');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const zlib = require('zlib');
|
|
4
|
+
|
|
5
|
+
const MAX_UNCOMPRESSED_BYTES = 50 * 1024 * 1024; // Safety guard to avoid runaway inflates
|
|
6
|
+
|
|
7
|
+
class ZipReader {
|
|
8
|
+
constructor(filename) {
|
|
9
|
+
this.filename = filename;
|
|
10
|
+
this.reader = null;
|
|
11
|
+
this._entries = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get position() {
|
|
15
|
+
if (this.reader) {
|
|
16
|
+
return this.reader.pc;
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
set position (pos) {
|
|
22
|
+
if (this.reader) {
|
|
23
|
+
this.reader.pc = pos;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
readLocalEntry() {
|
|
28
|
+
if (!this.reader) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let fnameLen = 0, extraLen = 0;
|
|
33
|
+
let header = {
|
|
34
|
+
header: this.reader.readUInt(),
|
|
35
|
+
version: this.reader.readUShort(),
|
|
36
|
+
flags: this.reader.readUShort(),
|
|
37
|
+
method: this.reader.readUShort(),
|
|
38
|
+
modificationTime: this.reader.readUShort(),
|
|
39
|
+
modificationDate: this.reader.readUShort(),
|
|
40
|
+
crc32: this.reader.readUInt(),
|
|
41
|
+
compressedSize: this.reader.readUInt(),
|
|
42
|
+
uncompressedSize: this.reader.readUInt(),
|
|
43
|
+
fileNameLength: (fnameLen = this.reader.readUShort()),
|
|
44
|
+
extraFieldLength: (extraLen = this.reader.readUShort()),
|
|
45
|
+
fileName: this.reader.readString(fnameLen),
|
|
46
|
+
extraField: this.reader.readString(extraLen),
|
|
47
|
+
offset: this.reader.pc
|
|
48
|
+
};
|
|
49
|
+
return header;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
readCDEntry() {
|
|
53
|
+
if (!this.reader) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let fnameLen = 0, extraLen = 0, commentLen = 0;
|
|
58
|
+
let header = {
|
|
59
|
+
header: this.reader.readUInt(),
|
|
60
|
+
versionMade: this.reader.readUShort(),
|
|
61
|
+
minVersion: this.reader.readUShort(),
|
|
62
|
+
flags: this.reader.readUShort(),
|
|
63
|
+
method: this.reader.readUShort(),
|
|
64
|
+
modificationTime: this.reader.readUShort(),
|
|
65
|
+
modificationDate: this.reader.readUShort(),
|
|
66
|
+
crc32: this.reader.readUInt(),
|
|
67
|
+
compressedSize: this.reader.readUInt(),
|
|
68
|
+
uncompressedSize: this.reader.readUInt(),
|
|
69
|
+
fileNameLength: (fnameLen = this.reader.readUShort()),
|
|
70
|
+
extraFieldLength: (extraLen = this.reader.readUShort()),
|
|
71
|
+
commentLength: (commentLen = this.reader.readUShort()),
|
|
72
|
+
diskNum: this.reader.readUShort(),
|
|
73
|
+
internalFileAttributes: this.reader.readUShort(),
|
|
74
|
+
externalFileAttributes: this.reader.readUInt(),
|
|
75
|
+
localHeaderOffseet: this.reader.readUInt(),
|
|
76
|
+
fileName: this.reader.readString(fnameLen),
|
|
77
|
+
extraField: this.reader.readString(extraLen),
|
|
78
|
+
comment: this.reader.readString(commentLen)
|
|
79
|
+
};
|
|
80
|
+
return header;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
readEOCD() {
|
|
84
|
+
if (!this.reader) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let header = {
|
|
89
|
+
header: this.reader.readUInt(),
|
|
90
|
+
diskNum: this.reader.readUShort(),
|
|
91
|
+
cdDisk: this.reader.readUShort(),
|
|
92
|
+
cdRecordsCurrent: this.reader.readUShort(),
|
|
93
|
+
cdRecordsTotal: this.reader.readUShort(),
|
|
94
|
+
cdSize: this.reader.readUInt(),
|
|
95
|
+
cdOffset: this.reader.readUInt()
|
|
96
|
+
};
|
|
97
|
+
return header;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
readEntry(entry) {
|
|
101
|
+
if (!entry) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (entry.uncompressedSize > MAX_UNCOMPRESSED_BYTES && entry.uncompressedSize !== 0) {
|
|
105
|
+
throw new Error(`Entry ${entry.fileName} exceeds size limit (${entry.uncompressedSize} bytes)`);
|
|
106
|
+
}
|
|
107
|
+
if (entry.compressedSize > 0) {
|
|
108
|
+
if (entry.compressedSize > MAX_UNCOMPRESSED_BYTES) {
|
|
109
|
+
throw new Error(`Compressed entry ${entry.fileName} exceeds size limit (${entry.compressedSize} bytes)`);
|
|
110
|
+
}
|
|
111
|
+
if (entry.method == 8) {
|
|
112
|
+
let data = zlib.inflateRawSync(entry.data);
|
|
113
|
+
if (data.length > MAX_UNCOMPRESSED_BYTES) {
|
|
114
|
+
throw new Error(`Inflated entry ${entry.fileName} exceeds size limit (${data.length} bytes)`);
|
|
115
|
+
}
|
|
116
|
+
if (entry.uncompressedSize && data.length !== entry.uncompressedSize) {
|
|
117
|
+
throw new Error(`Size mismatch for ${entry.fileName}: expected ${entry.uncompressedSize}, got ${data.length}`);
|
|
118
|
+
}
|
|
119
|
+
return data;
|
|
120
|
+
} else if (entry.method == 0) {
|
|
121
|
+
return entry.data;
|
|
122
|
+
} else {
|
|
123
|
+
console.log(`Unsupported method ${entry.method}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
open() {
|
|
130
|
+
if (!this.reader) {
|
|
131
|
+
try {
|
|
132
|
+
let data = fs.readFileSync(this.filename)
|
|
133
|
+
this.reader = new Reader(data, 'L');
|
|
134
|
+
} catch (ex) {
|
|
135
|
+
console.error(ex);
|
|
136
|
+
throw ex;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
readSignature() {
|
|
142
|
+
let value = this.reader.readUInt();
|
|
143
|
+
this.reader.pc -= 4;
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
isZipFile() {
|
|
148
|
+
if (!this.reader) {
|
|
149
|
+
this.open();
|
|
150
|
+
}
|
|
151
|
+
let oldPc = this.reader.pc;
|
|
152
|
+
this.reader.pc = 0;
|
|
153
|
+
let signature = this.readSignature();
|
|
154
|
+
this.reader.pc = oldPc;
|
|
155
|
+
if (signature != 0x04034b50) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
findEOCD() {
|
|
162
|
+
if (!this.reader) {
|
|
163
|
+
this.open();
|
|
164
|
+
}
|
|
165
|
+
this.reader.pc = this.reader.length - 22;
|
|
166
|
+
while(this.reader.pc >= 0 && this.readSignature() != 0x06054b50) {
|
|
167
|
+
this.reader.pc--;
|
|
168
|
+
}
|
|
169
|
+
if (this.reader.pc < 0) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
*entries() {
|
|
176
|
+
this.open();
|
|
177
|
+
|
|
178
|
+
let eocdEntry;
|
|
179
|
+
if (this.findEOCD()) {
|
|
180
|
+
eocdEntry = this.readEOCD();
|
|
181
|
+
this.reader.pc = eocdEntry.cdOffset;
|
|
182
|
+
} else {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let records = eocdEntry.cdRecordsCurrent;
|
|
187
|
+
this._entries = this._entries || [];
|
|
188
|
+
|
|
189
|
+
while(records--) {
|
|
190
|
+
let cdEntry = this.readCDEntry();
|
|
191
|
+
let nextCDPos = this.reader.pc;
|
|
192
|
+
if (cdEntry.header != 0x02014b50) {
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
this.reader.pc = cdEntry.localHeaderOffseet;
|
|
196
|
+
let localEntry = this.readLocalEntry();
|
|
197
|
+
if (cdEntry.compressedSize > 0){
|
|
198
|
+
cdEntry.data = this.reader.readBytes(cdEntry.compressedSize);
|
|
199
|
+
}
|
|
200
|
+
this.reader.pc = nextCDPos;
|
|
201
|
+
// TODO: Validate CRC32
|
|
202
|
+
|
|
203
|
+
yield cdEntry;
|
|
204
|
+
}
|
|
205
|
+
delete this.reader;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
findEntry(filename) {
|
|
209
|
+
if (!this._entries) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
let entry = this._entries.find(entry => entry.fileName == filename);
|
|
213
|
+
return entry;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = ZipReader;
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "depyo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Python bytecode decompiler (Python 1.0–3.14) implemented in Node.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"depyo": "./depyo.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "depyo.js",
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"files": [
|
|
11
|
+
"depyo.js",
|
|
12
|
+
"lib/"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/skuznetsov/depyo.js.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"python",
|
|
20
|
+
"pyc",
|
|
21
|
+
"decompiler",
|
|
22
|
+
"bytecode",
|
|
23
|
+
"ast",
|
|
24
|
+
"disassemble"
|
|
25
|
+
],
|
|
26
|
+
"author": "Sergey Kuznetsov",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/skuznetsov/depyo.js/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/skuznetsov/depyo.js#readme",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
}
|
|
35
|
+
}
|