naft 1.0.1__py3-none-any.whl
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.
- naft/__init__.py +0 -0
- naft/modules/__init__.py +0 -0
- naft/modules/gfe.py +150 -0
- naft/modules/icd.py +452 -0
- naft/modules/ii.py +243 -0
- naft/modules/iipf.py +495 -0
- naft/modules/impf.py +774 -0
- naft/modules/pfef.py +198 -0
- naft/modules/uf.py +234 -0
- naft/naft.py +149 -0
- naft-1.0.1.dist-info/METADATA +17 -0
- naft-1.0.1.dist-info/RECORD +16 -0
- naft-1.0.1.dist-info/WHEEL +5 -0
- naft-1.0.1.dist-info/entry_points.txt +2 -0
- naft-1.0.1.dist-info/licenses/LICENSE.md +21 -0
- naft-1.0.1.dist-info/top_level.txt +1 -0
naft/__init__.py
ADDED
|
File without changes
|
naft/modules/__init__.py
ADDED
|
File without changes
|
naft/modules/gfe.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
__description__ = "Network Appliance Forensic Toolkit - Generic Frame Extraction"
|
|
4
|
+
__version__ = "1.0.1"
|
|
5
|
+
__original_author__ = "Didier Stevens"
|
|
6
|
+
__current_authors__ = "@digitalsleuth and @G-K7"
|
|
7
|
+
__date__ = "2026/06/15"
|
|
8
|
+
|
|
9
|
+
import struct
|
|
10
|
+
import naft.modules.impf as impf
|
|
11
|
+
import naft.modules.uf as uf
|
|
12
|
+
import naft.modules.pfef as pfef
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ExtractIPPacketsFromFile(filenamesRawData, filenamePCAP, arguments):
|
|
16
|
+
uf.LogLine("Start")
|
|
17
|
+
if arguments["ouitxt"] is None:
|
|
18
|
+
oFrames = pfef.cFrames()
|
|
19
|
+
else:
|
|
20
|
+
oFrames = pfef.cFrames(arguments["ouitxt"])
|
|
21
|
+
countProcessedFiles = 0
|
|
22
|
+
for filenameRawData in filenamesRawData:
|
|
23
|
+
if arguments["buffer"]:
|
|
24
|
+
uf.LogLine(f"Buffering file {filenameRawData}")
|
|
25
|
+
oBufferFile = uf.cBufferFile(
|
|
26
|
+
filenameRawData,
|
|
27
|
+
arguments["buffersize"] * 1024 * 1024,
|
|
28
|
+
arguments["bufferoverlapsize"] * 1024 * 1024,
|
|
29
|
+
)
|
|
30
|
+
while oBufferFile.Read():
|
|
31
|
+
uf.LogLine(
|
|
32
|
+
f"Processing buffer 0x{oBufferFile.index:x} size {len(oBufferFile.buffer)/1024/1024:.2f} MB {oBufferFile.Progress():d}%"
|
|
33
|
+
)
|
|
34
|
+
uf.LogLine("Searching for IPv4 packets")
|
|
35
|
+
pfef.ExtractIPPackets(
|
|
36
|
+
oFrames,
|
|
37
|
+
oBufferFile.index,
|
|
38
|
+
oBufferFile.buffer,
|
|
39
|
+
arguments["options"],
|
|
40
|
+
arguments["duplicates"],
|
|
41
|
+
True,
|
|
42
|
+
filenameRawData,
|
|
43
|
+
)
|
|
44
|
+
uf.LogLine("Searching for ARP Ethernet frames")
|
|
45
|
+
pfef.ExtractARPFrames(
|
|
46
|
+
oFrames,
|
|
47
|
+
oBufferFile.index,
|
|
48
|
+
oBufferFile.buffer,
|
|
49
|
+
arguments["duplicates"],
|
|
50
|
+
True,
|
|
51
|
+
filenameRawData,
|
|
52
|
+
)
|
|
53
|
+
if oBufferFile.err == MemoryError:
|
|
54
|
+
uf.LogLine("Data is too large to fit in memory, use smaller buffer")
|
|
55
|
+
elif oBufferFile.err:
|
|
56
|
+
uf.LogLine("Error reading file")
|
|
57
|
+
countProcessedFiles += 1
|
|
58
|
+
else:
|
|
59
|
+
uf.LogLine(f"Reading file {filenameRawData}")
|
|
60
|
+
rawData = uf.File2Data(filenameRawData)
|
|
61
|
+
if rawData is None:
|
|
62
|
+
uf.LogLine("Error reading file")
|
|
63
|
+
if rawData == MemoryError:
|
|
64
|
+
uf.LogLine("File is too large to fit in memory")
|
|
65
|
+
else:
|
|
66
|
+
uf.LogLine("Searching for IPv4 packets")
|
|
67
|
+
pfef.ExtractIPPackets(
|
|
68
|
+
oFrames,
|
|
69
|
+
0,
|
|
70
|
+
rawData,
|
|
71
|
+
arguments["options"],
|
|
72
|
+
arguments["duplicates"],
|
|
73
|
+
True,
|
|
74
|
+
filenameRawData,
|
|
75
|
+
)
|
|
76
|
+
uf.LogLine("Searching for ARP Ethernet frames")
|
|
77
|
+
pfef.ExtractARPFrames(
|
|
78
|
+
oFrames, 0, rawData, arguments["duplicates"], True, filenameRawData
|
|
79
|
+
)
|
|
80
|
+
countProcessedFiles += 1
|
|
81
|
+
if countProcessedFiles > 0:
|
|
82
|
+
uf.LogLine(f"Writing PCAP file {filenamePCAP}")
|
|
83
|
+
if not oFrames.WritePCAP(filenamePCAP):
|
|
84
|
+
uf.LogLine("Error writing PCAP file")
|
|
85
|
+
uf.LogLine(f"Number of identified frames: {oFrames.countFrames:5d}")
|
|
86
|
+
uf.LogLine(f"Number of identified packets: {oFrames.countPackets:5d}")
|
|
87
|
+
uf.LogLine(f"Number of frames in PCAP file: {len(oFrames.frames):5d}")
|
|
88
|
+
uf.LogLine("Done")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def IOSFrames(coredumpFilename, filenameIOMEM, filenamePCAP, arguments):
|
|
92
|
+
uf.LogLine("Start")
|
|
93
|
+
uf.LogLine(f"Reading file {coredumpFilename}")
|
|
94
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
95
|
+
if oIOSCoreDump.err is not None:
|
|
96
|
+
print(oIOSCoreDump.err)
|
|
97
|
+
return
|
|
98
|
+
uf.LogLine("Searching for heap region")
|
|
99
|
+
_, memoryHeap = oIOSCoreDump.RegionHEAP() # _ = addressHeap
|
|
100
|
+
if memoryHeap is None:
|
|
101
|
+
print("Heap region not found")
|
|
102
|
+
return
|
|
103
|
+
oIOSMemoryParserHeap = impf.cIOSMemoryParser(memoryHeap)
|
|
104
|
+
oIOSMemoryParserHeap.ResolveNames(oIOSCoreDump)
|
|
105
|
+
uf.LogLine(f"Reading file {filenameIOMEM}")
|
|
106
|
+
dataIOMEM = uf.File2Data(filenameIOMEM)
|
|
107
|
+
uf.LogLine(f"Searching for base address from {filenameIOMEM}")
|
|
108
|
+
oIOSMemoryParserIOMEM = impf.cIOSMemoryParser(dataIOMEM)
|
|
109
|
+
addressIOMEM = oIOSMemoryParserIOMEM.baseAddress
|
|
110
|
+
if addressIOMEM is None:
|
|
111
|
+
print("Error parsing IOMEM")
|
|
112
|
+
return
|
|
113
|
+
oFrames = pfef.cFrames()
|
|
114
|
+
if arguments["verbose"]:
|
|
115
|
+
print(impf.cIOSMemoryBlockHeader.ShowHeader)
|
|
116
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParserHeap.Headers:
|
|
117
|
+
if oIOSMemoryBlockHeader.AllocNameResolved == "*Packet Header*":
|
|
118
|
+
frameAddress = struct.unpack(">I", oIOSMemoryBlockHeader.GetData()[40:44])[
|
|
119
|
+
0
|
|
120
|
+
]
|
|
121
|
+
frameSize = struct.unpack(">H", oIOSMemoryBlockHeader.GetData()[72:74])[0]
|
|
122
|
+
if frameSize <= 1:
|
|
123
|
+
frameSize = struct.unpack(">H", oIOSMemoryBlockHeader.GetData()[68:70])[
|
|
124
|
+
0
|
|
125
|
+
]
|
|
126
|
+
if frameAddress != 0 and frameSize != 0:
|
|
127
|
+
if arguments["verbose"]:
|
|
128
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
129
|
+
uf.DumpBytes(
|
|
130
|
+
dataIOMEM[
|
|
131
|
+
frameAddress
|
|
132
|
+
- addressIOMEM : frameAddress
|
|
133
|
+
- addressIOMEM
|
|
134
|
+
+ frameSize
|
|
135
|
+
],
|
|
136
|
+
frameAddress,
|
|
137
|
+
)
|
|
138
|
+
oFrames.AddFrame(
|
|
139
|
+
frameAddress - addressIOMEM,
|
|
140
|
+
dataIOMEM[
|
|
141
|
+
frameAddress
|
|
142
|
+
- addressIOMEM : frameAddress
|
|
143
|
+
- addressIOMEM
|
|
144
|
+
+ frameSize
|
|
145
|
+
],
|
|
146
|
+
True,
|
|
147
|
+
)
|
|
148
|
+
oFrames.WritePCAP(filenamePCAP)
|
|
149
|
+
uf.LogLine(f"{oFrames.countFrames:d} frames written to {filenamePCAP}")
|
|
150
|
+
uf.LogLine("Done")
|
naft/modules/icd.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
__description__ = "Network Appliance Forensic Toolkit - IOS Core Dumps"
|
|
4
|
+
__version__ = "1.0.1"
|
|
5
|
+
__original_author__ = "Didier Stevens"
|
|
6
|
+
__current_authors__ = "@digitalsleuth and @G-K7"
|
|
7
|
+
__date__ = "2026/06/15"
|
|
8
|
+
|
|
9
|
+
import struct
|
|
10
|
+
import re
|
|
11
|
+
import os
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
import naft.modules.uf as uf
|
|
14
|
+
import naft.modules.impf as impf
|
|
15
|
+
import naft.modules.pfef as pfef
|
|
16
|
+
import naft.modules.iipf as iipf
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def IOSRegions(coredumpFilename, arguments):
|
|
20
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
21
|
+
if oIOSCoreDump.err is not None:
|
|
22
|
+
print(oIOSCoreDump.err)
|
|
23
|
+
else:
|
|
24
|
+
print("Start End Size Name")
|
|
25
|
+
for region in oIOSCoreDump.regions:
|
|
26
|
+
if region[2] is not None:
|
|
27
|
+
print(
|
|
28
|
+
f"0x{region[1]:08X} 0x{(region[1] + region[2] - 1):08X} {region[2]:<10d} {region[0]}"
|
|
29
|
+
)
|
|
30
|
+
if arguments["output"]:
|
|
31
|
+
uf.Data2File(
|
|
32
|
+
oIOSCoreDump.Region(region[0])[1],
|
|
33
|
+
f"{os.path.basename(coredumpFilename)}-{region[0]}-0x{region[1]:08X}",
|
|
34
|
+
arguments["output"],
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
print(f'0x{region[1]:08X} {(" " * 21)} {region[0]}')
|
|
38
|
+
addressBSS, dataBSS = oIOSCoreDump.RegionBSS()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def File2Strings(filename):
|
|
42
|
+
try:
|
|
43
|
+
with open(filename, "r", encoding="utf-8") as f:
|
|
44
|
+
return [line.rstrip("\n") for line in f]
|
|
45
|
+
except:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def ProcessAt(argument):
|
|
50
|
+
if argument.startswith("@"):
|
|
51
|
+
strings = File2Strings(argument[1:])
|
|
52
|
+
if strings is None:
|
|
53
|
+
raise Exception(f"Error reading {argument}")
|
|
54
|
+
else:
|
|
55
|
+
return strings
|
|
56
|
+
return [argument]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def ProcessHeap(oIOSMemoryBlockHeader, arguments, coredumpFilename, output_path=None):
|
|
60
|
+
if not arguments["strings"]:
|
|
61
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
62
|
+
if arguments["strings"]:
|
|
63
|
+
dStrings = uf.SearchASCIIStrings(oIOSMemoryBlockHeader.GetData())
|
|
64
|
+
if arguments["grep"] != "":
|
|
65
|
+
printHeader = True
|
|
66
|
+
for key, value in dStrings.items():
|
|
67
|
+
if value.find(arguments["grep"].encode("utf-8")) >= 0:
|
|
68
|
+
if printHeader:
|
|
69
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
70
|
+
printHeader = False
|
|
71
|
+
print(
|
|
72
|
+
f' {(oIOSMemoryBlockHeader.address + oIOSMemoryBlockHeader.BlockSize + key):08X}: {value.decode("utf-8")}'
|
|
73
|
+
)
|
|
74
|
+
elif arguments["minimum"] == 0 or len(dStrings) >= arguments["minimum"]:
|
|
75
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
76
|
+
for key, value in dStrings.items():
|
|
77
|
+
print(
|
|
78
|
+
f' {(oIOSMemoryBlockHeader.address + oIOSMemoryBlockHeader.BlockSize + key):08X}: {value.decode("utf-8")}'
|
|
79
|
+
)
|
|
80
|
+
if arguments["dump"]:
|
|
81
|
+
uf.DumpBytes(
|
|
82
|
+
oIOSMemoryBlockHeader.GetData(),
|
|
83
|
+
oIOSMemoryBlockHeader.address + oIOSMemoryBlockHeader.headerSize,
|
|
84
|
+
)
|
|
85
|
+
if arguments["dumpraw"]:
|
|
86
|
+
uf.DumpBytes(oIOSMemoryBlockHeader.GetRawData(), oIOSMemoryBlockHeader.address)
|
|
87
|
+
if arguments["output"]:
|
|
88
|
+
uf.Data2File(
|
|
89
|
+
oIOSMemoryBlockHeader.GetData(),
|
|
90
|
+
f"{coredumpFilename}-heap-0x{oIOSMemoryBlockHeader.address:08X}.data",
|
|
91
|
+
output_path,
|
|
92
|
+
)
|
|
93
|
+
if arguments["verbose"]:
|
|
94
|
+
print(
|
|
95
|
+
f"\tFile: {output_path}{coredumpFilename}-heap-0x{oIOSMemoryBlockHeader.address:08X}.data created.\n"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def IOSHeap(coredumpFilename, arguments):
|
|
100
|
+
if arguments["output"] is not None:
|
|
101
|
+
output_path = os.path.join(arguments["output"], "heap_data")
|
|
102
|
+
os.mkdir(output_path)
|
|
103
|
+
else:
|
|
104
|
+
output_path = ""
|
|
105
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
106
|
+
if oIOSCoreDump.err is not None:
|
|
107
|
+
print(oIOSCoreDump.err)
|
|
108
|
+
return
|
|
109
|
+
addressHeap, memoryHeap = oIOSCoreDump.RegionHEAP()
|
|
110
|
+
if memoryHeap is None:
|
|
111
|
+
print("Heap region not found")
|
|
112
|
+
return
|
|
113
|
+
oIOSMemoryParser = impf.cIOSMemoryParser(memoryHeap)
|
|
114
|
+
if arguments["resolve"] or arguments["filter"] != "":
|
|
115
|
+
oIOSMemoryParser.ResolveNames(oIOSCoreDump)
|
|
116
|
+
if arguments["filter"] == "":
|
|
117
|
+
print(impf.cIOSMemoryBlockHeader.ShowHeader)
|
|
118
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers:
|
|
119
|
+
ProcessHeap(oIOSMemoryBlockHeader, arguments, coredumpFilename, output_path)
|
|
120
|
+
else:
|
|
121
|
+
print(impf.cIOSMemoryBlockHeader.ShowHeader)
|
|
122
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers:
|
|
123
|
+
if oIOSMemoryBlockHeader.AllocNameResolved == arguments["filter"]:
|
|
124
|
+
ProcessHeap(
|
|
125
|
+
oIOSMemoryBlockHeader, arguments, coredumpFilename, output_path
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def IOSCWStringsSub(data):
|
|
130
|
+
oCWStrings = impf.cCiscoCWStrings(data)
|
|
131
|
+
if oCWStrings.err is not None:
|
|
132
|
+
print(oCWStrings.err)
|
|
133
|
+
return
|
|
134
|
+
keys = list(oCWStrings.dCWStrings.keys())
|
|
135
|
+
keys.sort()
|
|
136
|
+
for key in keys:
|
|
137
|
+
if key == b"CW_SYSDESCR":
|
|
138
|
+
print(f'{key.decode("utf-8")}:')
|
|
139
|
+
print(oCWStrings.dCWStrings[key].decode("utf-8"))
|
|
140
|
+
else:
|
|
141
|
+
print(
|
|
142
|
+
f'{key.decode("utf-8")}:{(' ' * (22 - len(key)))}{oCWStrings.dCWStrings[key].decode("utf-8")}'
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def IOSCWStrings(coredumpFilename, arguments):
|
|
147
|
+
if arguments["raw"]:
|
|
148
|
+
coredump = uf.File2Data(coredumpFilename)
|
|
149
|
+
if coredump is None:
|
|
150
|
+
print(f"Error reading file {coredumpFilename}")
|
|
151
|
+
else:
|
|
152
|
+
IOSCWStringsSub(coredump)
|
|
153
|
+
else:
|
|
154
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
155
|
+
if oIOSCoreDump.err is not None:
|
|
156
|
+
print(oIOSCoreDump.err)
|
|
157
|
+
return
|
|
158
|
+
addressData, memoryData = oIOSCoreDump.RegionDATA()
|
|
159
|
+
if memoryData is None:
|
|
160
|
+
print("Data region not found")
|
|
161
|
+
return
|
|
162
|
+
IOSCWStringsSub(memoryData)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def PrintStatsAnalysis(dStats, oIOSCoreDump):
|
|
166
|
+
keys1 = list(dStats.keys())
|
|
167
|
+
keys1.sort()
|
|
168
|
+
for key1 in keys1:
|
|
169
|
+
countKeys = len(dStats[key1])
|
|
170
|
+
keys2 = list(dStats[key1].keys())
|
|
171
|
+
keys2.sort()
|
|
172
|
+
if 2 < countKeys <= 7:
|
|
173
|
+
bucket = "-> " + " ".join(
|
|
174
|
+
[f"{key2:X}:{dStats[key1][key2]:d}" for key2 in keys2]
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
bucket = ""
|
|
178
|
+
filtered = [x for x in dStats[key1] if x != 0]
|
|
179
|
+
if filtered == []:
|
|
180
|
+
filteredMin = min(dStats[key1])
|
|
181
|
+
else:
|
|
182
|
+
filteredMin = min(filtered)
|
|
183
|
+
unfilteredMax = max(dStats[key1])
|
|
184
|
+
regionNames = []
|
|
185
|
+
for region in oIOSCoreDump.regions:
|
|
186
|
+
if region[2] is not None:
|
|
187
|
+
if (
|
|
188
|
+
region[1] <= filteredMin <= region[1] + region[2] - 1
|
|
189
|
+
or region[1] <= unfilteredMax <= region[1] + region[2] - 1
|
|
190
|
+
):
|
|
191
|
+
if region[0] not in regionNames:
|
|
192
|
+
regionNames.append(region[0])
|
|
193
|
+
regionNames.sort()
|
|
194
|
+
regionName = " ".join(regionNames).strip()
|
|
195
|
+
print(
|
|
196
|
+
f"{key1:3d} {(key1*4):3X}: {countKeys:3d} {min(dStats[key1]):08X} {filteredMin:08X} {unfilteredMax:08X} {regionName} {bucket}"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def IOSProcesses(coredumpFilename, arguments):
|
|
201
|
+
oIOSCoreDumpAnalysis = impf.cIOSCoreDumpAnalysis(coredumpFilename)
|
|
202
|
+
if oIOSCoreDumpAnalysis.err is not None:
|
|
203
|
+
print(oIOSCoreDumpAnalysis.err)
|
|
204
|
+
return
|
|
205
|
+
print(
|
|
206
|
+
f"{'PID':>4} "
|
|
207
|
+
f"{'QTy':>3} "
|
|
208
|
+
f"{'PC':<8} "
|
|
209
|
+
f"{'Runtime (ms)':>12} "
|
|
210
|
+
f"{'Invoked':>10} "
|
|
211
|
+
f"{'uSecs':>8} "
|
|
212
|
+
f"{'Stacks':>12} "
|
|
213
|
+
f"{'TTY':>4} "
|
|
214
|
+
f"{'StackBlk':>8} "
|
|
215
|
+
f"Process"
|
|
216
|
+
)
|
|
217
|
+
for processID, addressProcess, oIOSProcess in oIOSCoreDumpAnalysis.processes:
|
|
218
|
+
if arguments["filter"] == "" or processID == int(arguments["filter"]):
|
|
219
|
+
if oIOSProcess is not None:
|
|
220
|
+
if oIOSProcess.err == "":
|
|
221
|
+
line = oIOSProcess.Line()
|
|
222
|
+
else:
|
|
223
|
+
line = f"{processID:4d} {oIOSProcess.err}"
|
|
224
|
+
print(line)
|
|
225
|
+
if arguments["dump"]:
|
|
226
|
+
uf.DumpBytes(oIOSProcess.data, addressProcess)
|
|
227
|
+
else:
|
|
228
|
+
print(
|
|
229
|
+
f" {processID:>3d} {addressProcess:08X} - addressProcess not found"
|
|
230
|
+
)
|
|
231
|
+
if oIOSCoreDumpAnalysis.RanHeuristics:
|
|
232
|
+
print("")
|
|
233
|
+
print("*** WARNING ***")
|
|
234
|
+
print("Unexpected process structure")
|
|
235
|
+
print("Please reports these results")
|
|
236
|
+
print("Fields determined with heuristics:")
|
|
237
|
+
print(f"Process structure size: {oIOSCoreDumpAnalysis.HeuristicsSize:d}")
|
|
238
|
+
keys = list(oIOSCoreDumpAnalysis.HeuristicsFields.keys())
|
|
239
|
+
keys.sort(key=str.lower)
|
|
240
|
+
for key in keys:
|
|
241
|
+
value = oIOSCoreDumpAnalysis.HeuristicsFields[key]
|
|
242
|
+
if value is not None:
|
|
243
|
+
print(f"{key:-22s}: 0x{value[1]:04X}")
|
|
244
|
+
if arguments["stats"]:
|
|
245
|
+
keys = list(oIOSCoreDumpAnalysis.dProcessStructureStats.keys())
|
|
246
|
+
keys.sort()
|
|
247
|
+
print(f"Number of different process structures: {len(keys):d}")
|
|
248
|
+
for index in keys:
|
|
249
|
+
print(f"Process structures length: {index:d}")
|
|
250
|
+
PrintStatsAnalysis(
|
|
251
|
+
oIOSCoreDumpAnalysis.dProcessStructureStats[index],
|
|
252
|
+
oIOSCoreDumpAnalysis.oIOSCoreDump,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def FilterInitBlocksForString(coredumpFilename, searchTerm):
|
|
257
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
258
|
+
if oIOSCoreDump.err is not None:
|
|
259
|
+
return []
|
|
260
|
+
addressHeap, memoryHeap = oIOSCoreDump.RegionHEAP()
|
|
261
|
+
if memoryHeap is None:
|
|
262
|
+
print("Heap region not found")
|
|
263
|
+
return []
|
|
264
|
+
oIOSMemoryParser = impf.cIOSMemoryParser(memoryHeap)
|
|
265
|
+
oIOSMemoryParser.ResolveNames(oIOSCoreDump)
|
|
266
|
+
found = []
|
|
267
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers:
|
|
268
|
+
if oIOSMemoryBlockHeader.AllocNameResolved == "Init":
|
|
269
|
+
dStrings = uf.SearchASCIIStrings(oIOSMemoryBlockHeader.GetData())
|
|
270
|
+
for value in dStrings.values():
|
|
271
|
+
if value.find(searchTerm) >= 0:
|
|
272
|
+
found.append(value)
|
|
273
|
+
return found
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def IOSHistory(coredumpFilename, arguments=None):
|
|
277
|
+
history = []
|
|
278
|
+
hist_time_format = "%b %d %Y %H:%M:%S.%f %Z"
|
|
279
|
+
CMD_PATTERN = re.compile(
|
|
280
|
+
rb"CMD: '(.+?)' " rb"(\d{2}:\d{2}:\d{2} \S+ \S+ \S+ \d{1,2} \d{4})"
|
|
281
|
+
)
|
|
282
|
+
for command in FilterInitBlocksForString(coredumpFilename, b"CMD: "):
|
|
283
|
+
oMatch = CMD_PATTERN.search(command)
|
|
284
|
+
if oMatch:
|
|
285
|
+
timestamp = oMatch.group(2).decode("utf-8")
|
|
286
|
+
dt = uf.ParseDateTime(timestamp)
|
|
287
|
+
history.append((dt, oMatch.group(1).decode("utf-8")))
|
|
288
|
+
if not history:
|
|
289
|
+
print("No history found")
|
|
290
|
+
return
|
|
291
|
+
for command in sorted(history, key=lambda x: x[0]):
|
|
292
|
+
formatted_time = command[0].strftime(hist_time_format)
|
|
293
|
+
print(f"{formatted_time}: {command[1]}")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def IOSEvents(coredumpFilename, arguments=None):
|
|
297
|
+
events = []
|
|
298
|
+
evt_time_format = "%b %d %Y %H:%M:%S.%f"
|
|
299
|
+
EVT_PATTERN = re.compile(
|
|
300
|
+
rb"([\w\s\d:]+\.\d{3,6})(.*)"
|
|
301
|
+
)
|
|
302
|
+
for raw_event in FilterInitBlocksForString(coredumpFilename, b": %"):
|
|
303
|
+
decoded_event = raw_event.decode("utf-8")
|
|
304
|
+
clean_event, cmd_string, _ = decoded_event.partition("CMD:")
|
|
305
|
+
oMatch = EVT_PATTERN.search(clean_event.encode("utf-8"))
|
|
306
|
+
if oMatch:
|
|
307
|
+
timestamp_str = oMatch.group(1).decode("utf-8").strip()
|
|
308
|
+
event_data = oMatch.group(2).decode("utf-8").strip(" :").rstrip()
|
|
309
|
+
dt_object = uf.ParseDateTime(timestamp_str, time_format=None)
|
|
310
|
+
events.append((dt_object, event_data))
|
|
311
|
+
if not events:
|
|
312
|
+
print("No events found")
|
|
313
|
+
return
|
|
314
|
+
for event in sorted(events, key=lambda x: x[0]):
|
|
315
|
+
formatted_time = event[0].strftime(evt_time_format)
|
|
316
|
+
print(f"{formatted_time}: {event[1]}")
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def IOSCheckText(coredumpFilename, imageFilename, arguments):
|
|
320
|
+
print("Comparing CW_SYSDESCR between core dump and IOS image")
|
|
321
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
322
|
+
if oIOSCoreDump.err is not None:
|
|
323
|
+
print(oIOSCoreDump.err)
|
|
324
|
+
return
|
|
325
|
+
textAddress, textCoredump = oIOSCoreDump.RegionTEXT()
|
|
326
|
+
if textCoredump is None:
|
|
327
|
+
print(f"Error extracting text region from coredump {coredumpFilename}")
|
|
328
|
+
return
|
|
329
|
+
sysdescrCoredump = ""
|
|
330
|
+
dataAddress, dataCoredump = oIOSCoreDump.RegionDATA()
|
|
331
|
+
if dataCoredump is not None:
|
|
332
|
+
oCWStrings = impf.cCiscoCWStrings(dataCoredump)
|
|
333
|
+
if oCWStrings.err is None and b"CW_SYSDESCR" in oCWStrings.dCWStrings:
|
|
334
|
+
sysdescrCoredump = oCWStrings.dCWStrings[b"CW_SYSDESCR"].decode("utf-8")
|
|
335
|
+
image = uf.File2Data(imageFilename)
|
|
336
|
+
if image is None:
|
|
337
|
+
print(f"Error reading image {imageFilename}")
|
|
338
|
+
return
|
|
339
|
+
oIOSImage = iipf.cIOSImage(image, imageFilename)
|
|
340
|
+
if oIOSImage.err != 0:
|
|
341
|
+
return
|
|
342
|
+
sysdescrImage = ""
|
|
343
|
+
if (
|
|
344
|
+
oIOSImage.oCWStrings is not None
|
|
345
|
+
and oIOSImage.oCWStrings.err is None
|
|
346
|
+
and b"CW_SYSDESCR" in oIOSImage.oCWStrings.dCWStrings
|
|
347
|
+
):
|
|
348
|
+
sysdescrImage = oIOSImage.oCWStrings.dCWStrings[b"CW_SYSDESCR"].decode("utf-8")
|
|
349
|
+
if sysdescrCoredump != "" or sysdescrImage != "":
|
|
350
|
+
if sysdescrCoredump == sysdescrImage:
|
|
351
|
+
print("CW_SYSDESCR are identical:\n")
|
|
352
|
+
print(sysdescrCoredump)
|
|
353
|
+
elif sysdescrCoredump == sysdescrImage.replace("-MZ", "-M", 1):
|
|
354
|
+
print("CW_SYSDESCR are equivalent:\n")
|
|
355
|
+
print(sysdescrCoredump)
|
|
356
|
+
else:
|
|
357
|
+
print("CW_SYSDESCR are different:\n")
|
|
358
|
+
print(sysdescrCoredump)
|
|
359
|
+
print("")
|
|
360
|
+
print(sysdescrImage)
|
|
361
|
+
print("")
|
|
362
|
+
oELF = iipf.cELF(oIOSImage.imageUncompressed)
|
|
363
|
+
if oELF.err != 0:
|
|
364
|
+
print(f"ELF parsing error {oELF.err:d}.")
|
|
365
|
+
return
|
|
366
|
+
countSectionExecutableInstructions = 0
|
|
367
|
+
countSectionSRELOC = 0
|
|
368
|
+
for oSectionHeader in oELF.sections:
|
|
369
|
+
if oSectionHeader.flags & 4: # SHF_EXECINSTR executable instructions
|
|
370
|
+
textSectionData = oSectionHeader.sectionData
|
|
371
|
+
countSectionExecutableInstructions += 1
|
|
372
|
+
if oSectionHeader.nameIndexString == "sreloc":
|
|
373
|
+
countSectionSRELOC += 1
|
|
374
|
+
if countSectionExecutableInstructions != 1:
|
|
375
|
+
print(
|
|
376
|
+
f"Error executable sections in image: found {countSectionExecutableInstructions:d} sections, expected 1"
|
|
377
|
+
)
|
|
378
|
+
return
|
|
379
|
+
if countSectionSRELOC != 0:
|
|
380
|
+
print(
|
|
381
|
+
f"Error found {countSectionSRELOC:d} sreloc section in image: checktext command does not support relocation"
|
|
382
|
+
)
|
|
383
|
+
return
|
|
384
|
+
start = textAddress & 0xFF # to be further researched
|
|
385
|
+
textImage = textSectionData[start : start + len(textCoredump)]
|
|
386
|
+
if len(textCoredump) != len(textImage):
|
|
387
|
+
print("the text region is longer than the text section")
|
|
388
|
+
print(f"len(textCoredump) = {len(textCoredump):d}")
|
|
389
|
+
print(f"len(textImage) = {len(textImage):d}")
|
|
390
|
+
countBytesDifferent = 0
|
|
391
|
+
shortestLength = min(len(textCoredump), len(textImage))
|
|
392
|
+
for iIter in range(shortestLength):
|
|
393
|
+
if textCoredump[iIter] != textImage[iIter]:
|
|
394
|
+
if countBytesDifferent == 0:
|
|
395
|
+
print(
|
|
396
|
+
f"text region and section are different starting 0x{(textAddress + iIter):08X} in coredump (iter = 0x{iIter:08X})"
|
|
397
|
+
)
|
|
398
|
+
countBytesDifferent += 1
|
|
399
|
+
if countBytesDifferent == 0:
|
|
400
|
+
print("text region and section are identical")
|
|
401
|
+
else:
|
|
402
|
+
print(
|
|
403
|
+
f"number of different bytes: {countBytesDifferent:d} ({((countBytesDifferent * 100.0) / shortestLength):.2f}%)"
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def IOSIntegrityText(coredumpFilename, arguments):
|
|
408
|
+
oIOSCoreDump = impf.cIOSCoreDump(coredumpFilename)
|
|
409
|
+
if oIOSCoreDump.err is not None:
|
|
410
|
+
print(oIOSCoreDump.err)
|
|
411
|
+
return
|
|
412
|
+
addressHeap, memoryHeap = oIOSCoreDump.RegionHEAP()
|
|
413
|
+
if memoryHeap is None:
|
|
414
|
+
print("Heap region not found")
|
|
415
|
+
return
|
|
416
|
+
oIOSMemoryParser = impf.cIOSMemoryParser(memoryHeap)
|
|
417
|
+
print("Check start magic:")
|
|
418
|
+
hit = False
|
|
419
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers:
|
|
420
|
+
if oIOSMemoryBlockHeader.GetRawData()[0:4] != impf.cCiscoMagic.STR_BLOCK_BEGIN:
|
|
421
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
422
|
+
hit = True
|
|
423
|
+
if not hit:
|
|
424
|
+
print("OK")
|
|
425
|
+
print("Check end magic:")
|
|
426
|
+
hit = False
|
|
427
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers:
|
|
428
|
+
if (
|
|
429
|
+
struct.unpack(">I", oIOSMemoryBlockHeader.GetRawData()[-4:])[0]
|
|
430
|
+
!= impf.cCiscoMagic.INT_BLOCK_CANARY
|
|
431
|
+
and oIOSMemoryBlockHeader.RefCnt > 0
|
|
432
|
+
):
|
|
433
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
434
|
+
hit = True
|
|
435
|
+
if not hit:
|
|
436
|
+
print("OK")
|
|
437
|
+
print("Check previous block:")
|
|
438
|
+
hit = False
|
|
439
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers[1:]:
|
|
440
|
+
if oIOSMemoryBlockHeader.PrevBlock == 0:
|
|
441
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
442
|
+
hit = True
|
|
443
|
+
if not hit:
|
|
444
|
+
print("OK")
|
|
445
|
+
print("Check next block:")
|
|
446
|
+
hit = False
|
|
447
|
+
for oIOSMemoryBlockHeader in oIOSMemoryParser.Headers[:-1]:
|
|
448
|
+
if oIOSMemoryBlockHeader.NextBlock == 0:
|
|
449
|
+
print(oIOSMemoryBlockHeader.ShowLine())
|
|
450
|
+
hit = True
|
|
451
|
+
if not hit:
|
|
452
|
+
print("OK")
|