ocran 1.3.17 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +306 -272
- data/LICENSE.txt +22 -22
- data/README.md +549 -531
- data/exe/ocran +5 -5
- data/ext/extconf.rb +15 -0
- data/lib/ocran/build_constants.rb +16 -16
- data/lib/ocran/build_facade.rb +17 -17
- data/lib/ocran/build_helper.rb +110 -105
- data/lib/ocran/command_output.rb +22 -22
- data/lib/ocran/dir_builder.rb +162 -0
- data/lib/ocran/direction.rb +623 -386
- data/lib/ocran/file_path_set.rb +69 -69
- data/lib/ocran/gem_spec_queryable.rb +172 -172
- data/lib/ocran/host_config_helper.rb +57 -37
- data/lib/ocran/inno_setup_script_builder.rb +111 -111
- data/lib/ocran/launcher_batch_builder.rb +85 -85
- data/lib/ocran/library_detector.rb +61 -61
- data/lib/ocran/library_detector_posix.rb +55 -0
- data/lib/ocran/option.rb +323 -273
- data/lib/ocran/refine_pathname.rb +104 -104
- data/lib/ocran/runner.rb +115 -105
- data/lib/ocran/runtime_environment.rb +46 -46
- data/lib/ocran/stub_builder.rb +298 -224
- data/lib/ocran/version.rb +5 -5
- data/lib/ocran/windows_command_escaping.rb +15 -15
- data/lib/ocran.rb +7 -7
- data/share/ocran/lzma.exe +0 -0
- data/src/Makefile +75 -0
- data/src/edicon.c +161 -0
- data/src/error.c +100 -0
- data/src/error.h +66 -0
- data/src/inst_dir.c +334 -0
- data/src/inst_dir.h +157 -0
- data/src/lzma/7zTypes.h +529 -0
- data/src/lzma/Compiler.h +43 -0
- data/src/lzma/LzmaDec.c +1363 -0
- data/src/lzma/LzmaDec.h +236 -0
- data/src/lzma/Precomp.h +10 -0
- data/src/script_info.c +246 -0
- data/src/script_info.h +7 -0
- data/src/stub.c +133 -0
- data/src/stub.manifest +29 -0
- data/src/stub.rc +3 -0
- data/src/system_utils.c +1002 -0
- data/src/system_utils.h +209 -0
- data/src/system_utils_posix.c +500 -0
- data/src/unpack.c +574 -0
- data/src/unpack.h +85 -0
- data/src/vit-ruby.ico +0 -0
- metadata +55 -22
- data/share/ocran/edicon.exe +0 -0
- data/share/ocran/stub.exe +0 -0
- data/share/ocran/stubw.exe +0 -0
data/src/unpack.c
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
#include <stdbool.h>
|
|
2
|
+
#include <stddef.h>
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#ifdef _WIN32
|
|
7
|
+
#include <windows.h>
|
|
8
|
+
#endif
|
|
9
|
+
#include "error.h"
|
|
10
|
+
#include "system_utils.h"
|
|
11
|
+
#include "inst_dir.h"
|
|
12
|
+
#include "script_info.h"
|
|
13
|
+
#include "unpack.h"
|
|
14
|
+
|
|
15
|
+
#if WITH_LZMA
|
|
16
|
+
#include <LzmaDec.h>
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
typedef uint32_t SizeType;
|
|
20
|
+
|
|
21
|
+
static inline size_t get_size(const void *p)
|
|
22
|
+
{
|
|
23
|
+
const uint8_t *b = p;
|
|
24
|
+
return (size_t)b[0]
|
|
25
|
+
| ((size_t)b[1] << 8)
|
|
26
|
+
| ((size_t)b[2] << 16)
|
|
27
|
+
| ((size_t)b[3] << 24);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
typedef struct {
|
|
31
|
+
const uint8_t *begin;
|
|
32
|
+
const uint8_t *end;
|
|
33
|
+
const uint8_t *cur;
|
|
34
|
+
} UnpackReader;
|
|
35
|
+
|
|
36
|
+
static bool read_bytes(UnpackReader *reader, size_t size, const uint8_t **ptr)
|
|
37
|
+
{
|
|
38
|
+
size_t avail = (size_t)(reader->end - reader->cur);
|
|
39
|
+
if (size > avail) {
|
|
40
|
+
DEBUG("failed to read requested data bytes");
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
*ptr = reader->cur;
|
|
45
|
+
reader->cur += size;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static bool read_integer(UnpackReader *reader, size_t *size);
|
|
50
|
+
|
|
51
|
+
static bool read_string(UnpackReader *reader, const char **str)
|
|
52
|
+
{
|
|
53
|
+
size_t len;
|
|
54
|
+
if (!read_integer(reader, &len)) {
|
|
55
|
+
DEBUG("failed to read string size");
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (len == 0) {
|
|
60
|
+
DEBUG("string size is zero");
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const uint8_t *bytes;
|
|
65
|
+
if (!read_bytes(reader, len, &bytes)) {
|
|
66
|
+
DEBUG("failed to read string data");
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (bytes[len - 1] != '\0') {
|
|
71
|
+
DEBUG("string is not null-terminated");
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
*str = (const char *)bytes;
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static bool read_integer(UnpackReader *reader, size_t *size)
|
|
80
|
+
{
|
|
81
|
+
const uint8_t *b;
|
|
82
|
+
if (!read_bytes(reader, sizeof(SizeType), &b)) {
|
|
83
|
+
DEBUG("failed to read integer value");
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
*size = get_size(b);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static bool read_opcode(UnpackReader *reader, Opcode *opcode)
|
|
92
|
+
{
|
|
93
|
+
const uint8_t *b;
|
|
94
|
+
if (!read_bytes(reader, 1, &b)) {
|
|
95
|
+
DEBUG("failed to read opcode");
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
*opcode = (Opcode)b[0];
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static bool process_opcode(UnpackReader *reader, Opcode opcode)
|
|
104
|
+
{
|
|
105
|
+
const char *name, *value;
|
|
106
|
+
const uint8_t *bytes;
|
|
107
|
+
size_t size;
|
|
108
|
+
|
|
109
|
+
switch (opcode) {
|
|
110
|
+
case OP_CREATE_DIRECTORY: {
|
|
111
|
+
if (!read_string(reader, &name)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
DEBUG("OP_CREATE_DIRECTORY: path='%s'", name);
|
|
115
|
+
return CreateDirectoryUnderInstDir(name);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case OP_CREATE_FILE: {
|
|
119
|
+
if (!read_string(reader, &name)) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (!read_integer(reader, &size)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (!read_bytes(reader, size, &bytes)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const void *data = bytes;
|
|
129
|
+
DEBUG("OP_CREATE_FILE: path='%s' (%zu bytes)", name, size);
|
|
130
|
+
return ExportFileToInstDir(name, data, size);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case OP_SETENV: {
|
|
134
|
+
if (!read_string(reader, &name)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (!read_string(reader, &value)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
DEBUG("OP_SETENV: name='%s', value='%s'", name, value);
|
|
141
|
+
return SetEnvWithInstDir(name, value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
case OP_SET_SCRIPT: {
|
|
145
|
+
if (!read_integer(reader, &size)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (!read_bytes(reader, size, &bytes)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const char *args = (const char *)bytes;
|
|
152
|
+
DEBUG("OP_SET_SCRIPT");
|
|
153
|
+
return SetScriptInfo(args, size);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
case OP_CREATE_SYMLINK: {
|
|
157
|
+
if (!read_string(reader, &name)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (!read_string(reader, &value)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
DEBUG("OP_CREATE_SYMLINK: link='%s', target='%s'", name, value);
|
|
164
|
+
#ifndef _WIN32
|
|
165
|
+
return CreateSymlinkUnderInstDir(name, value);
|
|
166
|
+
#else
|
|
167
|
+
DEBUG("OP_CREATE_SYMLINK: skipped on Windows");
|
|
168
|
+
return true;
|
|
169
|
+
#endif
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
default: {
|
|
173
|
+
DEBUG("Invalid opcode: %d", opcode);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Unreachable: every case returns earlier */
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static bool process_opcodes(const void *data, size_t data_size)
|
|
183
|
+
{
|
|
184
|
+
UnpackReader reader = {
|
|
185
|
+
.begin = (const uint8_t *)data,
|
|
186
|
+
.cur = (const uint8_t *)data,
|
|
187
|
+
.end = (const uint8_t *)data + data_size
|
|
188
|
+
};
|
|
189
|
+
Opcode opcode;
|
|
190
|
+
|
|
191
|
+
while (reader.cur < reader.end) {
|
|
192
|
+
if (!read_opcode(&reader, &opcode)) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (!process_opcode(&reader, opcode)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#if WITH_LZMA
|
|
203
|
+
#define LZMA_UNPACKSIZE_SIZE 8
|
|
204
|
+
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNPACKSIZE_SIZE)
|
|
205
|
+
#define LZMA_SIZET_MAX ((SizeT)-1)
|
|
206
|
+
|
|
207
|
+
void *SzAlloc(const ISzAlloc *p, size_t size) { p = p; return malloc(size); }
|
|
208
|
+
void SzFree(const ISzAlloc *p, void *address) { p = p; free(address); }
|
|
209
|
+
ISzAlloc alloc = { SzAlloc, SzFree };
|
|
210
|
+
|
|
211
|
+
static bool decompress_lzma(void *dest, unsigned long long dest_size,
|
|
212
|
+
const void *src, size_t src_size)
|
|
213
|
+
{
|
|
214
|
+
SizeT decompressed_size = (SizeT)dest_size;
|
|
215
|
+
SizeT inSizePure = src_size - LZMA_HEADER_SIZE;
|
|
216
|
+
ELzmaStatus status;
|
|
217
|
+
|
|
218
|
+
SRes res = LzmaDecode((Byte *)dest, &decompressed_size,
|
|
219
|
+
(Byte *)src + LZMA_HEADER_SIZE, &inSizePure,
|
|
220
|
+
(Byte *)src, LZMA_PROPS_SIZE,
|
|
221
|
+
LZMA_FINISH_END, &status, &alloc);
|
|
222
|
+
|
|
223
|
+
if (res != SZ_OK || status != LZMA_STATUS_FINISHED_WITH_MARK) {
|
|
224
|
+
APP_ERROR("LZMA decompression error: %d, status: %d", res, status);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static unsigned long long parse_lzma_unpack_size(const void *data)
|
|
231
|
+
{
|
|
232
|
+
const Byte *size_bytes = (const Byte *)data + LZMA_PROPS_SIZE;
|
|
233
|
+
unsigned long long size64 = 0;
|
|
234
|
+
for (int i = 0; i < LZMA_UNPACKSIZE_SIZE; i++) {
|
|
235
|
+
size64 |= (unsigned long long)size_bytes[i] << (i * 8);
|
|
236
|
+
}
|
|
237
|
+
return size64;
|
|
238
|
+
}
|
|
239
|
+
#endif
|
|
240
|
+
|
|
241
|
+
static void *DecompressLzmaData(const void *data, size_t data_size,
|
|
242
|
+
size_t *decompressed_size)
|
|
243
|
+
{
|
|
244
|
+
#if WITH_LZMA
|
|
245
|
+
if (data_size < LZMA_HEADER_SIZE) {
|
|
246
|
+
APP_ERROR("LZMA header is truncated");
|
|
247
|
+
return NULL;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
unsigned long long unpack_size = parse_lzma_unpack_size(data);
|
|
251
|
+
|
|
252
|
+
DEBUG("Parsed LZMA decompressed size: %llu bytes", unpack_size);
|
|
253
|
+
|
|
254
|
+
if (unpack_size > (unsigned long long)LZMA_SIZET_MAX) {
|
|
255
|
+
APP_ERROR("Decompression size exceeds LZMA SizeT limit");
|
|
256
|
+
return NULL;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (unpack_size > (unsigned long long)SIZE_MAX) {
|
|
260
|
+
APP_ERROR("Size too large to fit in size_t");
|
|
261
|
+
return NULL;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
void *unpack_data = malloc(unpack_size);
|
|
265
|
+
if (!unpack_data) {
|
|
266
|
+
APP_ERROR("Memory allocation failed during decompression");
|
|
267
|
+
return NULL;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!decompress_lzma(unpack_data, unpack_size, data, data_size)) {
|
|
271
|
+
APP_ERROR("LZMA decompression failed");
|
|
272
|
+
free(unpack_data);
|
|
273
|
+
return NULL;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
*decompressed_size = unpack_size;
|
|
277
|
+
return unpack_data;
|
|
278
|
+
#else
|
|
279
|
+
APP_ERROR("Does not support LZMA");
|
|
280
|
+
return NULL;
|
|
281
|
+
#endif
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const uint8_t Signature[] = { 0x41, 0xb6, 0xba, 0x4e };
|
|
285
|
+
|
|
286
|
+
/** Manages digital signatures **/
|
|
287
|
+
|
|
288
|
+
/* see https://en.wikipedia.org/wiki/Portable_Executable for explanation of these header fields */
|
|
289
|
+
#define SECURITY_ENTRY(header) ((header)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY])
|
|
290
|
+
|
|
291
|
+
#ifdef _WIN32
|
|
292
|
+
/* The NTHeader is another name for the PE header. It is the 'modern' executable header
|
|
293
|
+
as opposed to the DOS_HEADER which exists for legacy reasons */
|
|
294
|
+
static PIMAGE_NT_HEADERS retrieveNTHeader(const void *ptr) {
|
|
295
|
+
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)ptr;
|
|
296
|
+
|
|
297
|
+
/* e_lfanew is an RVA (relative virtual address, i.e offset) to the NTHeader
|
|
298
|
+
to get a usable pointer we add the RVA to the base address */
|
|
299
|
+
return (PIMAGE_NT_HEADERS)((DWORD_PTR)dosHeader + (DWORD)dosHeader->e_lfanew);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Check whether there's an embedded digital signature */
|
|
303
|
+
static bool isDigitallySigned(const void *ptr, size_t buffer_size) {
|
|
304
|
+
PIMAGE_NT_HEADERS ntHeader = retrieveNTHeader(ptr);
|
|
305
|
+
DWORD securityAddr = SECURITY_ENTRY(ntHeader).VirtualAddress;
|
|
306
|
+
DWORD securitySize = SECURITY_ENTRY(ntHeader).Size;
|
|
307
|
+
|
|
308
|
+
/* A valid digital signature must have non-zero size and the address must be within the file bounds */
|
|
309
|
+
if (securitySize == 0) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Check if the security entry points to a valid location within the file */
|
|
314
|
+
if (securityAddr == 0 || securityAddr >= buffer_size) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Check if the entire security entry fits within the file */
|
|
319
|
+
if (securityAddr + securitySize > buffer_size) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
#endif
|
|
326
|
+
|
|
327
|
+
static const void *find_signature(const void *buffer, size_t buffer_size)
|
|
328
|
+
{
|
|
329
|
+
#ifdef _WIN32
|
|
330
|
+
// Check if the executable is digitally signed
|
|
331
|
+
if (!isDigitallySigned(buffer, buffer_size)) {
|
|
332
|
+
// No digital signature, use the original logic
|
|
333
|
+
const void *sig = (const uint8_t *)buffer + buffer_size - sizeof(Signature);
|
|
334
|
+
if (memcmp(sig, Signature, sizeof(Signature))) {
|
|
335
|
+
return NULL;
|
|
336
|
+
}
|
|
337
|
+
return sig;
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Executable is digitally signed
|
|
341
|
+
PIMAGE_NT_HEADERS ntHeader = retrieveNTHeader(buffer);
|
|
342
|
+
DWORD securityAddr = SECURITY_ENTRY(ntHeader).VirtualAddress;
|
|
343
|
+
|
|
344
|
+
if (securityAddr == 0 || securityAddr > buffer_size) {
|
|
345
|
+
DEBUG("Invalid security address: %lu", (unsigned long)securityAddr);
|
|
346
|
+
return NULL;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
DWORD offset = securityAddr - 1;
|
|
350
|
+
const char *searchPtr = (const char *)buffer;
|
|
351
|
+
|
|
352
|
+
/* There is unfortunately a 'buffer' of null bytes between the
|
|
353
|
+
ocraSignature and the digital signature. This buffer appears to be random
|
|
354
|
+
in size, so the only way we can account for it is to search backwards
|
|
355
|
+
for the first non-null byte.
|
|
356
|
+
NOTE: this means that the hard-coded Ocra signature cannot end with a null byte.
|
|
357
|
+
*/
|
|
358
|
+
while(offset > sizeof(Signature) && !searchPtr[offset])
|
|
359
|
+
offset--;
|
|
360
|
+
|
|
361
|
+
/* -3 because we're already at the first byte and we need to go back 4 bytes */
|
|
362
|
+
if (offset < sizeof(Signature)) {
|
|
363
|
+
DEBUG("Signature search went out of bounds");
|
|
364
|
+
return NULL;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const void *sig = (const void *)&searchPtr[offset - 3];
|
|
368
|
+
|
|
369
|
+
if (memcmp(sig, Signature, sizeof(Signature))) {
|
|
370
|
+
DEBUG("Signature mismatch in signed executable");
|
|
371
|
+
return NULL;
|
|
372
|
+
}
|
|
373
|
+
return sig;
|
|
374
|
+
}
|
|
375
|
+
#else
|
|
376
|
+
// POSIX: no PE headers, just check the end of the file
|
|
377
|
+
if (buffer_size < sizeof(Signature)) {
|
|
378
|
+
return NULL;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const void *sig = (const uint8_t *)buffer + buffer_size - sizeof(Signature);
|
|
382
|
+
if (memcmp(sig, Signature, sizeof(Signature)) != 0) {
|
|
383
|
+
return NULL;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return sig;
|
|
387
|
+
#endif
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
typedef uint8_t OperationModesType;
|
|
391
|
+
|
|
392
|
+
OperationModes get_operation_modes(const void **p)
|
|
393
|
+
{
|
|
394
|
+
const OperationModesType *q = (const OperationModesType *)*p;
|
|
395
|
+
*p = q + 1;
|
|
396
|
+
return (OperationModes)*q;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
typedef SizeType OffsetType;
|
|
400
|
+
|
|
401
|
+
size_t get_offset(const void *p)
|
|
402
|
+
{
|
|
403
|
+
return get_size(p);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
struct UnpackContext {
|
|
407
|
+
MemoryMap *map;
|
|
408
|
+
OperationModes modes;
|
|
409
|
+
const void *data;
|
|
410
|
+
size_t data_size;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
UnpackContext *OpenPackFile(const char *self_path)
|
|
414
|
+
{
|
|
415
|
+
UnpackContext *context = NULL;
|
|
416
|
+
|
|
417
|
+
if (!self_path || !*self_path) {
|
|
418
|
+
APP_ERROR("self_path is NULL or empty");
|
|
419
|
+
|
|
420
|
+
goto cleanup;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
context = calloc(1, sizeof(UnpackContext)) ;
|
|
424
|
+
if (!context) {
|
|
425
|
+
APP_ERROR("Memory allocation failed for context");
|
|
426
|
+
|
|
427
|
+
goto cleanup;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
context->map = CreateMemoryMap(self_path);
|
|
431
|
+
if (!context->map) {
|
|
432
|
+
APP_ERROR("Failed to map the executable file");
|
|
433
|
+
|
|
434
|
+
goto cleanup;
|
|
435
|
+
}
|
|
436
|
+
const void *map_base = GetMemoryMapBase(context->map);
|
|
437
|
+
size_t map_size = GetMemoryMapSize(context->map);
|
|
438
|
+
|
|
439
|
+
/* Locate the end of the packed data */
|
|
440
|
+
if (map_size < sizeof(Signature)) {
|
|
441
|
+
APP_ERROR("Too small to contain signature");
|
|
442
|
+
|
|
443
|
+
goto cleanup;
|
|
444
|
+
}
|
|
445
|
+
const void *signature = find_signature(map_base, map_size);
|
|
446
|
+
if (!signature) {
|
|
447
|
+
APP_ERROR("Signature not found");
|
|
448
|
+
|
|
449
|
+
goto cleanup;
|
|
450
|
+
}
|
|
451
|
+
if ((size_t)((const uint8_t *)signature - (const uint8_t *)map_base)
|
|
452
|
+
< sizeof(OffsetType)) {
|
|
453
|
+
APP_ERROR("Signature too close to buffer start");
|
|
454
|
+
|
|
455
|
+
goto cleanup;
|
|
456
|
+
}
|
|
457
|
+
const void *tail = signature;
|
|
458
|
+
|
|
459
|
+
/* Determine the start of the packed data */
|
|
460
|
+
tail = (const uint8_t *)tail - sizeof(OffsetType);
|
|
461
|
+
size_t offset = get_offset(tail);
|
|
462
|
+
if (offset > map_size - sizeof(OperationModesType)) {
|
|
463
|
+
APP_ERROR("Offset out of range");
|
|
464
|
+
|
|
465
|
+
goto cleanup;
|
|
466
|
+
}
|
|
467
|
+
const void *head = (const uint8_t *)map_base + offset;
|
|
468
|
+
|
|
469
|
+
/* Verify that the header fits immediately before the offset */
|
|
470
|
+
if (head > tail
|
|
471
|
+
|| (size_t)((const uint8_t *)tail - (const uint8_t *)head)
|
|
472
|
+
< sizeof(OperationModesType)) {
|
|
473
|
+
APP_ERROR("Not enough space for header before offset");
|
|
474
|
+
|
|
475
|
+
goto cleanup;
|
|
476
|
+
}
|
|
477
|
+
context->modes = get_operation_modes(&head);
|
|
478
|
+
context->data = head;
|
|
479
|
+
context->data_size = (const uint8_t *)tail - (const uint8_t *)head;
|
|
480
|
+
|
|
481
|
+
DEBUG(
|
|
482
|
+
"OpenPackFile: offset=%zu, modes= %u, data_size=%zu",
|
|
483
|
+
offset, (unsigned)context->modes, context->data_size
|
|
484
|
+
);
|
|
485
|
+
return context;
|
|
486
|
+
|
|
487
|
+
cleanup:
|
|
488
|
+
if (context) {
|
|
489
|
+
ClosePackFile(context);
|
|
490
|
+
}
|
|
491
|
+
return NULL;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
bool ClosePackFile(UnpackContext *context)
|
|
495
|
+
{
|
|
496
|
+
if (context) {
|
|
497
|
+
if (context->map) {
|
|
498
|
+
DestroyMemoryMap(context->map);
|
|
499
|
+
}
|
|
500
|
+
free(context);
|
|
501
|
+
}
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
OperationModes GetOperationModes(const UnpackContext *context)
|
|
506
|
+
{
|
|
507
|
+
if (!context) {
|
|
508
|
+
APP_ERROR("context is NULL");
|
|
509
|
+
|
|
510
|
+
return 0;
|
|
511
|
+
}
|
|
512
|
+
return context->modes;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
static inline bool IsMode(OperationModes modes, OperationModes mask) {
|
|
516
|
+
return (modes & mask) == mask;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
bool IsDebugMode(OperationModes modes) {
|
|
520
|
+
return IsMode(modes, DEBUG_MODE);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
bool IsExtractToExeDir(OperationModes modes) {
|
|
524
|
+
return IsMode(modes, EXTRACT_TO_EXE_DIR);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
bool IsAutoCleanInstDir(OperationModes modes) {
|
|
528
|
+
return IsMode(modes, AUTO_CLEAN_INST_DIR);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
bool IsChdirBeforeScript(OperationModes modes) {
|
|
532
|
+
return IsMode(modes, CHDIR_BEFORE_SCRIPT);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
bool IsDataCompressed(OperationModes modes) {
|
|
536
|
+
return IsMode(modes, DATA_COMPRESSED);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
bool ProcessImage(const UnpackContext *context)
|
|
540
|
+
{
|
|
541
|
+
if (!context) {
|
|
542
|
+
APP_ERROR("context is NULL");
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
DEBUG("Data segment size: %zu bytes", context->data_size);
|
|
547
|
+
|
|
548
|
+
bool compressed = IsDataCompressed(context->modes);
|
|
549
|
+
void *data;
|
|
550
|
+
size_t data_size;
|
|
551
|
+
|
|
552
|
+
if (compressed) {
|
|
553
|
+
data = DecompressLzmaData(
|
|
554
|
+
context->data,
|
|
555
|
+
context->data_size,
|
|
556
|
+
&data_size
|
|
557
|
+
);
|
|
558
|
+
if (!data) {
|
|
559
|
+
APP_ERROR("LZMA decompression failed");
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
DEBUG("LZMA decompressed %zu bytes", data_size);
|
|
563
|
+
} else {
|
|
564
|
+
data = (void *)context->data;
|
|
565
|
+
data_size = context->data_size;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
bool ok = process_opcodes(data, data_size);
|
|
569
|
+
|
|
570
|
+
if (compressed) {
|
|
571
|
+
free(data);
|
|
572
|
+
}
|
|
573
|
+
return ok;
|
|
574
|
+
}
|
data/src/unpack.h
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#include <stdbool.h>
|
|
2
|
+
|
|
3
|
+
typedef enum {
|
|
4
|
+
OP_CREATE_DIRECTORY = 1,
|
|
5
|
+
OP_CREATE_FILE = 2,
|
|
6
|
+
OP_SETENV = 3,
|
|
7
|
+
OP_SET_SCRIPT = 4,
|
|
8
|
+
OP_CREATE_SYMLINK = 5,
|
|
9
|
+
} Opcode;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* OperationModes defines a set of flags used to control various aspects of the
|
|
13
|
+
* program's behavior during runtime. These flags enable or disable specific
|
|
14
|
+
* features and functionalities, allowing for a more flexible and customizable
|
|
15
|
+
* execution based on the needs of the user or the environment.
|
|
16
|
+
*
|
|
17
|
+
* The flags managed by OperationModes include, but are not limited to:
|
|
18
|
+
*
|
|
19
|
+
* - DEBUG_MODE: Activates verbose debugging information to assist in
|
|
20
|
+
* development or troubleshooting.
|
|
21
|
+
*
|
|
22
|
+
* - EXTRACT_TO_EXE_DIR: Directs the program to unpack data in the same
|
|
23
|
+
* directory as the executable.
|
|
24
|
+
*
|
|
25
|
+
* - AUTO_CLEAN_INST_DIR: Enables automatic cleanup of the extraction
|
|
26
|
+
* directory upon program termination.
|
|
27
|
+
*
|
|
28
|
+
* - CHDIR_BEFORE_SCRIPT: Changes the current directory to the script's
|
|
29
|
+
* location before its execution.
|
|
30
|
+
*
|
|
31
|
+
* - DATA_COMPRESSED: Indicates that the data to be processed is compressed and
|
|
32
|
+
* requires decompression.
|
|
33
|
+
*
|
|
34
|
+
* By adjusting these flags, developers and users can tailor the program's
|
|
35
|
+
* execution to suit specific scenarios, enhancing both usability and
|
|
36
|
+
* efficiency.
|
|
37
|
+
*/
|
|
38
|
+
typedef enum {
|
|
39
|
+
/**
|
|
40
|
+
* Enable debug information display. Output various execution information to
|
|
41
|
+
* stderr.
|
|
42
|
+
*/
|
|
43
|
+
DEBUG_MODE = 0x01,
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sets the extraction directory to the executable directory.
|
|
47
|
+
* Data will be unpacked in the same location as the executable file.
|
|
48
|
+
*/
|
|
49
|
+
EXTRACT_TO_EXE_DIR = 0x02,
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Enable automatic deletion of the extraction directory at the end of the
|
|
53
|
+
* application. Cleanup the extracted data upon program termination.
|
|
54
|
+
*/
|
|
55
|
+
AUTO_CLEAN_INST_DIR = 0x04,
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Change the current directory to the location of the script before
|
|
59
|
+
* executing the script. This allows file operations relative to that
|
|
60
|
+
* directory.
|
|
61
|
+
*/
|
|
62
|
+
CHDIR_BEFORE_SCRIPT = 0x08,
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Indicates that the data to be extracted is compressed. Decompression is
|
|
66
|
+
* required before using the data.
|
|
67
|
+
*/
|
|
68
|
+
DATA_COMPRESSED = 0x10,
|
|
69
|
+
} OperationModes;
|
|
70
|
+
|
|
71
|
+
bool IsDebugMode(OperationModes modes);
|
|
72
|
+
bool IsExtractToExeDir(OperationModes modes);
|
|
73
|
+
bool IsAutoCleanInstDir(OperationModes modes);
|
|
74
|
+
bool IsChdirBeforeScript(OperationModes modes);
|
|
75
|
+
bool IsDataCompressed(OperationModes modes);
|
|
76
|
+
|
|
77
|
+
typedef struct UnpackContext UnpackContext;
|
|
78
|
+
|
|
79
|
+
UnpackContext *OpenPackFile(const char *self_path);
|
|
80
|
+
|
|
81
|
+
bool ClosePackFile(UnpackContext *context);
|
|
82
|
+
|
|
83
|
+
OperationModes GetOperationModes(const UnpackContext *context);
|
|
84
|
+
|
|
85
|
+
bool ProcessImage(const UnpackContext *context);
|
data/src/vit-ruby.ico
ADDED
|
Binary file
|