cookies.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. const fs = require('fs')
  2. const log = require('./log')
  3. // Format from here:
  4. // http://www.securitylearn.net/2012/10/27/cookies-binarycookies-reader/
  5. // January 1st, 2001, 00:00:00 UTC
  6. const APPLE_2001_EPOCH = 978307200
  7. function parseCookie (cookieBuff) {
  8. // Read a null-terminated string from the buffer.
  9. function readNullTerminatedString (fromIndex) {
  10. let string = ''
  11. for (var i = fromIndex; cookieBuff.readInt8(i) !== 0 && i < size; i++) {
  12. string += String.fromCharCode(cookieBuff.readInt8(i))
  13. }
  14. return string
  15. }
  16. let size = cookieBuff.readInt32LE(0)
  17. // 4 unknown bytes
  18. let flagInt = cookieBuff.readInt32LE(8)
  19. // 4 unknown bytes
  20. let urlOffset = cookieBuff.readInt32LE(16)
  21. let nameOffset = cookieBuff.readInt32LE(20)
  22. let pathOffset = cookieBuff.readInt32LE(24)
  23. let valueOffset = cookieBuff.readInt32LE(28)
  24. // END OF COOKIE 8 bytes = 0x0
  25. let expirationEpoch = cookieBuff.readDoubleLE(40) + APPLE_2001_EPOCH
  26. let creationEpoch = cookieBuff.readDoubleLE(48) + APPLE_2001_EPOCH
  27. // Dictionary of flag strings.
  28. let flagDict = {
  29. 0: 'none',
  30. 1: 'secure',
  31. 4: 'httpOnly',
  32. 5: 'secure,httpOnly'
  33. }
  34. let flags = flagDict[flagInt]
  35. let url = readNullTerminatedString(urlOffset)
  36. let name = readNullTerminatedString(nameOffset)
  37. let path = readNullTerminatedString(pathOffset)
  38. let value = readNullTerminatedString(valueOffset)
  39. let expiration = new Date(expirationEpoch * 1000)
  40. let creation = new Date(creationEpoch * 1000)
  41. return { url, name, value, path, flags, creation, expiration }
  42. }
  43. function parsePage (page) {
  44. function checkPageHeader (page) {
  45. return page.readInt32BE(0) === 0x00000100
  46. }
  47. // Header check fails page parse. return nothing.
  48. if (!checkPageHeader(page)) {
  49. return []
  50. }
  51. // Get the count of cookies on this page.
  52. const cookieCount = page.readInt32LE(4)
  53. // Store the cookies.
  54. let cookies = []
  55. for (let i = 0; i < cookieCount; i++) {
  56. // Read offset and size.
  57. const cookieOffset = page.readInt32LE(8 + i * 4)
  58. const cookieSize = page.readInt32LE(cookieOffset)
  59. // Slice buff
  60. const cookieBuff = page.slice(cookieOffset, cookieOffset + cookieSize)
  61. // Parse cookie
  62. let cookie = parseCookie(cookieBuff)
  63. // Add the cookie if parsing succeded.
  64. if (cookie) {
  65. cookies.push(cookie)
  66. }
  67. }
  68. return cookies
  69. }
  70. function parseBase (buff) {
  71. function checkHeader (buff) {
  72. return buff.readInt8(0) === 'c'.charCodeAt(0) &&
  73. buff.readInt8(1) === 'o'.charCodeAt(0) &&
  74. buff.readInt8(2) === 'o'.charCodeAt(0) &&
  75. buff.readInt8(3) === 'k'.charCodeAt(0)
  76. }
  77. // Header check fails. Return nothing.
  78. if (!checkHeader(buff)) {
  79. return []
  80. }
  81. let pageCount = buff.readInt32BE(4)
  82. let dataStart = (pageCount * 4) + 8
  83. let cursor = dataStart
  84. let cookies = []
  85. for (let i = 0; i < pageCount; i++) {
  86. // Find the page size, and grab the slice from the buffer.
  87. let pageSize = buff.readInt32BE(8 + i * 4)
  88. let page = buff.slice(cursor, cursor + pageSize)
  89. cookies = [...cookies, ...parsePage(page)]
  90. // Advance the cursor to the next page's tart index.
  91. cursor += pageSize
  92. }
  93. return cookies
  94. }
  95. // This parser works on best-effort, to allow for maximum data retrival.
  96. // If parsing fails, we return nothing, or as much as we can.
  97. // errors are only raised for out-of-bounds errors, etc.
  98. module.exports.parse = function (filePath) {
  99. return new Promise((resolve, reject) => {
  100. log.verbose('parse', filePath)
  101. try {
  102. let buff = fs.readFileSync(filePath)
  103. let result = parseBase(buff)
  104. // console.log(result)
  105. resolve(result)
  106. } catch (e) {
  107. return reject(e)
  108. }
  109. })
  110. }