WinSCP 預設將用戶密碼加密存儲,本文彙總其存儲位置、加密邏輯及多語言(Go、Java、C#)解密實現代碼,供安全研究與授權審計參考。
HKEY_USERS\\Software\Martin Prikryl\WinSCP 2\Sessions\
C:\Users\\AppData\Local\VirtualStore\Program Files (x86)\WinSCP\WinSCP.ini (64 位系統)C:\Program Files (x86)\WinSCP\WinSCP.ini (64 位系統)C:\Users\\AppData\Local\VirtualStore\Program Files\WinSCP\WinSCP.ini (32 位系統)C:\Program Files\WinSCP\WinSCP.ini (32 位系統)winscppwd.exe 直接使用。const (
PW_MAGIC = 0xA3
PW_FLAG = 0xFF
)
func decrypt(host, username, password string) string {
key := username + host // 密鑰 = 用戶名 + 主機名
passbytes := hexDecode(password) // 16 進制字符串轉字節切片
flag, passbytes := decNext(passbytes)
var length byte
if flag == PW_FLAG {
_, passbytes = decNext(passbytes) // 跳過 dummy
length, passbytes = decNext(passbytes)
} else {
length = flag
}
toBeDeleted, passbytes := decNext(passbytes)
passbytes = passbytes[toBeDeleted*2:] // 跳過填充
clearpass := ""
for i := byte(0); i < length; i++ {
val, passbytes := decNext(passbytes)
clearpass += string(val)
}
if flag == PW_FLAG {
clearpass = clearpass[len(key):] // 去除前綴 key
}
return clearpass
}
func decNext(passbytes []byte) (byte, []byte) {
if len(passbytes) < 2 { return 0, passbytes }
a, b := passbytes[0], passbytes[1]
passbytes = passbytes[2:]
return ^(((a >> 4) + b) ^ PW_MAGIC) & 0xff, passbytes
}
Java 版邏輯完全一致,僅語法差異。核心點:
decNext 利用魔數 0xA3 與位運算還原明文字節。
4. Flag=0xFF 表示新版格式,含長度字節與填充;否則舊版格式。
5. 解出明文後,若為新版需去除前綴 Key。