__dyn 是一个动态数据,包含一些关于页面状态的信息。

在请求接口的参数中,会看到一个叫做 __dyn 的参数。

先全局搜索一下,看看会有什么结果。

这里找到一个 StaticSiteData 模块,建立了一个对象,__dyn 对应的键名是 jsmod_key。再根据找到的结果再搜索看看有没有其它的线索。

这里找到了 jsmod_key 的赋值方式 c("ServerJSDefine").getLoadedModuleHash()

把开头的 c 换成 require,尝试在 Console 调用一下 ServerJSDefine 模块,实验一下结果是否和 __dyn 参数一致。

看起来找对地方了,接下来再看看 · 这个函数是怎么实现的。

getLoadedModuleHash 这里面又调用了 toCompressedString 函数,那么再接着往下找,看看这个函数是怎么实现的。

这里就找到了 toCompressedString 的实现方法。

不过看起来都是从 $1 这个变量里面的内容进行计算,但是这里并没有提供什么内容,是一个空数组。

function a () {
  this.$1 = [],
  this.$2 = null
}

不过我在网页的模块里面找到了模块的标识或者索引,例如截图里的例子

IntlCurrentLocale: 5954
CookieDomain: 6421
JSSelfProfilerTrackedInteractions: 6918
CurrentAdAccountInitialData: 6828

只需要筛选出每个模块的标识或者索引就可以了,有可能会在 body 里面,也有可能会在 head 里面,所以合并成一个数组

const bodyData = document.body.innerHTML.match(/\},([0-9])+\]/gi)
const headData = document.head.innerHTML.match(/\},([0-9])+\]/gi)
const combinedData = bodyData.concat(headData)
const arr = []
for (const item in combinedData) {
  if (combinedData[item] != null) {
    const extractedNumber = combinedData[item].replace(/\},|]/g, '')
    if (parseInt(extractedNumber) >= 7) {
      arr.push(parseInt(extractedNumber))
    }
  }
}

然后再遍历 数组 中的每个数字,将 bitMap 中对应索引的位置设置为 1,表示该数字存在于 数组中。这样,bitMap 就成为了一个简单的表示数字存在性的位图。后续的压缩过程利用了这个 bitMap 来计算连续相同值的数量。

const bitMap = []
for (const item in arr) {
  bitMap[arr[item]] = 1
}

将刚才写入的 `bitMap` 数组压缩数据,并且转换成文本值的二进制数据。

```Javascript
const compressedBits = []
let count = 1
let currentBit = bitMap[0] || 0
const currentBitString = currentBit.toString(2)
for (let i = 1; i < bitMap.length; i++) {
  const nextBit = bitMap[i] || 0
  if (nextBit === currentBit) {
    count++
  } else {
    compressedBits.push(convertToBinaryString(count))
    currentBit = nextBit
    count = 1
  }
}

最后再将二进制转换成 base64 结果就出来了。

function convertToBase64String (binaryString) {
  const list = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'
  const sixBitChunks = (binaryString + '00000').match(/[01]{6}/g)
  let base64String = ''
  for (let i = 0; i < sixBitChunks.length; i++) {
    base64String += list[parseInt(sixBitChunks[i], 2)]
  }
  return base64String
}

getLoadedModuleHash 对比后结果是一致的,那么 __dyn 的参数就已经还原出来了。

在开头收集模块的标识或者索引的时候,这个数据也可以随机生成伪造参数。经过我的测试发现这个数据的长度大概在 115 到 265 之间,而且是大于 7 的,那么就可以根据下面的代码随机生成一段数据

const arr = []
const count = Math.floor(Math.random() * (265 - 115 + 1)) + 115
const allNumbers = Array.from({ length: 7331 - 7 + 1 }, (_, i) => i + 7)
for (let i = 0; i < count; i++) {
  const randomIndex = Math.floor(Math.random() * allNumbers.length)
  const randomNumber = allNumbers[randomIndex]
  arr.push(randomNumber)
  allNumbers.splice(randomIndex, 1)
}

相关代码:Facebook-API-Params-Generator