Skip to content

Commit 2c13a5c

Browse files
committed
directly stream to encrypted zip archive instead of writing unencrypted
to disk first
1 parent 35b4f9d commit 2c13a5c

22 files changed

+870
-145
lines changed

acquisition/acquisition.go

Lines changed: 151 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/csv"
1010
"encoding/json"
1111
"fmt"
12+
"io"
1213
"os"
1314
"path/filepath"
1415
"strings"
@@ -25,16 +26,19 @@ import (
2526

2627
// Acquisition is the main object containing all phone information
2728
type Acquisition struct {
28-
UUID string `json:"uuid"`
29-
AndroidQFVersion string `json:"androidqf_version"`
30-
StoragePath string `json:"storage_path"`
31-
Started time.Time `json:"started"`
32-
Completed time.Time `json:"completed"`
33-
Collector *adb.Collector `json:"collector"`
34-
TmpDir string `json:"tmp_dir"`
35-
SdCard string `json:"sdcard"`
36-
Cpu string `json:"cpu"`
37-
closeLog func() `json:"-"`
29+
UUID string `json:"uuid"`
30+
AndroidQFVersion string `json:"androidqf_version"`
31+
StoragePath string `json:"storage_path"`
32+
Started time.Time `json:"started"`
33+
Completed time.Time `json:"completed"`
34+
Collector *adb.Collector `json:"collector"`
35+
TmpDir string `json:"tmp_dir"`
36+
SdCard string `json:"sdcard"`
37+
Cpu string `json:"cpu"`
38+
closeLog func() `json:"-"`
39+
EncryptedWriter *EncryptedZipWriter `json:"-"`
40+
StreamingMode bool `json:"streaming_mode"`
41+
StreamingPuller *StreamingPuller `json:"-"`
3842
}
3943

4044
// New returns a new Acquisition instance.
@@ -76,25 +80,70 @@ func New(path string) (*Acquisition, error) {
7680
}
7781
acq.Collector = coll
7882

79-
// Init logging file
80-
logPath := filepath.Join(acq.StoragePath, "command.log")
81-
closeLog, err := log.EnableFileLog(log.DEBUG, logPath)
83+
// Try to initialize encrypted streaming mode
84+
encWriter, err := NewEncryptedZipWriter(acq.UUID)
8285
if err != nil {
83-
return nil, fmt.Errorf("failed to enable file logging: %v", err)
84-
}
86+
// No key file or encryption setup failed, use normal mode
87+
log.Debug("Encrypted streaming not available, using normal mode")
88+
acq.StreamingMode = false
8589

86-
// Store cleanup function for later use
87-
acq.closeLog = closeLog
90+
// Init logging file for normal mode
91+
logPath := filepath.Join(acq.StoragePath, "command.log")
92+
closeLog, err := log.EnableFileLog(log.DEBUG, logPath)
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to enable file logging: %v", err)
95+
}
96+
acq.closeLog = closeLog
97+
} else {
98+
// Encrypted streaming mode enabled
99+
log.Info("Using encrypted streaming mode - data will be written directly to encrypted archive")
100+
acq.StreamingMode = true
101+
acq.EncryptedWriter = encWriter
102+
acq.closeLog = nil // No separate log file in streaming mode
103+
104+
// Initialize streaming puller for direct operations
105+
acq.StreamingPuller = NewStreamingPuller(adb.Client.ExePath, 100) // 100MB max memory
106+
}
88107

89108
return &acq, nil
90109
}
91110

92111
func (a *Acquisition) Complete() {
93112
a.Completed = time.Now().UTC()
94113

95-
// Ensure log file is closed before cleanup operations
96-
if a.closeLog != nil {
97-
defer a.closeLog()
114+
// Handle streaming mode completion
115+
if a.StreamingMode && a.EncryptedWriter != nil {
116+
// Store acquisition info in the encrypted zip
117+
info, err := json.MarshalIndent(a, "", " ")
118+
if err != nil {
119+
log.Error("Failed to marshal acquisition info for encrypted archive")
120+
} else {
121+
err = a.EncryptedWriter.CreateFileFromBytes("acquisition.json", info)
122+
if err != nil {
123+
log.ErrorExc("Failed to store acquisition info in encrypted archive", err)
124+
}
125+
}
126+
127+
// Close the encrypted writer
128+
err = a.EncryptedWriter.Close()
129+
if err != nil {
130+
log.ErrorExc("Failed to close encrypted archive", err)
131+
}
132+
133+
// Remove the temporary storage directory if it was created and used
134+
if a.StoragePath != "" {
135+
if _, err := os.Stat(a.StoragePath); err == nil {
136+
err = os.RemoveAll(a.StoragePath)
137+
if err != nil {
138+
log.ErrorExc("Failed to clean up temporary storage directory", err)
139+
}
140+
}
141+
}
142+
} else {
143+
// Ensure log file is closed before cleanup operations
144+
if a.closeLog != nil {
145+
defer a.closeLog()
146+
}
98147
}
99148

100149
if a.Collector != nil {
@@ -144,6 +193,12 @@ func (a *Acquisition) GetSystemInformation() error {
144193
}
145194

146195
func (a *Acquisition) HashFiles() error {
196+
// In streaming mode, files are directly encrypted and no local files exist to hash
197+
if a.StreamingMode {
198+
log.Debug("Skipping hash generation in streaming mode (data is encrypted)")
199+
return nil
200+
}
201+
147202
log.Info("Generating list of files hashes...")
148203

149204
csvFile, err := os.Create(filepath.Join(a.StoragePath, "hashes.csv"))
@@ -183,6 +238,11 @@ func (a *Acquisition) HashFiles() error {
183238
}
184239

185240
func (a *Acquisition) StoreInfo() error {
241+
// In streaming mode, info is stored during Complete()
242+
if a.StreamingMode {
243+
return nil
244+
}
245+
186246
log.Info("Saving details about acquisition and device...")
187247

188248
info, err := json.MarshalIndent(a, "", " ")
@@ -201,3 +261,74 @@ func (a *Acquisition) StoreInfo() error {
201261

202262
return nil
203263
}
264+
265+
// StreamAPKToZip streams an APK file directly to encrypted zip with certificate processing
266+
func (a *Acquisition) StreamAPKToZip(remotePath, zipPath string, processFunc func(io.Reader) error) error {
267+
if !a.StreamingMode || a.EncryptedWriter == nil {
268+
return fmt.Errorf("streaming mode not available")
269+
}
270+
271+
// Pull APK data to memory buffer
272+
buffer, err := a.StreamingPuller.PullToBuffer(remotePath)
273+
if err != nil {
274+
return fmt.Errorf("failed to pull APK %s: %v", remotePath, err)
275+
}
276+
277+
// Process APK if processor provided (e.g., certificate verification)
278+
if processFunc != nil {
279+
err = processFunc(buffer.Reader())
280+
if err != nil {
281+
return fmt.Errorf("failed to process APK %s: %v", remotePath, err)
282+
}
283+
}
284+
285+
// Stream to encrypted zip
286+
err = a.EncryptedWriter.CreateFileFromReader(zipPath, buffer.Reader())
287+
if err != nil {
288+
return fmt.Errorf("failed to add APK %s to encrypted zip: %v", remotePath, err)
289+
}
290+
291+
return nil
292+
}
293+
294+
// StreamBackupToZip streams a backup directly to encrypted zip
295+
func (a *Acquisition) StreamBackupToZip(arg, zipPath string) error {
296+
if !a.StreamingMode || a.EncryptedWriter == nil {
297+
return fmt.Errorf("streaming mode not available")
298+
}
299+
300+
// Create zip entry writer
301+
writer, err := a.EncryptedWriter.CreateFile(zipPath)
302+
if err != nil {
303+
return fmt.Errorf("failed to create zip entry for backup: %v", err)
304+
}
305+
306+
// Stream backup directly to zip
307+
err = a.StreamingPuller.BackupToWriter(arg, writer)
308+
if err != nil {
309+
return fmt.Errorf("failed to stream backup to zip: %v", err)
310+
}
311+
312+
return nil
313+
}
314+
315+
// StreamBugreportToZip streams a bugreport directly to encrypted zip
316+
func (a *Acquisition) StreamBugreportToZip(zipPath string) error {
317+
if !a.StreamingMode || a.EncryptedWriter == nil {
318+
return fmt.Errorf("streaming mode not available")
319+
}
320+
321+
// Create zip entry writer
322+
writer, err := a.EncryptedWriter.CreateFile(zipPath)
323+
if err != nil {
324+
return fmt.Errorf("failed to create zip entry for bugreport: %v", err)
325+
}
326+
327+
// Stream bugreport directly to zip
328+
err = a.StreamingPuller.BugreportToWriter(writer)
329+
if err != nil {
330+
return fmt.Errorf("failed to stream bugreport to zip: %v", err)
331+
}
332+
333+
return nil
334+
}

0 commit comments

Comments
 (0)