大量にあるパスワード付きZIPをフォルダ構成を維持しつつまとめてクラックして解凍するPowerShellスクリプト

## 大量にあるパスワード付きZIPをフォルダ構成を維持しつつまとめてクラックして解凍？
こういうフォルダ構成だとする

```text:階層例
フォルダA/
    ├フォルダB/
    │    └フォルダC/
    │         ├パス有ZIP_1.zip (パスワード分からん)
    │         └パス有ZIP_2.zip (パスワード分からん)
    └フォルダD/
         ├パス有ZIP_3.zip (パスワードかけたっけ？)
         └パス無ZIP_4.zip (パスワードかけたっけ？)

```

「パス有/無ZIP_#.zip」内のすべての圧縮ファイルを「パス有/無ZIP_#」に解凍
このとき、**個々のZIPのパスワードは総当たりとか辞書攻撃で突破**する！

```text:解凍後
フォルダ1/
    ├フォルダB/
    │    └フォルダC/
    │         ├パス有ZIP_1.zip
    │         ├パス有ZIP_1/
    │         │    ├ ・・・ ←「パス付ZIP_1.zip」の中身
    │         │    :
    │         └パス有ZIP_2.zip
    │         └パス有ZIP_2/
    │              ├ ・・・
    │              :
    └フォルダD/
         ├パス有ZIP_3.zip
         ├パス有ZIP_3/
         │    ├ ・・・
         │    :
         ├パス無ZIP_4.zip
         └パス無ZIP_4/
              ├ ・・・
              :
```

## 必須環境
1. PowerShell 6 以上 (7.2で確認)
1. [7Zip4Powershell](https://github.com/thoemmi/7Zip4Powershell)　([PowerShellギャラリー](https://www.powershellgallery.com/packages/7Zip4Powershell/))
1. [John The Ripper](https://github.com/openwall/john-packages) （リリースに最新Buildがある。公式サイトは古い。）

## スクリプト

```powershell
#UTF-8 BOM
#親フォルダ以下のZipを探し、パスワード付きだったらJohn The Ripperでクラックし、Zipのある場所に解凍する。

$path = "E:\Parent\Folder\of\many\Zip"

$Zip2JohnPath = "C:\winX64_1_JtR\JtR\run\zip2john.exe"
$JtRPath = "C:\winX64_1_JtR\JtR\run\john.exe"
$HashPath = "E:\temporary\hash.txt"

$formatted_date = (Get-Date).ToString("yyyyMMddHHmmss")
$LogTextName = ".\Crack&ExPWZipJtR"+$formatted_date+".csv"

#親フォルダ以下のZIPスキャン
$archives = Get-ChildItem -LiteralPath $path -Include *.zip -Recurse
foreach($a in $archives){
	#前処理
	#フォルダパス作成
	$FolderPath = Join-Path $a.Directoryname $a.BaseName
	Write-Host ($a.FullName + "`t is crack target")
	Write-Host ($FolderPath + "`t is extract destination")
	
	#Run Zip2John
	$cmdArgList = @($a.FullName)
	& $Zip2JohnPath $cmdArgList | Out-File -LiteralPath $HashPath -Encoding utf8NoBOM
	
	If((Get-Item -LiteralPath $HashPath).length -ne 0){ #パスワード付きZipではない場合クラックしない
		#Run John クラック実行
		$cmdArgList = @( $HashPath,"--incremental=Alpha", "--max-length=3" )
		& $JtRPath $cmdArgList

		#Run John クラック後パス取得
		$cmdArgList = @( "--show", $HashPath )
		$Password = & $JtRPath $cmdArgList

		#下記の形式で出力されるのでSplitしてパスワードだけ取得
		#file.zip:password::file.zip:contents1.txt, content2.png:C:\Original\Path\to\Archive.zip
		$Password = $Password.Split(":")[1]
		Write-Host ($a.FullName + "`t password is`t" + $Password)
	}else{
		$Password = ""
		Write-Host ($a.FullName + "`t password is not set`t" + $Password)
	}
	
	#Zip解凍（パスワード無しの場合、どんなパスワード引数をつけても解凍可能）
	Expand-7Zip -ArchiveFileName $a.FullName -TargetPath $FolderPath -Password $Password

    #やっつけなログ出力部分
	If(Test-Path -LiteralPath $FolderPath){ #Pathチェック
		$ExtractCheck1 = (Get-ChildItem -LiteralPath $FolderPath -Recurse -Force | Measure-Object -Sum Length).Sum -eq $Null
		$ExtractCheck2 = (Get-ChildItem -LiteralPath $FolderPath -Recurse -Force | Measure-Object -Sum Length).Sum -eq 0
		If($ExtractCheck1 -or $ExtractCheck2){ #解凍したフォルダのサイズが0なら失敗
			$Message = ($a.FullName + "`t failed cracking`t" + $FolderPath + "`t with password`t" + $Password)
		}else{
			$Message = ($a.FullName + "`t successed cracking`t" + $FolderPath + "`t with password`t" + $Password)
		}
	}else{
		$Message = ($a.FullName + "`t failed folder creation`t" + $FolderPath + "`t with password`t" + $Password)
	}
	Write-Host $Message
	Write-Output $Message | Out-File -LiteralPath $LogTextName -Append

}
```

### 使い方
1. UTF-8(BOM有り)でどこかに保存。
1. パス変数を書き換え。
    | パス変数 | 説明 |
    |:-:|:-:|
    |`$path`|ZIPが沢山入っているフォルダの親フォルダ|
    |`$Zip2JohnPath`|zip2john.exeへのパス|
    |`$JtRPath`|john.exeへのパス|
    |`$HashPath`|ZIPのハッシュを一時保管する場所|

1. 77行目を利用ケースに合わせて変更。John the Ripperの使い方は他記事へ。
`$cmdArgList = @( $HashPath,"--incremental=Alpha", "--max-length=3" )`
1. お好きなTerminalから実行

### 特徴
 - ファイルパスUnicode対応
 - パス有りJohn the Ripperでクラック
 - パス無しZIPも解凍してくれる
 - やっつけなログを出力

## 最後に
CPU（i9-13900K）のみで実行した場合、ZIP 200個（内アルファベット3文字のパスワード付きZIP 120個）を全解凍するのに5分程度かかった。
John the ripperが対応している形式ならZIP以外でも少し書き換えれば使えると思います。