知乎可以实现动态头像的核心原因在于知乎头像的显示时是直接从图床中加载头像文件,实际上并不会根据文件名进行类型检查。换句话说,虽然在上传时会强行指定为png/jpg格式,但是我们只需要保证上传内容是一个具有自明性的动图,那么知乎的web端实际上就能直接根据图片的内容正确地解析并显示出来。
考虑到知乎对于GIF的支持方式实际上也是借助webp格式,所以只需要我们在上传头像时能够上传一个文件内容为『 完整的webp格式 』的文件即可。
但这一点并不能依靠简单的修改后缀名实现 。简单来说,知乎在上传头像的时候实际上会在点击保存时,实际上会调用『HTMLCanvasElement.prototype.toBlob』,这相当于只截取了上传文件的第一帧,并只将截取后的转换为 Blob对象。所谓的 Blob对象简单来说就是一个二进制数据块。之后再上传到图床的数据实际上就是此处的Blob对象中的内容。这样就会导致即使你修改了后缀名,实际上也不能上传完整的webp文件,而只能上传其第一帧。
那么,我们可以怎么做呢?
知道以上原理之后,实现就很简单了。实际上我们 只需要替换上传的Blob对象就行 。
有一个颇为邪道的实现方式是这样的:首先把webp文件传为base64的纯文本格式,然后再通过F12加载一个js脚本,从而实现把『HTMLCanvasElement.prototype.toBlob』上传数据替换为我们的webp文件。具体方式如下:
转为webp格式主要是为了降低GIF文件的体积。这一步可以借助在线工具实现,比如:
<a href="https://www.freeconvert.com/zh/gif-to-webp" target="blank"><div class="FileBoxS"><div class="file_left"><div class="name">GIF到WebP转换器</div></div><div class="file_right"><div class="file_line"></div><div class="download"><svg t="1715406031949" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7740" width="40" height="40"><path d="M451.52 608.64l65.024-64.96-420.928-0.064a31.616 31.616 0 0 1 0-63.104l420.928-0.128-65.024-64.96a31.424 31.424 0 1 1 44.544-44.544L614.912 489.6a31.744 31.744 0 0 1 0 44.8L496 653.248a31.552 31.552 0 0 1-44.544-44.544z m473.216-270.912C948.16 392.768 960 451.52 960 512a445.184 445.184 0 0 1-131.2 316.8A445.76 445.76 0 0 1 512 959.872a443.84 443.84 0 0 1-202.24-48.128 451.84 451.84 0 0 1-154.496-128.832 31.488 31.488 0 0 1 50.048-38.144 388.928 388.928 0 0 0 132.736 110.72A380.8 380.8 0 0 0 512 897.024a384.448 384.448 0 0 0 272.32-112.768A382.976 382.976 0 0 0 896.96 512a384.448 384.448 0 0 0-112.768-272.32A382.976 382.976 0 0 0 512 127.04c-61.184 0-119.616 13.888-173.76 41.344a388.096 388.096 0 0 0-132.736 110.72 31.36 31.36 0 1 1-50.048-38.208 450.56 450.56 0 0 1 154.24-128.768A443.84 443.84 0 0 1 512 64a445.184 445.184 0 0 1 316.8 131.2 446.592 446.592 0 0 1 95.936 142.528z" fill="#ffffff" p-id="7741"></path></svg></div></div></div></a>
我们知道图片的源文件一般都是个纯二进制文件,**而base64则相当于直接将二进制文件用直接的64进制的纯文本来表示,这在很多不便上传二进制的场合,base64格式可以起到用纯文本传输图片/动图的作用。**这一步主要是为了下一步中上传文件的方便。
这一点我们也可以借助在线工具实现:
<a href="https://base64.guru/converter/encode/image/webp" target="blank"><div class="FileBoxS"><div class="file_left"><div class="name">WebP to Base64</div></div><div class="file_right"><div class="file_line"></div><div class="download"><svg t="1715406031949" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7740" width="40" height="40"><path d="M451.52 608.64l65.024-64.96-420.928-0.064a31.616 31.616 0 0 1 0-63.104l420.928-0.128-65.024-64.96a31.424 31.424 0 1 1 44.544-44.544L614.912 489.6a31.744 31.744 0 0 1 0 44.8L496 653.248a31.552 31.552 0 0 1-44.544-44.544z m473.216-270.912C948.16 392.768 960 451.52 960 512a445.184 445.184 0 0 1-131.2 316.8A445.76 445.76 0 0 1 512 959.872a443.84 443.84 0 0 1-202.24-48.128 451.84 451.84 0 0 1-154.496-128.832 31.488 31.488 0 0 1 50.048-38.144 388.928 388.928 0 0 0 132.736 110.72A380.8 380.8 0 0 0 512 897.024a384.448 384.448 0 0 0 272.32-112.768A382.976 382.976 0 0 0 896.96 512a384.448 384.448 0 0 0-112.768-272.32A382.976 382.976 0 0 0 512 127.04c-61.184 0-119.616 13.888-173.76 41.344a388.096 388.096 0 0 0-132.736 110.72 31.36 31.36 0 1 1-50.048-38.208 450.56 450.56 0 0 1 154.24-128.768A443.84 443.84 0 0 1 512 64a445.184 445.184 0 0 1 316.8 131.2 446.592 446.592 0 0 1 95.936 142.528z" fill="#ffffff" p-id="7741"></path></svg></div></div></div></a>
这一步具体来说需要这么做:首先将以下脚本中 data:image/webp;base64,XXXXXXXX的 XXXXXXXX部分替换为第二步中的『base64文本』。需要注意的复制粘贴的时候不要把后面的 "; 也包含进去,这里的 "是和前面成对出现的,不能缺失。
接着在借助 base64ToBlob 将动图数据转化为Blob对象, 从而实现我们的偷梁换柱 。
具体脚本如下:
// 1. 定义转换函数(将 Base64 转为原生 Blob)
function base64ToBlob(base64) {
var parts = base64.split(';base64,');
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
}
// 2. 准备你的动图数据(填入你的长字符串)
var myRawData = "data:image/webp;base64,XXXXXXXX";
var myBlob = base64ToBlob(myRawData);
// 3. 替换 Blob 返回值(不使用 fetch,直接返回对象)
HTMLCanvasElement.prototype.toBlob = function(callback, type) {
console.log(" 拦截成功!已绕过 CSP 发送原始动图二进制流...");
callback(myBlob);
};
// 4. 兼容旧版调用
HTMLCanvasElement.prototype.toDataURL = function() {
return myRawData;
};
处理好脚本后,打开web端的『我的主页』,然后按F12 ,再在console中粘贴后的脚本, 再按回车 。
如果是第一次进行以上操作的话,还需要按浏览器提示输入对应的同意命令:
我们知道,第三步的实际功能是将常规流程中生成的Blob对象替换为我们选择的动图生成的Blob对象,并将其上传到知乎的图床。但这一步并没有实现主动上传,所以我们还需要借助常规换头像的流程来进行 『借鸡下蛋』 。
也就是进行一个常规的换头像流程。随便上传任意一张图片都可以,因为都会被替换为base64文件的对应内容。