reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. Please do not remove or change
|
||||
the license header comment from a contributed file except when
|
||||
necessary.
|
||||
|
||||
Copyright 2026 mukul975
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
name: analyzing-golang-malware-with-ghidra
|
||||
description: Reverse engineer Go-compiled malware in Ghidra by parsing Go buildinfo
|
||||
and pclntab structures, recovering stripped/obfuscated function names (e.g. via
|
||||
GoResolver), and extracting embedded module/dependency strings and types from Go
|
||||
binaries. Use when analyzing a Go-language malware sample, deobfuscating a garble-packed
|
||||
Go binary, or recovering function names and third-party dependencies from a stripped
|
||||
Go executable.
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- golang
|
||||
- ghidra
|
||||
- reverse-engineering
|
||||
- malware-analysis
|
||||
- binary-analysis
|
||||
- go-malware
|
||||
- disassembly
|
||||
version: '1.0'
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
nist_csf:
|
||||
- DE.AE-02
|
||||
- RS.AN-03
|
||||
- ID.RA-01
|
||||
- DE.CM-01
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1620
|
||||
- T1140
|
||||
- T1059
|
||||
---
|
||||
# Analyzing Golang Malware with Ghidra
|
||||
|
||||
## Overview
|
||||
|
||||
Go (Golang) has become a popular language for malware authors due to its cross-compilation capabilities, static linking that produces self-contained binaries, and the complexity it introduces for reverse engineering. Go binaries contain the entire runtime, standard library, and all dependencies statically linked, resulting in large binaries (often 5-15MB) with thousands of functions. Ghidra struggles with Go-specific string formats (non-null-terminated), stripped function names, and goroutine concurrency patterns. Specialized tools like GoResolver (Volexity, 2025) use control-flow graph similarity to automatically deobfuscate and recover function names in stripped or obfuscated Go binaries.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When investigating security incidents that require analyzing golang malware with ghidra
|
||||
- When building detection rules or threat hunting queries for this domain
|
||||
- When SOC analysts need structured procedures for this analysis type
|
||||
- When validating security monitoring coverage for related attack techniques
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ghidra 11.0+ with JDK 17+
|
||||
- GoResolver plugin (for function name recovery)
|
||||
- Go Reverse Engineering Tool Kit (go-re.tk)
|
||||
- Python 3.9+ for helper scripts
|
||||
- Understanding of Go runtime internals (goroutines, channels, interfaces)
|
||||
- Familiarity with Go binary structure (pclntab, moduledata, itab)
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Go Binary Structure
|
||||
|
||||
Go binaries embed rich metadata in the `pclntab` (PC Line Table) structure, which maps program counters to function names, source files, and line numbers. Even stripped binaries retain this metadata. The `moduledata` structure contains pointers to type information, itabs (interface tables), and the pclntab itself. Go strings are stored as a pointer-length pair rather than null-terminated C strings.
|
||||
|
||||
### Function Recovery in Stripped Binaries
|
||||
|
||||
Despite stripping symbol tables, Go binaries retain function names within the pclntab. However, obfuscation tools like garble rename functions to random strings. GoResolver addresses this by computing control-flow graph signatures of obfuscated functions and matching them against a database of known Go standard library and third-party package functions.
|
||||
|
||||
### Crate/Dependency Extraction
|
||||
|
||||
Go's dependency management embeds module paths and version strings in the binary. Extracting these reveals the malware's third-party dependencies (HTTP libraries, encryption packages, C2 frameworks), which provides insight into capabilities without full reverse engineering.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Initial Binary Analysis
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Analyze Go binary metadata for malware analysis."""
|
||||
import struct
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
def find_go_build_info(data):
|
||||
"""Extract Go build information from binary."""
|
||||
# Go buildinfo magic: \xff Go buildinf:
|
||||
magic = b'\xff Go buildinf:'
|
||||
offset = data.find(magic)
|
||||
if offset == -1:
|
||||
return None
|
||||
|
||||
print(f"[+] Go build info at offset 0x{offset:x}")
|
||||
|
||||
# Extract Go version string nearby
|
||||
go_version = re.search(rb'go\d+\.\d+(?:\.\d+)?', data[offset:offset+256])
|
||||
if go_version:
|
||||
print(f" Go Version: {go_version.group().decode()}")
|
||||
|
||||
return offset
|
||||
|
||||
|
||||
def find_pclntab(data):
|
||||
"""Locate the pclntab (PC Line Table) structure."""
|
||||
# pclntab magic bytes vary by Go version
|
||||
magics = {
|
||||
b'\xfb\xff\xff\xff\x00\x00': "Go 1.2-1.15",
|
||||
b'\xfa\xff\xff\xff\x00\x00': "Go 1.16-1.17",
|
||||
b'\xf1\xff\xff\xff\x00\x00': "Go 1.18-1.19",
|
||||
b'\xf0\xff\xff\xff\x00\x00': "Go 1.20+",
|
||||
}
|
||||
|
||||
for magic, version in magics.items():
|
||||
offset = data.find(magic)
|
||||
if offset != -1:
|
||||
print(f"[+] pclntab found at 0x{offset:x} ({version})")
|
||||
return offset, version
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def extract_function_names(data, pclntab_offset):
|
||||
"""Extract function names from pclntab."""
|
||||
if pclntab_offset is None:
|
||||
return []
|
||||
|
||||
functions = []
|
||||
# Function name strings follow specific patterns
|
||||
func_pattern = re.compile(
|
||||
rb'(?:main|runtime|fmt|net|os|crypto|encoding|io|sync|'
|
||||
rb'syscall|reflect|strings|bytes|path|time|math|sort|'
|
||||
rb'github\.com|golang\.org)[/\.][\w/.]+',
|
||||
)
|
||||
|
||||
for match in func_pattern.finditer(data):
|
||||
name = match.group().decode('utf-8', errors='replace')
|
||||
if len(name) > 4 and len(name) < 200:
|
||||
functions.append(name)
|
||||
|
||||
return sorted(set(functions))
|
||||
|
||||
|
||||
def extract_go_strings(data):
|
||||
"""Extract Go-style strings (pointer+length pairs)."""
|
||||
# Go strings are not null-terminated; extract readable sequences
|
||||
strings = []
|
||||
ascii_pattern = re.compile(rb'[\x20-\x7e]{10,}')
|
||||
|
||||
for match in ascii_pattern.finditer(data):
|
||||
s = match.group().decode('ascii')
|
||||
# Filter for interesting malware strings
|
||||
interesting = [
|
||||
'http', 'https', 'tcp', 'udp', 'dns',
|
||||
'cmd', 'shell', 'exec', 'upload', 'download',
|
||||
'encrypt', 'decrypt', 'key', 'token', 'password',
|
||||
'c2', 'beacon', 'agent', 'implant', 'bot',
|
||||
'mutex', 'persist', 'registry', 'scheduled',
|
||||
]
|
||||
if any(kw in s.lower() for kw in interesting):
|
||||
strings.append(s)
|
||||
|
||||
return strings
|
||||
|
||||
|
||||
def extract_dependencies(data):
|
||||
"""Extract Go module dependencies from binary."""
|
||||
deps = []
|
||||
# Module paths follow pattern: github.com/user/repo
|
||||
dep_pattern = re.compile(
|
||||
rb'((?:github\.com|gitlab\.com|golang\.org|gopkg\.in|'
|
||||
rb'go\.etcd\.io|google\.golang\.org)/[^\x00\s]{5,80})'
|
||||
)
|
||||
|
||||
for match in dep_pattern.finditer(data):
|
||||
dep = match.group().decode('utf-8', errors='replace')
|
||||
deps.append(dep)
|
||||
|
||||
unique_deps = sorted(set(deps))
|
||||
return unique_deps
|
||||
|
||||
|
||||
def analyze_go_binary(filepath):
|
||||
"""Full analysis of Go malware binary."""
|
||||
with open(filepath, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
print(f"[+] Analyzing Go binary: {filepath}")
|
||||
print(f" File size: {len(data):,} bytes")
|
||||
print("=" * 60)
|
||||
|
||||
# Build info
|
||||
find_go_build_info(data)
|
||||
|
||||
# pclntab
|
||||
pclntab_offset, go_version = find_pclntab(data)
|
||||
|
||||
# Functions
|
||||
functions = extract_function_names(data, pclntab_offset)
|
||||
print(f"\n[+] Recovered {len(functions)} function names")
|
||||
|
||||
# Categorize functions
|
||||
categories = {
|
||||
"network": [], "crypto": [], "os_exec": [],
|
||||
"file_io": [], "main": [], "third_party": [],
|
||||
}
|
||||
for f in functions:
|
||||
if 'net/' in f or 'http' in f.lower():
|
||||
categories["network"].append(f)
|
||||
elif 'crypto' in f:
|
||||
categories["crypto"].append(f)
|
||||
elif 'os/exec' in f or 'syscall' in f:
|
||||
categories["os_exec"].append(f)
|
||||
elif 'os.' in f or 'io/' in f:
|
||||
categories["file_io"].append(f)
|
||||
elif f.startswith('main.'):
|
||||
categories["main"].append(f)
|
||||
elif 'github.com' in f or 'golang.org' in f:
|
||||
categories["third_party"].append(f)
|
||||
|
||||
for cat, funcs in categories.items():
|
||||
if funcs:
|
||||
print(f"\n [{cat}] ({len(funcs)} functions):")
|
||||
for fn in funcs[:10]:
|
||||
print(f" {fn}")
|
||||
|
||||
# Dependencies
|
||||
deps = extract_dependencies(data)
|
||||
print(f"\n[+] Dependencies ({len(deps)}):")
|
||||
for dep in deps[:20]:
|
||||
print(f" {dep}")
|
||||
|
||||
# Suspicious strings
|
||||
sus_strings = extract_go_strings(data)
|
||||
print(f"\n[+] Suspicious strings ({len(sus_strings)}):")
|
||||
for s in sus_strings[:20]:
|
||||
print(f" {s}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} <go_binary>")
|
||||
sys.exit(1)
|
||||
analyze_go_binary(sys.argv[1])
|
||||
```
|
||||
|
||||
### Step 2: Ghidra Analysis Script
|
||||
|
||||
```python
|
||||
# Ghidra script (run within Ghidra's script manager)
|
||||
# Save as AnalyzeGoBinary.py in Ghidra scripts directory
|
||||
|
||||
# @category MalwareAnalysis
|
||||
# @description Analyze Go binary structure and recover metadata
|
||||
|
||||
def analyze_go_binary_ghidra():
|
||||
"""Ghidra script for Go binary analysis."""
|
||||
from ghidra.program.model.mem import MemoryAccessException
|
||||
|
||||
program = getCurrentProgram()
|
||||
memory = program.getMemory()
|
||||
listing = program.getListing()
|
||||
|
||||
print("[+] Go Binary Analysis Script")
|
||||
print(f" Program: {program.getName()}")
|
||||
|
||||
# Find pclntab
|
||||
pclntab_magics = [
|
||||
bytes([0xf0, 0xff, 0xff, 0xff]), # Go 1.20+
|
||||
bytes([0xf1, 0xff, 0xff, 0xff]), # Go 1.18-1.19
|
||||
bytes([0xfa, 0xff, 0xff, 0xff]), # Go 1.16-1.17
|
||||
bytes([0xfb, 0xff, 0xff, 0xff]), # Go 1.2-1.15
|
||||
]
|
||||
|
||||
for magic in pclntab_magics:
|
||||
addr = memory.findBytes(
|
||||
program.getMinAddress(), magic, None, True, None
|
||||
)
|
||||
if addr:
|
||||
print(f"[+] pclntab found at {addr}")
|
||||
# Create label
|
||||
program.getSymbolTable().createLabel(
|
||||
addr, "go_pclntab", None,
|
||||
ghidra.program.model.symbol.SourceType.ANALYSIS
|
||||
)
|
||||
break
|
||||
|
||||
# Fix Go string definitions
|
||||
# Go strings are ptr+len, not null terminated
|
||||
print("[+] Fixing Go string references...")
|
||||
|
||||
# Search for function names containing package paths
|
||||
symbol_table = program.getSymbolTable()
|
||||
func_count = 0
|
||||
for symbol in symbol_table.getAllSymbols(True):
|
||||
name = symbol.getName()
|
||||
if ('.' in name and
|
||||
any(pkg in name for pkg in
|
||||
['main.', 'runtime.', 'net.', 'crypto.', 'os.'])):
|
||||
func_count += 1
|
||||
|
||||
print(f"[+] Found {func_count} Go function symbols")
|
||||
|
||||
|
||||
# Execute
|
||||
analyze_go_binary_ghidra()
|
||||
```
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- Go version and build information extracted from binary
|
||||
- pclntab located and parsed for function name recovery
|
||||
- Third-party dependencies identified revealing malware capabilities
|
||||
- Main package functions enumerated for targeted analysis
|
||||
- Network, crypto, and OS exec functions categorized
|
||||
- Ghidra analysis correctly labels Go runtime structures
|
||||
|
||||
## References
|
||||
|
||||
- [CUJO AI - Reverse Engineering Go Binaries with Ghidra](https://cujo.com/blog/reverse-engineering-go-binaries-with-ghidra/)
|
||||
- [Volexity GoResolver](https://www.volexity.com/blog/2025/04/01/goresolver-using-control-flow-graph-similarity-to-deobfuscate-golang-binaries-automatically/)
|
||||
- [Go Reverse Engineering Tool Kit](https://go-re.tk/about/)
|
||||
- [SentinelOne AlphaGolang](https://www.sentinelone.com/labs/alphagolang-a-step-by-step-go-malware-reversing-methodology-for-ida-pro/)
|
||||
- [Go Binary Reversing Notes](https://gist.github.com/0xdevalias/4e430914124c3fd2c51cb7ac2801acba)
|
||||
@@ -0,0 +1,35 @@
|
||||
# Go Malware Analysis Report
|
||||
|
||||
## Sample Information
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| SHA-256 | |
|
||||
| File Size | |
|
||||
| Go Version | |
|
||||
| Architecture | amd64 / arm64 / 386 |
|
||||
| Stripped | Yes / No |
|
||||
| Obfuscated | Yes (garble) / No |
|
||||
|
||||
## Recovered Functions
|
||||
| Category | Count | Key Functions |
|
||||
|----------|-------|---------------|
|
||||
| main | | |
|
||||
| networking | | |
|
||||
| crypto | | |
|
||||
| os/exec | | |
|
||||
| third-party | | |
|
||||
|
||||
## Dependencies
|
||||
| Module | Purpose |
|
||||
|--------|---------|
|
||||
| | |
|
||||
|
||||
## C2 Infrastructure
|
||||
| Indicator | Type | Value |
|
||||
|-----------|------|-------|
|
||||
| | URL / IP / Domain | |
|
||||
|
||||
## Recommendations
|
||||
1. Block identified C2 infrastructure
|
||||
2. Create YARA rule for unique Go function signatures
|
||||
3. Monitor for similar Go binary compilation artifacts
|
||||
@@ -0,0 +1,90 @@
|
||||
# API Reference: Go Malware Analysis with Ghidra
|
||||
|
||||
## Ghidra Go Analysis Setup
|
||||
|
||||
### GoResolver Script (Volexity)
|
||||
```bash
|
||||
# Install GoResolver for stripped Go binary function recovery
|
||||
git clone https://github.com/volexity/GoResolver
|
||||
# Run against Ghidra project
|
||||
analyzeHeadless /ghidra_projects MyProject -process go_malware.exe \
|
||||
-postScript GoResolver.java
|
||||
```
|
||||
|
||||
### Ghidra Built-in Go Support (10.3+)
|
||||
```
|
||||
File > Import > Select Go binary
|
||||
Analysis > Auto Analyze (includes GolangAnalyzer)
|
||||
Window > Function Tags > Filter "go."
|
||||
```
|
||||
|
||||
## Go Binary Characteristics
|
||||
|
||||
### Build Info Magic
|
||||
```
|
||||
Offset in .go.buildinfo section: "\xff Go buildinf:"
|
||||
```
|
||||
|
||||
### gopclntab Magic Bytes
|
||||
| Go Version | Magic |
|
||||
|------------|-------|
|
||||
| 1.2-1.15 | `FB FF FF FF 00 00` |
|
||||
| 1.16-1.17 | `FA FF FF FF 00 00` |
|
||||
| 1.18-1.19 | `F0 FF FF FF 00 00` |
|
||||
| 1.20+ | `F1 FF FF FF 00 00` |
|
||||
|
||||
### String Format
|
||||
Go strings are length-prefixed (not null-terminated):
|
||||
```
|
||||
struct GoString {
|
||||
char *ptr; // pointer to string data
|
||||
int64 length; // string length
|
||||
};
|
||||
```
|
||||
|
||||
## Go-Specific Ghidra Scripts
|
||||
|
||||
### GoReSym (Mandiant)
|
||||
```bash
|
||||
GoReSym -t -d -p /path/to/binary
|
||||
# -t: Recover type information
|
||||
# -d: Dump function metadata
|
||||
# -p: Print package listing
|
||||
```
|
||||
|
||||
### redress (Go Reverse Engineering)
|
||||
```bash
|
||||
redress -src binary.exe # Reconstruct source tree
|
||||
redress -pkg binary.exe # List packages
|
||||
redress -type binary.exe # Type information
|
||||
redress -string binary.exe # Go string extraction
|
||||
redress -interface binary.exe # Interface types
|
||||
```
|
||||
|
||||
## Go Obfuscation Tools
|
||||
|
||||
| Tool | Technique | Detection |
|
||||
|------|-----------|-----------|
|
||||
| garble | Function name hashing, literal obfuscation | Hash-like symbols, missing debug info |
|
||||
| gobfuscate | Package/function renaming | Random 8-char names |
|
||||
| go-strip | Symbol table removal | Missing gopclntab entries |
|
||||
|
||||
## Common Go Malware Families
|
||||
|
||||
| Family | Type | Notable Packages |
|
||||
|--------|------|-----------------|
|
||||
| Sliver | C2 implant | protobuf, grpc, mtls |
|
||||
| Merlin | C2 agent | http2, jose, websocket |
|
||||
| Sunlogin/Cobalt | RAT | screenshot, clipboard, keylog |
|
||||
| BianLian | Ransomware | crypto/aes, filepath.Walk |
|
||||
| Royal | Ransomware | goroutine-based parallel encryption |
|
||||
|
||||
## Key Ghidra Analysis Steps
|
||||
```
|
||||
1. Search > For Strings > "go1." (version identification)
|
||||
2. Search > For Bytes > FB FF FF FF (gopclntab)
|
||||
3. Symbol Table > Filter "main." (entry points)
|
||||
4. Navigation > Go To "runtime.main" (program start)
|
||||
5. Decompiler > Check goroutine spawns (runtime.newproc)
|
||||
6. Data Types > Apply GoString struct to string references
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Go Binary Analysis Standards
|
||||
|
||||
## Go Binary Structure
|
||||
| Component | Description | Location |
|
||||
|-----------|-------------|----------|
|
||||
| pclntab | PC-to-function mapping table | .gopclntab or .text |
|
||||
| moduledata | Runtime metadata structure | .noptrdata |
|
||||
| itab | Interface method tables | .rodata |
|
||||
| buildinfo | Go version and module info | .go.buildinfo |
|
||||
| typelinks | Type descriptor table | .rodata |
|
||||
|
||||
## pclntab Magic Bytes by Go Version
|
||||
| Magic | Go Version |
|
||||
|-------|-----------|
|
||||
| 0xFBFFFFFF | 1.2 - 1.15 |
|
||||
| 0xFAFFFFFF | 1.16 - 1.17 |
|
||||
| 0xF1FFFFFF | 1.18 - 1.19 |
|
||||
| 0xF0FFFFFF | 1.20+ |
|
||||
|
||||
## Common Go Malware Families
|
||||
- Sliver C2 implant
|
||||
- Geacon (Go Cobalt Strike beacon)
|
||||
- GoBruteforcer
|
||||
- Kaiji botnet
|
||||
- Chaos botnet (Go-based)
|
||||
|
||||
## References
|
||||
- [Go Runtime Source](https://github.com/golang/go/tree/master/src/runtime)
|
||||
- [Go Internal ABI](https://go.dev/s/regcallabi)
|
||||
@@ -0,0 +1,37 @@
|
||||
# Go Malware Analysis Workflows
|
||||
|
||||
## Workflow 1: Stripped Binary Recovery
|
||||
```
|
||||
[Stripped Go Binary] --> [Find pclntab] --> [Recover Function Names]
|
||||
|
|
||||
v
|
||||
[Apply GoResolver] --> [Deobfuscate Names]
|
||||
|
|
||||
v
|
||||
[Categorize Functions]
|
||||
```
|
||||
|
||||
## Workflow 2: Full Ghidra Analysis
|
||||
```
|
||||
[Go Binary] --> [Import to Ghidra] --> [Run Go Analysis Scripts]
|
||||
|
|
||||
v
|
||||
[Fix String References]
|
||||
|
|
||||
v
|
||||
[Identify main Package]
|
||||
|
|
||||
v
|
||||
[Analyze C2/Network Logic]
|
||||
```
|
||||
|
||||
## Workflow 3: Dependency-Based Capability Assessment
|
||||
```
|
||||
[Go Binary] --> [Extract Module Info] --> [List Dependencies]
|
||||
|
|
||||
v
|
||||
[Map to Capabilities]
|
||||
|
|
||||
v
|
||||
[Prioritize Analysis]
|
||||
```
|
||||
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Go malware analysis agent for Ghidra-assisted reverse engineering.
|
||||
|
||||
Analyzes Go binaries to extract function names, strings, build metadata,
|
||||
package information, and detects common Go malware characteristics.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
import re
|
||||
import math
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def compute_hash(filepath):
|
||||
"""Compute SHA-256 hash of file."""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(65536), b""):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
def shannon_entropy(data):
|
||||
"""Calculate Shannon entropy."""
|
||||
if not data:
|
||||
return 0.0
|
||||
freq = Counter(data)
|
||||
length = len(data)
|
||||
return -sum((c / length) * math.log2(c / length) for c in freq.values())
|
||||
|
||||
|
||||
def detect_go_binary(filepath):
|
||||
"""Detect if a binary is compiled with Go and extract version info."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
indicators = {
|
||||
"is_go_binary": False,
|
||||
"go_version": None,
|
||||
"go_buildinfo": False,
|
||||
"gopclntab_found": False,
|
||||
}
|
||||
|
||||
# Go build info magic
|
||||
buildinfo_magic = b"\xff Go buildinf:"
|
||||
offset = data.find(buildinfo_magic)
|
||||
if offset != -1:
|
||||
indicators["is_go_binary"] = True
|
||||
indicators["go_buildinfo"] = True
|
||||
|
||||
# Go version string
|
||||
version_pattern = rb"go(\d+\.\d+(?:\.\d+)?)"
|
||||
matches = re.findall(version_pattern, data)
|
||||
if matches:
|
||||
indicators["is_go_binary"] = True
|
||||
versions = sorted(set(m.decode() for m in matches))
|
||||
indicators["go_version"] = versions[-1] if versions else None
|
||||
|
||||
# gopclntab (Go PC line table) magic bytes
|
||||
gopclntab_magics = [
|
||||
b"\xfb\xff\xff\xff\x00\x00", # Go 1.2-1.15
|
||||
b"\xfa\xff\xff\xff\x00\x00", # Go 1.16-1.17
|
||||
b"\xf0\xff\xff\xff\x00\x00", # Go 1.18+
|
||||
b"\xf1\xff\xff\xff\x00\x00", # Go 1.20+
|
||||
]
|
||||
for magic in gopclntab_magics:
|
||||
if magic in data:
|
||||
indicators["gopclntab_found"] = True
|
||||
indicators["is_go_binary"] = True
|
||||
break
|
||||
|
||||
# Runtime strings
|
||||
go_strings = [b"runtime.main", b"runtime.goexit", b"runtime.gopanic",
|
||||
b"runtime.newproc", b"GOROOT", b"GOPATH"]
|
||||
found_runtime = sum(1 for s in go_strings if s in data)
|
||||
if found_runtime >= 2:
|
||||
indicators["is_go_binary"] = True
|
||||
indicators["runtime_strings_found"] = found_runtime
|
||||
|
||||
return indicators
|
||||
|
||||
|
||||
def extract_go_strings(filepath, min_length=6):
|
||||
"""Extract Go-style strings (length-prefixed, not null-terminated)."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
# Standard ASCII string extraction
|
||||
ascii_pattern = re.compile(rb"[\x20-\x7e]{%d,}" % min_length)
|
||||
strings = [m.group().decode("ascii", errors="replace") for m in ascii_pattern.finditer(data)]
|
||||
return strings
|
||||
|
||||
|
||||
def extract_go_packages(strings_list):
|
||||
"""Identify Go packages from extracted strings."""
|
||||
packages = set()
|
||||
pkg_pattern = re.compile(r"^([a-zA-Z0-9_]+(?:/[a-zA-Z0-9_.-]+)+)\.")
|
||||
for s in strings_list:
|
||||
match = pkg_pattern.match(s)
|
||||
if match:
|
||||
packages.add(match.group(1))
|
||||
# Also look for known Go import paths
|
||||
for s in strings_list:
|
||||
if s.startswith("github.com/") or s.startswith("golang.org/"):
|
||||
parts = s.split("/")
|
||||
if len(parts) >= 3:
|
||||
packages.add("/".join(parts[:3]))
|
||||
return sorted(packages)
|
||||
|
||||
|
||||
SUSPICIOUS_GO_PACKAGES = {
|
||||
"github.com/kbinani/screenshot": "Screen capture capability",
|
||||
"github.com/atotto/clipboard": "Clipboard access",
|
||||
"github.com/go-vgo/robotgo": "Desktop automation / keylogging",
|
||||
"github.com/miekg/dns": "Custom DNS resolution (C2/tunneling)",
|
||||
"golang.org/x/crypto/ssh": "SSH client (lateral movement)",
|
||||
"github.com/shirou/gopsutil": "System enumeration",
|
||||
"github.com/mitchellh/go-ps": "Process listing",
|
||||
"github.com/gobuffalo/packr": "Binary resource embedding",
|
||||
"github.com/Ne0nd0g/merlin": "Merlin C2 agent",
|
||||
"github.com/BishopFox/sliver": "Sliver C2 framework",
|
||||
"github.com/traefik/yaegi": "Go interpreter (dynamic execution)",
|
||||
}
|
||||
|
||||
|
||||
def detect_suspicious_packages(packages):
|
||||
"""Flag suspicious Go packages commonly used in malware."""
|
||||
findings = []
|
||||
for pkg in packages:
|
||||
for sus_pkg, description in SUSPICIOUS_GO_PACKAGES.items():
|
||||
if sus_pkg in pkg:
|
||||
findings.append({"package": pkg, "concern": description})
|
||||
return findings
|
||||
|
||||
|
||||
def analyze_sections(filepath):
|
||||
"""Analyze PE/ELF sections for Go binary characteristics."""
|
||||
with open(filepath, "rb") as f:
|
||||
magic = f.read(4)
|
||||
f.seek(0)
|
||||
data = f.read()
|
||||
|
||||
sections = []
|
||||
if magic[:2] == b"MZ": # PE
|
||||
try:
|
||||
import pefile
|
||||
pe = pefile.PE(data=data)
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b"\x00").decode("ascii", errors="replace")
|
||||
entropy = section.get_entropy()
|
||||
sections.append({
|
||||
"name": name, "virtual_size": section.Misc_VirtualSize,
|
||||
"raw_size": section.SizeOfRawData, "entropy": round(entropy, 3),
|
||||
})
|
||||
pe.close()
|
||||
except ImportError:
|
||||
sections.append({"note": "pefile not installed"})
|
||||
elif magic[:4] == b"\x7fELF":
|
||||
try:
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from io import BytesIO
|
||||
elf = ELFFile(BytesIO(data))
|
||||
for section in elf.iter_sections():
|
||||
sec_data = section.data() if section.header.sh_size > 0 else b""
|
||||
entropy = shannon_entropy(sec_data) if sec_data else 0
|
||||
sections.append({
|
||||
"name": section.name, "size": section.header.sh_size,
|
||||
"entropy": round(entropy, 3), "type": section.header.sh_type,
|
||||
})
|
||||
except ImportError:
|
||||
sections.append({"note": "pyelftools not installed"})
|
||||
return sections
|
||||
|
||||
|
||||
def detect_obfuscation(go_info, strings_list):
|
||||
"""Detect Go binary obfuscation (garble, gobfuscate)."""
|
||||
indicators = {"obfuscated": False, "techniques": []}
|
||||
|
||||
# Garble replaces function names with hashes
|
||||
hash_names = sum(1 for s in strings_list if re.match(r"^[a-f0-9]{16,}$", s))
|
||||
if hash_names > 20:
|
||||
indicators["obfuscated"] = True
|
||||
indicators["techniques"].append("Possible garble obfuscation (hash-like function names)")
|
||||
|
||||
# Missing gopclntab suggests stripping
|
||||
if not go_info.get("gopclntab_found"):
|
||||
indicators["techniques"].append("gopclntab not found - may be stripped or modified")
|
||||
|
||||
# Low runtime string count
|
||||
if go_info.get("runtime_strings_found", 0) < 2:
|
||||
indicators["obfuscated"] = True
|
||||
indicators["techniques"].append("Low Go runtime string count - possible obfuscation")
|
||||
|
||||
return indicators
|
||||
|
||||
|
||||
def generate_report(filepath):
|
||||
"""Generate comprehensive Go malware analysis report."""
|
||||
report = {
|
||||
"file": filepath,
|
||||
"sha256": compute_hash(filepath),
|
||||
"size": os.path.getsize(filepath),
|
||||
}
|
||||
|
||||
go_info = detect_go_binary(filepath)
|
||||
report["go_detection"] = go_info
|
||||
|
||||
if not go_info["is_go_binary"]:
|
||||
report["conclusion"] = "Not identified as a Go binary"
|
||||
return report
|
||||
|
||||
strings_list = extract_go_strings(filepath)
|
||||
report["total_strings"] = len(strings_list)
|
||||
|
||||
packages = extract_go_packages(strings_list)
|
||||
report["packages"] = packages[:50]
|
||||
|
||||
suspicious = detect_suspicious_packages(packages)
|
||||
report["suspicious_packages"] = suspicious
|
||||
|
||||
sections = analyze_sections(filepath)
|
||||
report["sections"] = sections
|
||||
|
||||
obfuscation = detect_obfuscation(go_info, strings_list)
|
||||
report["obfuscation"] = obfuscation
|
||||
|
||||
return report
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Go Malware Analysis Agent (Ghidra-assisted)")
|
||||
print("Go binary detection, package extraction, obfuscation detection")
|
||||
print("=" * 60)
|
||||
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
|
||||
if not target or not os.path.exists(target):
|
||||
print("\n[DEMO] Usage: python agent.py <go_binary>")
|
||||
sys.exit(0)
|
||||
|
||||
report = generate_report(target)
|
||||
go = report.get("go_detection", {})
|
||||
print(f"\n[*] File: {target}")
|
||||
print(f"[*] SHA-256: {report['sha256']}")
|
||||
print(f"[*] Go binary: {go.get('is_go_binary', False)}")
|
||||
print(f"[*] Go version: {go.get('go_version', 'unknown')}")
|
||||
print(f"[*] Strings: {report.get('total_strings', 0)}")
|
||||
|
||||
print("\n--- Packages ---")
|
||||
for pkg in report.get("packages", [])[:15]:
|
||||
print(f" {pkg}")
|
||||
|
||||
print("\n--- Suspicious Packages ---")
|
||||
for s in report.get("suspicious_packages", []):
|
||||
print(f" [!] {s['package']}: {s['concern']}")
|
||||
|
||||
print("\n--- Obfuscation ---")
|
||||
obf = report.get("obfuscation", {})
|
||||
print(f" Obfuscated: {obf.get('obfuscated', False)}")
|
||||
for t in obf.get("techniques", []):
|
||||
print(f" {t}")
|
||||
|
||||
print(f"\n{json.dumps(report, indent=2, default=str)}")
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Go Malware Binary Analyzer
|
||||
|
||||
Extracts metadata, function names, dependencies, and suspicious
|
||||
indicators from Go-compiled malware binaries.
|
||||
|
||||
Usage:
|
||||
python process.py --file malware.exe --output report.json
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PCLNTAB_MAGICS = {
|
||||
b'\xf0\xff\xff\xff': "Go 1.20+",
|
||||
b'\xf1\xff\xff\xff': "Go 1.18-1.19",
|
||||
b'\xfa\xff\xff\xff': "Go 1.16-1.17",
|
||||
b'\xfb\xff\xff\xff': "Go 1.2-1.15",
|
||||
}
|
||||
|
||||
|
||||
def find_pclntab(data):
|
||||
for magic, version in PCLNTAB_MAGICS.items():
|
||||
offset = data.find(magic)
|
||||
if offset != -1:
|
||||
return offset, version
|
||||
return None, None
|
||||
|
||||
|
||||
def extract_go_version(data):
|
||||
match = re.search(rb'go(\d+\.\d+(?:\.\d+)?)', data)
|
||||
return match.group(1).decode() if match else "unknown"
|
||||
|
||||
|
||||
def extract_functions(data):
|
||||
func_pattern = re.compile(
|
||||
rb'((?:main|runtime|fmt|net|os|crypto|encoding|io|sync|'
|
||||
rb'syscall|reflect|strings|bytes|path|time|math|sort|'
|
||||
rb'github\.com|golang\.org|gopkg\.in)[/\.][\w/.]+)'
|
||||
)
|
||||
functions = set()
|
||||
for match in func_pattern.finditer(data):
|
||||
name = match.group(1).decode('utf-8', errors='replace')
|
||||
if 4 < len(name) < 200:
|
||||
functions.add(name)
|
||||
return sorted(functions)
|
||||
|
||||
|
||||
def extract_dependencies(data):
|
||||
dep_pattern = re.compile(
|
||||
rb'((?:github\.com|gitlab\.com|golang\.org|gopkg\.in|'
|
||||
rb'go\.etcd\.io|google\.golang\.org)/[\w./-]{5,80})'
|
||||
)
|
||||
deps = set()
|
||||
for match in dep_pattern.finditer(data):
|
||||
dep = match.group(1).decode('utf-8', errors='replace')
|
||||
# Clean up trailing artifacts
|
||||
dep = dep.rstrip('/.')
|
||||
deps.add(dep)
|
||||
return sorted(deps)
|
||||
|
||||
|
||||
def extract_suspicious_strings(data):
|
||||
interesting_patterns = [
|
||||
rb'https?://[\w./:?&=-]+',
|
||||
rb'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?',
|
||||
rb'(?:cmd|powershell|bash|sh)(?:\.exe)?',
|
||||
rb'(?:HKLM|HKCU)\\[^\x00]+',
|
||||
rb'/etc/(?:passwd|shadow|crontab)',
|
||||
]
|
||||
|
||||
results = {}
|
||||
for pattern in interesting_patterns:
|
||||
matches = re.findall(pattern, data)
|
||||
if matches:
|
||||
decoded = [m.decode('utf-8', errors='replace') for m in matches]
|
||||
results[pattern.decode('utf-8', errors='replace')] = list(set(decoded))
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def categorize_functions(functions):
|
||||
categories = {
|
||||
"main_logic": [],
|
||||
"networking": [],
|
||||
"cryptography": [],
|
||||
"os_execution": [],
|
||||
"file_operations": [],
|
||||
"third_party": [],
|
||||
"runtime": [],
|
||||
}
|
||||
|
||||
for func in functions:
|
||||
fl = func.lower()
|
||||
if func.startswith('main.'):
|
||||
categories["main_logic"].append(func)
|
||||
elif any(x in fl for x in ['net/', 'http', 'tcp', 'udp', 'dns']):
|
||||
categories["networking"].append(func)
|
||||
elif 'crypto' in fl:
|
||||
categories["cryptography"].append(func)
|
||||
elif any(x in fl for x in ['os/exec', 'syscall']):
|
||||
categories["os_execution"].append(func)
|
||||
elif any(x in fl for x in ['os.', 'io/', 'ioutil']):
|
||||
categories["file_operations"].append(func)
|
||||
elif any(x in fl for x in ['github.com', 'golang.org', 'gopkg.in']):
|
||||
categories["third_party"].append(func)
|
||||
elif func.startswith('runtime.'):
|
||||
categories["runtime"].append(func)
|
||||
|
||||
return {k: v for k, v in categories.items() if v}
|
||||
|
||||
|
||||
def analyze(filepath):
|
||||
with open(filepath, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
report = {
|
||||
"file": str(filepath),
|
||||
"size": len(data),
|
||||
"go_version": extract_go_version(data),
|
||||
}
|
||||
|
||||
pclntab_offset, pclntab_version = find_pclntab(data)
|
||||
report["pclntab"] = {
|
||||
"offset": f"0x{pclntab_offset:x}" if pclntab_offset else None,
|
||||
"version": pclntab_version,
|
||||
}
|
||||
|
||||
functions = extract_functions(data)
|
||||
report["total_functions"] = len(functions)
|
||||
report["function_categories"] = categorize_functions(functions)
|
||||
|
||||
report["dependencies"] = extract_dependencies(data)
|
||||
report["suspicious_strings"] = extract_suspicious_strings(data)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Go Malware Analyzer")
|
||||
parser.add_argument("--file", required=True, help="Go binary to analyze")
|
||||
parser.add_argument("--output", help="Output JSON report")
|
||||
|
||||
args = parser.parse_args()
|
||||
report = analyze(args.file)
|
||||
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user