Skip to content

Commit 9a4df88

Browse files
jcrussellqkaiser
andcommitted
feat(handler): add support for PE files with extraction of NSIS executable
Add support for PE file by relying on LIEF to parse PE file once matched on 'MZ' or 'PE' signature. If the file is a self-extractable NSIS executable ("Nullsoft.NSIS.exehead" present in manifest) we extract it with 7zip. Co-authored-by: Quentin Kaiser <quentin.kaiser@onekey.com>
1 parent b0e81ab commit 9a4df88

File tree

908 files changed

+2827
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

908 files changed

+2827
-1
lines changed

python/unblob/handlers/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@
3737
zlib,
3838
zstd,
3939
)
40-
from .executable import elf
40+
from .executable import (
41+
elf,
42+
pe
43+
)
4144
from .filesystem import (
4245
cramfs,
4346
extfs,
@@ -115,6 +118,7 @@
115118
zstd.ZSTDHandler,
116119
elf.ELF32Handler,
117120
elf.ELF64Handler,
121+
pe.PEHandler,
118122
zlib.ZlibHandler,
119123
engenius.EngeniusHandler,
120124
ecc.AutelECCHandler,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import io
2+
from pathlib import Path
3+
from typing import Optional
4+
5+
import lief
6+
from structlog import get_logger
7+
8+
from unblob.extractors.command import Command
9+
10+
from ...models import (
11+
Extractor,
12+
ExtractResult,
13+
File,
14+
Handler,
15+
HandlerDoc,
16+
HandlerType,
17+
HexString,
18+
Reference,
19+
ValidChunk,
20+
)
21+
22+
lief.logging.disable()
23+
24+
logger = get_logger()
25+
26+
27+
class PEExtractor(Extractor):
28+
def extract(self, inpath: Path, outdir: Path) -> Optional[ExtractResult]:
29+
binary = lief.PE.parse(inpath)
30+
if binary and self.is_nsis(binary):
31+
return Command("7z", "x", "-y", "{inpath}", "-o{outdir}").extract(
32+
inpath, outdir
33+
)
34+
return None
35+
36+
def is_nsis(self, binary: lief.PE.Binary) -> bool:
37+
# Test if binary appears to be a Nullsoft Installer self-extracting archive
38+
# see https://github.com/file/file/blob/7ed3febfcd616804a2ec6495b3e5f9ccb6fc5f8f/magic/Magdir/msdos#L383
39+
40+
if binary.has_resources:
41+
resource_manager = binary.resources_manager
42+
if (
43+
isinstance(resource_manager, lief.PE.ResourcesManager)
44+
and resource_manager.has_manifest
45+
):
46+
manifest = (
47+
resource_manager.manifest
48+
if isinstance(resource_manager.manifest, str)
49+
else resource_manager.manifest.decode(errors="ignore")
50+
)
51+
if "Nullsoft.NSIS.exehead" in manifest:
52+
return True
53+
return False
54+
55+
56+
class PEHandler(Handler):
57+
NAME = "pe"
58+
59+
PATTERNS = [
60+
HexString(
61+
"""
62+
// MZ header
63+
4d 5a
64+
"""
65+
),
66+
HexString(
67+
"""
68+
// PE header
69+
50 45 00 00
70+
"""
71+
),
72+
]
73+
74+
EXTRACTOR = PEExtractor()
75+
76+
DOC = HandlerDoc(
77+
name="pe",
78+
description="The PE (Portable Executable) is a binary file format used for executable code on 32-bit and 64-bit Windows operating systems as well as in UEFI environments.",
79+
handler_type=HandlerType.EXECUTABLE,
80+
vendor="Microsoft",
81+
references=[
82+
Reference(
83+
title="PE Format",
84+
url="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format",
85+
),
86+
Reference(
87+
title="Portable Executable Wikipedia",
88+
url="https://en.wikipedia.org/wiki/Portable_Executable",
89+
),
90+
],
91+
limitations=[],
92+
)
93+
94+
def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
95+
file.seek(start_offset, io.SEEK_SET)
96+
97+
binary = lief.PE.parse(file[start_offset:])
98+
if not binary:
99+
return None
100+
101+
return ValidChunk(
102+
start_offset=start_offset,
103+
end_offset=start_offset + binary.original_size,
104+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:38d49f8fe09b1c332b01d0940e57b7258f4447733643273a01c59959ad9d3b0a
3+
size 1564991
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:3a2b771cbf044fadb9c584575f66037d3126a75c6a20faa5e53d5dad56bb0443
3+
size 1565023
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:407be964e953ca6fe0380f56f1df3d72f7789cb210506ca27cc428cb654ec609
3+
size 23040
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:c9d34d054b23078eb6b86b82d3dc152f7f9df983fa1654f8dd49320bc166129e
3+
size 23072
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb
3+
size 16
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:3474e0a8b672e53f9667c72322788e459c40f9d0632705f790fa329e539edeac
3+
size 1565007
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:8b4c47c4cf5e76ec57dd5a050d5acd832a0d532ee875d7b44f6cdaf68f90d37c
3+
size 12288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:0f3b85a4a7bd2c6baa7b2b24590ea69e656384130fa8212e944016c46ac7e7d1
3+
size 25820

0 commit comments

Comments
 (0)