@@ -2,185 +2,109 @@ package main
2
2
3
3
import (
4
4
"bufio"
5
- "bytes"
6
5
"fmt"
7
- "io"
8
6
"log"
9
7
"os"
10
8
"sync"
11
9
"sync/atomic"
12
- "time"
13
10
)
14
11
15
12
// process logic
16
- func startProc (wordlistFileFlag string , outputPath string , numGoroutines int , stopChan chan struct {}, vaults []Vault , crackedCountCh chan int , linesProcessedCh chan int ) {
17
- const readBufferSize = 1024 * 1024 // read buffer
18
- const writeBufferSize = 128 * 1024 // write buffer
19
-
20
- var linesHashed int64 = 0
21
- var procWg sync.WaitGroup
22
- var readWg sync.WaitGroup
23
- var writeWg sync.WaitGroup
24
- var hexDecodeErrors int64 = 0 // hex error counter
25
-
26
- readChunks := make (chan []byte , 500 ) // channel for reading chunks of data
27
- writeData := make (chan []byte , 10 ) // channel for writing processed data
28
-
13
+ func startProc (wordlistFileFlag string , outputPath string , numGoroutines int , vaults []Vault , crackedCount * int32 , linesProcessed * int32 , stopChan chan struct {}) {
29
14
var file * os.File
30
15
var err error
16
+
31
17
if wordlistFileFlag == "" {
32
- file = os .Stdin // default to stdin if no input flag is provided
18
+ file = os .Stdin
33
19
} else {
34
20
file , err = os .Open (wordlistFileFlag )
35
21
if err != nil {
36
- log .Printf ("Error opening file: %v\n " , err )
37
- return
22
+ log .Fatalf ("Error opening file: %v\n " , err )
38
23
}
39
24
defer file .Close ()
40
25
}
41
26
42
- startTime := time .Now ()
43
-
44
- readWg .Add (1 )
45
- go func () {
46
- defer readWg .Done ()
47
- var remainder []byte
48
- reader := bufio .NewReaderSize (file , readBufferSize )
49
- for {
50
- chunk := make ([]byte , readBufferSize )
51
- n , err := reader .Read (chunk )
52
- if err == io .EOF {
53
- break
54
- }
55
- if err != nil {
56
- fmt .Println (os .Stderr , "Error reading chunk:" , err )
57
- return
58
- }
27
+ var outputFile * os.File
28
+ if outputPath != "" {
29
+ outputFile , err = os .OpenFile (outputPath , os .O_APPEND | os .O_CREATE | os .O_WRONLY , 0644 )
30
+ if err != nil {
31
+ log .Fatalf ("Error opening output file: %v" , err )
32
+ }
33
+ defer outputFile .Close ()
34
+ }
59
35
60
- chunk = chunk [:n ]
61
- chunk = append (remainder , chunk ... )
36
+ var writer * bufio.Writer
37
+ if outputPath != "" {
38
+ writer = bufio .NewWriter (outputFile )
39
+ } else {
40
+ writer = bufio .NewWriter (os .Stdout )
41
+ }
42
+ defer writer .Flush ()
62
43
63
- lastNewline := bytes .LastIndexByte (chunk , '\n' )
64
- if lastNewline == - 1 {
65
- remainder = chunk
66
- } else {
67
- readChunks <- chunk [:lastNewline + 1 ]
68
- remainder = chunk [lastNewline + 1 :]
69
- }
70
- }
71
- if len (remainder ) > 0 {
72
- readChunks <- remainder
73
- }
74
- close (readChunks )
75
- }()
44
+ var (
45
+ writerMu sync.Mutex
46
+ wg sync.WaitGroup
47
+ )
76
48
49
+ // start worker goroutines
50
+ linesCh := make (chan []byte , 1000 )
77
51
for i := 0 ; i < numGoroutines ; i ++ {
78
- procWg .Add (1 )
52
+ wg .Add (1 )
79
53
go func () {
80
- defer procWg .Done ()
81
- for chunk := range readChunks {
82
- localBuffer := bytes .NewBuffer (nil )
83
- writer := bufio .NewWriterSize (localBuffer , writeBufferSize )
84
- processChunk (chunk , & linesHashed , & hexDecodeErrors , writer , stopChan , vaults , crackedCountCh , linesProcessedCh )
85
- writer .Flush ()
86
- if localBuffer .Len () > 0 {
87
- writeData <- localBuffer .Bytes ()
88
- }
54
+ defer wg .Done ()
55
+ for password := range linesCh {
56
+ processPassword (password , vaults , & writerMu , writer , crackedCount , linesProcessed , stopChan )
89
57
}
90
58
}()
91
59
}
92
60
93
- writeWg .Add (1 )
94
- go func () {
95
- defer writeWg .Done ()
96
- var writer * bufio.Writer
97
- if outputPath != "" {
98
- outFile , err := os .OpenFile (outputPath , os .O_APPEND | os .O_CREATE | os .O_WRONLY , 0644 )
99
- if err != nil {
100
- fmt .Println (os .Stderr , "Error creating output file:" , err )
101
- return
102
- }
103
- defer outFile .Close ()
104
- writer = bufio .NewWriterSize (outFile , writeBufferSize )
105
- } else {
106
- writer = bufio .NewWriterSize (os .Stdout , writeBufferSize )
107
- }
108
-
109
- for data := range writeData {
110
- writer .Write (data )
111
- }
112
- writer .Flush ()
113
- }()
114
-
115
- procWg .Wait ()
116
- readWg .Wait ()
117
- close (writeData )
118
- writeWg .Wait ()
119
-
120
- elapsedTime := time .Since (startTime )
121
- runTime := float64 (elapsedTime .Seconds ())
122
- linesPerSecond := float64 (linesHashed ) / elapsedTime .Seconds ()
123
- if hexDecodeErrors > 0 {
124
- log .Printf ("HEX decode errors: %d\n " , hexDecodeErrors )
61
+ // read lines from file and send them to workers
62
+ scanner := bufio .NewScanner (file )
63
+ for scanner .Scan () {
64
+ line := scanner .Bytes ()
65
+ password := make ([]byte , len (line ))
66
+ copy (password , line )
67
+ linesCh <- password
125
68
}
126
- log .Printf ("Finished processing %d lines in %.3f sec (%.3f lines/sec)\n " , linesHashed , runTime , linesPerSecond )
127
- }
69
+ close (linesCh )
128
70
129
- // process wordlist chunks
130
- func processChunk (chunk []byte , count * int64 , hexErrorCount * int64 , writer * bufio.Writer , stopChan chan struct {}, vaults []Vault , crackedCountCh chan int , linesProcessedCh chan int ) {
131
- lineStart := 0
132
- for i := 0 ; i < len (chunk ); i ++ {
133
- if chunk [i ] == '\n' {
134
- password := chunk [lineStart :i ]
135
- decodedBytes , _ , hexErrCount := checkForHexBytes (password )
136
- startCracker (stopChan , decodedBytes , vaults , crackedCountCh , linesProcessedCh , writer )
137
- atomic .AddInt64 (count , 1 )
138
- atomic .AddInt64 (hexErrorCount , int64 (hexErrCount ))
139
- lineStart = i + 1 // move start index past the newline
140
- }
71
+ if err := scanner .Err (); err != nil {
72
+ log .Fatalf ("Error reading file: %v\n " , err )
141
73
}
142
74
143
- // handle cases where there is no newline at the end of the chunk
144
- if lineStart < len (chunk ) {
145
- password := chunk [lineStart :]
146
- decodedBytes , _ , hexErrCount := checkForHexBytes (password )
147
- startCracker (stopChan , decodedBytes , vaults , crackedCountCh , linesProcessedCh , writer )
148
- atomic .AddInt64 (count , 1 )
149
- atomic .AddInt64 (hexErrorCount , int64 (hexErrCount ))
150
- }
75
+ wg .Wait ()
151
76
152
- writer . Flush ( )
77
+ log . Println ( "Finished" )
153
78
}
154
79
155
- // hash cracking worker
156
- func startCracker (stopChan chan struct {}, password []byte , vaults []Vault , crackedCountCh chan int , linesProcessedCh chan int , writer * bufio.Writer ) {
157
- allDecrypted := true
80
+ func processPassword (password []byte , vaults []Vault , writerMu * sync.Mutex , writer * bufio.Writer , crackedCount * int32 , linesProcessed * int32 , stopChan chan struct {}) {
81
+ atomic .AddInt32 (linesProcessed , 1 )
82
+ // check for hex, ignore hexErrCount
83
+ decodedPassword , _ , _ := checkForHexBytes (password )
158
84
159
85
for i := range vaults {
160
- if ! vaults [i ].Decrypted { // check only undecrypted vaults
161
- decryptedData , err := decryptVault (vaults [i ].EncryptedData , password , vaults [i ].Salt , vaults [i ].Nonce , vaults [i ].Iterations , vaults [i ].Kdf )
162
- if err != nil {
163
- allDecrypted = false
164
- continue // skip to next vault if decryption fails
86
+ if atomic .LoadInt32 (& vaults [i ].Decrypted ) == 0 {
87
+ decryptedData , err := decryptVault (vaults [i ].EncryptedData , decodedPassword , vaults [i ].Salt , vaults [i ].Nonce , vaults [i ].Iterations , vaults [i ].Kdf )
88
+ if err != nil || ! isValid (decryptedData ) {
89
+ continue
165
90
}
166
- if isValid ( decryptedData ) {
167
- crackedCountCh <- 1
168
- vaults [i ].Decrypted = true
91
+
92
+ if atomic . CompareAndSwapInt32 ( & vaults [ i ]. Decrypted , 0 , 1 ) {
93
+ output := fmt . Sprintf ( "%s:%s \n " , vaults [i ].VaultText , string ( decodedPassword ))
169
94
if writer != nil {
170
- writer .WriteString (fmt .Sprintf ("\n Password: '%s'\n " , password ))
95
+ writerMu .Lock ()
96
+ atomic .AddInt32 (crackedCount , 1 )
97
+ writer .WriteString (output )
171
98
writer .Flush ()
172
- } else {
173
- fmt .Printf ("\n Password: '%s'\n " , password )
99
+ writerMu .Unlock ()
100
+ }
101
+
102
+ // exit if all vaults are cracked
103
+ if isAllVaultsCracked (vaults ) {
104
+ closeStopChannel (stopChan )
174
105
}
175
- } else {
176
- allDecrypted = false
106
+ return
177
107
}
178
108
}
179
109
}
180
-
181
- linesProcessedCh <- 1
182
-
183
- if allDecrypted {
184
- closeStopChannel (stopChan )
185
- }
186
110
}
0 commit comments