node项目打包成可执行文件pkg(二):从exe文件中提取源码

1,151 阅读1分钟

如果有虚拟文件系统元数据 VIRTUAL_FILESYSTEM,并且知道 PAYLOAD_POSITION,我们就可以从pkg 打包的 exe 二进制文件中提取出 javascript 代码。 关于VIRTUAL_FILESYSTEM 和PAYLOAD_POSITION是什么,请查看 pkg介绍

我们可以直接从 exe 文件中获取到 VIRTUAL_FILESYSTEM 和 PAYLOAD_POSITION。

  1. 用 vscode 打开 exe 文件
  2. 在文件底部可以看到 VIRTUAL_FILESYSTEM

20210627113518

  1. 搜索 'PAYLOAD_POSITION ='就可以得到PAYLOAD_POSITION。

20210627113641

async function extraJsFromPkgExe(exeBinary, VIRTUAL_FILESYSTEM, PAYLOAD_POSITION) {
  const snapshotPaths = Object.keys(VIRTUAL_FILESYSTEM);
  const exeBuffer = fs.readFileSync(exeBinary);

  for(let i=0; i<snapshotPaths.length; i++){
    const snapshotPath = snapshotPaths[i];
    if(!snapshotPath.includes('node_modules')){
      const entityContent =  VIRTUAL_FILESYSTEM[snapshotPath][1] || VIRTUAL_FILESYSTEM[snapshotPath][0];
      if(entityContent) {
        const filePath = getRealPath(snapshotPath);
        const content = readFileFromVfs(entityContent);
        await saveFile(`./data/codes/${filePath}`, content);
      } else {
        console.log(`${snapshotPath}未找到`);
      }
    }
    
  }

  function readFileFromVfs(entityContent) {
    const [position, size] = entityContent;
    const buffer = Buffer.alloc(size);
    exeBuffer.copy(buffer, 0, PAYLOAD_POSITION + position, PAYLOAD_POSITION + position + size);
    return buffer;
  }
}


const getRealPath = (snapshotPath) => {
  return snapshotPath.replace(/\\/g, '/').slice(12);
};

// 递归的创建目录
function mkdirsSync(dirname) {
  if (fs.existsSync(dirname)) {
    return true;
  } else {
    if (mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname);
      return true;
    }
  }
}

const ensureDir = (_path) => {
  const dir = path.parse(_path).dir;
  if(!fs.existsSync(dir)){
    mkdirsSync(dir);
  }
};


const saveFile = (filePath, buffer) => {
  return new Promise((resolve, reject) => {    
    const source = intoStream(buffer);
    ensureDir(filePath);
    const dest = fs.createWriteStream(filePath);
    source.pipe(dest);
    dest.on('close', resolve);
    source.on('error', reject);    
  });
};