Threat Hunting Methods for PE-files analysis
This commit is contained in:
+206
-47
@@ -17,48 +17,8 @@ def allowed_file(filename, config):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in config['upload']['allowed_extensions']
|
||||
|
||||
def save_uploaded_file(file, upload_folder):
|
||||
file_content = file.read()
|
||||
file.close()
|
||||
md5_hash = hashlib.md5(file_content).hexdigest()
|
||||
sha256_hash = hashlib.sha256(file_content).hexdigest()
|
||||
|
||||
original_filename = secure_filename(file.filename)
|
||||
extension = os.path.splitext(original_filename)[1].lower()
|
||||
filename = f"{md5_hash}_{original_filename}"
|
||||
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
filepath = os.path.join(upload_folder, filename)
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(file_content)
|
||||
|
||||
file_info = {
|
||||
'original_name': original_filename,
|
||||
'md5': md5_hash,
|
||||
'sha256': sha256_hash,
|
||||
'size': len(file_content),
|
||||
'extension': extension,
|
||||
'mime_type': mimetypes.guess_type(original_filename)[0] or 'application/octet-stream',
|
||||
'upload_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'entropy': calculate_entropy(file_content),
|
||||
}
|
||||
|
||||
# Add specific file type information for PE files
|
||||
if extension in ['.exe', '.dll', '.sys']:
|
||||
file_info.update(get_pe_info(filepath))
|
||||
|
||||
# Add specific file type information for Office documents
|
||||
if extension in ['.docx', '.xlsx', '.doc', '.xls', '.xlsm', '.docm']:
|
||||
office_result = get_office_info(filepath)
|
||||
if 'error' in office_result:
|
||||
print(f"Warning: {office_result['error']}")
|
||||
file_info.update(office_result)
|
||||
|
||||
return file_info
|
||||
|
||||
def calculate_entropy(data):
|
||||
"""Calculate Shannon entropy of data"""
|
||||
"""Calculate Shannon entropy of data with detection insights"""
|
||||
if len(data) == 0:
|
||||
return 0
|
||||
|
||||
@@ -80,18 +40,136 @@ def calculate_entropy(data):
|
||||
return round(entropy, 2)
|
||||
|
||||
def get_pe_info(filepath):
|
||||
"""Get PE file information"""
|
||||
"""Enhanced PE file analysis with deep import analysis and detection vectors"""
|
||||
try:
|
||||
pe = pefile.PE(filepath)
|
||||
|
||||
# Enhanced section analysis
|
||||
sections_info = []
|
||||
suspicious_imports = []
|
||||
high_risk_imports = {
|
||||
'kernel32.dll': {
|
||||
'createremotethread': 'Process Injection capability detected',
|
||||
'virtualallocex': 'Memory allocation in remote process detected',
|
||||
'writeprocessmemory': 'Process memory manipulation detected',
|
||||
'getprocaddress': 'Dynamic API resolution - possible evasion technique',
|
||||
'loadlibrarya': 'Dynamic library loading - possible evasion technique',
|
||||
'openprocess': 'Process manipulation capability',
|
||||
'createtoolhelp32snapshot': 'Process enumeration capability',
|
||||
'process32first': 'Process enumeration capability',
|
||||
'process32next': 'Process enumeration capability'
|
||||
},
|
||||
'user32.dll': {
|
||||
'getasynckeystate': 'Potential keylogging capability',
|
||||
'getdc': 'Screen capture capability',
|
||||
'getforegroundwindow': 'Window/Process monitoring capability'
|
||||
},
|
||||
'wininet.dll': {
|
||||
'internetconnect': 'Network communication capability',
|
||||
'internetopen': 'Network communication capability',
|
||||
'ftpputfile': 'FTP upload capability',
|
||||
'ftpopenfile': 'FTP communication capability'
|
||||
},
|
||||
'urlmon.dll': {
|
||||
'urldownloadtofile': 'File download capability'
|
||||
}
|
||||
}
|
||||
"""
|
||||
https://practicalsecurityanalytics.com/threat-hunting-with-function-imports/
|
||||
"""
|
||||
# Check imports for suspicious behavior
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
dll_name = entry.dll.decode().lower()
|
||||
|
||||
# Check each imported function
|
||||
for imp in entry.imports:
|
||||
if imp.name:
|
||||
func_name = imp.name.decode().lower()
|
||||
|
||||
# Check if this is a high-risk import
|
||||
if dll_name in high_risk_imports and func_name in high_risk_imports[dll_name]:
|
||||
suspicious_imports.append({
|
||||
'dll': dll_name,
|
||||
'function': func_name,
|
||||
'note': high_risk_imports[dll_name][func_name],
|
||||
'hint': imp.hint if hasattr(imp, 'hint') else 0
|
||||
})
|
||||
|
||||
"""
|
||||
https://practicalsecurityanalytics.com/file-entropy/
|
||||
"""
|
||||
# Section Analysis with entropy
|
||||
for section in pe.sections:
|
||||
section_name = section.Name.decode().rstrip('\x00')
|
||||
section_data = section.get_data()
|
||||
section_entropy = calculate_entropy(section_data)
|
||||
|
||||
# Standard PE sections
|
||||
standard_sections = ['.text', '.data', '.bss', '.rdata', '.edata', '.idata', '.pdata', '.reloc', '.rsrc', '.tls', '.debug']
|
||||
is_standard = section_name in standard_sections
|
||||
|
||||
sections_info.append({
|
||||
'name': section_name,
|
||||
'entropy': section_entropy,
|
||||
'size': len(section_data),
|
||||
'characteristics': section.Characteristics,
|
||||
'is_standard': is_standard,
|
||||
'detection_notes': []
|
||||
})
|
||||
|
||||
# Add section-specific detection notes
|
||||
if section_entropy > 7.2:
|
||||
sections_info[-1]['detection_notes'].append('High entropy may trigger detection')
|
||||
if section_name == '.text' and section_entropy > 7.0:
|
||||
sections_info[-1]['detection_notes'].append('Unusual entropy for code section')
|
||||
if not is_standard:
|
||||
sections_info[-1]['detection_notes'].append('Non-standard section name - may trigger detection')
|
||||
|
||||
"""
|
||||
https://practicalsecurityanalytics.com/pe-checksum/
|
||||
"""
|
||||
# Check PE Checksum
|
||||
is_valid_checksum = pe.verify_checksum()
|
||||
calculated_checksum = pe.generate_checksum()
|
||||
stored_checksum = pe.OPTIONAL_HEADER.CheckSum
|
||||
|
||||
info = {
|
||||
'file_type': 'PE32+ executable' if pe.PE_TYPE == pefile.OPTIONAL_HEADER_MAGIC_PE_PLUS else 'PE32 executable',
|
||||
'machine_type': pefile.MACHINE_TYPE[pe.FILE_HEADER.Machine].replace('IMAGE_FILE_MACHINE_', ''),
|
||||
'compile_time': datetime.datetime.fromtimestamp(pe.FILE_HEADER.TimeDateStamp).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'subsystem': pefile.SUBSYSTEM_TYPE[pe.OPTIONAL_HEADER.Subsystem].replace('IMAGE_SUBSYSTEM_', ''),
|
||||
'entry_point': hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint),
|
||||
'sections': [section.Name.decode().rstrip('\x00') for section in pe.sections],
|
||||
'imports': list(set(entry.dll.decode() for entry in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', [])))
|
||||
'sections': sections_info,
|
||||
'imports': list(set(entry.dll.decode() for entry in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', []))),
|
||||
'suspicious_imports': suspicious_imports,
|
||||
'detection_notes': [],
|
||||
'checksum_info': {
|
||||
'is_valid': is_valid_checksum,
|
||||
'stored_checksum': hex(stored_checksum),
|
||||
'calculated_checksum': hex(calculated_checksum),
|
||||
'needs_update': calculated_checksum != stored_checksum
|
||||
}
|
||||
}
|
||||
|
||||
# Add overall detection insights
|
||||
if not is_valid_checksum:
|
||||
info['detection_notes'].append('Invalid PE checksum - Common in modified/packed files (~83% correlation with malware)')
|
||||
|
||||
if suspicious_imports:
|
||||
info['detection_notes'].append(f'Found {len(suspicious_imports)} suspicious API imports - Review import analysis')
|
||||
|
||||
if any(section['entropy'] > 7.2 for section in sections_info):
|
||||
info['detection_notes'].append('High entropy sections detected - Consider entropy reduction techniques')
|
||||
|
||||
if '.text' in [section['name'] for section in sections_info]:
|
||||
text_section = next(s for s in sections_info if s['name'] == '.text')
|
||||
if text_section['entropy'] > 7.0:
|
||||
info['detection_notes'].append('Packed/encrypted code section may trigger heuristics')
|
||||
|
||||
if any(not section['is_standard'] for section in sections_info):
|
||||
info['detection_notes'].append('Non-standard PE sections detected - May trigger static analysis')
|
||||
|
||||
pe.close()
|
||||
return {'pe_info': info}
|
||||
except Exception as e:
|
||||
@@ -99,20 +177,101 @@ def get_pe_info(filepath):
|
||||
return {'pe_info': None}
|
||||
|
||||
def get_office_info(filepath):
|
||||
"""Get Office document information"""
|
||||
"""Enhanced Office document analysis with detection insights"""
|
||||
try:
|
||||
vbaparser = VBA_Parser(filepath)
|
||||
detection_notes = []
|
||||
|
||||
info = {
|
||||
'file_type': 'Microsoft Office Document',
|
||||
'has_macros': vbaparser.detect_vba_macros(),
|
||||
'macro_info': vbaparser.analyze_macros() if vbaparser.detect_vba_macros() else None,
|
||||
'macro_info': None,
|
||||
'detection_notes': detection_notes
|
||||
}
|
||||
vbaparser.close() # Release the VBA Parser resource
|
||||
|
||||
if vbaparser.detect_vba_macros():
|
||||
macro_analysis = vbaparser.analyze_macros()
|
||||
info['macro_info'] = macro_analysis
|
||||
|
||||
# Analyze macros for detection vectors
|
||||
macro_text = str(macro_analysis).lower()
|
||||
detection_patterns = {
|
||||
'shell': 'Shell command execution detected',
|
||||
'wscript': 'WScript execution detected',
|
||||
'powershell': 'PowerShell execution detected',
|
||||
'http': 'Network communication detected',
|
||||
'auto': 'Auto-execution mechanism detected',
|
||||
'document_open': 'Document open auto-execution',
|
||||
'windowshide': 'Hidden window execution',
|
||||
'createobject': 'COM object creation detected'
|
||||
}
|
||||
|
||||
for pattern, note in detection_patterns.items():
|
||||
if pattern in macro_text:
|
||||
detection_notes.append(note)
|
||||
|
||||
vbaparser.close()
|
||||
return {'office_info': info}
|
||||
except Exception as e:
|
||||
print(f"Error analyzing Office file: {e}")
|
||||
return {'office_info': None}
|
||||
|
||||
def save_uploaded_file(file, upload_folder):
|
||||
file_content = file.read()
|
||||
file.close()
|
||||
md5_hash = hashlib.md5(file_content).hexdigest()
|
||||
sha256_hash = hashlib.sha256(file_content).hexdigest()
|
||||
|
||||
original_filename = secure_filename(file.filename)
|
||||
extension = os.path.splitext(original_filename)[1].lower()
|
||||
filename = f"{md5_hash}_{original_filename}"
|
||||
|
||||
os.makedirs(upload_folder, exist_ok=True)
|
||||
filepath = os.path.join(upload_folder, filename)
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(file_content)
|
||||
|
||||
entropy_value = calculate_entropy(file_content)
|
||||
|
||||
file_info = {
|
||||
'original_name': original_filename,
|
||||
'md5': md5_hash,
|
||||
'sha256': sha256_hash,
|
||||
'size': len(file_content),
|
||||
'extension': extension,
|
||||
'mime_type': mimetypes.guess_type(original_filename)[0] or 'application/octet-stream',
|
||||
'upload_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'entropy': entropy_value,
|
||||
'entropy_analysis': {
|
||||
'value': entropy_value,
|
||||
'detection_risk': 'High' if entropy_value > 7.2 else
|
||||
'Medium' if entropy_value > 6.8 else 'Low',
|
||||
'notes': []
|
||||
}
|
||||
}
|
||||
|
||||
# Add entropy-based detection notes
|
||||
if entropy_value > 7.2:
|
||||
file_info['entropy_analysis']['notes'].append(
|
||||
'High entropy indicates encryption/packing - consider entropy reduction')
|
||||
elif entropy_value > 6.8:
|
||||
file_info['entropy_analysis']['notes'].append(
|
||||
'Moderate entropy - may trigger basic detection')
|
||||
|
||||
# Add specific file type information for PE files
|
||||
if extension in ['.exe', '.dll', '.sys']:
|
||||
file_info.update(get_pe_info(filepath))
|
||||
|
||||
# Add specific file type information for Office documents
|
||||
if extension in ['.docx', '.xlsx', '.doc', '.xls', '.xlsm', '.docm']:
|
||||
office_result = get_office_info(filepath)
|
||||
if 'error' in office_result:
|
||||
print(f"Warning: {office_result['error']}")
|
||||
file_info.update(office_result)
|
||||
|
||||
return file_info
|
||||
|
||||
def find_file_by_hash(file_hash, upload_folder):
|
||||
for filename in os.listdir(upload_folder):
|
||||
if filename.startswith(file_hash):
|
||||
|
||||
@@ -662,7 +662,7 @@ const tools = {
|
||||
<div class="grid grid-cols-3 gap-4 mb-6">
|
||||
<div class="bg-gray-900/30 rounded-lg border ${isClean ? 'border-green-500/30' : 'border-red-500/30'} p-4">
|
||||
<div class="text-sm text-gray-500">Status</div>
|
||||
<div class="text-xl font-semibold ${isClean ? 'text-green-500' : 'text-red-500'}">
|
||||
<div class="text-base font-semibold ${isClean ? 'text-green-500' : 'text-red-500'}">
|
||||
${isClean ? 'Clean' : (findings.initial_threat || 'Unknown Threat')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+231
-39
@@ -31,7 +31,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
step2Circle: document.getElementById('step2Circle'),
|
||||
step2Text: document.getElementById('step2Text'),
|
||||
progressLine: document.getElementById('progressLine'),
|
||||
toastContainer: document.getElementById('toastContainer')
|
||||
toastContainer: document.getElementById('toastContainer'),
|
||||
entropyBar: document.getElementById('entropyBar'),
|
||||
entropyNotes: document.getElementById('entropyNotes'),
|
||||
detectionRisk: document.getElementById('detectionRisk'),
|
||||
peInfo: document.getElementById('peInfo'),
|
||||
sectionsList: document.getElementById('sectionsList'),
|
||||
detectionNotes: document.getElementById('detectionNotes'),
|
||||
officeInfo: document.getElementById('officeInfo'),
|
||||
macroStatus: document.getElementById('macroStatus'),
|
||||
checksumInfo: document.getElementById('checksumInfo'),
|
||||
checksumStatus: document.getElementById('checksumStatus'),
|
||||
storedChecksum: document.getElementById('storedChecksum'),
|
||||
calculatedChecksum: document.getElementById('calculatedChecksum'),
|
||||
checksumNotes: document.getElementById('checksumNotes'),
|
||||
// New suspicious imports elements
|
||||
suspiciousImports: document.getElementById('suspiciousImports'),
|
||||
suspiciousImportsList: document.getElementById('suspiciousImportsList'),
|
||||
suspiciousImportsCount: document.getElementById('suspiciousImportsCount'),
|
||||
suspiciousImportsSummary: document.getElementById('suspiciousImportsSummary')
|
||||
};
|
||||
|
||||
let currentFileHash = null;
|
||||
@@ -75,6 +93,38 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
icon.classList.add('scale-110');
|
||||
}
|
||||
|
||||
function updateEntropyAnalysis(fileInfo) {
|
||||
if (fileInfo.entropy_analysis) {
|
||||
const entropyPercentage = (fileInfo.entropy / 8) * 100;
|
||||
|
||||
elements.entropyBar.style.width = `${entropyPercentage}%`;
|
||||
elements.entropyBar.className = `absolute h-full transition-all duration-300 ${
|
||||
fileInfo.entropy_analysis.detection_risk === 'High' ? 'bg-red-500' :
|
||||
fileInfo.entropy_analysis.detection_risk === 'Medium' ? 'bg-yellow-500' :
|
||||
'bg-green-500'
|
||||
}`;
|
||||
|
||||
elements.detectionRisk.className = `px-3 py-1 text-sm rounded-full ${
|
||||
fileInfo.entropy_analysis.detection_risk === 'High' ? 'bg-red-500/10 text-red-500' :
|
||||
fileInfo.entropy_analysis.detection_risk === 'Medium' ? 'bg-yellow-500/10 text-yellow-500' :
|
||||
'bg-green-500/10 text-green-500'
|
||||
}`;
|
||||
elements.detectionRisk.textContent = `${fileInfo.entropy_analysis.detection_risk} Detection Risk`;
|
||||
|
||||
if (fileInfo.entropy_analysis.notes.length > 0) {
|
||||
elements.entropyNotes.innerHTML = fileInfo.entropy_analysis.notes.map(note => `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>${note}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unhighlight() {
|
||||
const label = elements.dropZone.querySelector('label');
|
||||
const icon = elements.dropZone.querySelector('.upload-icon');
|
||||
@@ -153,13 +203,111 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
elements.step2Text.classList.add('text-red-500');
|
||||
}
|
||||
}
|
||||
|
||||
// Add this function after your existing utility functions
|
||||
function getDetectionRiskColor(risk) {
|
||||
const colors = {
|
||||
'High': 'bg-red-500/10 text-red-500',
|
||||
'Medium': 'bg-yellow-500/10 text-yellow-500',
|
||||
'Low': 'bg-green-500/10 text-green-500'
|
||||
};
|
||||
return colors[risk] || colors['Low'];
|
||||
}
|
||||
// File Info Functions
|
||||
// Modify your renderFileTypeSpecificInfo function
|
||||
function renderFileTypeSpecificInfo(fileInfo) {
|
||||
let html = '';
|
||||
// Hide both specific info sections by default
|
||||
elements.peInfo.classList.add('hidden');
|
||||
elements.officeInfo.classList.add('hidden');
|
||||
elements.suspiciousImports.classList.add('hidden');
|
||||
|
||||
// Update entropy analysis
|
||||
if (fileInfo.entropy_analysis) {
|
||||
const entropyPercentage = (fileInfo.entropy / 8) * 100;
|
||||
elements.entropyBar.style.width = `${entropyPercentage}%`;
|
||||
elements.entropyBar.className = `absolute h-full transition-all duration-300 ${
|
||||
fileInfo.entropy_analysis.detection_risk === 'High' ? 'bg-red-500' :
|
||||
fileInfo.entropy_analysis.detection_risk === 'Medium' ? 'bg-yellow-500' : 'bg-green-500'
|
||||
}`;
|
||||
|
||||
elements.detectionRisk.className = `px-3 py-1 text-sm rounded-full ${
|
||||
getDetectionRiskColor(fileInfo.entropy_analysis.detection_risk)
|
||||
}`;
|
||||
elements.detectionRisk.textContent = `${fileInfo.entropy_analysis.detection_risk} Detection Risk`;
|
||||
|
||||
elements.entropyNotes.innerHTML = fileInfo.entropy_analysis.notes.map(note => `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>${note}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Render PE-specific information
|
||||
if (fileInfo.pe_info) {
|
||||
elements.peInfo.classList.remove('hidden');
|
||||
const pe = fileInfo.pe_info;
|
||||
|
||||
// Handle suspicious imports
|
||||
if (pe.suspicious_imports && pe.suspicious_imports.length > 0) {
|
||||
elements.suspiciousImports.classList.remove('hidden');
|
||||
elements.suspiciousImportsCount.textContent = `${pe.suspicious_imports.length} Found`;
|
||||
|
||||
elements.suspiciousImportsList.innerHTML = pe.suspicious_imports.map(imp => `
|
||||
<div class="border-b border-gray-800 last:border-b-0 pb-3">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-red-500 font-mono">${imp.dll}</span>
|
||||
<span class="text-gray-400">→</span>
|
||||
<span class="text-gray-300 font-mono">${imp.function}</span>
|
||||
</div>
|
||||
<span class="text-xs text-gray-500">Hint: ${imp.hint}</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span class="text-sm text-gray-400">${imp.note}</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
elements.suspiciousImportsSummary.textContent =
|
||||
`Found ${pe.suspicious_imports.length} potentially suspicious imports that may indicate malicious capabilities.`;
|
||||
}
|
||||
|
||||
// Add checksum info display
|
||||
if (pe.checksum_info) {
|
||||
elements.checksumInfo.classList.remove('hidden');
|
||||
elements.storedChecksum.textContent = pe.checksum_info.stored_checksum;
|
||||
elements.calculatedChecksum.textContent = pe.checksum_info.calculated_checksum;
|
||||
|
||||
// Set checksum status
|
||||
elements.checksumStatus.className = `px-3 py-1 text-sm rounded-full ${
|
||||
pe.checksum_info.is_valid ? 'bg-green-500/10 text-green-500' : 'bg-red-500/10 text-red-500'
|
||||
}`;
|
||||
elements.checksumStatus.textContent = pe.checksum_info.is_valid ? 'Valid' : 'Invalid';
|
||||
|
||||
// Add checksum notes if needed
|
||||
if (!pe.checksum_info.is_valid) {
|
||||
elements.checksumNotes.innerHTML = `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span>Invalid checksum - Common in packed/modified payloads</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
elements.checksumInfo.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Original PE info display
|
||||
html = `
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -187,11 +335,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<span class="text-sm text-gray-400">${pe.sections.length} sections</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
${pe.sections.map(section => `
|
||||
<span class="px-2 py-1 text-sm bg-gray-900/50 rounded-lg border border-gray-800 text-gray-400">
|
||||
${section}
|
||||
</span>
|
||||
`).join('')}
|
||||
${pe.sections.map(section => {
|
||||
const isStandardSection = ['.text', '.data', '.bss', '.rdata', '.edata', '.idata', '.pdata', '.reloc', '.rsrc', '.tls', '.debug'].includes(section.name);
|
||||
return `
|
||||
<span class="px-2 py-1 text-sm ${isStandardSection ? 'bg-gray-900/50 text-gray-400' : 'bg-red-500/10 text-red-500'} rounded-lg border ${isStandardSection ? 'border-gray-800' : 'border-red-900/20'}">
|
||||
${section.name}
|
||||
</span>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
${pe.imports && pe.imports.length > 0 ? `
|
||||
@@ -211,40 +362,81 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
} else if (fileInfo.office_info) {
|
||||
const office = fileInfo.office_info;
|
||||
html = `
|
||||
<div class="space-y-4">
|
||||
<h6 class="text-base font-medium text-gray-300">Office Document Information</h6>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-base text-gray-400">Macros:</span>
|
||||
<span class="px-2 py-1 text-sm rounded-lg ${office.has_macros ?
|
||||
'bg-red-500/10 text-red-500 border-red-900/20' :
|
||||
'bg-gray-900/50 text-gray-400 border-gray-800'} border">
|
||||
${office.has_macros ? 'Present' : 'None'}
|
||||
</span>
|
||||
|
||||
elements.fileSpecificInfo.innerHTML = html;
|
||||
|
||||
// New section analysis display
|
||||
elements.sectionsList.innerHTML = pe.sections.map(section => {
|
||||
const isStandardSection = ['.text', '.data', '.bss', '.rdata', '.edata', '.idata', '.pdata', '.reloc', '.rsrc', '.tls', '.debug'].includes(section.name);
|
||||
return `
|
||||
<div class="border-b border-gray-800 pb-4 last:border-0">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="text-base ${isStandardSection ? 'text-gray-300' : 'text-red-500'}">${section.name}</span>
|
||||
<span class="px-2 py-1 text-sm rounded ${
|
||||
section.entropy > 7.2 ? 'text-red-500 bg-red-500/10' :
|
||||
section.entropy > 6.8 ? 'text-yellow-500 bg-yellow-500/10' :
|
||||
'text-green-500 bg-green-500/10'
|
||||
}">
|
||||
Entropy: ${section.entropy}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-sm text-gray-400">${formatFileSize(section.size)}</span>
|
||||
</div>
|
||||
${section.detection_notes.length > 0 ? `
|
||||
<div class="text-sm text-gray-400">
|
||||
${section.detection_notes.map(note => `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span>${note}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
${office.macro_info ? `
|
||||
<div class="space-y-2">
|
||||
<div class="text-base text-gray-400">Macro Information</div>
|
||||
<pre class="text-base text-gray-300 bg-gray-900/50 rounded-lg p-4 overflow-x-auto">
|
||||
${JSON.stringify(office.macro_info, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
html = `
|
||||
<div class="text-base text-gray-400 text-center py-4">
|
||||
No specific file information available
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
elements.fileSpecificInfo.innerHTML = html;
|
||||
// Render detection notes
|
||||
if (fileInfo.pe_info.detection_notes.length > 0) {
|
||||
elements.detectionNotes.innerHTML = fileInfo.pe_info.detection_notes.map(note => `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span>${note}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
}
|
||||
// Render Office-specific information
|
||||
else if (fileInfo.office_info) {
|
||||
elements.officeInfo.classList.remove('hidden');
|
||||
const office = fileInfo.office_info;
|
||||
|
||||
// Update macro status
|
||||
elements.macroStatus.className = `px-3 py-1 text-sm rounded-full ${
|
||||
office.has_macros ? 'bg-red-500/10 text-red-500' : 'bg-green-500/10 text-green-500'
|
||||
}`;
|
||||
elements.macroStatus.textContent = office.has_macros ? 'Macros Present' : 'No Macros';
|
||||
|
||||
// Show detection notes if any
|
||||
if (office.detection_notes && office.detection_notes.length > 0) {
|
||||
elements.macroDetectionNotes.innerHTML = office.detection_notes.map(note => `
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span>${note}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateFileInfo(fileInfo) {
|
||||
|
||||
+100
-6
@@ -265,12 +265,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- File Metrics -->
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<div class="bg-gray-900/30 rounded-lg p-4">
|
||||
<div class="text-base text-gray-300 mb-1">Entropy</div>
|
||||
<div id="fileEntropy" class="text-lg font-semibold text-gray-300"></div>
|
||||
|
||||
<!-- Enhanced Entropy Analysis -->
|
||||
<div class="bg-gray-900/30 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-base text-gray-300">Entropy Analysis</span>
|
||||
<span id="detectionRisk" class="px-3 py-1 text-sm rounded-full"></span>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-400">Overall Entropy</span>
|
||||
<div id="fileEntropy" class="text-lg font-semibold text-gray-300"></div>
|
||||
</div>
|
||||
<div class="relative h-2 bg-gray-800 rounded-full overflow-hidden">
|
||||
<div id="entropyBar" class="absolute h-full transition-all duration-300"></div>
|
||||
</div>
|
||||
<div id="entropyNotes" class="text-sm text-gray-400 space-y-1">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Basic Info -->
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="bg-gray-900/30 rounded-lg p-4">
|
||||
<div class="text-base text-gray-300 mb-1">Type</div>
|
||||
<div id="fileCategory" class="text-lg font-semibold text-gray-300"></div>
|
||||
@@ -285,7 +302,84 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Type Specific Info -->
|
||||
<!-- PE Checksum Info -->
|
||||
<div id="checksumInfo" class="bg-gray-900/30 rounded-lg p-4 mb-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-base text-gray-300">PE Checksum Analysis</span>
|
||||
<span id="checksumStatus" class="px-3 py-1 text-sm rounded-full"></span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">Stored Checksum</div>
|
||||
<div id="storedChecksum" class="text-base font-mono text-gray-300"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm text-gray-400 mb-1">Calculated Checksum</div>
|
||||
<div id="calculatedChecksum" class="text-base font-mono text-gray-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="checksumNotes" class="mt-4 text-sm text-gray-400 space-y-1">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Suspicious Imports Analysis -->
|
||||
<div id="suspiciousImports" class="bg-gray-900/30 rounded-lg p-4 mb-4 hidden">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-base text-gray-300">Suspicious Imports Analysis</span>
|
||||
<span id="suspiciousImportsCount" class="px-3 py-1 text-sm bg-red-500/10 text-red-500 rounded-full">
|
||||
<!-- Count populated by JS -->
|
||||
</span>
|
||||
</div>
|
||||
<div id="suspiciousImportsList" class="space-y-4">
|
||||
<!-- List populated by JS -->
|
||||
</div>
|
||||
<div class="border-t border-gray-800 pt-4 mt-4">
|
||||
<div class="text-sm text-gray-400">
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-4 h-4 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<span id="suspiciousImportsSummary"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PE File Specific Info -->
|
||||
<div id="peInfo" class="bg-gray-900/30 rounded-lg p-4 hidden">
|
||||
<div class="space-y-6">
|
||||
<div class="text-base text-gray-300 mb-2">Section Analysis</div>
|
||||
<div id="sectionsList" class="space-y-4">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
<div class="border-t border-gray-800 pt-4">
|
||||
<div class="text-base text-gray-300 mb-2">Detection Notes</div>
|
||||
<div id="detectionNotes" class="space-y-2 text-sm text-gray-400">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Office File Specific Info -->
|
||||
<div id="officeInfo" class="bg-gray-900/30 rounded-lg p-4 hidden">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-base text-gray-300">Macro Analysis</span>
|
||||
<span id="macroStatus" class="px-3 py-1 text-sm rounded-full"></span>
|
||||
</div>
|
||||
<div id="macroDetectionNotes" class="space-y-2 text-sm text-gray-400">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
<div id="macroInfo" class="text-sm text-gray-400">
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generic File Info -->
|
||||
<div id="fileSpecificInfo" class="bg-gray-900/30 rounded-lg p-4 text-base text-gray-300">
|
||||
<!-- Dynamically populated based on file type -->
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user