├── License.md ├── NetWireLogDecoder.au3 ├── README.md ├── ReadFromDevice.ps1 ├── extras └── merge-resident-extract.au3 ├── scan-launch-netwire.ps1 └── sigscan-core.ps1 /License.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Arsenal Consulting, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | NetWire Log Decoder ("the Software") is provided "AS IS" and "WITH ALL FAULTS," without warranty of any kind, including without limitation the warranties of merchantability, fitness for a particular purpose and non-infringement. Arsenal Consulting, Inc. (d/b/a "Arsenal Recon") makes no warranty that the Software is free of defects or is suitable for any particular purpose. In no event shall Arsenal Consulting, Inc. be responsible for loss or damages arising from the installation or use of the Software, including but not limited to any indirect, punitive, special, incidental or consequential damages of any character including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. The entire risk as to the quality and performance of the Software is borne by you. Should the Software prove defective, you and not Arsenal Consulting, Inc. assume the entire cost of any service and repair. -------------------------------------------------------------------------------- /NetWireLogDecoder.au3: -------------------------------------------------------------------------------- 1 | #RequireAdmin 2 | #Region ;**** Directives created by AutoIt3Wrapper_GUI **** 3 | #AutoIt3Wrapper_Icon=C:\Program Files (x86)\AutoIt3\Icons\au3.ico 4 | #AutoIt3Wrapper_Outfile=netwiredecoder32.exe 5 | #AutoIt3Wrapper_Outfile_x64=netwiredecoder64.exe 6 | #AutoIt3Wrapper_Compile_Both=y 7 | #AutoIt3Wrapper_UseX64=y 8 | #AutoIt3Wrapper_Change2CUI=y 9 | #AutoIt3Wrapper_Res_Comment=NetWire Log Decoder 10 | #AutoIt3Wrapper_Res_Description=NetWire Log Decoder 11 | #AutoIt3Wrapper_Res_Fileversion=1.0.0.1 12 | #AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 5 13 | #AutoIt3Wrapper_Run_Au3Stripper=y 14 | #Au3Stripper_Parameters=/sf /sv /rm 15 | #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #Include 26 | #include 27 | 28 | Global $sectorsize = 512 29 | Global $outputPrefix = "netwire" 30 | Global $sPSScript = '"' & @ScriptDir & "\scan-launch-netwire.ps1" & '"' 31 | 32 | Global $detectInvalid = 0 ; attempt to skip invalid data within sector. see notes 33 | Global $skipScanAndTrim = 0 ; only useful with carved data etc, not necessary wiht healthy logs 34 | Global $minHits = 5 ; minimum signature hits per sector, used in validation 35 | ; signatures used in validation 36 | Global $signature1 = "B4BBB4BB" ;WINDOW 37 | Global $signature2 = "EA020D1315DA" ; [Ctrl+ 38 | Global $signature3 = "EAFD1C151C0D1CE4" ; [Delete] 39 | Global $signature4 = "E4EA" ; ][ 40 | Global $signature5 = "E4E1D4E1EA" ; ] - [ 41 | Global $signature6 = "E4001313160EE1" ; [Arrow 42 | Global $signature7 = "EAFC170D1C13E4" ; [Enter] 43 | Global $signature8 = "EA0320221A121120221CE4" ; [Backspace] 44 | Global $signature9 = "E1F516221AE4" ; Lock] 45 | 46 | 47 | Global $parsingMode ;0=file, 1=device 48 | Global $AddedSector=0 ; currently not used in this scenario 49 | Global $decodedOutput 50 | 51 | Global $AdlibInterval = 5000 ;Millisec for the script to halt and update progress bar 52 | Global $ProgressBar2, $CurrentProgress2 = 0, $ProgressTotal2 = 0 53 | Global $ProgressBar3, $CurrentProgress3 = 0, $ProgressTotal3 = 10 54 | 55 | Global $ButtonColor = 0x2f4e57, $active = False 56 | Global $myctredit, $ButtonCancel, $ButtonStart, $ButtonOpenOutput, $LabelProgress3, $ButtonOutput, $OutputField, $LabelOutput, $LabelDevice, $DeviceField 57 | Global $ButtonInput, $InputField, $Form, $ButtonOpenDecode, $radioFile, $radioDevice 58 | Global $file_input, $folder_output = @ScriptDir, $OutPutPath = @ScriptDir 59 | Global $CommandlineMode, $mainlogfile, $TargetInput, $hCsv 60 | 61 | Const $sRootSlash = "\\.\" 62 | 63 | If $cmdline[0] > 0 Then 64 | $CommandlineMode = 1 65 | _GetInputParams() 66 | _Init() 67 | Exit 68 | Else 69 | DllCall("kernel32.dll", "bool", "FreeConsole") 70 | $CommandlineMode = 0 71 | OnAutoItExitRegister("_GuiExitMessage") 72 | 73 | Opt("GUIOnEventMode", 1) 74 | $Form = GUICreate("NetWire Log Decoder", 830, 500, -1, -1) 75 | GUISetOnEvent($GUI_EVENT_CLOSE, "_HandleExit") 76 | 77 | $radioFile = GUICtrlCreateRadio("Source is file", 20, 20, 100, 20) 78 | $InputField = GUICtrlCreateInput("", 140, 20, 510, 20) 79 | GUICtrlSetState($InputField, $GUI_DISABLE) 80 | $ButtonInput = GUICtrlCreateButton("Browse", 700, 20, 100, 30) 81 | GUICtrlSetOnEvent($ButtonInput, "_HandleEvent") 82 | GUICtrlSetBkColor(-1, $ButtonColor) 83 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 84 | GUICtrlSetColor(-1, 0xFFFFFF) 85 | 86 | $radioDevice = GUICtrlCreateRadio("Source is device", 20, 70, 100, 20) 87 | $comboDevice = GUICtrlCreateCombo("", 140, 70, 510, 21) 88 | $ButtonRefreshDevice = GUICtrlCreateButton("Refresh", 700, 70, 100, 30) 89 | GUICtrlSetOnEvent($ButtonRefreshDevice, "_HandleEvent") 90 | GUICtrlSetBkColor(-1, $ButtonColor) 91 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 92 | GUICtrlSetColor(-1, 0xFFFFFF) 93 | 94 | $checkDetectInvalid = GUICtrlCreateCheckbox("DetectInvalid", 20, 120, 150, 20) 95 | GUICtrlSetTip($checkDetectInvalid, "Attempt to skip invalid data, typically with carved data") 96 | 97 | $checkSkipScanAndTrim = GUICtrlCreateCheckbox("SkipScanAndTrim", 190, 120, 150, 20) 98 | GUICtrlSetTip($checkSkipScanAndTrim, "Only activate when input is a healthy log") 99 | GUICtrlSetState($checkSkipScanAndTrim, $GUI_UNCHECKED) 100 | 101 | $LabelOutput = GUICtrlCreateLabel("Select output folder:", 20, 170, 120, 20) 102 | $OutputField = GUICtrlCreateInput("Optional. Defaults to program directory", 140, 170, 510, 20) 103 | GUICtrlSetState($OutputField, $GUI_DISABLE) 104 | $ButtonOutput = GUICtrlCreateButton("Browse", 700, 170, 100, 30) 105 | GUICtrlSetOnEvent($ButtonOutput, "_HandleEvent") 106 | GUICtrlSetBkColor(-1, $ButtonColor) 107 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 108 | GUICtrlSetColor(-1, 0xFFFFFF) 109 | 110 | ;$LabelProgress2 = GUICtrlCreateLabel("Progress scanning and filtering:", 10, 210, 200, 20) 111 | $LabelProgress2 = GUICtrlCreateLabel("", 10, 210, 200, 20) 112 | $ProgressBar2 = GUICtrlCreateProgress(10, 230, 810, 30) 113 | ;$LabelProgress3 = GUICtrlCreateLabel("Progress decoding:", 10, 270, 200, 20) 114 | $LabelProgress3 = GUICtrlCreateLabel("Progress:", 10, 270, 200, 20) 115 | $ProgressBar3 = GUICtrlCreateProgress(10, 290, 810, 30) 116 | 117 | $ButtonStart = GUICtrlCreateButton("Start Parsing", 20, 450, 150, 40, $BS_BITMAP) 118 | GUICtrlSetOnEvent($ButtonStart, "_HandleEvent") 119 | GUICtrlSetBkColor(-1, $ButtonColor) 120 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 121 | GUICtrlSetColor(-1, 0xFFFFFF) 122 | $ButtonCancel = GUICtrlCreateButton("Exit", 175, 450, 150, 40) 123 | GUICtrlSetOnEvent($ButtonCancel, "_HandleCancel") 124 | GUICtrlSetBkColor(-1, $ButtonColor) 125 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 126 | GUICtrlSetColor(-1, 0xFFFFFF) 127 | $ButtonOpenOutput = GUICtrlCreateButton("Open Output", 330, 450, 150, 40) 128 | GUICtrlSetOnEvent($ButtonOpenOutput, "_HandleOpenOutput") 129 | GUICtrlSetBkColor(-1, $ButtonColor) 130 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 131 | GUICtrlSetColor(-1, 0xFFFFFF) 132 | $ButtonOpenDecode = GUICtrlCreateButton("Open Decode", 485, 450, 150, 40) 133 | GUICtrlSetOnEvent($ButtonOpenDecode, "_HandleOpenDecode") 134 | GUICtrlSetBkColor(-1, $ButtonColor) 135 | GUICtrlSetFont(-1, 9, $FW_SEMIBOLD, $GUI_FONTNORMAL, "", $CLEARTYPE_QUALITY) 136 | GUICtrlSetColor(-1, 0xFFFFFF) 137 | 138 | $myctredit = GUICtrlCreateEdit("", 0, 330, 830, 100, BitOR($ES_AUTOVSCROLL, $WS_VSCROLL, $ES_READONLY)) 139 | GUICtrlSetBkColor($myctredit, 0xFFFFFF) 140 | _GUICtrlEdit_SetLimitText($myctredit, 128000) 141 | 142 | GUISetState(@SW_SHOW) 143 | 144 | _RefreshDevices() 145 | 146 | While Not $active 147 | ;Wait for event. The $active variable is set when parsing and reset when done in order for multiple parsing executions to run subsequently 148 | 149 | Sleep(500) 150 | If $active Then 151 | _HandleParsing() 152 | $active = False 153 | EndIf 154 | WEnd 155 | 156 | EndIf 157 | 158 | Func _Init() 159 | 160 | $TargetVolume = $TargetInput 161 | $decodedOutput = "" 162 | 163 | _UpdateProgress3(20) 164 | 165 | Local $sTimestampStart = @YEAR & "-" & @MON & "-" & @MDAY & "_" & @HOUR & "-" & @MIN & "-" & @SEC 166 | 167 | $OutPutPath = $folder_output & "\NetWireDecoder-" & $sTimestampStart 168 | If DirCreate($OutputPath) = 0 Then 169 | _DisplayWrapper("Error creating: " & $OutputPath & @CRLF) 170 | Return 171 | EndIf 172 | 173 | 174 | Local $sDebugLog = $OutPutPath & "\" & $outputPrefix & ".log" 175 | If FileExists($sDebugLog) Then 176 | _DisplayWrapper("Error: Output file already exist: " & $sDebugLog & @CRLF) 177 | Return 178 | EndIf 179 | Global $hDebugLog = FileOpen($sDebugLog, 2+32) 180 | 181 | _DebugOut("Input: " & $TargetVolume & @CRLF) 182 | _DebugOut("SkipScanAndTrim: " & $skipScanAndTrim & @CRLF) 183 | _DebugOut("DetectInvalid: " & $detectInvalid & @CRLF) 184 | 185 | If Not $skipScanAndTrim Then 186 | 187 | _DebugOut("Attention: Scanning of larger disks may take some time (hours).." & @CRLF) 188 | _DisplayWrapper("Attention: Scanning of larger disks may take some time (hours).." & @CRLF) 189 | 190 | Local $sOutTxt = $OutPutPath & "\" & $outputPrefix & ".txt" 191 | 192 | Local $Timerstart = TimerInit() 193 | 194 | Local $matches = _RunPsScript($TargetVolume, $sOutTxt) 195 | $matches = StringStripCR($matches) 196 | _DebugOut("Total signature matches: " & $matches & @CRLF) 197 | _DisplayWrapper("Total signature matches: " & $matches & @CRLF) 198 | If $matches = 0 Then 199 | _DisplayWrapper("Nothing to parse.." & @CRLF) 200 | Return 201 | EndIf 202 | _UpdateProgress3(50) 203 | _DebugOut("Scanning for signatures took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF) 204 | _DisplayWrapper("Scanning for signatures took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF) 205 | 206 | Local $sOutFile = $OutPutPath & "\" & $outputPrefix & "_stage1_scanned.bin" 207 | If FileExists($sOutFile) Then 208 | _DisplayWrapper("Error: Output file already exist: " & $sOutFile & @CRLF) 209 | Return 210 | EndIf 211 | Global $hOutFile = _WinAPI_CreateFile($sOutFile, 3, 6, 7) 212 | If Not $hOutFile Then 213 | _DebugOut("Error in CreateFile for " & $sOutFile & " " & _WinAPI_GetLastErrorMessage() & @CRLF) 214 | _DisplayWrapper("Error in CreateFile for " & $sOutFile & " " & _WinAPI_GetLastErrorMessage() & @CRLF) 215 | Return 216 | EndIf 217 | 218 | Local $sOutCsv = $OutPutPath & "\" & $outputPrefix & ".csv" 219 | Local $hOutCsv = FileOpen($sOutCsv, 2+32) 220 | 221 | Global $hVol 222 | If Not StringLeft($TargetVolume, 2) = "\\" Then 223 | $hVol = _WinAPI_CreateFileEx("\\.\" & $TargetVolume, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ,$FILE_SHARE_WRITE,$FILE_SHARE_DELETE), $FILE_ATTRIBUTE_NORMAL) 224 | Else 225 | $hVol = _WinAPI_CreateFileEx($TargetVolume, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ,$FILE_SHARE_WRITE,$FILE_SHARE_DELETE), $FILE_ATTRIBUTE_NORMAL) 226 | EndIf 227 | If Not $hVol Then 228 | _DebugOut("Error in CreateFile for " & $TargetVolume & " " & _WinAPI_GetLastErrorMessage() & @CRLF) 229 | _DisplayWrapper("Error in CreateFile for " & $TargetVolume & " " & _WinAPI_GetLastErrorMessage() & @CRLF) 230 | Return 231 | EndIf 232 | 233 | _DebugOut("Reading offsets from: " & $sOutTxt & @CRLF) 234 | _DebugOut("Reading sector bytes from: " & $TargetVolume & @CRLF) 235 | _DebugOut("Writing data to: " & $sOutFile & @CRLF) 236 | 237 | $Timerstart = TimerInit() 238 | 239 | _DebugOut(@CRLF & "Started parsing input txt file.." & @CRLF) 240 | 241 | $aFinal = _ParseTxt($sOutTxt) 242 | _UpdateProgress3(60) 243 | _DebugOut("Parsing of txt and array sorting took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF) 244 | 245 | _DebugOut("Sectors to extract data from: " & UBound($aFinal) & @CRLF) 246 | _DebugOut("Bytes to extract: " & UBound($aFinal) * $sectorsize & @CRLF) 247 | _DisplayWrapper("Bytes to extract: " & UBound($aFinal) * $sectorsize & @CRLF) 248 | 249 | _DebugOut("Writing array content to " & $sOutCsv & @CRLF) 250 | _FileWriteFromArray($hOutCsv, $aFinal) 251 | 252 | $Timerstart = TimerInit() 253 | 254 | _WriteSectorsFromArray($aFinal, $hVol, $hOutFile) 255 | 256 | _DebugOut("Writing data from array took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF) 257 | 258 | _WinAPI_CloseHandle($hOutFile) 259 | _WinAPI_CloseHandle($hVol) 260 | FileClose($hOutCsv) 261 | 262 | Local $filteredOutput = $OutPutPath & "\" & $outputPrefix & "_stage2_filtered.bin" 263 | _DebugOut("Writing filtered binary output to " & $filteredOutput & @CRLF) 264 | 265 | Local $falsePositives = _FilterOutput($sOutFile, $filteredOutput) 266 | _DebugOut("False positive sectors removed: " & $falsePositives & @CRLF) 267 | _DebugOut("Core bytes to parse: " & (UBound($aFinal) * $sectorsize) - ($falsePositives * $sectorsize) & @CRLF) 268 | _DisplayWrapper("Core bytes to parse: " & (UBound($aFinal) * $sectorsize) - ($falsePositives * $sectorsize) & @CRLF) 269 | 270 | Else 271 | $filteredOutput = $TargetInput 272 | _DebugOut("Core bytes to parse: " & FileGetSize($filteredOutput) & @CRLF) 273 | _DisplayWrapper("Core bytes to parse: " & FileGetSize($filteredOutput) & @CRLF) 274 | EndIf 275 | 276 | _UpdateProgress3(70) 277 | 278 | $Timerstart = TimerInit() 279 | 280 | _DisplayWrapper("Started decoding.." & @CRLF) 281 | _DebugOut("Parsing: " & $filteredOutput & @CRLF) 282 | 283 | $decodedOutput = $OutPutPath & "\" & $outputPrefix & "_stage3_decoded.txt" 284 | _DecodeNetWire($filteredOutput, $decodedOutput) 285 | _UpdateProgress3(100) 286 | 287 | _DebugOut("Decode written to: " & $decodedOutput & @CRLF) 288 | _DebugOut("Decoding took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF & @CRLF) 289 | _DisplayWrapper("Decoding took " & _WinAPI_StrFromTimeInterval(TimerDiff($Timerstart)) & @CRLF & @CRLF) 290 | 291 | FileClose($hDebugLog) 292 | 293 | $OutPutPath = '"' & $OutPutPath & '"' 294 | 295 | EndFunc 296 | 297 | Func _WriteSectorsFromArray($aInput, $hVol, $hOutFile) 298 | ; Each entry/offset in the array is sector size aligned 299 | Local $nBytes 300 | Local $pBuff = DllStructCreate("byte[" & $sectorsize & "]") 301 | $arraysize = UBound($aInput) 302 | For $i = 0 To $arraysize - 1 303 | 304 | _WinAPI_SetFilePointerEx($hVol, $aInput[$i], $FILE_BEGIN) 305 | If Not _WinAPI_ReadFile($hVol, DllStructGetPtr($pBuff), DllStructGetSize($pBuff), $nBytes) Then 306 | _DebugOut("Error in ReadFile: " & _WinAPI_GetLastErrorMessage() & @CRLF) 307 | Exit 308 | EndIf 309 | If Not _WinAPI_WriteFile($hOutFile, DllStructGetPtr($pBuff), DllStructGetSize($pBuff), $nBytes) Then 310 | _DebugOut("Error in WriteFile: " & _WinAPI_GetLastErrorMessage() & @CRLF) 311 | Exit 312 | EndIf 313 | 314 | Next 315 | EndFunc 316 | 317 | Func _ParseTxt($sOutTxt) 318 | 319 | If Not FileExists($sOutTxt) Then 320 | _DebugOut("Error file not found" & @CRLF) 321 | Return 322 | EndIf 323 | Local $aArray = FileReadToArray($sOutTxt) 324 | Local $iLineCount = @extended 325 | If @error Then 326 | _DebugOut("Error reading file: " & $sOutTxt & @CRLF) 327 | Return 328 | EndIf 329 | 330 | _DebugOut("Lines to parse: " & $iLineCount & @CRLF) 331 | 332 | Local $aOffsetsAligned[Ceiling($iLineCount / 2)] 333 | _DebugOut("Setting target array to size: " & UBound($aOffsetsAligned) & @CRLF) 334 | Local $hits=0 335 | 336 | For $i = 1 To $iLineCount - 1 337 | 338 | If $aArray[$i] = "" Then 339 | ContinueLoop 340 | EndIf 341 | 342 | $split = StringSplit($aArray[$i], " ") 343 | 344 | If Not IsArray($split) Then 345 | ContinueLoop 346 | EndIf 347 | $Entry = $split[3] 348 | 349 | If StringIsDigit($Entry) = 0 Then 350 | ContinueLoop 351 | EndIf 352 | 353 | $ValueAligned = _OffsetAlignToSector($Entry) 354 | 355 | $aOffsetsAligned[$hits] = $ValueAligned 356 | 357 | $hits += 1 358 | 359 | Next 360 | 361 | ReDim $aOffsetsAligned[$hits] 362 | _DebugOut("Adjusted target array to size: " & UBound($aOffsetsAligned) & @CRLF) 363 | 364 | _DebugOut("Removing duplicates from array.." & @CRLF) 365 | 366 | _ArraySort($aOffsetsAligned, 0, 0, 0, 0, 1) 367 | $aUnique = _MyArrayUnique0($aOffsetsAligned) 368 | _DebugOut("Done" & @CRLF) 369 | 370 | _DebugOut("Sorting the array.." & @CRLF) 371 | 372 | _ArraySort($aUnique, 0, 0, 0, 0, 1) 373 | If @error Then 374 | _DebugOut("Error sorting array $aUnique" & @CRLF) 375 | Exit 376 | EndIf 377 | 378 | If Not $AddedSector Then 379 | _DebugOut("Done" & @CRLF) 380 | Return $aUnique 381 | EndIf 382 | 383 | _DebugOut("Adding additional sectors..." & @CRLF) 384 | 385 | Local $aModified = _ArrayAddDoubleSector($aUnique) 386 | 387 | _ArraySort($aModified, 0, 0, 0, 0, 1) 388 | If @error Then 389 | _DebugOut("Error sorting array $aModified" & @CRLF) 390 | Exit 391 | EndIf 392 | 393 | $aUnique2 = _MyArrayUnique0($aModified) 394 | _DebugOut("Done" & @CRLF) 395 | 396 | _DebugOut("Sorting the array.." & @CRLF) 397 | 398 | _ArraySort($aUnique2) 399 | If @error Then 400 | _DebugOut("Error sorting array $aUnique2" & @CRLF) 401 | Exit 402 | EndIf 403 | 404 | _DebugOut("Done" & @CRLF) 405 | 406 | Return $aUnique2 407 | EndFunc 408 | 409 | Func _OffsetAlignToSector($dec) 410 | If Mod($dec, 0x200) Then 411 | While 1 412 | $dec -= 1 413 | If Mod($dec, 0x200) = 0 Then 414 | ExitLoop 415 | EndIf 416 | WEnd 417 | EndIf 418 | Return $dec 419 | EndFunc 420 | 421 | Func _ArrayAddDoubleSector($aArray) 422 | 423 | Local $inputArraySize = UBound($aArray) * 3 424 | 425 | Local $aNewArray[$inputArraySize] 426 | Local $counter = 0 427 | 428 | If $aArray[0] >= 512 Then 429 | $aNewArray[$counter] = $aArray[0] - 512 430 | $counter += 1 431 | EndIf 432 | 433 | $aNewArray[$counter] = $aArray[0] 434 | $counter += 1 435 | 436 | For $i = 1 To UBound($aArray) - 1 437 | 438 | Select 439 | Case $aArray[$i - 1] + 512 = $aArray[$i] 440 | $aNewArray[$counter] = $aArray[$i] 441 | $counter += 1 442 | 443 | Case $aArray[$i - 1] + 1024 = $aArray[$i] 444 | $aNewArray[$counter] = $aArray[$i] - 512 445 | $counter += 1 446 | $aNewArray[$counter] = $aArray[$i - 1] + 512 447 | $counter += 1 448 | $aNewArray[$counter] = $aArray[$i] 449 | $counter += 1 450 | 451 | Case $aArray[$i - 1] + 1024 < $aArray[$i] 452 | $aNewArray[$counter] = $aArray[$i] - 512 453 | $counter += 1 454 | $aNewArray[$counter] = $aArray[$i - 1] + 512 455 | $counter += 1 456 | $aNewArray[$counter] = $aArray[$i] 457 | $counter += 1 458 | 459 | EndSelect 460 | 461 | Next 462 | 463 | $aNewArray[$counter] = $aArray[$i - 1] + 512 464 | $counter += 1 465 | 466 | ReDim $aNewArray[$counter] 467 | Return $aNewArray 468 | EndFunc 469 | 470 | Func _MyArrayUnique0(Const ByRef $aArray) 471 | ; just to work around a bug in the obfuscator 472 | ; currently only 1 dimensional arrays 473 | ; will be slow for large arrays 474 | 475 | Local $aNewArray[UBound($aArray)] 476 | Local $counter = 0 477 | 478 | For $i = 0 To UBound($aArray) - 1 479 | 480 | If $counter > 0 Then 481 | If $aArray[$i] = $aNewArray[$counter - 1] Then 482 | ContinueLoop 483 | EndIf 484 | EndIf 485 | $counter += 1 486 | $aNewArray[$counter - 1] = $aArray[$i] 487 | 488 | Next 489 | ReDim $aNewArray[$counter] 490 | Return $aNewArray 491 | EndFunc 492 | 493 | Func _DebugOut($text) 494 | FileWrite($hDebugLog, $text) 495 | EndFunc 496 | 497 | Func _RunPsScript($FilePath, $OutputFile) 498 | 499 | Local $sCMD = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File " & $sPSScript & " -inputFile " & '"' & $FilePath & '"' & " -OutputFile " & '"' & $OutputFile & '"' 500 | 501 | Local $pid = Run($sCMD, @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD + $STDERR_CHILD) 502 | If @error Then 503 | _DebugOut("Error: Could not execute external script" & @CRLF) 504 | Exit 505 | EndIf 506 | 507 | StdinWrite($pid) 508 | Local $AllOutput = "", $sOutput = "" 509 | 510 | While 1 511 | $sOutput = StdoutRead($pid) 512 | If @error Then ExitLoop 513 | If $sOutput <> "" Then $AllOutput &= $sOutput 514 | If Not ProcessExists($pid) Then ExitLoop 515 | ; exit the loop if processing is +10 min 516 | ;If TimerDiff($hTimer) > 600000 Then ExitLoop 517 | WEnd 518 | 519 | Return $AllOutput 520 | 521 | EndFunc 522 | 523 | Func _TestIfDataIsFalsePositive($hFile, $Offset, $hFileOut) 524 | Local $TestData, $nBytes 525 | 526 | _WinAPI_SetFilePointerEx($hFile, $Offset, $FILE_BEGIN) 527 | Local $tBuffer = DllStructCreate("byte[" & $sectorsize & "]") 528 | If Not _WinAPI_ReadFile($hFile, DllStructGetPtr($tBuffer), $sectorsize, $nBytes) Then 529 | ConsoleWrite("Error in ReadFile." & @CRLF) 530 | _WinAPI_CloseHandle($hFile) 531 | Exit 532 | EndIf 533 | $TestData = DllStructGetData($tBuffer,1) 534 | $TestData = StringTrimLeft($TestData,2) 535 | 536 | 537 | StringReplace($TestData, $signature1, $signature1) 538 | Local $nbOccurences1 = @extended 539 | ; ConsoleWrite("$nbOccurences1: " & $nbOccurences1 & @CRLF) 540 | 541 | StringReplace($TestData, $signature2, $signature2) 542 | Local $nbOccurences2 = @extended 543 | ; ConsoleWrite("$nbOccurences2: " & $nbOccurences2 & @CRLF) 544 | 545 | StringReplace($TestData, $signature3, $signature3) 546 | Local $nbOccurences3 = @extended 547 | ; ConsoleWrite("$nbOccurences3: " & $nbOccurences3 & @CRLF) 548 | 549 | StringReplace($TestData, $signature4, $signature4) 550 | Local $nbOccurences4 = @extended 551 | ; ConsoleWrite("$nbOccurences4: " & $nbOccurences4 & @CRLF) 552 | 553 | StringReplace($TestData, $signature5, $signature5) 554 | Local $nbOccurences5 = @extended 555 | ; ConsoleWrite("$nbOccurences5: " & $nbOccurences5 & @CRLF) 556 | 557 | StringReplace($TestData, $signature6, $signature6) 558 | Local $nbOccurences6 = @extended 559 | ; ConsoleWrite("$nbOccurences6: " & $nbOccurences6 & @CRLF) 560 | 561 | StringReplace($TestData, $signature7, $signature7) 562 | Local $nbOccurences7 = @extended 563 | ; ConsoleWrite("$nbOccurences7: " & $nbOccurences7 & @CRLF) 564 | 565 | StringReplace($TestData, $signature8, $signature8) 566 | Local $nbOccurences8 = @extended 567 | ; ConsoleWrite("$nbOccurences8: " & $nbOccurences8 & @CRLF) 568 | 569 | StringReplace($TestData, $signature9, $signature9) 570 | Local $nbOccurences9 = @extended 571 | ; ConsoleWrite("$nbOccurences9: " & $nbOccurences9 & @CRLF) 572 | 573 | Local $allOccurrences = $nbOccurences1 + $nbOccurences2 + $nbOccurences3 + $nbOccurences4 + $nbOccurences5 + $nbOccurences6 + $nbOccurences7 + $nbOccurences8 + $nbOccurences9 574 | ; ConsoleWrite("$allOccurrences: " & $allOccurrences & @CRLF) 575 | 576 | If $allOccurrences < $minHits Then 577 | _DebugOut("Skipping false positive sector at 0x" & Hex($Offset) & @CRLF) 578 | Return 1 579 | Else 580 | _WinAPI_WriteFile($hFileOut, DllStructGetPtr($tBuffer), $sectorsize, $nBytes) 581 | Return 0 582 | EndIf 583 | EndFunc 584 | 585 | Func _FilterOutput($inputFile, $filteredOutput) 586 | 587 | Local $hFile = _WinAPI_CreateFileEx("\\.\" & $inputFile, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ,$FILE_SHARE_WRITE)) 588 | If Not $hFile Then 589 | _DebugOut("Error in CreateFile on " & $inputFile & " : " & _WinAPI_GetLastErrorMessage() & @CRLF) 590 | _WinAPI_CloseHandle($hFile) 591 | Exit 592 | EndIf 593 | 594 | Local $hFileOut = _WinAPI_CreateFile("\\.\" & $filteredOutput,3,6,7) 595 | If $hFileOut = 0 Then 596 | _DebugOut("CreateFile error on " & $filteredOutput & " : " & _WinAPI_GetLastErrorMessage() & @CRLF) 597 | Exit 598 | EndIf 599 | Local $FileSize = _WinAPI_GetFileSizeEx($hFile) 600 | 601 | Local $PageCounter=0, $TargetOffset=0, $falsePositives=0 602 | 603 | While 1 604 | $TargetOffset = $PageCounter * $sectorsize 605 | If $TargetOffset >= $FileSize Then ExitLoop 606 | $falsePositives += _TestIfDataIsFalsePositive($hFile, $TargetOffset, $hFileOut) 607 | 608 | $PageCounter += 1 609 | WEnd 610 | 611 | _WinAPI_CloseHandle($hFile) 612 | _WinAPI_CloseHandle($hFileOut) 613 | 614 | Return $falsePositives 615 | EndFunc 616 | 617 | Func _HexPrecheck($hex) 618 | Local $retArr[2] 619 | Select 620 | Case StringMid($hex, 1, 12) = "EA020D1315DA" 621 | ; CTRL + SHIFT + x 622 | If StringMid($hex, 13, 2) = "2A" Then 623 | $retArr[0] = 14 624 | $retArr[1] = "[" 625 | Return $retArr 626 | EndIf 627 | If StringMid($hex, 13, 2) = "24" Then 628 | $retArr[0] = 14 629 | $retArr[1] = "]" 630 | Return $retArr 631 | EndIf 632 | If StringMid($hex, 13, 2) = "4A" Then 633 | $retArr[0] = 14 634 | $retArr[1] = "{" 635 | Return $retArr 636 | EndIf 637 | If StringMid($hex, 13, 2) = "44" Then 638 | $retArr[0] = 14 639 | $retArr[1] = "}" 640 | Return $retArr 641 | EndIf 642 | If StringMid($hex, 13, 2) = "41" Then 643 | $retArr[0] = 14 644 | $retArr[1] = "@" 645 | Return $retArr 646 | EndIf 647 | If StringMid($hex, 13, 2) = "A2" Then 648 | $retArr[0] = 14 649 | $retArr[1] = "£" 650 | Return $retArr 651 | EndIf 652 | If StringMid($hex, 13, 2) = "1D" Then 653 | $retArr[0] = 14 654 | $retArr[1] = "$" 655 | Return $retArr 656 | EndIf 657 | If StringMid($hex, 13, 2) = "81" Then 658 | $retArr[0] = 14 659 | $retArr[1] = "€" 660 | Return $retArr 661 | EndIf 662 | Case StringMid($hex, 1, 10) = "EAED2023E4" 663 | $retArr[0] = 10 664 | $retArr[1] = @TAB 665 | Return $retArr 666 | Case StringMid($hex, 1, 6) = "B4BBB4" 667 | $retArr[0] = 6 668 | $retArr[1] = @CRLF & @CRLF & "" 669 | Return $retArr 670 | Case StringMid($hex, 1, 4) = "B4BB" 671 | $retArr[0] = 4 672 | $retArr[1] = " " & @CRLF 673 | Return $retArr 674 | Case StringMid($hex, 1, 4) = "E4E1" 675 | $retArr[0] = 4 676 | $retArr[1] = "] " 677 | Return $retArr 678 | Case StringMid($hex, 1, 4) = "E1EA" 679 | $retArr[0] = 4 680 | $retArr[1] = " [" 681 | Return $retArr 682 | Case StringMid($hex, 1, 4) = "BBEA" 683 | $retArr[0] = 4 684 | $retArr[1] = " [" 685 | Return $retArr 686 | EndSelect 687 | Return SetError(1, 0, "") 688 | EndFunc 689 | 690 | Func _keyRemapper($char) 691 | ;ConsoleWrite("Char: " & $char & @CRLF) 692 | Select 693 | 694 | Case $char = "EA" 695 | Return "[" 696 | Case $char = "E4" 697 | Return "]" 698 | Case $char = "D0" 699 | Return "1" 700 | Case $char = "D3" 701 | Return "2" 702 | Case $char = "D2" 703 | Return "3" 704 | Case $char = "CD" 705 | Return "4" 706 | Case $char = "CC" 707 | Return "5" 708 | Case $char = "CF" 709 | Return "6" 710 | Case $char = "CE" 711 | Return "7" 712 | Case $char = "C9" 713 | Return "8" 714 | Case $char = "C8" 715 | Return "9" 716 | Case $char = "D1" 717 | Return "0" 718 | Case $char = "20" 719 | Return "a" 720 | Case $char = "23" 721 | Return "b" 722 | Case $char = "22" 723 | Return "c" 724 | Case $char = "1D" 725 | Return "d" 726 | Case $char = "1C" 727 | Return "e" 728 | Case $char = "1F" 729 | Return "f" 730 | Case $char = "1E" 731 | Return "g" 732 | Case $char = "19" 733 | Return "h" 734 | Case $char = "18" 735 | Return "i" 736 | Case $char = "1B" 737 | Return "j" 738 | Case $char = "1A" 739 | Return "k" 740 | Case $char = "15" 741 | Return "l" 742 | Case $char = "14" 743 | Return "m" 744 | Case $char = "17" 745 | Return "n" 746 | Case $char = "16" 747 | Return "o" 748 | Case $char = "11" 749 | Return "p" 750 | Case $char = "10" 751 | Return "q" 752 | Case $char = "13" 753 | Return "r" 754 | Case $char = "12" 755 | Return "s" 756 | Case $char = "0D" 757 | Return "t" 758 | Case $char = "0C" 759 | Return "u" 760 | Case $char = "0F" 761 | Return "v" 762 | Case $char = "0E" 763 | Return "w" 764 | Case $char = "09" 765 | Return "x" 766 | Case $char = "08" 767 | Return "y" 768 | Case $char = "0B" 769 | Return "z" 770 | Case $char = "9F" 771 | Return "æ" 772 | Case $char = "89" 773 | Return "ø" 774 | Case $char = "9C" 775 | Return "å" 776 | Case $char = "00" 777 | Return "A" 778 | Case $char = "03" 779 | Return "B" 780 | Case $char = "02" 781 | Return "C" 782 | Case $char = "FD" 783 | Return "D" 784 | Case $char = "FC" 785 | Return "E" 786 | Case $char = "FF" 787 | Return "F" 788 | Case $char = "FE" 789 | Return "G" 790 | Case $char = "F9" 791 | Return "H" 792 | Case $char = "F8" 793 | Return "I" 794 | Case $char = "FB" 795 | Return "J" 796 | Case $char = "FA" 797 | Return "K" 798 | Case $char = "F5" 799 | Return "L" 800 | Case $char = "F4" 801 | Return "M" 802 | Case $char = "F7" 803 | Return "N" 804 | Case $char = "F6" 805 | Return "O" 806 | Case $char = "F1" 807 | Return "P" 808 | Case $char = "F0" 809 | Return "Q" 810 | Case $char = "F3" 811 | Return "R" 812 | Case $char = "F2" 813 | Return "S" 814 | Case $char = "ED" 815 | Return "T" 816 | Case $char = "EC" 817 | Return "U" 818 | Case $char = "EF" 819 | Return "V" 820 | Case $char = "EE" 821 | Return "W" 822 | Case $char = "E9" 823 | Return "X" 824 | Case $char = "E8" 825 | Return "Y" 826 | Case $char = "EB" 827 | Return "Z" 828 | Case $char = "7F" 829 | Return "Æ" 830 | Case $char = "69" 831 | Return "Ø" 832 | Case $char = "7C" 833 | Return "Å" 834 | Case $char = "C5" 835 | Return "<" 836 | Case $char = "C7" 837 | Return ">" 838 | Case $char = "D5" 839 | Return "," 840 | Case $char = "D7" 841 | Return "." 842 | Case $char = "D4" 843 | Return "-" 844 | Case $char = "CA" 845 | Return ";" 846 | Case $char = "CB" 847 | Return ":" 848 | Case $char = "E6" 849 | Return "_" 850 | Case $char = "E0" 851 | Return "!" 852 | Case $char = "E3" 853 | Return '"' 854 | Case $char = "E2" 855 | Return "#" 856 | Case $char = "5D" 857 | Return "¤" 858 | Case $char = "DC" 859 | Return "%" 860 | Case $char = "DF" 861 | Return "&" 862 | Case $char = "D6" 863 | Return "/" 864 | Case $char = "D9" 865 | Return "(" 866 | Case $char = "D8" 867 | Return ")" 868 | Case $char = "C4" 869 | Return "=" 870 | Case $char = "C6" 871 | Return "?" 872 | Case $char = "E5" 873 | Return "\" 874 | Case $char = "DE" 875 | Return "'" 876 | Case $char = "DB" 877 | Return "*" 878 | Case $char = "05" 879 | Return "|" 880 | Case $char = "DA" 881 | Return "+" 882 | Case $char = "E1" 883 | Return " " 884 | Case $char = "01" 885 | Return "@" 886 | Case $char = "2F" 887 | Return "-" 888 | Case $char = "BB" 889 | Return " " 890 | Case $char = "07" 891 | Return "~" 892 | Case Else 893 | ;ConsoleWrite("Unknown: " & $char & @CRLF) 894 | Return "" 895 | 896 | EndSelect 897 | ConsoleWrite("Error: Should not be here!!" & @CRLF) 898 | EndFunc 899 | 900 | Func _DecodeNetWire($inputfile, $outputFile) 901 | 902 | ConsoleWrite("Parsing: " & $inputfile & @CRLF) 903 | Local $logfile = FileOpen($outputFile, 2+32) 904 | 905 | Local $hFile = FileOpen($inputfile, 16) 906 | Local $rFile = FileRead($hFile) 907 | Local $fileSize = FileGetSize($inputfile) 908 | Local $length = StringLen($rFile) 909 | Local $out = "", $str = "", $outChunks 910 | 911 | Select 912 | Case $length < 100000 913 | $outChunks = 100 914 | Case $length < 1000000 915 | $outChunks = 1000 916 | Case $length < 10000000 917 | $outChunks = 10000 918 | Case $length < 100000000 919 | $outChunks = 100000 920 | Case $length < 1000000000 921 | $outChunks = 1000000 922 | Case Else 923 | $outChunks = 10000000 924 | EndSelect 925 | 926 | Local $outChunk = 0, $outChunkSize = Int($length / $outChunks) 927 | Local $aOut[$outChunks] 928 | 929 | If Mod($outChunkSize, 2) = 0 Then 930 | $outChunkSize += 1 931 | EndIf 932 | 933 | Local $inChunkSize = 32768 934 | Local $inChunks = $fileSize / $inChunkSize 935 | $inChunks = Int($inChunks) 936 | Local $remainder = Mod($fileSize, $inChunkSize) 937 | 938 | Local $invalidCounter = 0, $testArr 939 | Local $currOffset = 3, $currLength = 0 940 | 941 | For $inChunk = 0 To $inChunks 942 | 943 | If $inChunk = $inChunks Then 944 | $currLength = $remainder * 2 945 | Else 946 | $currLength = $inChunkSize * 2 947 | EndIf 948 | 949 | $inChunkData = StringMid($rFile, $currOffset, $currLength) 950 | 951 | For $i = 1 To $currLength Step 2 952 | 953 | If Mod($i, $outChunkSize) = 0 Then 954 | ;ConsoleWrite(Round((($currOffset + $i - 1) / $length) * 100, 2) & " %" & @CRLF) 955 | $aOut[$outChunk] = $out 956 | $outChunk += 1 957 | $out = "" 958 | EndIf 959 | 960 | If $currLength - $i > 16 Then 961 | $testArr = _HexPrecheck(StringMid($inChunkData, $i, 16)) 962 | If Not @error And IsArray($testArr) Then 963 | $str = $testArr[1] 964 | $out &= $str 965 | $i += $testArr[0] - 2 966 | ContinueLoop 967 | EndIf 968 | EndIf 969 | 970 | ;ConsoleWrite("$hex: " & StringFormat("%s", $hex) & @CRLF) 971 | $str = _keyRemapper(StringMid($inChunkData, $i, 2)) 972 | If $str = "" Then 973 | If $detectInvalid Then 974 | $invalidCounter += 1 975 | If $invalidCounter > 3 Then 976 | $i = $i + (1024 - Mod($i-3, 1024)) 977 | _DebugOut("Jumping to string offset: " & $i & @CRLF) 978 | $invalidCounter = 0 979 | $out &= @CRLF 980 | ContinueLoop 981 | EndIf 982 | EndIf 983 | $out &= @CRLF 984 | ContinueLoop 985 | EndIf 986 | If $detectInvalid Then 987 | If $str == "A" Then 988 | ; check for sequence of 00's 989 | If StringMid($inChunkData, $i, 8) = "00000000" Then 990 | $i = $i + (1024 - Mod($i-3, 1024)) 991 | _DebugOut("Jumping to string offset: " & $i & @CRLF) 992 | $out &= @CRLF 993 | $invalidCounter = 0 994 | ContinueLoop 995 | EndIf 996 | ;$invalidCounter += 1 997 | ;ContinueLoop 998 | EndIf 999 | If $str == "F" Then 1000 | ; check for sequence of FF's 1001 | If StringMid($inChunkData, $i, 8) = "FFFFFFFF" Then 1002 | $i = $i + (1024 - Mod($i-3, 1024)) 1003 | _DebugOut("Jumping to string offset: " & $i & @CRLF) 1004 | $out &= @CRLF 1005 | $invalidCounter = 0 1006 | ContinueLoop 1007 | EndIf 1008 | EndIf 1009 | EndIf 1010 | ;ConsoleWrite($hex & " = " & $str & @CRLF) 1011 | $out &= $str 1012 | Next 1013 | $currOffset += $inChunkSize * 2 1014 | Next 1015 | 1016 | Local $sFinal = "" 1017 | For $i = 0 To $outChunk 1018 | $sFinal &= $aOut[$i] 1019 | Next 1020 | $sFinal &= $out 1021 | 1022 | FileWrite($logfile, $sFinal) 1023 | 1024 | FileClose($hFile) 1025 | FileClose($logfile) 1026 | EndFunc 1027 | 1028 | Func _HandleCancel() 1029 | Exit 1030 | EndFunc 1031 | 1032 | Func _HandleExit() 1033 | Exit 1034 | EndFunc 1035 | 1036 | Func _ResetProgress($ProgressBar) 1037 | GUICtrlSetData($ProgressBar, 0) 1038 | EndFunc 1039 | 1040 | 1041 | ;Func _UpdateProgress3() 1042 | ; GUICtrlSetData($ProgressBar3, 100 * $CurrentProgress3 / $ProgressTotal3) 1043 | ;EndFunc 1044 | Func _UpdateProgress3($value) 1045 | GUICtrlSetData($ProgressBar3, $value) 1046 | EndFunc 1047 | 1048 | Func _PrintBeforeExit($input) 1049 | _DebugOut($input) 1050 | _DisplayWrapper($input) 1051 | EndFunc 1052 | 1053 | Func _DisplayWrapper($input) 1054 | 1055 | If $CommandlineMode Then 1056 | ConsoleWrite($input) 1057 | Else 1058 | _DisplayInfo($input) 1059 | EndIf 1060 | 1061 | EndFunc 1062 | 1063 | Func _DisplayInfo($DebugInfo) 1064 | _GUICtrlEdit_AppendText($myctredit, $DebugInfo) 1065 | EndFunc 1066 | 1067 | Func _GuiExitMessage() 1068 | If Not $CommandlineMode Then 1069 | If @exitCode Then 1070 | MsgBox(0, "Error", "An error was triggered. Check the output buffer.") 1071 | EndIf 1072 | EndIf 1073 | EndFunc 1074 | 1075 | Func _HandleEvent() 1076 | If Not $active Then 1077 | Switch @GUI_CtrlId 1078 | Case $ButtonInput 1079 | _HandleFileInput() 1080 | Case $ButtonRefreshDevice 1081 | _RefreshDevices() 1082 | Case $ButtonOutput 1083 | _HandleOutput() 1084 | Case $ButtonStart 1085 | $active = True 1086 | Case $ButtonCancel 1087 | _HandleCancel() 1088 | Case $ButtonOpenOutput 1089 | _HandleOpenOutput() 1090 | Case $ButtonOpenDecode 1091 | _HandleOpenDecode() 1092 | Case $GUI_EVENT_CLOSE 1093 | _HandleExit() 1094 | EndSwitch 1095 | EndIf 1096 | EndFunc 1097 | 1098 | Func _HandleFileInput() 1099 | $file_input = FileOpenDialog("Select input file", @ScriptDir, "All (*.*)") 1100 | If $file_input Then 1101 | GUICtrlSetData($InputField, $file_input) 1102 | EndIf 1103 | 1104 | _ResetProgress($ProgressBar2) 1105 | _ResetProgress($ProgressBar3) 1106 | EndFunc 1107 | 1108 | Func _HandleOpenDecode() 1109 | If FileExists($decodedOutput) Then 1110 | Run("notepad " & $decodedOutput) 1111 | Else 1112 | _DisplayWrapper("Could not find: " & $decodedOutput & @CRLF) 1113 | EndIf 1114 | EndFunc 1115 | 1116 | Func _HandleOpenOutput() 1117 | Run("explorer.exe " & $OutPutPath) 1118 | EndFunc 1119 | 1120 | Func _HandleOutput() 1121 | $folder_output = FileSelectFolder("Select output folder", @ScriptDir) 1122 | If $folder_output Then 1123 | GUICtrlSetData($OutputField, $folder_output) 1124 | EndIf 1125 | 1126 | _ResetProgress($ProgressBar2) 1127 | _ResetProgress($ProgressBar3) 1128 | EndFunc 1129 | 1130 | Func _HandleParsing() 1131 | _ResetProgress($ProgressBar2) 1132 | _ResetProgress($ProgressBar3) 1133 | If _GuiGetSettings() Then 1134 | _SetControlState($GUI_DISABLE) 1135 | _Init() 1136 | _SetControlState($GUI_ENABLE) 1137 | EndIf 1138 | EndFunc 1139 | 1140 | Func _SetControlState($ctrlState) 1141 | GUICtrlSetState($ButtonStart, $ctrlState) 1142 | GUICtrlSetState($ButtonInput, $ctrlState) 1143 | GUICtrlSetState($comboDevice, $ctrlState) 1144 | GUICtrlSetState($ButtonRefreshDevice, $ctrlState) 1145 | GUICtrlSetState($checkDetectInvalid, $ctrlState) 1146 | GUICtrlSetState($checkSkipScanAndTrim, $ctrlState) 1147 | GUICtrlSetState($ButtonOutput, $ctrlState) 1148 | GUICtrlSetState($radioFile, $ctrlState) 1149 | GUICtrlSetState($radioDevice, $ctrlState) 1150 | GUICtrlSetState($ButtonOpenDecode, $ctrlState) 1151 | EndFunc 1152 | 1153 | Func _GuiGetSettings() 1154 | 1155 | If Int(GUICtrlRead($radioFile) + GUICtrlRead($radioDevice)) <> 5 Then 1156 | _DisplayInfo("Error: You must configure file or device mode" & @CRLF) 1157 | Return 1158 | EndIf 1159 | 1160 | Select 1161 | Case Int(GUICtrlRead($radioFile)) = 1 1162 | $parsingMode = 0 1163 | _DisplayInfo("File mode" & @CRLF) 1164 | $TargetInput = $file_input 1165 | Case Int(GUICtrlRead($radioDevice)) = 1 1166 | $parsingMode = 1 1167 | _DisplayInfo("Device mode" & @CRLF) 1168 | $TargetInput = _CorrectVolume(GUICtrlRead($comboDevice)) 1169 | EndSelect 1170 | 1171 | If $TargetInput = "" Then 1172 | _DisplayInfo("Error: Input was empty." & @CRLF) 1173 | Return 1174 | EndIf 1175 | 1176 | _DisplayInfo("Input: " & $TargetInput & @CRLF) 1177 | $TargetInput = StringReplace($TargetInput, "\\.\", "") 1178 | 1179 | Local $hDrive 1180 | If StringMid($TargetInput, 1, 4) <> "\\.\" Then 1181 | $hDrive = _WinAPI_CreateFileEx("\\.\" & $TargetInput, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE), $FILE_ATTRIBUTE_NORMAL) 1182 | Else 1183 | $hDrive = _WinAPI_CreateFileEx($TargetInput, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE), $FILE_ATTRIBUTE_NORMAL) 1184 | EndIf 1185 | 1186 | If Not $hDrive Then 1187 | _DisplayInfo(_WinAPI_GetLastErrorMessage() & @CRLF) 1188 | _DisplayInfo("Error: input not valid: " & $TargetInput & @CRLF) 1189 | Return 1190 | Else 1191 | _WinAPI_CloseHandle($hDrive) 1192 | EndIf 1193 | 1194 | If Not FileExists($OutPutPath) Then 1195 | _DisplayInfo("Error: output directory not found: " & $OutPutPath & @CRLF) 1196 | Return 1197 | EndIf 1198 | 1199 | If GUICtrlRead($checkDetectInvalid) = 1 Then 1200 | $detectInvalid = 1 1201 | Else 1202 | $detectInvalid = 0 1203 | EndIf 1204 | 1205 | If GUICtrlRead($checkSkipScanAndTrim) = 1 Then 1206 | $skipScanAndTrim = 1 1207 | Else 1208 | $skipScanAndTrim = 0 1209 | EndIf 1210 | 1211 | If $parsingMode = 1 And $skipScanAndTrim = 1 Then 1212 | _DisplayInfo("Error: The settings of device mode and skipping of scan and trim will not work" & @CRLF) 1213 | Return 1214 | EndIf 1215 | 1216 | Return 1 1217 | 1218 | EndFunc 1219 | 1220 | Func _GUI_Disable_Control() 1221 | GUICtrlSetData($myctredit, "Processing started.." & @CRLF) 1222 | GUICtrlSetState($ButtonInput, $GUI_DISABLE) 1223 | GUICtrlSetState($ButtonOutput, $GUI_DISABLE) 1224 | GUICtrlSetState($ButtonStart, $GUI_DISABLE) 1225 | EndFunc 1226 | 1227 | Func _GUI_Enable_Controls() 1228 | GUICtrlSetState($ButtonInput, $GUI_ENABLE) 1229 | GUICtrlSetState($ButtonOutput, $GUI_ENABLE) 1230 | GUICtrlSetState($ButtonStart, $GUI_ENABLE) 1231 | EndFunc 1232 | 1233 | Func _RefreshDevices() 1234 | GUICtrlSetData($comboDevice, "", "") 1235 | _GetVolumes() 1236 | EndFunc 1237 | 1238 | Func _GetVolumes() 1239 | _DisplayInfo("Reading drive list... " & @CRLF) 1240 | 1241 | $asDrives = DriveGetDrive("All") 1242 | For $i=1 To $asDrives[0] 1243 | $asDrives[$i]=StringUpper($asDrives[$i]) 1244 | Next 1245 | 1246 | Const $iDrives = 16 1247 | For $i=0 To $iDrives-1 1248 | For $j=1 To $iDrives 1249 | $sDrive = $sRootSlash & "Harddisk" & $i & "Partition" & $j 1250 | If _VolumeFound($sDrive) Then 1251 | _AddVolume($asDrives, $sDrive) 1252 | EndIf 1253 | Next 1254 | Next 1255 | 1256 | For $i=0 To $iDrives-1 1257 | $sDrive = $sRootSlash & "PhysicalDrive" & $i 1258 | If _VolumeFound($sDrive) Then 1259 | _AddVolume($asDrives, $sDrive) 1260 | EndIf 1261 | Next 1262 | 1263 | $sDrives = "" 1264 | For $i = 1 to $asDrives[0] 1265 | $iDriveBusType = _WinAPI_GetDriveBusType($asDrives[$i]) 1266 | $sDriveType = DriveGetType($asDrives[$i]) 1267 | $sDriveFileSystem = DriveGetFileSystem($asDrives[$i]) 1268 | $sDriveLabel = DriveGetLabel($asDrives[$i]) 1269 | Select 1270 | Case StringInStr($asDrives[$i], "\\.\PhysicalDrive") 1271 | $num = StringReplace($asDrives[$i], "\\.\PhysicalDrive", "") 1272 | $nDriveCapacity = _WinAPI_GetDriveGeometryEx($num) 1273 | If Not @error And IsArray($nDriveCapacity) Then 1274 | $nDriveCapacity = $nDriveCapacity[5]/1024/1024 1275 | Else 1276 | $nDriveCapacity = 0 1277 | EndIf 1278 | Case StringInStr($asDrives[$i], "Harddisk") And StringInStr($asDrives[$i], "Partition") 1279 | $nDriveCapacity = _getPartitionSize($asDrives[$i]) 1280 | If Not @error And $nDriveCapacity > 0 Then 1281 | $nDriveCapacity = $nDriveCapacity/1024/1024 1282 | Else 1283 | $nDriveCapacity = 0 1284 | EndIf 1285 | Case Else 1286 | $nDriveCapacity = DriveSpaceTotal($asDrives[$i]) 1287 | EndSelect 1288 | 1289 | Switch $iDriveBusType 1290 | Case $DRIVE_BUS_TYPE_UNKNOWN 1291 | $sDriveBusType = "UNKNOWN" 1292 | Case $DRIVE_BUS_TYPE_SCSI 1293 | $sDriveBusType = "SCSI" 1294 | Case $DRIVE_BUS_TYPE_ATAPI 1295 | $sDriveBusType = "ATAPI" 1296 | Case $DRIVE_BUS_TYPE_ATA 1297 | $sDriveBusType = "ATA" 1298 | Case $DRIVE_BUS_TYPE_1394 1299 | $sDriveBusType = "1394" 1300 | Case $DRIVE_BUS_TYPE_SSA 1301 | $sDriveBusType = "SSA" 1302 | Case $DRIVE_BUS_TYPE_FIBRE 1303 | $sDriveBusType = "FIBRE" 1304 | Case $DRIVE_BUS_TYPE_USB 1305 | $sDriveBusType = "USB" 1306 | Case $DRIVE_BUS_TYPE_RAID 1307 | $sDriveBusType = "RAID" 1308 | Case $DRIVE_BUS_TYPE_ISCSI 1309 | $sDriveBusType = "ISCSI" 1310 | Case $DRIVE_BUS_TYPE_SAS 1311 | $sDriveBusType = "SAS" 1312 | Case $DRIVE_BUS_TYPE_SATA 1313 | $sDriveBusType = "SATA" 1314 | Case $DRIVE_BUS_TYPE_SD 1315 | $sDriveBusType = "SD" 1316 | Case $DRIVE_BUS_TYPE_MMC 1317 | $sDriveBusType = "MMC" 1318 | Case Else 1319 | $sDriveBusType = "" 1320 | EndSwitch 1321 | $sDrive = $asDrives[$i] & " (" & $sDriveType & ", " & $sDriveBusType & ", " & _ 1322 | $sDriveFileSystem & ", " & $sDriveLabel & ", " & Round($nDriveCapacity, 0) & " MB)" 1323 | $sDrives &= $sDrive & "|" 1324 | Next 1325 | GUICtrlSetData($comboDevice, $sDrives, StringMid($sDrive, 1, StringLen($sDrive)-1)) 1326 | _DisplayInfo(UBound($asDrives) & " drives populated into the dropdown" & @CRLF & @CRLF) 1327 | EndFunc 1328 | 1329 | Func _VolumeFound(ByRef Const $sDrive) 1330 | $hDrive = _WinAPI_CreateFileEx($sDrive, $OPEN_EXISTING, BitOR($GENERIC_READ, $GENERIC_WRITE), _ 1331 | BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE)) 1332 | If $hDrive Then 1333 | _WinAPI_CloseHandle($hDrive) 1334 | Return True 1335 | Else 1336 | Return False 1337 | EndIf 1338 | EndFunc 1339 | 1340 | Func _AddVolume(ByRef $asDrives, ByRef Const $sDrive) 1341 | ReDim $asDrives[UBound($asDrives) + 1] 1342 | $asDrives[0] += 1 1343 | $asDrives[UBound($asDrives)-1] = $sDrive 1344 | EndFunc 1345 | 1346 | Func _CorrectVolume(ByRef Const $sVolume0) 1347 | $sVolume = StringStripWS($sVolume0, BitOR($STR_STRIPLEADING, $STR_STRIPTRAILING, $STR_STRIPSPACES)) 1348 | 1349 | $iSpacePos = StringInStr($sVolume, " ") 1350 | If ($iSpacePos>1) Then 1351 | $sVolume = StringLeft($sVolume, $iSpacePos-1) 1352 | EndIf 1353 | 1354 | $iSlashPos = StringInStr($sVolume, "\") 1355 | If ($iSlashPos<>1) Then 1356 | $sVolume = $sRootSlash & $sVolume 1357 | EndIf 1358 | 1359 | Return $sVolume 1360 | EndFunc 1361 | 1362 | Func _getPartitionSize($sPartition) 1363 | Local Const $tagGUID1 = "uint Data1;ushort Data2;ushort Data3;ubyte Data4[8];" 1364 | Local Const $tagPARTITION_INFORMATION_GPT = $tagGUID1 & $tagGUID1 & "uint64 Attributes;wchar Name[36];" 1365 | 1366 | Local Const $tagPARTITION_INFORMATION_EX_GPT = _ 1367 | "int PartitionStyle;" & _ 1368 | "int64 StartingOffset;" & _ 1369 | "int64 PartitionLength;" & _ 1370 | "uint PartitionNumber;" & _ 1371 | "ubyte RewritePartition;" & _ 1372 | $tagPARTITION_INFORMATION_GPT 1373 | 1374 | Local $tPIX = DllStructCreate($tagPARTITION_INFORMATION_EX_GPT) 1375 | 1376 | Local $hDevice 1377 | Local $a_hCall, $a_iCall 1378 | 1379 | If Not StringLeft($sPartition, 4) = "\\.\" Then 1380 | $sPartition = "\\.\" & $sPartition 1381 | EndIf 1382 | 1383 | $a_hCall = DllCall("kernel32.dll", "hwnd", "CreateFile", _ 1384 | "str", $sPartition, _ 1385 | "dword", 0, _ 1386 | "dword", 0, _ 1387 | "ptr", 0, _ 1388 | "dword", 3, _; OPEN_EXISTING 1389 | "dword", 128, _; FILE_ATTRIBUTE_NORMAL 1390 | "ptr", 0) 1391 | 1392 | $hDevice = $a_hCall[0] 1393 | If Not $hDevice Then 1394 | Return SetError(1) 1395 | EndIf 1396 | 1397 | $a_iCall = DllCall("kernel32.dll", "int", "DeviceIoControl", _ 1398 | "hwnd", $hDevice, _ 1399 | "dword", 0x70048, _; IOCTL_DISK_GET_PARTITION_INFO_EX 1400 | "ptr", 0, _ 1401 | "dword", 0, _ 1402 | "ptr", DllStructGetPtr($tPIX), _ 1403 | "dword", DllStructGetSize($tPIX), _ 1404 | "dword*", 0, _ 1405 | "ptr", 0) 1406 | 1407 | If Not $a_iCall[0] Or @error Then 1408 | Return SetError(2) 1409 | EndIf 1410 | 1411 | DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hDevice) 1412 | 1413 | Return DllStructGetData($tPIX, "PartitionLength") 1414 | EndFunc 1415 | 1416 | Func _GetInputParams() 1417 | Local $TmpInputPath, $TmpOutPath, $TmpMode 1418 | For $i = 1 To $cmdline[0] 1419 | ;ConsoleWrite("Param " & $i & ": " & $cmdline[$i] & @CRLF) 1420 | If StringLeft($cmdline[$i],2) = "/?" Or StringLeft($cmdline[$i],2) = "-?" Or StringLeft($cmdline[$i],2) = "-h" Then _PrintHelp() 1421 | If StringLeft($cmdline[$i],7) = "/Input:" Then $TmpInputPath = StringMid($cmdline[$i],8) 1422 | If StringLeft($cmdline[$i],8) = "/Output:" Then $TmpOutPath = StringMid($cmdline[$i],9) 1423 | If StringLeft($cmdline[$i],6) = "/Mode:" Then $TmpMode = StringMid($cmdline[$i],7) 1424 | If $cmdline[$i] = "/StripInvalid" Then $detectInvalid = 1 1425 | If $cmdline[$i] = "/SkipScan" Then $skipScanAndTrim = 1 1426 | Next 1427 | 1428 | If StringLen($TmpOutPath) > 0 Then 1429 | 1430 | If FileExists($TmpOutPath) Then 1431 | $folder_output = $TmpOutPath 1432 | Else 1433 | ConsoleWrite("Warning: The specified Output path could not be found: " & $TmpOutPath & @CRLF) 1434 | ConsoleWrite("Relocating output to current directory: " & @ScriptDir & @CRLF) 1435 | $folder_output = @ScriptDir 1436 | EndIf 1437 | EndIf 1438 | 1439 | If StringLen($TmpMode) > 0 Then 1440 | Select 1441 | Case $TmpMode = "file" 1442 | $parsingMode = 0 1443 | Case $TmpMode = "device" 1444 | $parsingMode = 1 1445 | Case Else 1446 | ConsoleWrite("Error: Could not validate arch: " & $TmpMode & @CRLF) 1447 | Exit 1448 | EndSelect 1449 | Else 1450 | ConsoleWrite("Error: missing mode" & @CRLF) 1451 | Exit 1452 | EndIf 1453 | 1454 | If StringLen($TmpInputPath) > 0 Then 1455 | Local $hDrive 1456 | If Not StringLeft($TmpInputPath, 2) = "\\" Then 1457 | $hDrive = _WinAPI_CreateFileEx("\\.\" & $TmpInputPath, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE)) 1458 | Else 1459 | $hDrive = _WinAPI_CreateFileEx($TmpInputPath, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE)) 1460 | EndIf 1461 | 1462 | If Not $hDrive Then 1463 | ConsoleWrite("Error: input not valid: " & $TmpInputPath & @CRLF) 1464 | Exit 1465 | Else 1466 | _WinAPI_CloseHandle($hDrive) 1467 | EndIf 1468 | $TargetInput = $TmpInputPath 1469 | Else 1470 | ConsoleWrite("Error: missing input file/device" & @CRLF) 1471 | Exit 1472 | EndIf 1473 | 1474 | If $parsingMode = 1 And $skipScanAndTrim = 1 Then 1475 | ConsoleWrite("Error: The settings of device mode and skipping of scan and trim will not work" & @CRLF) 1476 | Exit 1477 | EndIf 1478 | 1479 | EndFunc 1480 | 1481 | Func _PrintHelp() 1482 | ConsoleWrite("Syntax:" & @CRLF) 1483 | ConsoleWrite("netwiredecoder.exe /Input: /Output: /Mode: /SkipScan /StripInvalid" & @CRLF) 1484 | ConsoleWrite(" Input: Full path to the file or device name to parse" & @CRLF) 1485 | ConsoleWrite(" Output: Optionally set path for the output. Defaults to program directory." & @CRLF) 1486 | ConsoleWrite(" Mode: The mode of operation. Must be file or device." & @CRLF) 1487 | ConsoleWrite(" SkipScan: A switch to deactivate scan and trim. Use with healthy logs. See notes." & @CRLF) 1488 | ConsoleWrite(" StripInvalid: A switch to attempt skipping invalid data. See notes." & @CRLF & @CRLF) 1489 | ConsoleWrite("Examples:" & @CRLF) 1490 | ConsoleWrite("netwiredecoder.exe /Input:D:\temp\02-05-2017 /Mode:file /SkipScan /Output:D:\nwout" & @CRLF) 1491 | ConsoleWrite("netwiredecoder.exe /Input:D:\temp\merged_output.bin /Mode:file /SkipScan" & @CRLF) 1492 | ConsoleWrite("netwiredecoder.exe /Input:F: /Mode:device /StripInvalid" & @CRLF) 1493 | ConsoleWrite("netwiredecoder.exe /Input:Harddisk3Partition1 /Mode:device /StripInvalid" & @CRLF) 1494 | ConsoleWrite("netwiredecoder.exe /Input:PhysicalDrive2 /Mode:device /StripInvalid" & @CRLF) 1495 | Exit 1496 | EndFunc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Intro ## 2 | 3 | Arsenal's NetWire Log Decoder carves and parses (a/k/a scans, filters, and decodes) NetWire log data from files or devices. NetWire is a popular multi-platform remote access trojan (RAT) system. NetWire has surveillance functionality which stores keystrokes and other information from victims in log files known as "NetWire logs." Arsenal has found valuable NetWire log data in many places within disk images other than intact NetWire logs - for example, in properly processed Windows hibernation data, crash dumps, swap files, file slack, and unallocated space. Since NetWire log data has no file format or defined data structure to parse, NetWire Log Decoder searches for typical byte sequences (signatures). In some situations it is difficult to separate NetWire log data from other data (for example, when dealing with unallocated space), so NetWire Log Decoder includes logic which fine tunes both carving and parsing in problematic situations. 4 | 5 | ## NetWire versions tested: ## 6 | 7 | NetWire versions 1.6 and 1.7 on Windows and Linux 8 | 9 | ## Requirements: ## 10 | 11 | NetWire Log Decoder uses PowerShell. You can set the appropriate policy (allowing all scripts whether they are signed or not to run) from an administrative PowerShell prompt by entering "Set-ExecutionPolicy Unrestricted". Arsenal recommends only temporarily enabling the "Unrestricted" execution policy, replacing your existing policy when done running NetWire Log Decoder. 12 | 13 | ## Usage: ## 14 | 15 | Input = Either a file or a device. For files, just launch the browse dialog and locate the target file. For devices, a dropdown (populated during startup and can be refreshed at any point later) is provided. When working in command line mode the devices must be specified in a certain format such as; 16 | 17 | * F: 18 | * PhysicalDrive3 19 | * Harddisk3Partition1 20 | 21 | SkipScan = Skips the entire process of carving (scanning and filtering). Useful when input is an intact NetWire log without any invalid data. 22 | StripInvalid = Attempts to detect invalid data by unresolved keystrokes or other suspicious byte sequences such as 00 or FF. When invalid data is detected, NetWire Log Decoder will jump to the next sector and continue. Typically this setting is useful when you can expect to encounter invalid data, for example when running NetWire Log Decoder against a complete device, volume, or unallocated space. 23 | 24 | ### Examples: ### 25 | 26 | ``` 27 | netwiredecoder64.exe /Input:D:\temp\02-05-2017 /Mode:file /SkipScan /Output:D:\nwout 28 | netwiredecoder64.exe /Input:D:\temp\merged_output.bin /Mode:file /SkipScan 29 | netwiredecoder64.exe /Input:F: /Mode:device /StripInvalid 30 | netwiredecoder64.exe /Input:Harddisk3Partition1 /Mode:device /StripInvalid 31 | netwiredecoder64.exe /Input:PhysicalDrive2 /Mode:device /StripInvalid 32 | ``` 33 | 34 | ## Output files: ## 35 | * netwire_stage1_scanned.bin = Stage 1 output (Sectors with one or more signature hits) 36 | * netwire_stage2_filtered.bin = Stage 2 output (Sectors with five or more signature hits) 37 | * netwire_stage3_decoded.txt = (Final human-friendly output) 38 | * netwire.csv = An ordered listing of unique offsets (sector aligned) used as extract basis 39 | * netwire.log = For verbose logging 40 | * netwire.txt = An unsorted listing of all signature matches 41 | 42 | 43 | ## Notes: ## 44 | 45 | **1** 46 | All keystrokes captured in NetWire log data belong to a given window, but identifying the start and end of window information in raw output can be challenging. For that reason, NetWire Log Decoder places markers for the start and end of window information into the output. For example; 47 | ``` 48 | [new 1 - Notepad++ [Administrator]] - [04/09/2020 15:46:33] 49 | ``` 50 | becomes; 51 | ``` 52 | [new 1 - Notepad++ [Administrator]] - [04/09/2020 15:46:33] 53 | ``` 54 | These start and end markers make identification of unique entries in the output easier and helps spot corrupt window information. 55 | 56 | **2** 57 | Carving NetWire log data from MFT records can be challenging. During NetWire operation, log data is first written to each new log file's MFT record... at some point becoming too big to fit within the MFT record and turning into non-resident data. When this happens, some of the old log data within the MFT record can be found in MFT record slack. However, there might be some invalid log data at the outer boundary of the MFT record slack. When using NetWire Log Decoder to carve a large volume of input, there may be many examples of this situation. One proposal to handle this situation is; 58 | a) on the NetWire Log Decoder filtered output ("netwire_stage2_filtered.bin") run MftCarver to extract MFT records 59 | b) use Mft2Csv to extract resident data and record slack (which will also remove most of the invalid data that originates from the MFT record itself). This Mft2Csv feature was built specifically for this use case. 60 | c) run merge-resident-extract (available in the same GitHub repository as NetWire Log Decoder) on the folder with the extract from step b above 61 | d) finally re-run the decoder on the merged output file, this time with options /SkipScan active and /StripInvalid deactivated 62 | 63 | **3** 64 | In the first stage of processing, NetWire Log Decoder scans and extracts any sector with at least 1 signature hit. In the second stage, NetWire Log Decoder uses validation logic which considers any sector with less than 5 signature hits to be a false positive. In our testing this balance between the first and second stages of processing has worked quite well, but please be aware there will be edge cases where valid NetWire log data is missed in the final (stage 3) output. 65 | 66 | **4** 67 | As volumes originating from a Linux system are not possible to mount natively on Windows, we have at least 2 options. One option is to run NetWire Log Decoder against an entire disk image and another is to mount the disk image using Arsenal Image Mounter and run NetWire Log Decoder against a particular partition. 68 | 69 | ## Contributions: ## 70 | 71 | Contributions and improvements to the code are welcomed. 72 | 73 | ## License: ## 74 | 75 | Distributed under the MIT License. See License.md for details. 76 | 77 | ## More Information: ## 78 | 79 | To learn more about Arsenal’s digital forensics software and training, please visit https://ArsenalRecon.com and follow us on Twitter @ArsenalRecon (https://twitter.com/ArsenalRecon). 80 | 81 | To learn more about Arsenal’s digital forensics consulting services, please visit https://ArsenalExperts.com and follow us on Twitter @ArsenalArmed (https://twitter.com/ArsenalArmed). -------------------------------------------------------------------------------- /ReadFromDevice.ps1: -------------------------------------------------------------------------------- 1 | $source = @" 2 | using System; 3 | using System.Runtime.InteropServices; 4 | using System.IO; 5 | using Microsoft.Win32.SafeHandles; 6 | 7 | namespace ReadFromDevice 8 | { 9 | public class DeviceStream : Stream, IDisposable 10 | { 11 | public const short FILE_ATTRIBUTE_NORMAL = 0x80; 12 | public const short INVALID_HANDLE_VALUE = -1; 13 | public const uint GENERIC_READ = 0x80000000; 14 | public const uint GENERIC_WRITE = 0x40000000; 15 | public const uint CREATE_NEW = 1; 16 | public const uint CREATE_ALWAYS = 2; 17 | public const uint OPEN_EXISTING = 3; 18 | public const uint FILE_SHARE_ALL = 0x00000007; 19 | 20 | // Use interop to call the CreateFile function. 21 | // For more information about CreateFile, 22 | // see the unmanaged MSDN reference library. 23 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 24 | private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, 25 | uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, 26 | uint dwFlagsAndAttributes, IntPtr hTemplateFile); 27 | 28 | [DllImport("kernel32.dll", SetLastError = true)] 29 | private static extern bool ReadFile( 30 | IntPtr hFile, // handle to file 31 | byte[] lpBuffer, // data buffer 32 | int nNumberOfBytesToRead, // number of bytes to read 33 | ref int lpNumberOfBytesRead, // number of bytes read 34 | IntPtr lpOverlapped 35 | // 36 | // ref OVERLAPPED lpOverlapped // overlapped buffer 37 | ); 38 | 39 | private SafeFileHandle handleValue = null; 40 | private FileStream _fs = null; 41 | 42 | public DeviceStream(string device) 43 | { 44 | Load(device); 45 | } 46 | 47 | private void Load(string Path) 48 | { 49 | if (string.IsNullOrEmpty(Path)) 50 | { 51 | throw new ArgumentNullException("Path"); 52 | } 53 | 54 | // Try to open the file. 55 | //IntPtr ptr = CreateFile(Path, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 56 | IntPtr ptr = CreateFile(Path, GENERIC_READ, FILE_SHARE_ALL, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 57 | 58 | handleValue = new SafeFileHandle(ptr, true); 59 | _fs = new FileStream(handleValue, FileAccess.Read); 60 | 61 | // If the handle is invalid, 62 | // get the last Win32 error 63 | // and throw a Win32Exception. 64 | if (handleValue.IsInvalid) 65 | { 66 | Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); 67 | } 68 | } 69 | 70 | public override bool CanRead 71 | { 72 | get { return true; } 73 | } 74 | 75 | public override bool CanSeek 76 | { 77 | get { return false; } 78 | } 79 | 80 | public override bool CanWrite 81 | { 82 | get { return false; } 83 | } 84 | 85 | public override void Flush() 86 | { 87 | return; 88 | } 89 | 90 | public override long Length 91 | { 92 | get { return -1; } 93 | } 94 | 95 | public override long Position 96 | { 97 | get 98 | { 99 | throw new NotImplementedException(); 100 | } 101 | set 102 | { 103 | throw new NotImplementedException(); 104 | } 105 | } 106 | /// 107 | /// 108 | /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and 109 | /// (offset + count - 1) replaced by the bytes read from the current source. 110 | /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. 111 | /// The maximum number of bytes to be read from the current stream. 112 | /// 113 | public override int Read(byte[] buffer, int offset, int count) 114 | { 115 | int BytesRead =0; 116 | var BufBytes = new byte[count]; 117 | if (!ReadFile(handleValue.DangerousGetHandle(), BufBytes, count, ref BytesRead, IntPtr.Zero)) 118 | { 119 | Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); 120 | } 121 | for (int i = 0; i < BytesRead; i++) 122 | { 123 | buffer[offset + i] = BufBytes[i]; 124 | } 125 | return BytesRead; 126 | } 127 | public override int ReadByte() 128 | { 129 | int BytesRead = 0; 130 | var lpBuffer = new byte[1]; 131 | if (!ReadFile( 132 | handleValue.DangerousGetHandle(), // handle to file 133 | lpBuffer, // data buffer 134 | 1, // number of bytes to read 135 | ref BytesRead, // number of bytes read 136 | IntPtr.Zero 137 | )) 138 | { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); ;} 139 | return lpBuffer[0]; 140 | } 141 | 142 | public override long Seek(long offset, SeekOrigin origin) 143 | { 144 | throw new NotImplementedException(); 145 | } 146 | 147 | public override void SetLength(long value) 148 | { 149 | throw new NotImplementedException(); 150 | } 151 | 152 | public override void Write(byte[] buffer, int offset, int count) 153 | { 154 | throw new NotImplementedException(); 155 | } 156 | 157 | public override void Close() 158 | { 159 | handleValue.Close(); 160 | handleValue.Dispose(); 161 | handleValue = null; 162 | base.Close(); 163 | } 164 | private bool disposed = false; 165 | 166 | new void Dispose() 167 | { 168 | Dispose(true); 169 | base.Dispose(); 170 | GC.SuppressFinalize(this); 171 | } 172 | 173 | private new void Dispose(bool disposing) 174 | { 175 | // Check to see if Dispose has already been called. 176 | if (!this.disposed) 177 | { 178 | if (disposing) 179 | { 180 | if (handleValue != null) 181 | { 182 | _fs.Dispose(); 183 | handleValue.Close(); 184 | handleValue.Dispose(); 185 | handleValue = null; 186 | } 187 | } 188 | // Note disposing has been done. 189 | disposed = true; 190 | 191 | } 192 | } 193 | 194 | } 195 | } 196 | "@ 197 | 198 | Add-Type -TypeDefinition $source -Language CSharp; 199 | 200 | -------------------------------------------------------------------------------- /extras/merge-resident-extract.au3: -------------------------------------------------------------------------------- 1 | #Region ;**** Directives created by AutoIt3Wrapper_GUI **** 2 | #AutoIt3Wrapper_Icon=C:\Program Files (x86)\AutoIt3\Icons\au3.ico 3 | #AutoIt3Wrapper_UseX64=n 4 | #AutoIt3Wrapper_Change2CUI=y 5 | #AutoIt3Wrapper_Res_Fileversion=1.0.0.0 6 | #AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 5 7 | #AutoIt3Wrapper_Run_Au3Stripper=y 8 | #Au3Stripper_Parameters=/sf /sv /rm 9 | #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** 10 | 11 | #Include 12 | #include 13 | #include 14 | 15 | $startPath = FileSelectFolder("Select input folder", @ScriptDir) 16 | If @error Then 17 | Exit 18 | EndIf 19 | 20 | ConsoleWrite("Listing files.." & @CRLF) 21 | Local $aDirList = _FileListToArray($startPath, "*", $FLTA_FILES, False) 22 | 23 | Local $TimestampStart = @YEAR & "-" & @MON & "-" & @MDAY & "_" & @HOUR & "-" & @MIN & "-" & @SEC 24 | Local $sOutFile = @ScriptDir & "\merged_output_" & $TimestampStart & ".bin" 25 | 26 | Local $hOutFile = _WinAPI_CreateFile("\\.\" & $sOutFile, 3, 6, 7) 27 | If Not $hOutFile Then 28 | ConsoleWrite("Error in CreateFile for " & $sOutFile & " " & _WinAPI_GetLastErrorMessage() & @CRLF) 29 | Exit 30 | EndIf 31 | 32 | For $i = 1 To $aDirList[0] 33 | $targetFile = $startPath & "\" & $aDirList[$i] 34 | ConsoleWrite("Targeting: " & $targetFile & @CRLF) 35 | _ParseFileAndMerge($targetFile, $hOutFile) 36 | Next 37 | 38 | _WinAPI_CloseHandle($hOutFile) 39 | 40 | Func _ParseFileAndMerge($sInputFile, $hOutput) 41 | Local $hFile = _WinAPI_CreateFileEx("\\.\" & $sInputFile, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ,$FILE_SHARE_WRITE)) 42 | If Not $hFile Then 43 | ConsoleWrite("Error in CreateFile: " & _WinAPI_GetLastErrorMessage() & @CRLF) 44 | _WinAPI_CloseHandle($hFile) 45 | Exit 46 | EndIf 47 | Local $nBytes 48 | Local $FileSize = _WinAPI_GetFileSizeEx($hFile) 49 | _WinAPI_SetFilePointerEx($hFile, 0, $FILE_BEGIN) 50 | Local $tBuffer = DllStructCreate("byte[" & $FileSize & "]") 51 | If Not _WinAPI_ReadFile($hFile, DllStructGetPtr($tBuffer), $FileSize, $nBytes) Then 52 | ConsoleWrite("Error in ReadFile." & @CRLF) 53 | _WinAPI_CloseHandle($hFile) 54 | Exit 55 | EndIf 56 | $data = DllStructGetData($tBuffer,1) 57 | $data = StringTrimLeft($data, 2) 58 | 59 | While 1 60 | If StringRight($data, 16) = "FFFFFFFF82794711" Then 61 | $data = StringTrimRight($data, 16) 62 | ContinueLoop 63 | EndIf 64 | If StringRight($data, 8) = "82794711" Then 65 | $data = StringTrimRight($data, 8) 66 | ContinueLoop 67 | EndIf 68 | If StringRight($data, 8) = "00000000" Then 69 | $data = StringTrimRight($data, 8) 70 | ContinueLoop 71 | EndIf 72 | If StringRight($data, 4) = "4711" Then 73 | $data = StringTrimRight($data, 4) 74 | ContinueLoop 75 | EndIf 76 | ExitLoop 77 | WEnd 78 | 79 | Local $binlength = StringLen($data)/2 80 | Local $buff = DllStructCreate("byte[" & $binlength & "]") 81 | DllStructSetData($buff, 1, "0x" & $data) 82 | 83 | _WinAPI_WriteFile($hOutput, DllStructGetPtr($buff), $binlength, $nBytes) 84 | _WinAPI_CloseHandle($hFile) 85 | 86 | ConsoleWrite("Bytes removed: " & $FileSize - $binlength & @CRLF) 87 | ConsoleWrite("Bytes written: " & $binlength & @CRLF) 88 | EndFunc -------------------------------------------------------------------------------- /scan-launch-netwire.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$inputFile = "", 3 | [string]$outputFile = "" 4 | ) 5 | 6 | If([string]::IsNullOrEmpty($inputFile)){ 7 | Throw "Error: Missing inputFile" 8 | } 9 | If([string]::IsNullOrEmpty($outputFile)){ 10 | Throw "Error: Missing outputFile" 11 | } 12 | 13 | $hexPatterns = @( 14 | "\xB4\xBB\xB4\xBB", # WINDOW 15 | "\xE4\x00\x13\x13\x16\x0E\xE1", # [Arrow 16 | "\xEA\x02\x0D\x13\x15\xDA", # [Ctrl+ 17 | "\xEA\xFD\x1C\x15\x1C\x0D\x1C\xE4", # [Delete] 18 | "\xEA\x03\x20\x22\x1A\x12\x11\x20\x22\x1C\xE4", # [Backspace] 19 | "\xEA\xFC\x17\x0D\x1C\x13\xE4", # [Enter] 20 | "\xEA.{2}\xD6.{2}\xD6.{4}\xE1.{2}\xCB.{2}\xCB.{2}\xE4", # [04/09/2020 11:53:01] 21 | "\xE4\xE1\xD4\xE1\xEA", # ] - [ 22 | "\xE1\xF5\x16\x22\x1A\xE4" # Lock] 23 | ); 24 | 25 | 26 | $ExecutionTime = [DateTime]::UtcNow.ToString("o"); 27 | $ExecutionTime = $ExecutionTime.Replace(":", ".") 28 | 29 | function Resolve-Path-Internal() 30 | { 31 | $inv = (Get-Variable MyInvocation -Scope 1).Value 32 | $Path1 = Split-Path $inv.scriptname 33 | Return $Path1 34 | } 35 | 36 | function Get-ScriptDirectory { 37 | [String]$TmpPath = ""; 38 | if ($psise) {$TmpPath = Split-Path $psise.CurrentFile.FullPath} 39 | else {$TmpPath = Resolve-Path-Internal} 40 | Return $TmpPath; 41 | } 42 | 43 | $currentdir = Get-ScriptDirectory 44 | 45 | function Log([string]$text) 46 | { 47 | $date = Get-Date 48 | "$date $text" | Out-File -Append -Force -FilePath:$outputFile -Encoding:utf8; 49 | } 50 | 51 | $stopwatch = [system.diagnostics.stopwatch]::StartNew(); 52 | 53 | $ScanScript = $currentdir + "\sigscan-core.ps1"; 54 | 55 | Log "Started: $(Get-Date)"; 56 | Log "Script: $ScanScript"; 57 | Log "Input: $inputFile"; 58 | 59 | $counter = 0 60 | 61 | ForEach($pattern in $hexPatterns){ 62 | 63 | Log "Regex: $pattern"; 64 | $Matches = & $ScanScript -filepath $inputFile -hex $pattern; 65 | $Matches | ForEach-Object {Log $_; Log "0x$(([uint64]$_).ToString('X16'))"} 66 | $counter += $Matches.Count 67 | 68 | } 69 | Log "Matches: $counter" 70 | Log "Finished parsing in $($stopwatch.Elapsed)"; 71 | 72 | $hexPatterns = $null; 73 | 74 | Return $counter -------------------------------------------------------------------------------- /sigscan-core.ps1: -------------------------------------------------------------------------------- 1 | param([string]$hex, 2 | [string]$filepath 3 | ) 4 | 5 | If([string]::IsNullOrEmpty($filepath)){ 6 | Write-Host "Error: No filepath supplied" 7 | Exit 8 | } 9 | 10 | If([string]::IsNullOrEmpty($hex)){ 11 | Write-Host "Error: No hex supplied" 12 | Exit 13 | } 14 | 15 | $TargetDevice = $filepath 16 | 17 | function Resolve-Path-Internal() 18 | { 19 | $inv = (Get-Variable MyInvocation -Scope 1).Value 20 | $Path1 = Split-Path $inv.scriptname 21 | Return $Path1 22 | } 23 | 24 | function Get-ScriptDirectory { 25 | [String]$TmpPath = ""; 26 | if ($psise) {$TmpPath = Split-Path $psise.CurrentFile.FullPath} 27 | else {$TmpPath = Resolve-Path-Internal} 28 | Return $TmpPath; 29 | } 30 | 31 | $currentdir = Get-ScriptDirectory 32 | 33 | $deviceScript = $currentdir + "\ReadFromDevice.ps1"; 34 | & $deviceScript 35 | 36 | #Require -Version 5 37 | Invoke-Expression -Command "using namespace ReadFromDevice"; 38 | 39 | 40 | If($TargetDevice.Contains("PhysicalDrive")){ 41 | # physical device 42 | $DiskNumber = $TargetDevice.Substring(13) 43 | $disk = Get-PhysicalDisk | Where-Object {$_.DeviceId -eq $DiskNumber} 44 | $file_size = $disk.AllocatedSize 45 | }ElseIf($TargetDevice.Contains("Harddisk") -and $TargetDevice.Contains("Partition")){ 46 | # partition not mounted 47 | $pattern1 = 'Harddisk(.*?)Partition' 48 | $harddisknum = [regex]::Match($TargetDevice,$pattern1).Groups[1].Value 49 | $pattern2 = '(?<=.+Partition).*' 50 | $partitionnum = [regex]::Match($TargetDevice,$pattern2).Groups[0].Value 51 | $partition = Get-Partition | where-object {$_.DiskNumber -eq $harddisknum -and $_.PartitionNumber -eq $partitionnum} 52 | $file_size = $partition.Size 53 | }ElseIf(($TargetDevice.Length -eq 2 -OR $TargetDevice.Length -eq 6) -AND $TargetDevice -match ":"){ 54 | # mounted volume 55 | $disk = Get-WmiObject Win32_LogicalDisk -ComputerName "localhost" -Filter "DeviceID='$TargetDevice'" | Select-Object Size,FreeSpace 56 | $file_size = $disk.Size 57 | }Else{ 58 | # file 59 | If(!(Test-Path $TargetDevice)){Throw "Could not find file: $TargetDevice"} 60 | $file_size = (Get-Item $TargetDevice).length 61 | } 62 | #write-host "file_size: $file_size" 63 | 64 | # do not add if unc path 65 | If($TargetDevice.Substring(0, 2) -ne "\\"){ 66 | $TargetDevice = "\\.\" + $TargetDevice 67 | } 68 | 69 | $Stream = [DeviceStream]::new("$TargetDevice"); 70 | If($Stream -eq $null){ 71 | Write-Host "Error: Stream failure" 72 | Exit 73 | } 74 | $Encoding = [Text.Encoding]::GetEncoding(28591); 75 | $BinaryReader = New-Object System.IO.BinaryReader -ArgumentList $Stream, $Encoding 76 | 77 | $MyRegex = [Regex]::New($hex) 78 | 79 | $chunk_size = 67108864 #4096, 65536, 262144, 1048576, 4194304, 16777216, 67108864 80 | 81 | $step = 0 82 | $offset = 0 83 | 84 | $Matches = foreach($chunk in (0..[math]::Ceiling($file_size/$chunk_size))){ 85 | 86 | # reset $data 87 | $BinaryText = $null 88 | 89 | if($offset -ge $file_size){break} 90 | if($offset + $chunk_size -gt $file_size){ 91 | $chunk_size = $file_size - $offset 92 | } 93 | 94 | # Initialize the buffer to be save size as the data block 95 | $buffer = [System.Byte[]]::new($chunk_size) 96 | 97 | # Read each offset to the buffer 98 | [Void]$BinaryReader.Read($buffer,0,$chunk_size) 99 | 100 | # Convert the buffer data to byte 101 | $BinaryText = [System.Text.Encoding]::GetEncoding(28591).getstring($buffer) 102 | if($step -gt 0){ 103 | if(!!$MyRegex.Matches($BinaryText).success){foreach($index in $MyRegex.Matches($BinaryText).index){$index + $step*$chunk_size}} 104 | }else{ 105 | if(!!$MyRegex.Matches($BinaryText).success){$MyRegex.Matches($BinaryText).index} 106 | } 107 | $step=$step+1 108 | $offset += $chunk_size 109 | } 110 | 111 | $Stream.Dispose() 112 | 113 | $MatchCount = $Matches.Count 114 | 115 | If ($MatchCount -eq 0){ 116 | #Write-Output "Error: No hits." 117 | Exit 118 | } 119 | 120 | $Matches|ForEach-Object {$_} 121 | --------------------------------------------------------------------------------