
Una libreria leggera di strumentazione dinamica
Copyright 2020 Google LLC
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
https://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.
## Cos'è TinyInst?
TinyInst è una libreria di instrumentazione dinamica leggera che può essere utilizzata per instrumentare solo i moduli selezionati del processo, lasciando il resto del processo in esecuzione in modo nativo. È pensata per essere facile da capire, facile da modificare e facile da utilizzare. Non è progettata per essere compatibile con tutti i target (maggiori dettagli in seguito).
### Come si confronta con [DynamoRIO](https://dynamorio.org/) e [PIN](https://software.intel.com/en-us/articles/pintool)?
TinyInst non è pensato come sostituto di framework di instrumentazione complessi come DynamoRIO e PIN, ma piuttosto come alternativa per scenari in cui una soluzione più leggera sarebbe sufficiente. TinyInst presuppone che il target sia ben educato (nel senso spiegato sotto), cosa che non vale per i framework più complessi. Di conseguenza, probabilmente non riuscirai a eseguire TinyInst contro malware come [era stato fatto con DynamoRIO in precedenza](https://www.slideshare.net/MaximShudrak/fuzzing-malware-for-fun-profit-applying-coverageguided-fuzzing-to-find-bugs-in-modern-malware). D'altra parte, se un target non funziona con altri framework a causa del modulo che non deve essere instrumentato, e il modulo instrumentato è ben educato, potrebbe funzionare con TinyInst. Poiché con TinyInst la maggior parte del processo viene eseguita in modo nativo, il tempo di avvio del processo sarà più breve e potrebbe superare altre soluzioni nei casi in cui il processo target trascorre molto tempo nei moduli in cui l'instrumentazione non è necessaria.
### Come si confronta con [Mesos](https://github.com/gamozolabs/mesos) e [TrapFuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz)?
TinyInst è una soluzione completa di riscrittura binaria, quindi è possibile modificare qualsiasi comportamento nel modulo target. Questo le consente, ad esempio, di estrarre edge coverage invece dei soli blocchi di base. Inoltre, TinyInst non dipende da altro software, come IDA Pro, per identificare i blocchi di base.
### Quali sistemi operativi supporta TinyInst?
TinyInst funziona su Windows (x86 e x64), macOS (x64 e ARM64), Linux (x64 e ARM64) e Android (ARM64). Consulta il README nella directory corrispondente per ciascun sistema operativo per note e limitazioni aggiuntive.
### Quali target sono compatibili con TinyInst?
TinyInst presuppone che tutti i moduli instrumentati siano ben educati nel senso che
- Non è presente codice auto-modificante
- L'indirizzo di ritorno sullo stack non viene mai acceduto direttamente dal programma
OR/AND (a seconda delle impostazioni)
- Nessun dato viene mai memorizzato prima della cima dello stack (su indirizzi inferiori a quelli puntati da ESP/RSP). Questa condizione può essere rilassata in "nessun dato prima di (ESP/RSP - arbitrary_offset)" usando il flag `-stack_offset`.
TinyInst richiede inoltre che DEP/NX sia abilitato per il processo target. Se non lo è già, puoi usare il flag `-force_dep` per forzarlo. Tuttavia, nell'improbabile caso in cui il target abbia realmente bisogno di DEP disattivato per funzionare correttamente, forzarlo potrebbe causare comportamenti anomali.
### Qual è l'overhead prestazionale?
Secondo le prime misurazioni sulla decodifica delle immagini, su un target a 64 bit ben educato con le impostazioni predefinite di TinyInst, l'overhead prestazionale era di circa il 15% senza client e di circa il 20% con l'esempio di client per la raccolta della copertura. Nota che questo non include il timeout introdotto dall'instrumentazione iniziale dei moduli. Vedi i suggerimenti sulle prestazioni qui sotto per maggiori dettagli.
## Compilare TinyInst
1. Apri un terminale e configura il tuo ambiente di build (ad es. su Windows, esegui vcvars64.bat / vcvars32.bat)
2. Vai nella directory che contiene il sorgente
3. Esegui i seguenti comandi (modifica il generatore in base alla versione dell'IDE e alla piattaforma per cui vuoi compilare):
#### Windows```
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -A x64 ..
cmake --build . --config Release
mkdir build cd build cmake -G Xcode .. cmake --build . --config Release
#### Linux```
mkdir build
cd build
cmake ..
cmake --build . --config Release
mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE=</path/to/android/ndk>build/cmake/android.toolchain.cmake -DANDROID_NDK=</path/to/android/ndk> -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM= .. cmake --build . --config Release
Nota #1: la build a 64 bit funzionerà anche su target a 32 bit sui sistemi operativi Windows e Linux
Nota #2: Riscontri problemi nella creazione di una build a 32 bit su Windows a 64 bit perché l'ambiente non è configurato correttamente e mancano delle librerie? Apri il file .sln generato in Visual Studio e compila da lì invece di eseguire cmake --build. Nota anche che la build a 64 bit funzionerà su target a 32 bit, quindi potrebbe non essere necessario creare una build a 32 bit.
## Utilizzo di TinyInst
TinyInst è pensato principalmente per essere usato come libreria all'interno di altri programmi.
Un client TinyInst è scritto come sottoclasse della classe TinyInst. Il client può quindi fare override dei metodi API di cui ha bisogno. I metodi API sono definiti di seguito.
Dopo la creazione del client, deve essere inizializzato con le opzioni della riga di comando chiamando
`void init(int argc, char **argv);`
Le opzioni della riga di comando sono definite di seguito e un client può anche definirne di proprie. Dopodiché, per eseguire e controllare un programma strumentato, si possono usare le seguenti funzioni.
`DebuggerStatus Run(int argc, char **argv, uint32_t timeout);`
`DebuggerStatus Attach(unsigned int pid, uint32_t timeout);`
Queste funzioni eseguono un programma (usando la riga di comando specificata) oppure si agganciano a un programma già in esecuzione. Se non viene specificato un metodo target, il target continuerà a essere eseguito finché il programma non termina, non va in crash, o fino alla scadenza del timeout (espresso in millisecondi). Se è definito un metodo target, TinyInst restituirà il controllo ogni volta che il metodo target viene invocato e ogni volta che il metodo target restituisce, consentendo al chiamante di eseguire attività aggiuntive.
Quando `Run` e `Attach` restituiscono mentre il processo target è ancora vivo, si possono usare le seguenti funzioni per terminare il processo o continuare l'esecuzione.
`DebuggerStatus Kill();`
`DebuggerStatus Continue(uint32_t timeout);`
TinyInst include un binario di copertura di esempio, che può essere invocato usando
`<options> -- <target command line>`
Esempio su Windows:
`litecov.exe -instrument_module notepad.exe -coverage_file coverage.txt -- notepad.exe`
## API di strumentazione
### Callback di eventi del debugger
Questi callback sono solo informativi e il client non deve emettere codice strumentato durante la loro esecuzione. I client devono chiamare lo stesso handler definito nella superclasse prima di gestire questi eventi direttamente.
`OnProcessCreated`
Chiamato quando il processo target viene creato o quando ci si aggancia a esso.
`OnProcessExit`
Chiamato quando il processo target termina.
`OnProcessEntrypoint`
Chiamato quando viene raggiunto l'entrypoint del processo (binario principale)