Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,28 @@ These commands will generate binaries in a *build/* folder.

### Building for distribution packages without bundled assets

Distribution packages can opt out of embedding the bundled ADB and collector
binaries by building with the `unbundle` build tag:
Default builds always use the bundled ADB binary. Distribution packages can opt
out of embedding the bundled ADB and collector binaries by building with the
`unbundle` build tag:

```bash
go build -tags unbundle -o build/
```

When this tag is enabled, androidqf expects:

- `adb` to be available from the system `PATH`.
- `adb` to be available from the system `PATH` with Android SDK Platform-Tools
`36.0.2` or newer.
- collector binaries to be installed under
`/usr/lib/androidqf/android-collector/` using the names expected by
androidqf, such as `collector_arm` and `collector_arm64`.

Packagers can run the same ADB version check with:

```bash
go test -tags unbundle ./adb
```

Packagers may remove the bundled binary assets from `assets/` before building,
but the `assets/` package directory and its Go source files must remain present.
The `unbundle` build still imports the `assets` package, and the build will fail
Expand Down
9 changes: 5 additions & 4 deletions acquisition/acquisition.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/botherder/go-savetime/hashes"
"github.com/google/uuid"
"github.com/mvt-project/androidqf/adb"
"github.com/mvt-project/androidqf/assets"
"github.com/mvt-project/androidqf/log"
"github.com/mvt-project/androidqf/utils"
)
Expand All @@ -29,6 +28,7 @@ type Acquisition struct {
UUID string `json:"uuid"`
AndroidQFVersion string `json:"androidqf_version"`
StoragePath string `json:"storage_path"`
BaseDir string `json:"base_dir"`
Started time.Time `json:"started"`
Completed time.Time `json:"completed"`
Collector *adb.Collector `json:"collector"`
Expand All @@ -55,6 +55,7 @@ func New(path string) (*Acquisition, error) {
} else {
acq.StoragePath = path
}
acq.BaseDir = filepath.Dir(acq.StoragePath)
// Check if the path exist
stat, err := os.Stat(acq.StoragePath)
if os.IsNotExist(err) {
Expand Down Expand Up @@ -82,7 +83,7 @@ func New(path string) (*Acquisition, error) {
acq.Collector = coll

// Try to initialize encrypted streaming mode
encWriter, err := NewEncryptedZipWriter(acq.UUID)
encWriter, err := NewEncryptedZipWriter(acq.UUID, acq.BaseDir)
if err != nil {
// No key file or encryption setup failed, use normal mode
log.Debug("Encrypted streaming not available, using normal mode")
Expand Down Expand Up @@ -179,9 +180,9 @@ func (a *Acquisition) Complete() {
a.Collector.Clean()
}

// Stop ADB server before trying to remove extracted assets
// Stop ADB server, then clean up any temp directory used for bundled assets.
adb.Client.KillServer()
assets.CleanAssets()
adb.Client.Cleanup()
}

func (a *Acquisition) GetSystemInformation() error {
Expand Down
8 changes: 3 additions & 5 deletions acquisition/encrypted_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"time"

"filippo.io/age"
saveRuntime "github.com/botherder/go-savetime/runtime"
"github.com/mvt-project/androidqf/log"
)

Expand Down Expand Up @@ -54,9 +53,8 @@ func (hw *hashingWriter) Write(p []byte) (int, error) {
}

// NewEncryptedZipWriter creates a new encrypted zip writer if key.txt exists
func NewEncryptedZipWriter(uuid string) (*EncryptedZipWriter, error) {
cwd := saveRuntime.GetExecutableDirectory()
keyFilePath := filepath.Join(cwd, "key.txt")
func NewEncryptedZipWriter(uuid, baseDir string) (*EncryptedZipWriter, error) {
keyFilePath := filepath.Join(baseDir, "key.txt")

// Check if key file exists
if _, err := os.Stat(keyFilePath); os.IsNotExist(err) {
Expand All @@ -79,7 +77,7 @@ func NewEncryptedZipWriter(uuid string) (*EncryptedZipWriter, error) {

// Create output file
encFileName := fmt.Sprintf("%s.zip.age", uuid)
outputPath := filepath.Join(cwd, encFileName)
outputPath := filepath.Join(baseDir, encFileName)

file, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
Expand Down
9 changes: 3 additions & 6 deletions acquisition/secure.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"strings"

"filippo.io/age"
saveRuntime "github.com/botherder/go-savetime/runtime"
"github.com/mvt-project/androidqf/log"
)

Expand Down Expand Up @@ -44,17 +43,15 @@ func (a *Acquisition) StoreSecurely() error {
return nil
}

cwd := saveRuntime.GetExecutableDirectory()

keyFilePath := filepath.Join(cwd, "key.txt")
keyFilePath := filepath.Join(a.BaseDir, "key.txt")
if _, err := os.Stat(keyFilePath); os.IsNotExist(err) {
return nil
}

log.Info("You provided an age public key, storing the acquisition securely.")

zipFileName := fmt.Sprintf("%s.zip", a.UUID)
zipFilePath := filepath.Join(cwd, zipFileName)
zipFilePath := filepath.Join(a.BaseDir, zipFileName)

log.Info("Compressing the acquisition folder. This might take a while...")

Expand Down Expand Up @@ -83,7 +80,7 @@ func (a *Acquisition) StoreSecurely() error {
defer zipFile.Close()

encFileName := fmt.Sprintf("%s.age", zipFileName)
encFilePath := filepath.Join(cwd, encFileName)
encFilePath := filepath.Join(a.BaseDir, encFileName)
encFile, err := os.OpenFile(encFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o600)
if err != nil {
return fmt.Errorf("unable to create encrypted file: %v", err)
Expand Down
15 changes: 13 additions & 2 deletions adb/adb.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package adb
import (
"errors"
"fmt"
"os"
"os/exec"
"strings"

Expand All @@ -16,8 +17,18 @@ import (
)

type ADB struct {
ExePath string
Serial string
ExePath string
Serial string
TmpAssetsDir string
}

// Cleanup removes the temporary directory used to store extracted adb assets,
// if one was created. It is a no-op when the system adb was used instead.
func (a *ADB) Cleanup() {
if a.TmpAssetsDir != "" {
os.RemoveAll(a.TmpAssetsDir)
a.TmpAssetsDir = ""
}
}

var Client *ADB
Expand Down
29 changes: 19 additions & 10 deletions adb/adb_darwin.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !unbundle

// androidqf - Android Quick Forensics
// Copyright (c) 2021-2022 Claudio Guarnieri.
// Use of this software is governed by the MVT License 1.1 that can be found at
Expand All @@ -6,25 +8,32 @@
package adb

import (
"os/exec"
"fmt"
"os"
"path/filepath"

saveRuntime "github.com/botherder/go-savetime/runtime"
"github.com/mvt-project/androidqf/assets"
)

func (a *ADB) findExe() error {
err := assets.DeployAssets()
// Extract the bundled binary into a temp directory so we
// never try to write next to the executable (which may be /usr/bin or
// another read-only system path).
tmpDir, err := os.MkdirTemp("", "androidqf-adb-*")
if err != nil {
return err
return fmt.Errorf("failed to create temp dir for adb: %v", err)
}

adbPath, err := exec.LookPath("adb")
if err == nil {
a.ExePath = adbPath
return nil
} else {
a.ExePath = filepath.Join(saveRuntime.GetExecutableDirectory(), "adb")
if err := assets.DeployAssetsToDir(tmpDir); err != nil {
os.RemoveAll(tmpDir)
return fmt.Errorf("failed to deploy bundled adb: %v", err)
}

a.ExePath = filepath.Join(tmpDir, "adb")
if err := validatePlatformToolsVersion(a.ExePath); err != nil {
os.RemoveAll(tmpDir)
return err
}
a.TmpAssetsDir = tmpDir
return nil
}
28 changes: 19 additions & 9 deletions adb/adb_linux.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !unbundle

// androidqf - Android Quick Forensics
// Copyright (c) 2021-2022 Claudio Guarnieri.
// Use of this software is governed by the MVT License 1.1 that can be found at
Expand All @@ -6,24 +8,32 @@
package adb

import (
"os/exec"
"fmt"
"os"
"path/filepath"

saveRuntime "github.com/botherder/go-savetime/runtime"
"github.com/mvt-project/androidqf/assets"
)

func (a *ADB) findExe() error {
err := assets.DeployAssets()
// Extract the bundled binary into a temp directory so we
// never try to write next to the executable (which may be /usr/bin or
// another read-only system path).
tmpDir, err := os.MkdirTemp("", "androidqf-adb-*")
if err != nil {
return err
return fmt.Errorf("failed to create temp dir for adb: %v", err)
}

adbPath, err := exec.LookPath("adb")
if err == nil {
a.ExePath = adbPath
} else {
a.ExePath = filepath.Join(saveRuntime.GetExecutableDirectory(), "adb")
if err := assets.DeployAssetsToDir(tmpDir); err != nil {
os.RemoveAll(tmpDir)
return fmt.Errorf("failed to deploy bundled adb: %v", err)
}

a.ExePath = filepath.Join(tmpDir, "adb")
if err := validatePlatformToolsVersion(a.ExePath); err != nil {
os.RemoveAll(tmpDir)
return err
}
a.TmpAssetsDir = tmpDir
return nil
}
35 changes: 35 additions & 0 deletions adb/adb_unbundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//go:build unbundle

// androidqf - Android Quick Forensics
// Copyright (c) 2021-2022 Claudio Guarnieri.
// Use of this software is governed by the MVT License 1.1 that can be found at
// https://license.mvt.re/1.1/

package adb

import (
"fmt"
"os/exec"
"runtime"
)

func (a *ADB) findExe() error {
path, err := exec.LookPath(systemADBName())
if err != nil {
return fmt.Errorf("unbundle builds require a package-maintained %s on PATH: %v", systemADBName(), err)
}

if err := validatePlatformToolsVersion(path); err != nil {
return err
}

a.ExePath = path
return nil
}

func systemADBName() string {
if runtime.GOOS == "windows" {
return "adb.exe"
}
return "adb"
}
46 changes: 26 additions & 20 deletions adb/adb_windows.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !unbundle

// androidqf - Android Quick Forensics
// Copyright (c) 2021-2022 Claudio Guarnieri.
// Use of this software is governed by the MVT License 1.1 that can be found at
Expand All @@ -7,37 +9,41 @@ package adb

import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/mvt-project/androidqf/assets"
"github.com/mvt-project/androidqf/log"
)

func (a *ADB) findExe() error {
// TODO: only deploy assets when needed
err := assets.DeployAssets()
// Extract the bundled binary (and the required DLLs) into a temp directory
// so we never try to write next to the executable (which may be a read-only
// system path).
tmpDir, err := os.MkdirTemp("", "androidqf-adb-*")
if err != nil {
return err
return fmt.Errorf("failed to create temp dir for adb: %v", err)
}

if err := assets.DeployAssetsToDir(tmpDir); err != nil {
os.RemoveAll(tmpDir)
return fmt.Errorf("failed to deploy bundled adb: %v", err)
}

adbPath, err := exec.LookPath("adb.exe")
if err == nil {
a.ExePath = adbPath
} else {
// Get path of the current directory
ex, err := os.Executable()
if err != nil {
return err
}
// Need full path to bypass go 1.19 restrictions about local path
a.ExePath = filepath.Join(filepath.Dir(ex), "adb.exe")
_, err = os.Stat(a.ExePath)
if err != nil {
log.Debugf("ADB doesn't exist at %s", a.ExePath)
return errors.New("Impossible to find ADB")
}
// Need full path to bypass Go 1.19+ restrictions about relative executable paths.
exePath := filepath.Join(tmpDir, "adb.exe")
if _, err := os.Stat(exePath); err != nil {
os.RemoveAll(tmpDir)
log.Debugf("ADB doesn't exist at %s", exePath)
return errors.New("impossible to find ADB")
}

a.ExePath = exePath
if err := validatePlatformToolsVersion(a.ExePath); err != nil {
os.RemoveAll(tmpDir)
return err
}
a.TmpAssetsDir = tmpDir
return nil
}
Loading
Loading