18

这篇文章讲述的一些很通用简单的前端技术,但却是实现某些灰色产业的关键技术,当然我只实现一些很简单的思路和方法,尽量写得简单明了,希望能举一反三,一起探索前端技术的各种无限可能。我在几年前也写过几篇这方面的文章,后面由于某些关系删减了不少,有兴趣的可以去找回看看 😀

相信我们经常看直播或者电视剧都会留意到有很多的弹幕,有时候弹幕比电视剧或者直播的游戏更有意思,细心的同学会发现有时候弹幕会出现一致的相同,然后不断的循环,有时候还是出自同一个用户发送的,那么这个用户怎么做到在短时间内循环不断的发送同一个消息或者不同的消息的呢。

26

其实很简单,以网页版的花椒直播作为例子,任何的评论都肯定有输入框,如果人为的输入文字再发送实在是太低效了,作为灰产专业户,肯定不会自己手动输入的。

那么最直接暴力的方式是把任务交给开发者去自动实现,如果你是前端开发者,你可以使用选择器获取到该输入框的节点。

25

那节点非常容易,直接对着输入框右键查看即可,我们可以看到它本质是下面代码实现的。

<input name="message" class="tt-type-msg" placeholder="说点什么" />

那么你拿这个节点方式就能多种多样了,只要保证拿到是唯一的节点即可:

24

下面三行其实都可以:

document.querySelector('[name="message"]');
document.querySelector(".tt-type-msg");
document.querySelector('[placeholder="说点什么"]');

拿到节点我们下一步应该就是模拟我们的用户操作,输入弹幕的文字,可以看到我们在浏览器的控制台输入以下代码的敲回车运行的时候,我们的 666牛逼 自动的写入到输入框,懂前端的同学一看就明白这代码的意思:

23

无非就是改写输入框的 value,然后赋上输入的值。

document.querySelector('[placeholder="说点什么"]').value = "Wscats 666";

当然输入的值这样是写死的,我们还可以使用词典去生成随机的弹幕或者评论,具体复杂的评论可以线上找字典生成,你如果不想麻烦,可以使用一个简单的数组,把你想要发送的话定制好,然后配合随机函数来发送。

const offset = parseInt(Math.random() * 7);
const word = ["厉害", "牛逼", "666", "佩服", "真实", "下饭", "菜"];
document.querySelector('[placeholder="说点什么"]').value = word[offset];

自动输入是完成了,我们还差最后一步,自动点击发送,其实模拟就是鼠标点击发送按钮或者键盘敲回车键,那这都好办,我们还是刚才那个理念,模拟操作那个页面位置,就要寻找那个位置背后的代码节点,我们同样把鼠标放到红色的发送按钮右键查看,在控制台中就可以看到这个节点的信息。

22

由于这个节点没什么很多属性,我们直接根据 class 做唯一标记检索即可。

document.querySelector(".tt-type-submit").click();

同样按照上面的方法把代码输入到控制台敲回车,就可以看到输入框的文字被自动发送出去。

21

但是这里还差一点不完美,我们整个代码只是模拟了一次操作,那么怎么实现循环发呢,不要忘了,浏览器拥有定时器和延时器这种接口,所以我们可以去利用 setInterval 函数去实现按规定的时间评率发送消息,那么我们就可以解放双手安心去欣赏美女主播,哦,不,是游戏主播精彩的对局了是吧,游戏主播还不时线上截屏抽奖,挂着脚本在上面白嫖这不是美滋滋。

setInterval(() => {
    document.querySelector('[placeholder="说点什么"]').value = "一楼牛逼";
    document.querySelector(".tt-type-submit").click();
}, 2000);

这是我们实现的效果,当然这个只是最基本最入门的基础,我们可以还可以改变很多玩法,举个例子,如果我是一个主播,我会安排机器人帮我自动刷礼物,自动帮我回复用户,自动帮我点赞。

20

当然这里篇幅有限,我就简单写点,比如我们可以接入第三方给我们实现的图灵机器人接口,key 是我自己注册的,每天有使用次数限制,想测试的同学最好去官网自己注册一个哈:

http://www.tuling123.com/openapi/api?key=c75ba576f50ddaa5fd2a87615d144ecf&info=你好

这个接口只要改变 info 的参数,去请求图灵机器人就可以得到机器人的回复,比如你可以跟他说,讲个笑话我听,张学友是谁等等,我们自己就不花时间去实现这种 AI 机器人了,直接调用接口即可,具体你还可以去官网定制。

我们可以获取最后回复用户的内容,然后回复他的内容,当然如果你足够无聊,你还可以实现两个机器人互相聊天的功能,在直播室瞬间热闹起来,只要水军够多,直播间就可以有多热闹,在直播带货中尤为有用。

但这里还是使用花椒直播作为例子,欣赏下美女动人的歌声嘛。还是之前的思路,你如果想回复最后一个弹幕,你就需要获取这个弹幕背后所在的节点:

19

console.log("最后的一条评论为:" + $(
    $(".tt-msg-content-h5.tt-msg-content-h5-chat")[
        $(".tt-msg-content-h5.tt-msg-content-h5-chat").length - 1
    ]
)
.text()
.replace(/(^\s*)|(\s*$)/g, ""));

这段代码不复杂,其实就是做了几件事,把直播室聊天记录的最后一条内容的纯文本获取出来,然后删除里面的空格,具体你还可以做一下其他的处理,我这里就不弄复杂了。

18

拿到最后的回复,只要交给图灵接口去发送即可,当然很多直播网站都会防一手脚本刷的,首先如果网站是 http协议,图灵接口注意就使用 http 协议,如果是 https 的话,就直接改成 https 即可,不然可能会报协议错误。

image

这里直接放代码,就不写原生了,用花椒直播做例子试了下是支持 jQuery,那么直接上 $ 符号,我们发送一个 ajax 请求,请求的服务端地址就是图灵接口,请求的 info 参数就是最后一个用户回复的内容。

$.ajax({
    type: "GET",
    url:
        "https://www.tuling123.com/openapi/api?key=c75ba576f50ddaa5fd2a87615d144ecf&info=" +
        $(
            $(".tt-msg-content-h5.tt-msg-content-h5-chat")[
                $(".tt-msg-content-h5.tt-msg-content-h5-chat").length - 1
            ]
        )
            .text()
            .replace(/(^\s*)|(\s*$)/g, ""),
    success: function (data) {
        console.log(data);
        $(".tt-type-msg").val(data.text);
        $(".tt-type-submit").click();
    },
});

当我们在控制台输入以下代码敲回车的时候,发现请求是发送成功的,但是花椒这里做了些信息的判断,估计是防止小白刷留言,但是这怎么能难道我呢,想把我们卡在控制台,不能操作,这还是太嫩了。

我们只需要做一下骚操作即可,学过前端都知道断点是怎么打的,直接展开源码,加一个条件断点,输入 false 把这个错误忽略掉吧,让它顺利往下执行。

17

现在我们就顺利调通了机器自动回复最后一个用户,控制台出现的红色报错不影响我们的自动回复,当然这里我就不浪费时间继续写下去了,懂得同学都懂,只要加一些其他的处理即可,比如加定时器即可在上面看着机器人自言自语,加 id 判断即可只回复其他用户不回复自己的话,都可以发散思路去实现。

16

在一些灰色行业,这种技术是大行其道的,比如微博点赞,热搜控评,淘宝优惠券批量获取,商品秒杀等等,一切即可交给各种机器人水军去完成,当然这些一般不会在客户端实现,会把他们搬到服务器上,有后台专门去处理...

说了那么多,我们还是纯粹点用在技术领域吧,毕竟其他领域我不感兴趣,之前在一些技术简历上看到有些人的掘金,Github,博客等粉丝,等级,提交数和阅读数甚多,这里也是可以做手脚的,这里还是强调只针对技术,我就不用掘金做例子了,用 Github 这些作为例子吧,比如 Github 我们的提交量其实可以任意更改时间段,范围和数量。

15

比如这种经典的提交记录,看上去很有意思,有的开发者是自己慢慢用真实提交数叠的,但是可以轻易人为的,其实实现起来思路也是很简单,直接交给前端技术吧,思路跟刚才上面的代码类似。

首先每个 commit 其实就像刚才的直播发送评论,由于刚才是纯客户端的前端技术实现,现在我们换个口味用纯服务端的前端技术实现吧,扩展下思路。

我们一般在 Github 的仓库里面提交代码都是使用这种命令方式的,这也是 Github 官网提供的方式:

git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/wscats/test.git
git push -u origin main

这种提交方式会以当前本地端的时间为准,所以如果不想改动任何代码,你可以简单粗暴的更改系统时间,然后再执行上面的命令,这种方式适合新手,错过提交,想补回一些提交去画对应的图案:

14

但是作为具备一定前端技术的开发者,肯定不希望用这种方式,实在太低效了,每次都得在可视化界面操作一次时间,这怎么能忍。

那我们可以借助 node 来实现以下,为了能在服务端批量执行 git 命令,我们可以使用 child_process 的接口,熟悉 shell 脚本的同学,其实可以直接用 shell 来实现,我们使用 exec 方法,他可以使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

const exec = require("child_process").exec;
module.exports = (cmd) => {
    return new Promise((resolve, reject) => {
        exec(cmd, function (error, stdout, stderr) {
            if (error) {
                console.log(error);
                reject(stderr);
            } else {
                resolve(stdout);
            }
        });
    });
};

我们再暴露一个写入代码的接口,我们可以新建一个 message.txt 文件,记录每一个代码片段,最简单粗暴就是直接生成一个随机数或者日期写入进去,每次写入代表着每一个 commit,后期你更可以改良成把一份完整的代码按 AST 语法树等,切成有规律的代码块,然后附上真实的提交记录,来进行每一次代码提交。

const fs = require("fs");
module.exports = (message) => {
    return new Promise((resolve, reject) => {
        fs.appendFile("message.txt", `${message}\n`, (err) => {
            err ? reject() : resolve();
        });
    });
};

最后就是核心的代码了,这里最关键的其实 git commit -m "${commitTime}" --no-edit --date="${commitTime}" 这一段,--date 参数可以改成其他的时间(没记错的一般最早是 1970 的 1 月 1 号到 2038 年的 1 月 19 号),具体原因是因为这是时间戳的范围,我也没去试过这么前和这么后的时间了,有兴趣的同学可以自己试试。

const cmd = require("./cmd");
const file = require("./file");
let day = 10;
const random = (lower, upper) => {
    return Math.floor(Math.random() * (upper - lower + 1)) + lower;
};
const commit = async () => {
    const today = new Date();
    today.setTime(
        today.getTime() - 0 * 24 * 60 * 60 * 1000 - day * 24 * 60 * 60 * 1000
    );
    let commitTime = `${today.getFullYear()}.${
        today.getMonth() + 1
    }.${today.getDate()}`;
    if (today.getFullYear() > 2019) {
        return;
    }
    let commitNumber = random(1, 10);
    let dayNumber = random(1, 3);
    while (commitNumber) {
        await file(commitTime);
        await cmd("git status");
        await cmd("git add .");
        await cmd(`git commit -m "${commitTime}" --no-edit --date="${commitTime}"`);
        commitNumber--;
    }
    if (day >= 10) {
        day -= dayNumber;
        commit();
    } else {
        // await cmd('git push origin master');
    }
};
commit();

上面的代码执行成功后,就会自动提交,然后帮你铺满 Contributions 的时间表,这远远比你的手动更改面板效率高多了。

13

为了观赏性,你可以用随机函数去制造每天的 commit 量,也可以随机制造出隔天提交的操作,当然也可以随机提交 commit 的信息,比如接入上面提到的图灵机器人就可以实现提交信息的自言自语,当然我自己牺牲一下,拿我自己的 Github 做一次小白鼠吧,各位同学就慎重娱乐了,万一最后只学会打满格子,不会删除格子就尴尬了,至于如何删除这些格子,其实多补点 Git 知识就懂了,就不展开说了,放一下实现后的效果,具体可以去我 Github 看看。

12

那么这里代码实现可以告诉我们,很多时候看技术文章不要只看表面的量化数据,诸如一些阅读量,点赞量和提交量都是我们参考的一部分而已,更多的是看这位作者呈现内容的质量。

我搜了下 Github,还是有不少这种骚操作的,具体都有很多开源代码的实现,大家都可以参考下,作为程序员日常可以拿来娱乐下,切记就千万不要拿来糊弄人了。

11

上面这个例子就点到即止,它的应用面其实很广,比如各种排行榜上的名次,在很多的榜单上面一定要斟酌细看,一般用户可能会难以发现使用了这种技术,时间还有不少,我就再续点吧,还是照样,我们拿技术社区做实验,我们用微软 VSCode 的插件排行榜上做实验,本来想用微博或者掘金做实验,但想想这可能要负后果啊,要讲武德,我们还是使用技术社区纯粹点,没太多的功利性。

10

这里都是为我们开发者提供便利的插件,这种榜单本质上是流行榜单,具有一定的参考意义,但是我们更多是要理性的去分析,因为进榜可能分分钟背后是一种"技术"在推动。

我们这次前端技术客户端和服务端都试一下,是否可行,我们尝试帮我们把一款插件的下载量提升上来,我们发现网页端其实是有下载按钮,那么根据上面介绍的技术就已经足够实现了,思路很简单。

9

找到该按钮背后的节点代码,然后按频率触发点击下载即可,这个下载完全模拟了人在客户端的基本操作,那么这个下载会成功发出请求,并被完全记录到后台,然后最后更新呈现到下载量里面。

8

当然这种方法简单粗暴可以快速达到目的,但是有没有比这种方式更迅速有效呢?答案肯定是有的,并且还有很多很多的方案,具体参考这个插件。

7

执行后直接在一天内进入下载量第一梯队了,当然可以直接把它拉满,进入下载量第一,但是这样娱乐娱乐就够了,如果只是为了处处争第一,太过了就违背技术的初衷了,提供一个插件的本质是为更多的开发者提供服务,年轻人还是要讲武德的,我实验了部分插件,发现这里是没有任何限制的,据我观察大部分靠前的插件都是官方插件,其中有些下载量是有点微妙的,这里我也希望微软在后续能修复这种漏洞,就像国产的 Hbuilder 那样,插件是账号绑定才能下载的。

这里我顺便讲下原理,因为插件除了在网页下载也支持在客户端下载,所以同样我们只要监听客户端的下载,就可以模拟整个下载流程,然后循环执行即可,有点像小号刷淘宝的好评一样,但是一般商业化项目这些入口都会有防范,没那么容易给你执行这些脚本。

6

这里可以非常灵活,既可以使用前端技术去动态执行下载,也可以劫持下载的请求去循环发送达到欺骗服务器的效果,就是我们可以在 node 端去伪造这个请求的头部和请求体,然后发送过去服务器。

这种方案稍微做改变可能用于暴力破解(wifi,服务器密码),中间人攻击等。

6.1

在网络世界其实每天都有这类人跑着这些脚本去扫描漏洞的,然后伪造请求去达到目的,我的服务器就经常被这些人光顾,难受啊。

当然除了 node,能实现这种能力的工具还有很多,这里就不一一列举,我们只围绕前端范围去分析。

5

所以我们为什么有时候登录注册需要这么多重的验证,还不是开发者为了保护我们跟恶势力抵抗嘛,对于一些榜单,比如热搜和热评,我们可作为一个参考,不能过度迷信。

这里顺便一提的不管手机还是电脑,其实应用和插件监听系统的方式有很多,就如上面的插件,尽量还是使有用稳定代码源的比较稳妥,因为他们其实可以轻易在本地对系统进行操作,比如使用简单的代码去监听你的键盘输入,轻易去读写你的本地文件等等。这些代码有时候甚至很简单,它可以离我们很远,也可以离我们很近。

4

就如下面这种简单的代码,在不合适的场景它将会是一段可怕的代码,配合一些手段可以悄悄进入你的系统,然后记录你输入的点点滴滴,包括输入的密码,输入习惯,语法习惯,虚词实词分布,常用字词和语言组织能力等,配合一些隐蔽的上报,能做到对你用户精确的分析,只要用户输入的足够多,它对你就越了解,因为每个人都是生长在一个独一无二的环境和时代,这种环境对用户语言的影响是非常的深远,就如曹雪晴和高鹗两位红楼梦的作者都不在了,但是留下的珍贵文学,通过数据分析,足够证明这本书拥有前后不一样的风格。

const inputs = document.querySelectorAll("input");
[].forEach.call(inputs, (input) => {
    input.addEventListener("input", (e) => {
        console.log(e.target);
    });
});

这些恶意代码可能会伴随着插件等方式植入到你的电脑,方式多种多样,比如套壳,"颜色"网站引导,魔改的山寨系统或者浏览器等...

3

技术是一把双刃剑,本质应该是向善的,小时候背三字经,印象最深刻的一句话是人之初,性本善,同样我也认为,技术之初,本质也是善的,换句话来说,技术的初衷是为用户带来价值,而非损失。

2

我非常乐此不疲的挖掘和探索技术的价值和乐趣,最近我就喜欢在微信中写点简单的脚本去自动回复下朋友家人,工作在外可能一句早安和晚安都是温暖的。

定时提醒下老人家吃饭睡觉,准点给朋友发送生日快乐,对方还是蛮开心的,这是里面其中一些关键的代码,当然你自己可以赋予它更多的功能。

setInterval(function () {
    $(".edit_area").html("微信需要发送的文字");
    $(".edit_area").trigger($.Event("keydown", { keyCode: 13, ctrlKey: true }));
    $(".btn_send").click();
}, 3000);

当然作为程序员也得有生活追求嘛,打工人的生活都是相似的,抽空去看看美丽的世界,帮我刷刷优惠券哈哈,让浏览器帮我自动完成些事情吧,程序员的世界可能注定这么平平无奇 😁

1

let y = 0;
let num = 0;
let imgArr = [];
setInterval(() => {
    let imgs = document.querySelectorAll("img");
    let length = imgs.length;
    if (num !== length) {
        num = length;
        imgArr = imgs;
        console.log(length, imgArr);
    }
    y = y + 1;
    scrollTo(0, y);
}, 1);

希望自己以后能有更多的时间去贡献更多有意思的代码,开源不易,且行且珍惜。

最后

上面所有的代码其实本质都很简单,单纯的分享和交流,仅供大家娱乐,如有不足请多多包涵。

实际很多场景还有很多的门槛等待着我们去解决的,比如验证码的识别,用户行为和调用链分析,木马植入等,以后如果有机会再找时间写写,作为开发者我们也应当有义务和意识,在用户操作的每个入口去把好关和提醒用户。

0

当然技术的乐趣在于探索,而非作恶,最后附上我心爱的小钢琴,与君共乐:🎹 https://github.com/Wscats/piano

往期的文章可以移步这里:🔖 https://github.com/Wscats/articles

你的支持(Star 和 Fork)是我前进的最大动力~

感谢音乐和编程的陪伴!也致敬各位奋斗于 996/007 的代码家,音乐不曾辜负任何人,正如 Leehom Wang 歌曲中唱到:

如果世界太危险,只有音乐最安全,带着我进梦里面,让歌词都实现! —— 《我们的歌》

wscats
7.1k 声望13k 粉丝

分享和总结不易,求关注一下⭐️