Skip to content

Commit 73061b9

Browse files
committed
First commit
1 parent 910be4d commit 73061b9

44 files changed

Lines changed: 13509 additions & 20 deletions

Some content is hidden

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

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@
1313

1414
# Dependency directories (remove the comment below to include it)
1515
# vendor/
16+
data/
17+
examples/
18+

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Versione 0.0.1-alpha:
2+
- Prima versione

LICENSE

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
MIT License
1+
Copyright 2022 MicroSCOPE
22

3-
Copyright (c) 2022 SeekBytes
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
46

5-
Permission is hereby granted, free of charge, to any person obtaining a copy
6-
of this software and associated documentation files (the "Software"), to deal
7-
in the Software without restriction, including without limitation the rights
8-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9-
copies of the Software, and to permit persons to whom the Software is
10-
furnished to do so, subject to the following conditions:
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of the MicroSCOPE project nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
1116

12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
14-
15-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,22 @@
1-
# MicroSCOPE
2-
MicroSCOPE analizza binari PE/ELF per individuare comportamenti simili a ransomware
1+
![MicroSCOPE logo](https://github.com/seekbytes/MicroSCOPE/blob/main/utils/MicroSCOPE.jpg?raw=true)
2+
3+
## Scopo del progetto
4+
5+
MicroSCOPE è un software sviluppato tramite il linguaggio di programmazione [Go](https://go.dev) che permette di inidividuare una precisa categoria di software dannoso. Il programma è stato studiato specificamente per una classe di programmi dannosi chiamata _ransomware_ il cui funzionamento consiste nella crittazione dei dati e richiesta di riscatto per poter riaccedere al contenuto.
6+
7+
In particolare, MicroSCOPE è stato sviluppato per poter supportare due tra i formati principalmente utilizzati: il formato PE (_Portable Executable_) per piattaforme Windows ed ELF (_Executable and Linking Format_) per piattaforme Unix-based. Tramite l'applicazione di alcune euristiche, MicroSCOPE è in grado di attribuire un punteggio che corrisponde al livello di pericolosità del file che si vuole analizzare. Tanto più alto è il punteggio, tanto più il software presenterà caratteristiche simili a ransomware già studiati. Le euristiche sono state estrapolate da numerosi casi di studio e verranno migliorate nel corso del tempo.
8+
9+
## Struttura del progetto
10+
11+
* `analysis`: cartella relativa all'analisi statica dei binari (incluso le varie fasi di MicroSCOPE)
12+
* `docs`: cartella contenente la documentazione del progetto MicroSCOPE
13+
* `formats`: cartella relativa ai formati file binari (ELF e PE) incluse costanti, controlli e parsing del binario;
14+
* `heuristics`: le euristiche vere e proprie
15+
* `utils`: utilità generali
16+
17+
## Come funziona
18+
19+
L'analisi effettuata da MicroSCOPE ha tre fasi principali:
20+
* **data mining**: analisi approfondita del file binario in base al tipo di estensione (ad esempio: se file PE o ELF), estrapolando stringhe, funzioni che utilizza e qualsiasi altra informazione potenzialmente utile per prevedere l'esecuzione del programma;
21+
* **applicazione delle euristiche**: in base alle informazioni estrapolate dalla prima fase, si applicano le euristiche che consentono di capire che comportamento avrà il programma una volta eseguito. In questa fase viene calcolato un punteggio (sommatoria dei vari punteggi delle euristiche);
22+
* **determinazione del risultato**: in base al punteggio e sopra un certo valore (chiamato valore di threshold - definito dall'utente), MicroSCOPE assocerà un certo punteggio a un comportamento malevolo;

analysis/analysis.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package analysis
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"crypto/sha256"
7+
"fmt"
8+
"html/template"
9+
"io"
10+
"microscope/formats"
11+
"microscope/formats/elf"
12+
"microscope/formats/pe"
13+
"microscope/heuristics"
14+
"os"
15+
"strconv"
16+
"time"
17+
)
18+
19+
const (
20+
FILE_HEADER_PHASE_1 = 5 // Numero di byte letti all'inizio della fase 1 (vedi documenti) per discriminare il file
21+
)
22+
23+
var OutputTemplateHTML string
24+
var OutputTemplateTXT string
25+
26+
func divide(a int, b int) int {
27+
return a / b
28+
}
29+
30+
func PrintResult(Analyzed *formats.FileAnalyzed) {
31+
// lista delle funzioni passate al template
32+
funcMap := template.FuncMap{
33+
"now": time.Now,
34+
"divide": divide,
35+
"PEprintArchitecture": pe.PrintArchitecture,
36+
"PEprintResource": pe.PrintResource,
37+
"PEprintSubsystem": pe.PrintSubsystem,
38+
"PEprintMajorOperatingVersion": pe.PrintMajorOperatingSystemVersion,
39+
"PEprintCharacteristics": pe.PrintCharacteristic,
40+
"ELFprintMachine": elf.PrintMachine,
41+
"ELFprintSectionType": elf.PrintSectionType,
42+
"ELFprintFileType": elf.PrintFileType,
43+
}
44+
45+
if Analyzed.OutputFormat == "txt" {
46+
47+
// carica un template .txt e popolalo con la struttura fileAnalyzed
48+
t := template.Must(template.New("output_template.txt").Funcs(funcMap).Parse(OutputTemplateTXT))
49+
var processed bytes.Buffer
50+
51+
err := t.Execute(&processed, Analyzed)
52+
if err != nil {
53+
fmt.Println("Non è stato possibile eseguire il template per questo motivo: " + err.Error())
54+
return
55+
}
56+
outputPath := "./results/" + Analyzed.Name + "_analysis.txt"
57+
f, err := os.Create(outputPath)
58+
if err != nil {
59+
fmt.Println("Impossibile creare nuovo file.")
60+
return
61+
}
62+
w := bufio.NewWriter(f)
63+
_, err = w.WriteString(string(processed.Bytes()))
64+
if err != nil {
65+
fmt.Println("Impossibile scrivere il template sul file per il seguente errore: " + err.Error())
66+
return
67+
}
68+
err = w.Flush()
69+
if err != nil {
70+
fmt.Println("Impossibile rimuovere il contenuto dell'IOBuffer.")
71+
return
72+
}
73+
74+
} else if Analyzed.OutputFormat == "html" {
75+
76+
// carica un template html e popolalo con la struttura fileAnalyzed
77+
t := template.Must(template.New("output_template.html").Funcs(funcMap).Parse(OutputTemplateHTML))
78+
79+
var processed bytes.Buffer
80+
81+
err := t.Execute(&processed, Analyzed)
82+
if err != nil {
83+
fmt.Println("Non è stato possibile eseguire il template per questo motivo: " + err.Error())
84+
return
85+
}
86+
outputPath := "./results/" + Analyzed.Name + "_analysis.html"
87+
f, err := os.Create(outputPath)
88+
if err != nil {
89+
fmt.Println("Impossibile creare nuovo file.")
90+
return
91+
}
92+
w := bufio.NewWriter(f)
93+
_, err = w.WriteString(string(processed.Bytes()))
94+
if err != nil {
95+
fmt.Println("Impossibile scrivere il template sul file per il seguente errore: " + err.Error())
96+
return
97+
}
98+
err = w.Flush()
99+
if err != nil {
100+
fmt.Println("Impossibile rimuovere il contenuto dell'IOBuffer.")
101+
return
102+
}
103+
} else {
104+
fmt.Println("Formato file non valido.")
105+
}
106+
107+
fmt.Println("Microscope risultato. ")
108+
fmt.Println("Formato file : " + Analyzed.Format)
109+
fmt.Println("Architettura : " + Analyzed.Architecture)
110+
fmt.Println("Dimensione del file " + strconv.FormatInt(Analyzed.Size, 10))
111+
fmt.Println("Punteggio " + strconv.FormatInt(int64(Analyzed.Score), 10))
112+
113+
if Analyzed.Score > Analyzed.Threshold {
114+
fmt.Println("!!!!!RANSOMWARE!!!!")
115+
}
116+
117+
if Analyzed.Score > Analyzed.Threshold/2 && Analyzed.Score < Analyzed.Threshold {
118+
fmt.Println("È un possibile malware.")
119+
}
120+
121+
}
122+
123+
func ReadContentFile(file *os.File) []byte {
124+
// Molto importante impostare l'offset del file dall'inizio altrimenti c'è il rischio che la io.ReadAll
125+
// legga dal punto in cui l'offset è rimasto dalla funzione analysis
126+
newPosition, err := file.Seek(0, io.SeekStart)
127+
128+
if err != nil {
129+
fmt.Println("Errore durante l'impostazione dell'offset " + err.Error())
130+
return nil
131+
}
132+
133+
if newPosition != 0 {
134+
fmt.Println("Errore durante l'impostazione dell'offset")
135+
return nil
136+
}
137+
138+
content, err := io.ReadAll(file)
139+
if err != nil {
140+
fmt.Println("Impossibile leggere il contenuto del file per il seguente motivo " + err.Error())
141+
return nil
142+
}
143+
return content
144+
}
145+
146+
func StartAnalysis(file *os.File, Analyzed *formats.FileAnalyzed) {
147+
148+
// Controlla i primi FILE_HEADER_PHASE_1 byte di file
149+
var header [FILE_HEADER_PHASE_1]byte
150+
// Ritorna il numero di byte letti
151+
n, err := io.ReadFull(file, header[:])
152+
if err != nil || n != FILE_HEADER_PHASE_1 {
153+
fmt.Println("Impossibile leggere i primi 10 byte.")
154+
return
155+
}
156+
157+
// Leggi contenuto del file
158+
Analyzed.Raw = ReadContentFile(file)
159+
h := sha256.New()
160+
h.Write(Analyzed.Raw)
161+
Analyzed.Hash = fmt.Sprintf("%x", h.Sum(nil))
162+
163+
// Se il file contiene un possibile elf header, allora..
164+
if IsELFBinary(header) {
165+
Analyzed.Format = "ELF"
166+
// Procediamo con l'analisi dei segmenti ELF
167+
elf.ELFAnalysis(file, &Analyzed.ELFInterface)
168+
var machine uint16
169+
switch headerFields := Analyzed.ELFInterface.Header.(type) {
170+
case elf.Header32:
171+
machine = headerFields.Machine
172+
case elf.Header64:
173+
machine = headerFields.Machine
174+
}
175+
176+
Analyzed.Architecture = elf.PrintMachine(machine)
177+
}
178+
179+
// Altrimenti controlla se è un possibile binario PE
180+
if isPEBinary(header) {
181+
Analyzed.Format = "PE"
182+
// Procediamo con l'analisi del file PE
183+
pe.Analysis(&Analyzed.PEInterface, Analyzed.Raw)
184+
Analyzed.Architecture = pe.PrintArchitecture(Analyzed.PEInterface.COFFHeader.Machine)
185+
}
186+
187+
if Analyzed.Format == "PE" || Analyzed.Format == "ELF" {
188+
// Estrai le stringhe
189+
Analyzed.ExtractedStrings = ExtractStrings(file, 6, 256, true)
190+
191+
// Applica le euristiche
192+
heuristics.Execute(Analyzed)
193+
194+
// Stampa il risultato
195+
PrintResult(Analyzed)
196+
} else {
197+
fmt.Println("Questo tipo di file non è al momento supportato da MicroSCOPE.")
198+
}
199+
}
200+
201+
func IsELFBinary(content [FILE_HEADER_PHASE_1]byte) bool {
202+
// Controllo dei primi tre byte "ELF"
203+
if content[0] == 0x7f && content[1] == byte('E') && content[2] == byte('L') && content[3] == byte('F') {
204+
return true
205+
}
206+
207+
return false
208+
}
209+
210+
func isPEBinary(content [FILE_HEADER_PHASE_1]byte) bool {
211+
212+
// TODO: Questa è una prima euristica molto veloce che consente subito di determinare se un file potrebbe essere un file PE oppure no
213+
// Rimangono da verificare tutti i casi limite
214+
// Potrebbe essere utile passare di nuovo il puntatore del file a questa funzione per cercare l'offset 0x3c che indica la struttura PE
215+
// e il successivo controllo sulla signature PE
216+
217+
// Controlla lo stub per Microsoft MS-DOS (i primi due byte devono essere MZ dal nome del creatore del formato)
218+
if content[0] == byte('M') || content[1] == byte('Z') {
219+
return true
220+
}
221+
222+
// Esistono anche binari che hanno la sigla "MZ" invertita
223+
224+
if content[0] == byte('Z') || content[0] == byte('M') {
225+
return true
226+
}
227+
228+
return false
229+
}

analysis/strings.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package analysis
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"os"
8+
"strconv"
9+
)
10+
11+
func ExtractStrings(file *os.File, min, max int, ascii bool) []string {
12+
// Optimize the function
13+
_, err := file.Seek(1000, io.SeekStart)
14+
if err != nil {
15+
fmt.Println("Impossibile fare il seek per il seguente motivo: " + err.Error())
16+
}
17+
content, _ := io.ReadAll(file)
18+
in := bytes.NewReader(content)
19+
20+
str := make([]rune, 0, max)
21+
var res []string
22+
next := func() {
23+
if len(str) >= min {
24+
res = append(res, string(str))
25+
}
26+
str = str[0:0]
27+
}
28+
29+
// One string per loop.
30+
for {
31+
ch, _, err := in.ReadRune()
32+
if err != nil {
33+
if err != io.EOF {
34+
fmt.Println(err.Error())
35+
}
36+
return res
37+
}
38+
if !strconv.IsPrint(ch) || ascii && ch >= 0xFF {
39+
next()
40+
continue
41+
}
42+
// It's printable. Keep it.
43+
if len(str) >= max {
44+
next()
45+
}
46+
str = append(str, ch)
47+
}
48+
}

docs/HOWTO.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# HOW TO
2+
3+
## Compilare il codice
4+
5+
Attraverso `go build` è possibile compilare il codice e ottenere un binario statico da poter eseguire nella maggior parte delle architetture disponibili sul mercato. Per modificare l'architettura target per il binario che vorremo generare, bisogna impostare le variabili d'ambiente `GOOS` (sistema operativo target) e `GOARCH` (l'architettura di riferimento).
6+
7+
## Utilizzare MicroSCOPE
8+
9+
È possibile avviare l'analisi di un qualsiasi binario utilizzando il comando `microscope` o avviando l'eseguibile tramite `./microscope` (assumendo che il binario sia nella stessa cartella da dove si lancia il comando). Di seguito le flags:
10+
11+
* `-f` **OBBLIGATORIA** specifica il file per l'analisi (di default è vuoto); è possibile specificare path assoluti o relativi, purché siano validi.
12+
* `-t` : threshold, valore numerico specifico per il valore di threshold. Default è 100.
13+
* `-o`: il formato file di analisi: `html` oppure `txt`
14+
* `-d`: limite che specifica il massimo numero di byte che un binario ha e può essere analizzato da MicroSCOPE. Scarta tutti i binari maggiori del limite. Per default, questo valore è stato impostato a `2^32 - 1` (4 GByte).
15+
16+
Esempio (carica in microscope un binario chiamato sample-ransomware, imposta il threshold a 10 e come output HTML):
17+
```
18+
microscope -f sample-ransomware -t 10 -o html
19+
```
20+
21+
Il risultato sarà all'interno della cartella `results` creato da MicroSCOPE nella stessa cartella da dove viene eseguito. Come nome del file del risultato verrà utilizzato il nome del file analizzato.
22+
23+
## Contribuire a MicroSCOPE
24+
25+
Ognuno può contribuire al progetto MicroSCOPE, a patto di non violare la licenza.
26+
27+
Parti in cui serve un aiuto o un contributo:
28+
* testing di MicroSCOPE con ransomware recenti;
29+
* testing di MicroSCOPE con binari malformati;
30+
* approfondimento e dettagli delle euristiche;
31+
32+
Hai due principali metodi per contribuire: puoi creare un nuova segnalazione se pensi di essere incappato in un [problema](https://github.com/seekbytes/MicroSCOPE/issues), oppure aprire una nuova [Pull Request](https://github.com/seekbytes/MicroSCOPE/pulls).

0 commit comments

Comments
 (0)