编程导航爬虫话题讨论

爬虫

31 参与
分享

快来分享你的内容吧~

点击登录,快来和大家讨论吧~
表情
图片
话题
打卡
综合
交流
文章
问答

旧版 chromium 源码编译,以 86.0.4240.198 版本为例

<img src="https://pic.code-nav.cn/post_picture/1922184057994162177/tGT9sg21coJqAsvb.webp" alt="在这里插入图片描述" width="100%" /> ## chromium各版本发布日程 [chromium各版本发布日程](https://chromiumdash.appspot.com/schedule) ## chromium各版本git日志 [chromium各版本git日志](https://chromium.googlesource.com/chromium/src/+refs) 在编译之前我们需要下载好全量的 chromium 源码,之前文章中,我们下载的时候是加上了 --no-history 参数,是不会去拉取全量源码,只会拉取最新版本的源码。 ## 下载 depot_tools 工具 首先下载 depot_tools 工具,并将 depot_tools 目录添加到系统变量 path 中 ```sh git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git ``` 下载好了之后,不要执行 gclient 命令和修改任何文件。在C盘根目录或者其他目录下新建 boto.cfg 文件,文件内容为以下内容:(proxy_port参数修改为自己设备的代理的端口) ```sh [Boto] proxy = 127.0.0.1 proxy_port = 10808 ``` 然后新增环境变量 NO_AUTH_BOTO_CONFIG ,值为boto.cfg文件路径,不会的可以参考之前的源码编译章节内容 ## 拉取 chromium 全量源码 然后新建一个 chromiumAll 目录,用来放置全量 chromium 的源码(此目录所在磁盘建议剩余存储空间要在 150GB 以上),进到你刚刚新建的chromiumAll目录,然后拉取代码。 打开powerShell 7,建议使用前面文章介绍的新版powershell,设置全局代理,开启vpn,执行以下代码进行拉取即可,一共大约 80GB 的源码,中途不可中断,中断直接重头再来,所以需要保证你的网络顺畅,并且你的代理服务流量额度要够,我跑了好多次才成功将他全部拉取下来,都是中途中断了,然后只能重头再来,浪费了接近 300GB 的vpn流量,**(如果你成功拉取了,可以将这个代码不做任何操作,备份一份到本地,然后用复制出来的代码去做项目,保证一直会有一份比较干净的源码)** ```sh $env:HTTP_PROXY="http://127.0.0.1:10808"; $env:HTTPS_PROXY="http://127.0.0.1:10808" fetch chromium ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/4a6fvIFTawg4xd1Q.webp" alt="在这里插入图片描述" width="446px" /> 下载好了之后,进到chromium各版本git日志中,地址在开头位置,全局搜索一下需要的版本 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/C8mHsibNx8fam2Uj.webp" alt="在这里插入图片描述" width="100%" /> 点击图中位置 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/xZBD4GxZ34mxgL8m.webp) <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/ckdgHC0eDT0ZHRqg.webp" alt="在这里插入图片描述" width="100%" /> 点击图中位置 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/BywyCeaLe1AyVFSG.webp" alt="在这里插入图片描述" width="100%" /> 进入之后,首先记住commit的提交编号,这个非常重要,然后找到 Checking Out and Building,然后找到对应系统,打开构建文档链接即可。 环境要求按照构建文档要求的环境来,例如Windows 10 SDK,Visual Studio这些,严格按照文档要求来就可以。 环境配好之后,就是切换版本,在powershell 7 中,src目录中执行一下命令,5f8e74b8f1dab431a1a57d84101185f0e8da33d4就是刚刚的commit编号 ```sh git checkout 5f8e74b8f1dab431a1a57d84101185f0e8da33d4 ``` 执行完之后,执行 git log -1,查看一下当前版本的状态,目前的状态就是回到了,也可以通过查看前面的chromium各版本发布日程链接查看每个版本的发布时间来验证对错,一般不会差太久。 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/DzCnW8Yq8HRjLsS4.webp" alt="在这里插入图片描述" width="100%" /> 在src的DESP文件中,搜索一下depot_tools,记住这个版本号即可 ![image.png](https://pic.code-nav.cn/post_picture/1922184057994162177/wHNyilVdSwgdx1ID.webp) powershell 切换到depot_tools目录,执行以下命令,87a7605160fdc1370d6fbaa6347a36cbc5287546就是图片中的版本号 ```sh git checkout 87a7605160fdc1370d6fbaa6347a36cbc5287546 ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/MHvIzA9jB8MlXaoC.webp" alt="在这里插入图片描述" width="100%" /> 在执行gclient之前,可以先运行一次depot_tools下的 bootstrap\win_tools.bat 文件,会自动下载一个对应版本的python,执行完之后,就可以执行 gclient 了,在gclient运行成之后,切换到chromium的src目录下,跟之前一样开始执行编译所需要的步骤就可以。 在执行 gn gen out/Default 的时候,构建配置可以使用我这份,当然你也可以根据GN官网的配置来。 ```bash # 系统 target_os = "win" # 架构 target_cpu = "x64" # 是否开启调试 is_debug = false # 不允许远程调试 enable_remoting = false # 使用 Chrome 官方的编译优化建议 is_official_build = true # 分散链接编译 is_component_build = false # 不使用官方 API 密钥 use_official_google_api_keys = false google_api_key = "" google_default_client_secret = "" # 关闭支持 NaCl enable_nacl = false # 支持 H264 编码 proprietary_codecs = true # 解码器 ffmpeg_branding = "Chrome" # 启用调试符号以满足 GRIT 要求 symbol_level = 1 v8_symbol_level = 1 blink_symbol_level = 1 # 明确禁用 strip_debug_info strip_debug_info = false # 禁用 PGO 优化以避免配置文件缺失问题 chrome_pgo_phase = 0 ``` 最后构建的时候,用 **ninja -C out/Default chrome**,不要用 autoninja -C out/Default chrome 在编译过程中可能会报的错误: 如果你运行gclient sync 的时候报了gbk 编码之类的错误,可以尝试运行这个命令来重新执行 ```bash cmd /c "set PYTHONIOENCODING=utf-8 && gclient sync" ``` 如果在执行hooks阶段报以下类似错误: ```bash Running hooks: 46% (45/96) tools_traffic_annotation_windows ________ running 'vpython.bat src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --num_threads=4 --bucket chromium-tools-traffic_annotation -d src/tools/traffic_annotation/bin/win32' in 'J:\chromium' 1> File gs://chromium-tools-traffic_annotation/f983cccf0780bec2aa066a691f50e4c8a3a07153 for src/tools/traffic_annotation/bin/win32\traffic_annotation_auditor.exe does not exist, skipping. Downloading 1 files took 4.006000 second(s) File gs://chromium-tools-traffic_annotation/f983cccf0780bec2aa066a691f50e4c8a3a07153 for src/tools/traffic_annotation/bin/win32\traffic_annotation_auditor.exe does not exist. Error: Command 'vpython.bat src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --num_threads=4 --bucket chromium-tools-traffic_annotation -d src/tools/traffic_annotation/bin/win32' returned non-zero exit status 1 in J:\chromium ``` 可以在src目录中的DESP文件中,将此项注释,原因是谷歌存储桶中,tools_traffic_annotation_windows文件已经被删除不存在了,根据官方解释,这个东西的作用是用来记录你的流量用途,没有的话,不影响我们后面的编译 ![image.png](https://pic.code-nav.cn/post_picture/1922184057994162177/7kKIJ0uEDFTnDLG2.webp) ![image.png](https://pic.code-nav.cn/post_picture/1922184057994162177/gBPVQgszmpDW81jX.webp) 编译好了之后,就可以看到我们对应版本的chromium了。 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/07KKVtK8Y10DOoCA.webp) 要点总结: 对应历史版本的chromium在编译的时候,就要用DESP文件中记录的对应版本的depot_tools,将他们都回滚到历史版本,然后执行编译所需要的步骤即可,否则会发生很多奇怪的错误

魔改chromium源码——自定义浏览器启动参数

在进行以下操作之前,请确保已完成之前文章中提到的 [源码拉取及编译](https://blog.csdn.net/weixin_45307278/article/details/146828832) 部分。 如果已顺利完成相关配置,即可继续执行后续操作。 --- ## 为什么需要自定义传参? >在讨论这个问题之前,我们需要引入一个重要的背景概念:环境持久化 和运行环境一致性 。这是许多现代浏览器应用场景中不可忽视的关键点。 > >众所周知,当浏览器启动时,其内部参数(如指纹、用户代理、硬件信息等)会被初始化并固定下来。例如,假设你登录了某个网站(比如A网站),第一次启动浏览器时,系统计算出的设备指纹是1111。但当你关闭浏览器后再次打开时,由于浏览器内部可能默认启用了指纹随机化机制 ,指纹可能会变成2222。对于A网站的风控系统来说,这种指纹的变化会被视为异常行为,进而将你的运行环境标记为可疑,甚至可能导致账户被限制或封禁。 >因此,在实际应用中,为了实现更灵活且可控的浏览器行为,我们需要解决以下几个核心问题: > 1. 明确随机化的控制点 :我们需要知道哪些参数是可以被随机化的,以及如何修改它们。 >2. 实现随机值的持久化:随机生成的值需要能够在多次启动之间保持一致,避免因频繁变化而触发风控。 >3. 支持外部干预:通过外部手段动态调整内部参数,以满足不同场景的需求。 > --- > >在这种情况下,自定义传参 就显得尤为重要。通过在浏览器启动时传递不同的参数,我们可以为每一个浏览器实例设置独立的配置,从而实现多样化的运行效果。例如: > - 数据隔离 :浏览器自带的--user-data-dir参数可以指定不同的用户数据目录,确保每个实例的数据完全隔离。 > - 指纹定制 :通过传参,我们可以为每个浏览器实例分配固定的指纹值,避免因随机化导致的风控问题。 > - 灵活扩展 :自定义传参不仅限于指纹,还可以用于调整其他关键参数(如分辨率、语言、时区等),从而更好地模拟真实用户环境。 > > 综上所述,自定义传参是实现环境持久化和运行环境一致性的基础工具之一。它不仅能够帮助我们规避风控系统的误判,还能为复杂的应用场景提供更高的灵活性和可控性。这正是我们在设计和使用浏览器自动化方案时需要重点关注的核心能力 以下为实现步骤以及验证方式: 验证方式我们会用到 [新增自定义变量到windows属性](https://blog.csdn.net/weixin_45307278/article/details/147150394) 这篇文章中的方法,将传参接收的值挂载到 window 全局变量。然后在devtool中输出,验证我们传值是否符合预期。 # 步骤 1:定义新的命令行开关 修改位置: ```bash src\chrome\common\chrome_switches.h ``` 在 namespace switches 中新增变量 ```cpp extern const char kMyCustomFlag[]; // 新增 ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/7kNgJfoG5TSlnoNk.webp" alt="在这里插入图片描述" width="100%" /> 修改位置: ```bash src\chrome\common\chrome_switches.cc ``` 在 namespace switches 中新增变量,my-custom-flag 就是我们要传的链接参数 ```cpp const char kMyCustomFlag[] = "my-custom-flag"; // 新增 ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/kEkWIyAMsaQAM0HD.webp" alt="在这里插入图片描述" width="100%" /> # 步骤 2:创建 JavaScript 绑定,用于测试验证 在 src\content\renderer\ 中新增文件 my_custom_flag.h ```cpp #ifndef CONTENT_RENDERER_MY_CODE_BINDING_H_ #define CONTENT_RENDERER_MY_CODE_BINDING_H_ #include "v8/include/v8.h" namespace content { class myCustomFlagBinding { public: static void Install(v8::Local<v8::Context> context); }; } #endif ``` 在 src\content\renderer\ 中新增文件 my_custom_flag.cc ```cpp #include "content/renderer/my_custom_flag.h" #include "base/logging.h" // NEW: For LOG(INFO) #include "v8/include/v8.h" #include "base/command_line.h" #include "chrome/common/chrome_switches.h" namespace content { void myCustomFlagBinding::Install(v8::Local<v8::Context> context) { v8::Isolate* isolate = context->GetIsolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Object> global = context->Global(); std::string value; auto* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kMyCustomFlag)) { value = command_line->GetSwitchValueASCII(switches::kMyCustomFlag); LOG(INFO) << "Custom flag detected in renderer: --my-custom-flag=" << value; // NEW: Enable logging } else { LOG(INFO) << "No --my-custom-flag found in renderer"; // NEW: Log when switch is missing } // commandFlag 就是新增的全局变量,访问方式就是 window.commandFlag global->Set( context, v8::String::NewFromUtf8(isolate, "commandFlag").ToLocalChecked(), v8::String::NewFromUtf8(isolate, value.c_str()).ToLocalChecked()) .Check(); } } // namespace content ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/jrwYbKZSgzGo6p0J.webp" alt="在这里插入图片描述" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/q0mWnbfavoEodWUV.webp" alt="在这里插入图片描述" width="100%" /> # 步骤 3:绑定到 RenderFrameImpl,用于测试验证 修改位置: ```bash src\content\renderer\render_frame_impl.cc ``` 新增头文件 ```cpp #include "content/renderer/my_custom_flag.h" ``` 新增内容 ```cpp if (world_id == 0) { myCustomFlagBinding::Install(context); } ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/5VjC0W27QrdaqCqc.webp" alt="在这里插入图片描述" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/bILXbWx1SrI6eWVR.webp" alt="在这里插入图片描述" width="100%" /> # 步骤 4:确保开关传播到渲染进程 修改位置: ```bash src\content\browser\renderer_host\render_process_host_impl.cc ``` 新增头文件 ```cpp #include "chrome/common/chrome_switches.h" ``` 在void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer中新增 ```cpp switches::kMyCustomFlag, ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/05RFoHc92YKylyNm.webp" alt="在这里插入图片描述" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/v7KthHTf9evbu7pT.webp" alt="在这里插入图片描述" width="100%" /> 最后,在src目录下,执行 gn gen out/Default ,重新生成构建文件 构建成功之后运行一下命令进行编译 ```cpp autoninja -C out/Default chrome ``` 使用以下方式启动编译好的chrome.exe <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/a2KCZbkzmzpwuVRw.webp" alt="image.png" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/IcfWIjH4tA0sTr5a.webp" alt="image.png" width="712px" />

魔改chromium源码——新增自定义变量到windows属性

在进行以下操作之前,请确保已完成之前文章中提到的 [源码拉取及编译](https://blog.csdn.net/weixin_45307278/article/details/146828832) 部分。 如果已顺利完成相关配置,即可继续执行后续操作。 --- ### 目标 在 Chromium 中添加一个全局变量 myCode,值为 "你好!我是来自C++的字符串",并通过 JavaScript 的 console.log(window.myCodeApi.myCode()) 直接访问。 ### 实现步骤 ## 步骤 1:定义全局变量 在 src/base 模块中定义全局变量 myCode,以便在整个 Chromium 项目中复用 在src/base目录下,创建文件:my_globals.h **文件内容:** ```cpp #ifndef BASE_MY_GLOBALS_H_ #define BASE_MY_GLOBALS_H_ #include <string> namespace base { extern const char* const kMyCode; } #endif ``` base目录下创建文件:my_globals.cc **文件内容:** ```cpp #include "base/my_globals.h" namespace base { const char* const kMyCode = "你好!我是来自C++的字符串"; } ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/UHvgxWc4Bd8MCqtv.webp" alt="在这里插入图片描述" width="100%" /> **修改 base/BUILD.gn** **文件路径:** src/base/BUILD.gn **操作:** 在 component("base") 的 sources 列表中添加新文件的文件名 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/KtOeff9HLpmEKEmY.webp" alt="在这里插入图片描述" width="100%" /> ## 步骤 2:创建 JavaScript 绑定 在 src/content/renderer 目录中,创建文件:my_code_binding.h **文件内容:** ```cpp #ifndef CONTENT_RENDERER_MY_CODE_BINDING_H_ #define CONTENT_RENDERER_MY_CODE_BINDING_H_ #include "v8/include/v8.h" namespace content { class MyCodeBinding { public: // 安装绑定到指定的 V8 上下文中 static void Install(v8::Local<v8::Context> context); private: // 获取 my_code 值的 JavaScript 函数 static void GetMyCode(const v8::FunctionCallbackInfo<v8::Value>& args); }; } #endif ``` 在 src/content/renderer 目录中,创建文件:my_code_binding.cc ```cpp #include "content/renderer/my_code_binding.h" #include "base/my_globals.h" #include "third_party/blink/public/web/blink.h" #include "v8/include/v8.h" namespace content { void MyCodeBinding::Install(v8::Local<v8::Context> context) { // 从 context 获取 Isolate v8::Isolate* isolate = context->GetIsolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Object> global = context->Global(); v8::Local<v8::Object> my_code_api = v8::Object::New(isolate); my_code_api->Set( context, v8::String::NewFromUtf8(isolate, "myCode").ToLocalChecked(), v8::Function::New(context, &MyCodeBinding::GetMyCode).ToLocalChecked()) .Check(); global->Set( context, v8::String::NewFromUtf8(isolate, "myCodeApi").ToLocalChecked(), my_code_api) .Check(); } void MyCodeBinding::GetMyCode(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope handle_scope(isolate); args.GetReturnValue().Set( v8::String::NewFromUtf8(isolate, base::kMyCode) .ToLocalChecked()); } } ``` **修改 content/renderer/BUILD.gn** **文件路径:** src/content/renderer/BUILD.gn **操作:** 在 target(link_target_type, "renderer") 的 sources 列表中添加新文件 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/61XZJDRDM0UP9Nto.webp" alt="在这里插入图片描述" width="100%" /> ## 步骤 3:绑定到 RenderFrameImpl 在 RenderFrameImpl 中调用绑定逻辑,将 myCode 属性安装到脚本上下文中 修改文件路径:src/content/renderer/render_frame_impl.cc 在文件顶部添加 my_code_binding.h 头文件,可以按文件头字母顺序添加 ```cpp #include "content/renderer/my_code_binding.h" <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/lS7yrj8F0pciPuuk.webp" alt="在这里插入图片描述" width="100%" /> uuk.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6ea99626d9114475a4516ccbe9df0b0c.png) 在RenderFrameImpl::DidCreateScriptContext中添加如下代码 ```cpp void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context, int world_id) { // 新增代码 if (world_id == 0) { MyCodeBinding::Install(context); } // 新增代码 TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::DidCreateScriptContext", TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); v8::MicrotasksScope microtasks(GetAgentGroupScheduler().Isolate(), context->GetMicrotaskQueue(), v8::MicrotasksScope::kDoNotRunMicrotasks); if (((enabled_bindings_.Has(BindingsPolicyValue::kMojoWebUi)) || enable_mojo_js_bindings_) && IsMainFrame() && world_id == ISOLATED_WORLD_ID_GLOBAL) { // We only allow these bindings to be installed when creating the main // world context of the main frame. blink::WebV8Features::EnableMojoJS(context, true); if (mojo_js_features_) { if (mojo_js_features_->file_system_access) blink::WebV8Features::EnableMojoJSFileSystemAccessHelper(context, true); } } if (world_id == ISOLATED_WORLD_ID_GLOBAL && mojo_js_interface_broker_.is_valid()) { // MojoJS interface broker can be enabled on subframes, and will limit the // interfaces JavaScript can request to those provided in the broker. blink::WebV8Features::EnableMojoJSAndUseBroker( context, std::move(mojo_js_interface_broker_)); } for (auto& observer : observers_) observer.DidCreateScriptContext(context, world_id); ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/LYqQuST5d27m0w7u.webp) ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/LYqQuST5d27m0w7u.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ce9eb93c7d504410a503717c12d74ef6.png) 最后,在src目录下,执行 gn gen out/Default ,重新生成构建文件 构建成功之后运行一下命令进行编译 ```bash autoninja -C out/Default chrome ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/oA1Dtz6KPpD9RNUe.webp) 如果你想实现console.log(window.myCode);这样的效果 将 my_code_binding.h 修改为 ```cpp #ifndef CONTENT_RENDERER_MY_CODE_BINDING_H_ #define CONTENT_RENDERER_MY_CODE_BINDING_H_ #include "v8/include/v8.h" namespace content { class MyCodeBinding { public: static void Install(v8::Local<v8::Context> context); }; } #endif ``` 将前面的 my_code_binding.cc 修改为 ```cpp #include "content/renderer/my_code_binding.h" #include "base/my_globals.h" #include "v8/include/v8.h" namespace content { void MyCodeBinding::Install(v8::Local<v8::Context> context) { v8::Isolate* isolate = context->GetIsolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Object> global = context->Global(); global->Set( context, v8::String::NewFromUtf8(isolate, "myCode").ToLocalChecked(), v8::String::NewFromUtf8(isolate, base::kMyCode).ToLocalChecked()) .Check(); } } ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/z37cVJciKWw0SGhf.webp)

魔改chromium源码——canvas指纹修改 第一节

在进行以下操作之前,请确保已完成之前文章中提到的 [源码拉取及编译](https://www.codefather.cn/post/1922187045236019201) 部分。 如果已顺利完成相关配置,即可继续执行后续操作。 --- # Canvas 指纹技术简介 Canvas 指纹 是一种基于浏览器的隐式用户标识技术,利用 HTML5 的 <canvas> 元素及其绘图功能生成设备或浏览器的唯一标识符。其核心原理是:不同设备、浏览器或操作系统在渲染相同的图形内容时,可能会产生细微的差异。这些差异可以被提取并转换为一个唯一的“指纹”,从而用于识别用户的设备或浏览器。 # Canvas 指纹的实现过程 以字节某音产品中的 Canvas 指纹生成代码为例,分析其实现过程: ```javascript function canvasFingerprint(e = 3735928559) { var urlStr; // 创建 Canvas 元素并设置尺寸 var canvasEl = document.createElement('canvas'); canvasEl.width = 100; canvasEl.height = 100; // 获取绘图上下文并绘制内容 var canvas = canvasEl.getContext('2d'); canvas.fillStyle = '#0078d4'; canvas.fillRect(0, 0, 100, 100); canvas.font = '14px serif'; // 设置字体样式 canvas.fillText('龘ฑภ경', 2, 12); // 绘制多语言字符 canvas.shadowBlur = 2; // 添加阴影效果 canvas.showOffsetX = 1; // 自定义属性(非标准) canvas.showColor = 'lime'; // 自定义属性(非标准) canvas.arc(8, 8, 8, 0, 2); // 绘制圆弧 canvas.stroke(); // 描边 // 将 Canvas 内容转换为 Base64 数据 URL urlStr = canvasEl.toDataURL(); // 基于数据 URL 计算哈希值 for (var i = 0; i < 32; i++) { e = (65599 * e + urlStr.charCodeAt(e % urlStr.length)) >>> 0; } return e; // 返回最终的指纹值 } // 调用函数生成指纹 canvasFingerprint(); ``` # 实现步骤解析 1. **创建 Canvas 元素:** 使用 document.createElement('canvas') 创建一个 <canvas> 元素,并设置其宽度为 100 像素,高度为 100 像素。 这些尺寸经过精心设计,既能保证足够的复杂性,又能避免过高的计算开销。 2. **绘制图形内容:** 设置字体样式为 14px serif,并在画布上绘制包含多种语言字符的文本 '龘ฑภ경'。这种多语言字符组合增加了渲染结果的多样性。 添加阴影效果(shadowBlur)和绘制一个绿色圆弧(arc),进一步丰富图形内容。 3. **提取渲染结果:** 使用 canvasEl.toDataURL() 方法将画布内容转换为 Base64 编码的数据 URL。这个 URL 包含了当前设备对图形内容的渲染结果。 4. **计算哈希值:** 遍历数据 URL 的字符序列,通过一个自定义的哈希算法(基于乘法因子 65599 和字符 ASCII 值)计算出一个 32 位整数作为最终的指纹值。 这个哈希值具有高区分度,能够唯一标识当前设备或浏览器。 在src\third_party\blink\renderer\modules\canvas\canvas2d\canvas_2d_recorder_context.cc所在目录中,新建一个random_hex_color.h文件。文件内容如下 ```cpp #ifndef RANDOM_COLOR_H #define RANDOM_COLOR_H #include <array> #include <random> #include <string> #include "base/time/time.h" // 用于动态种子 namespace content { // 声明并定义 getRandomColorCode 函数 inline std::string getRandomColorCode() { // 十六进制字符集合 const std::array<char, 17> hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', '\0'}; // 静态字符数组,用于存储颜色代码('#' + 6个字符 + '\0') static thread_local std::array<char, 8> colorCode; // 初始化伪随机数生成器 static thread_local std::mt19937 gen(static_cast<unsigned>(base::Time::Now().ToTimeT())); static std::uniform_int_distribution<> dis(0, 15); // 生成 [0, 15] 范围内的随机数 // 构造颜色代码字符串 colorCode[0] = '#'; // 设置第c一个字符为 '#' for (int i = 1; i < 7; ++i) { colorCode[i] = hexChars[dis(gen)]; // 随机选择一个十六进制字符 } colorCode[7] = '\0'; // 添加字符串终止符 return colorCode.data(); // 返回指向静态数组的指针 } } #endif // RANDOM_COLOR_H ``` 在src\third_party\blink\renderer\modules\canvas\canvas2d\canvas_2d_recorder_context.cc目录中,找到底下这段函数的源码。 **我们以修改 canvas 的 fillStyle 值为例进行说明。对于其他属性的随机化处理,可以参考同样的思路:找到对应的代码位置,并按照类似的方式调整逻辑。这里不再逐一演示,但方法思路是通用的。** 如果在这个文件找不到这个代码,全局搜索一下canvas2d文件夹,不同版本的 chromium 源码结构会有些许差别,但功能是一致的 ```cpp #include "third_party/blink/renderer/modules/canvas/canvas2d/random_color.h" // 引入头文件 void Canvas2DRecorderContext::setFillStyle(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exception_state) { ValidateStateStack(); CanvasRenderingContext2DState& state = GetState(); // This block is similar to that in setStrokeStyle(), see comments there for // details on this. if (value->IsString()) { // v8::Local<v8::String> v8_string = value.As<v8::String>(); 这行注释 // 新增的 std::string color_code_str = content::getRandomColorCode(); const char* color_code = color_code_str.c_str(); // 新增的 v8::Local<v8::String> v8_string = v8::String::NewFromUtf8(isolate, color_code, v8::NewStringType::kNormal).ToLocalChecked(); UpdateIdentifiabilityStudyBeforeSettingStrokeOrFill( v8_string, CanvasOps::kSetFillStyle); if (state.IsUnparsedFillColor(v8_string)) { return; } Color parsed_color = Color::kTransparent; if (!ExtractColorFromV8StringAndUpdateCache( isolate, v8_string, exception_state, parsed_color)) { return; } if (state.FillStyle().IsEquivalentColor(parsed_color)) { state.SetUnparsedFillColor(isolate, v8_string); return; } state.SetFillColor(parsed_color); state.ClearUnparsedFillColor(); state.ClearResolvedFilter(); return; } V8CanvasStyle v8_style; if (!ExtractV8CanvasStyle(isolate, value, v8_style, exception_state)) { return; } UpdateIdentifiabilityStudyBeforeSettingStrokeOrFill(v8_style, CanvasOps::kSetFillStyle); switch (v8_style.type) { case V8CanvasStyleType::kCSSColorValue: state.SetFillColor(v8_style.css_color_value); break; case V8CanvasStyleType::kGradient: state.SetFillGradient(v8_style.gradient); break; case V8CanvasStyleType::kPattern: if (!origin_tainted_by_content_ && !v8_style.pattern->OriginClean()) { SetOriginTaintedByContent(); } state.SetFillPattern(v8_style.pattern); break; case V8CanvasStyleType::kString: { Color parsed_color = Color::kTransparent; if (ParseColorOrCurrentColor(v8_style.string, parsed_color) == ColorParseResult::kParseFailed) { return; } if (!state.FillStyle().IsEquivalentColor(parsed_color)) { state.SetFillColor(parsed_color); } break; } } state.ClearUnparsedFillColor(); state.ClearResolvedFilter(); } ``` 新增头文件 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/rnci4hUNDKfYQlq4.webp" alt="在这里插入图片描述" width="100%" /> 先将v8::Local<v8::String> v8_string = value.As<v8::String>();这行注释掉,然后新增底下两行代码。 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/zykAcU2GWg3Zfxt7.webp" alt="image.png" width="100%" /> 修改完了之后保存,然后执行以下命令编译 ```sh autoninja -C out/Default chrome ``` 创建一个index.html文件,文件内容如下: ```html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <title>canvas</title> </head> <body> <canvas class="" id="myCanvas1" width="100" height="100"></canvas> <canvas class="" id="myCanvas2" width="100" height="100"></canvas> <script> function canvasFingerprint (urlStr) { var e = 3735928559 // 基于数据 URL 计算哈希值 for (var i = 0; i < 32; i++) { e = (65599 * e + urlStr.charCodeAt(e % urlStr.length)) >>> 0; } return e; // 返回最终的指纹值 } function cavasCreate (el) { // 同一段逻辑的canvas做多次渲染 const canvas = document.getElementById(el); const ctx = canvas.getContext('2d'); ctx.fillStyle = '#0078d4'; ctx.fillRect(0, 0, 100, 100); ctx.font = '14px Arial, sans-serif'; ctx.fillStyle = 'black'; ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; ctx.shadowBlur = 2; ctx.fillText('龘ฑภ경', 10, 50); const urlStr = canvas.toDataURL(); return urlStr } const urlStr1 = cavasCreate('myCanvas1') const urlStr2 = cavasCreate('myCanvas2') // 调用函数生成指纹 console.log(canvasFingerprint(urlStr1)); console.log(canvasFingerprint(urlStr2)); </script> </body> </html> ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/nq7HomaWb1cXUYcn.png) <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/yBDqTYCUzJrXLsMp.png" alt="在这里插入图片描述" width="641px" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/CJpLiIS4thunozC8.png" alt="在这里插入图片描述" width="675px" /> --- # 特别说明 通过对上述随机化逻辑的分析和实现,可以发现一种简单而有效的检测方法: 对同一段 Canvas 逻辑代码多次执行,并对比每次生成的结果。如果所有结果完全一致,则说明浏览器环境未被篡改,处于正常状态;反之,如果结果存在不一致,则表明相关函数内容或者浏览器环境已被篡改。 **关于这一问题的解决方案,我们将在下一节详细探讨。**

魔改chromium源码——绕过无限debugger反调试

在进行以下操作之前,请确保已完成之前文章中提到的 [源码拉取及编译](https://www.codefather.cn/post/1922187045236019201) 部分。 如果已顺利完成相关配置,即可继续执行后续操作。 --- 在浏览器中实现“无限 debugger”的反调试技术是一种常见的手段,用于防止他人通过开发者工具对网页进行调试或逆向分析。这种技术的核心是利用 JavaScript 的 debugger 语句,当开发者工具打开时,debugger 会触发断点,从而阻止代码继续执行。 以下是如何实现“无限 debugger”的几种方式 使用 setInterval ```javascript setInterval(() => { debugger; }, 100); ``` 递归调用 ```javascript function infiniteDebugger() { debugger; infiniteDebugger(); } infiniteDebugger(); ``` 事件监听 ```javascript document.addEventListener('keydown', (event) => { if (event.key === 'F12' || (event.ctrlKey && event.shiftKey && event.key === 'I')) { debugger; } }); document.addEventListener('contextmenu', (event) => { debugger; }); ``` 检测开发者工具状态 ```javascript setInterval(() => { const start = performance.now(); debugger; const end = performance.now(); // 如果执行时间过长,说明开发者工具可能已打开 if (end - start > 100) { console.clear(); // 清空控制台 debugger; // 再次触发断点 } }, 500); ``` 使用console.debug ```javascript const hook = new Error() let num = -1 Object.defineProperty(hook, "stack", { get: function () { num += 1 num ? debugger : '' return num; } }); function isOpenDevTool() { num = -1 console.debug(hook); hook.stack requestAnimationFrame(isOpenDevTool); } requestAnimationFrame(isOpenDevTool); ``` 在了解实现原理后,我们现在的思路是让 debugger 关键字像 JavaScript 中的其他独立关键字一样,无需特定上下文即可单独执行。以下是一些可以在 JavaScript 中独立执行且不需要特定上下文的关键字示例: ```javascript true; false; this; null; undefined; NaN; Infinity; -Infinity; ``` 这些关键字在 JavaScript 中具有独立的意义和用途,我们的目标是使 debugger 关键字也能以类似的方式运行。 以下为修改路径 ```sh \src\v8\src\parsing\keywords-gen.h ``` 我们将 debugger 关键字替换为与 null 等效的值。当然,也可以选择其他关键字,但需确保其能够独立运行,且不依赖任何参数或上下文环境。 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/0wbxk1aXcD2yl86H.webp" alt="在这里插入图片描述" width="100%" /> 修改保存之后,执行以下命编译 ```sh autoninja -C out/Default chrome ``` 经过验证可以发现,运行 debugger 语句的效果与返回 null 是等价的。这种情况下,程序既不会抛出错误,也不会因断点而中断执行。 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/FoIL3TKZmQ6VUQkZ.webp" alt="在这里插入图片描述" width="100%" />

魔改chromium源码——navigator.webdriver 检测

在进行以下操作之前,请确保已完成之前文章中提到的 [源码拉取及编译](https://www.codefather.cn/post/1922187045236019201) 部分。 如果已顺利完成相关配置,即可继续执行后续操作。 --- [chromium源码官网 https://source.chromium.org/chromium/chromium/src](https://source.chromium.org/chromium/chromium/src) 说下修改的chromium源码思路: 首先在修改源码过检测之前,我们要知道它是怎么检测的,找到他通过哪个JS的API来做的检测,只有知道了如何检测,我们才能想办法去绕过,俗话说,知己知彼百战百胜嘛! # 绕过 navigator.webdriver 检测 在浏览器中环境对抗的时候,会通过 navigator.webdriver 来检测是否为自动化在控制网站,如果返回为true,则表示是自动化在控制,否则不是,所以我们只需要将他的返回值永远返回false即可 自动化控制返回的值 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/qZbeJZ5lVBilRHPG.png" alt="在这里插入图片描述" width="499px" /> 正常浏览器返回的值 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/KLZgxUPtVPxvPFws.png) 在chromium源码中,找到以下路径 ```sh \src\third_party\blink\renderer\core\frame\navigator.cc ``` 有以下两种修改方式,选其中一种即可。 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/1L0Sewqva24P0UZi.webp" alt="在这里插入图片描述" width="100%" /> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/45a1b116be604d29bb43ea4bb5e0bbdf.png) <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/ONHMfDN1aLldhElH.webp" alt="在这里插入图片描述" width="100%" /> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d9226745d4d340a98ab75b410e9ac6ef.png) 修改完之后保存代码即可,在修改的时候不要将原始代码删除,注释即可方便出错的时候查问题。 保存好了之后,运行以下命令,这个命令就是前面编译章节中的编译命令,你当时编译的时候用的是什么,这里就写什么,他不是重新编译,是增量编译,只对新增的内容进行编译,所以速度会快很多。 ```sh autoninja -C out/Default chrome <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/XMb2pHdURJPFdEtR.webp" alt="在这里插入图片描述" width="100%" /> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3cb7167285ac498885a24894a38f5e01.png) 编译好之后,通过自动化程序检测一下 我们这里使用 playwright 来测试 ```python from playwright.sync_api import sync_playwright def run(): with sync_playwright() as p: browser = p.chromium.launch( executable_path="E:\my_code\chromium\src\out\Default\chrome.exe", headless=False ) # 创建一个新的浏览器上下文 context = browser.new_context() # 打开一个新页面 page = context.new_page() # 访问目标网站 page.goto("https://www.baidu.com") # 等待用户输入后再关闭浏览器 input("Press Enter to close the browser...") # 关闭浏览器 browser.close() if __name__ == "__main__": run() ``` 可以看到是已经成功了 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/RGcpK12i3nLMBppq.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/363bd93d8c054e47b101684677a3210b.png)

魔改chromium源码——源码拉取及编译

[魔改chromium——基础环境搭建](https://juejin.cn/spost/7485614368596738100) 上一节在完成了基础环境搭建之后,这一节内容是将源码拉取到我们本地,为后续修改chromium源码做准备,这节内容非常漫长,速度快慢取决于你的网络和机器配置,如果网络和机器不好的话,编译和拉取代码的时间可能会超过十几个小时,在我自己的机器上拉取源码过程大约花了40分钟,机器上编译chromium过程花了6个多小时,才编译出chromium浏览器,所以,有一台好的机器和网络非常重要。 # 设置git全局环境变量 首先在新下载的powershell中执行以下命令,设置一下git全局环境变量,一行一行执行,本地代理端口号改成自己的即可 ```sh git config --global http.proxy http://127.0.0.1:10808 git config --global https.proxy http://127.0.0.1:10808 git config --global http.sslVerify false # 禁用 HTTPS 请求的 SSL 证书验证 git config --global core.autocrlf false # 禁用换行符的自动转换 git config --global core.filemode false # 禁用 Git 对文件权限模式(如可执行权限)的检测 git config --global branch.autosetuprebase always # 控制新分支的默认行为,使其在拉取(pull)时自动使用 rebase 而不是 merge 这两个可以选择性开启 git config --global core.fscache true # 启用文件系统缓存功能 git config --global core.preloadindex true # 启用索引预加载功能 ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/B0xcrGLkwKbcEUOa.webp" alt="在这里插入图片描述" width="692px" /> 设置好了之后,在depot\_tools同级目录中新建一个chromium目录 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/gutJJNcQng5YtKIm.webp" alt="在这里插入图片描述" width="505px" /> # 创建boto.cfg和设置boto.cfg 创建boto.cfg文件写入内容,本地代理的端口号proxy\_port参数改成自己的就可以 ```sh [Boto] proxy = 127.0.0.1 proxy_port = 10808 ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/x0pBFt3bQQzAFCfO.webp" alt="在这里插入图片描述" width="395px" /> 新增环境变量NO_AUTH BOTO_CONFIG,值为boto.cfg文件路径 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/Ch91hsrz62V3fynS.webp) # 拉取源码 设置好了之后,首先将powershell当前目录切换到我们新建的chromium目录,然后执行以下命令,就可以开始拉取源码了,本地代理的端口号记得改成自己的就可以 ```sh $env:HTTP_PROXY="http://127.0.0.1:10808"; $env:HTTPS_PROXY="http://127.0.0.1:10808" fetch --no-history chromium ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ec66c24aa37340498c590bfef79a7691.png) 拉取中的状态,如果你的网络不好,这将会是一个非常漫长的过程,需要耐心等待 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/QU20ftU9vS3ziYL4.webp) ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/odUGfEHcuh55jvSE.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/19447ad655144c8490dad8fc3662abdf.png) <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/L8q8Wu6G4HdAEjtq.webp" alt="在这里插入图片描述" width="100%" /> 拉取完之后,还会自动执行Hooks ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ea4d7abc8bb1407087274e90802e545d.png) ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/5HG88Vb6qPsQYqzW.webp) 当这2个都是百分之百,就说明成功了,下一步就可以执行编译了。 以下是chromium每个文件夹大概作用的介绍 | 目录名称 | 描述 | |-----------------------|----------------------------------------------------------------------------------------| | android_webview | 集成 Android 平台的外观展示 | | apps | 参考如何构建 Chrome 应用程序 | | ash | 包含了 Chrome OS 的窗口管理器和其他用户界面相关的代码 | | base | Chromium 中的基础库,包含了许多通用的数据结构、工具函数等 | | build | 包含了构建 Chromium 时使用的脚本和配置文件 | | build_overrides | 用于存放特定配置或定制化构建所需的构建脚本和覆盖文件 | | buildtools | 用于构建和编译 Chromium 项目的工具和脚本 | | cc | Chromium 的渲染合成层 | | chrome | 这个目录包含了 Chrome 浏览器特定的功能和 UI,如书签、扩展程序、设置等 | | chromecast | 与 Google Chromecast 相关的代码和功能 | | chromeos | 负责 Chrome OS 相关的功能和特性 | | codelabs | 用于存储代码示例、教程和演示性的实验项目 | | components | 用于存放浏览器各种组件,如 PDF 渲染器、插件系统、媒体播放器 | | content | Chromium 内层的代码,包含了浏览器核心功能,例如网络通信、多媒体处理等 | | courgette | 数据压缩和差分算法相关的代码 | | crypto | 用于处理加密和密码学相关操作,包含各种加密算法的实现和安全相关的功能 | | dbus | 用于处理 D-Bus(Desktop Bus)相关功能的部分 | | device | 提供跨平台底层硬件 API 抽象层 | | docs | 用于存储各种文档、说明和指南,通常用于开发者、贡献者或者维护者参考 | | extensions | 用来存放浏览器扩展(Extensions)相关代码 | | fuchsia_web | 为 Fuchsia 操作系统提供支持的一部分 | | gin | 一个用于实现 JavaScript 和 C++ 之间的互操作的库 | | google_apis | 用于存储与 Google API 相关的代码和配置 | | google_update | 负责处理 Google 更新相关的功能 | | gpu | Chromium 的 GPU 加速相关的代码,用于提高浏览器的性能 | | headless | 实现 Chromium 的无头(headless)模式 | | infra | 存储与基础设施、自动化测试以及持续集成/持续部署(CI/CD)相关的代码和配置文件 | | internal | 存放一些内部工具、测试或者辅助代码,而不是直接与浏览器的核心功能相关 | | ios | 存储与 iOS 平台相关的代码和资源 | | ios_internal | 存储与 iOS 平台相关的内部实现代码和工具 | | ipc | 用于实现进程间通信(IPC)的关键组件之一 | | media | Chromium 的媒体框架,包括音频和视频播放等功能 | | mojo | 一个跨进程通信框架,用于在 Chromium 浏览器的不同进程之间进行高效的通信 | | native_client | 存放 Native Client(NaCl)相关代码的地方 | | native_client_sdk | Native Client 的软件开发工具包(SDK) | | net | Chromium 开发的网络库,使用 WebKit 下的 test_shell 时可以单独使用 | | out | 编译输出的目录 | | pdf | PDF 文档相关的代码和功能 | | ppapi | Pepper 插件 API(PPAPI)的实现 | | printing | 处理打印相关的功能 | | remoting | 包含与远程桌面相关的代码 | | rlz | 处理 RLZ(Referrer Language Zwiebelbrot)相关功能 | | sandbox | 包含了 Chromium 的沙盒功能,用于增强浏览器的安全性 | | services | 包含了 Chromium 的后台服务代码,处理一些后台任务和服务 | | signing_keys | 用于存储签名密钥相关的文件 | | skia | Chromium 浏览器对图形处理的基础支持,如 2D 图形渲染、图像处理等 | | sql | 提供 SQLite 相关的模块 | | storage | 数据存储和持久性相关的代码,提供了与本地数据交互的功能 | | styleguide | 存放代码风格指南和规范的相关文件 | | testing | 包含了 Chromium 的测试代码,用于自动化测试和测试框架 | | third_party | 包含了 Chromium 使用的第三方库和工具,如 Skia、WebRTC、SQLite 等 | | tools | 包含了用于开发、测试和调试 Chromium 的工具和脚本 | | ui | 包含了 Chromium 的用户界面相关的代码,包括窗口、UI 控件等 | | url | Google 开源的 URL 解析和规范库 | | v8 | Google 开发的一款基于 JavaScript 的解释引擎 | | webkit | 将网页内容转换为可视化的页面,并提供与用户交互的基础功能,用来解析 HTML/CSS 等 | # 生成配置 在chromium目录下执行以下命令,设置生成配置 ```sh cd src && gn gen out/Default ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/Mr9zoTxhKpq0EiKE.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2279102925fb4a188ce37d0d7b7803ef.png) # 编辑配置 执行成功之后,运行一下命令编辑配置 ```sh gn args out/Default ``` 运行之后他会打开一个txt的文件,这里面就是写后面要编译的条件,这是官方的编译条件配置参考链接<https://www.chromium.org/developers/gn-build-configuration> ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/ZnXJdrRrdjktYw2l.webp) 将这些内容粘贴进去,然后保存并关闭记事本,他就会自动执行接下来的操作 ```sh # 系统 target_os="win" # 架构 target_cpu="x64" # 是否开启调试 is_debug=false # 不允许远程调试 enable_remoting=false # 使用Chrome官方的编译优化建议 is_official_build=true # 分散链接编译 is_component_build=false #不使用官方API秘钥 use_official_google_api_keys=false #不使用官方API秘钥 google_api_key=false #不使用官方API秘钥 google_default_client_secret=false #关闭支持NACL,这是一种Chrome插件,因为安全性,稳定性存在问题,已经很少使用了 enable_nacl=false #支持H264编码 proprietary_codecs=true #解码器 ffmpeg_branding="Chrome" #使用无符号或最少符号实现更快的构建 symbol_level=0 #删除v8符号 v8_symbol_level=0 #删除WebCore符号 blink_symbol_level=0 #Chrome品牌标识和实验性标志 disable_fieldtrial_testing_config=true ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/jAX4Oh2CkABO1Mzj.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/268c99a676d04296ad9136ce05f49bd8.png) 记事本打开chromium目录下的 .gclient 文件 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/V2zEBBwJaL8tVtSs.webp) 将内容新增到这个位置 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/A5uF4qkgOTgg4Hva.webp) 修改保存之后,根据前面报错日志中的提示,再运行一次以下命令 ```sh gclient runhooks ``` <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/6d3gmyw6LUwdgIIA.webp" alt="在这里插入图片描述" width="100%" /> gclient runhooks运行成功之后,再从之前的编辑配置步骤重新执行一下 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e43d574814ef49258748e5cb70a4f7a6.png) ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/LJ8yOPqifShR62KQ.webp) # 编译chromium 以上都正常之后,就可以开始编译了,执行以下命令即可,这是一个非常漫长的过程,所以如果你是笔记本,请连接好你的电源,保证你的网络一直是畅通的,保持屏幕常亮,关闭所有杀毒软件的目录扫描,关闭所有会让你电脑息屏的软件 ```sh autoninja -C out/Default chrome ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/0BbYsrGUzBLfNbRS.webp) 大约53000多个组件要编译,时间的话,需要几个小时到几十个小时不等,具体看你的机器配置,全程几乎跑满CPU,编译好了之后结果会生成在 chromium目录下的 src/out/Default 目录。然后运行chrome.exe, 正常之后,说明我们编译配置和编译环境是ok的,就可以到下一节修改源码内容了 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/t06TBUi0sss0VMl3.webp" alt="在这里插入图片描述" width="100%" /> ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/5oEzwUSoPUvKNZIz.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ebd2aca22c4d461cb2dd4f973b78f03c.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/07a08010471b42ce8a5e14ead1724f84.png) 下一节就是修改chromium源码,实现一些我们想要实现的功能

魔改chromium源码——基础环境搭建

[谷歌chromium环境要求详细文档](https://chromium.googlesource.com/chromium/src.git/+/HEAD/docs/windows_build_instructions.md) # 软件和环境要求,必须安装,硬性要求 * 系统环境:Windows 10,内存最小8GB,推荐16GB+,NTFS格式磁盘最少100GB空间 * Git版本:安装最新版本即可,[Git桌面端下载地址](https://git-scm.com),会自动将Git添加到系统Path * Visual Studio版本:[Visual Studio 2022 IDE](https://visualstudio.microsoft.com/zh-hans/vs/) 不要下载错了版本,目前谷歌chromium文档的环境要求是Visual Studio 2022,后面随着chromium迭代可能会是其他版本,严格按照官方文档要求来就不会有错 * NodeJS:下载最新的TLS版本就可以,[NodeJS官网链接](https://nodejs.cn/download/) * python环境:python 10.0 版本即可,也可以装最新的 python 13.0 版本,但不建议 * PowerShell:安装最新版本即可,不要用电脑自带的,自带的PowerShell版本太低,后面拉取的时候会出问题,下载地址[PowerShell](https://github.com/PowerShell/PowerShell/releases) * 准备一个相对稳定的梯子,这个非常重要,没有梯子的话,可以不用往下看了 # 安装和设置Visual Studio Visual Studio安装和C++组件要求: <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/EJswLTaHxmERiOuw.webp" alt="在这里插入图片描述" width="100%" /> 图片中这些C++组件是最基础的要求,如果需要其他组件,可根据需要自行添加,然后是对windows 11 SDK版本要求,会有一个默认的,然后,根据谷歌chromium文档的要求,新添加谷歌要求版本的SDK即可,所以我这里会有2个win11的SDK <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/GkxiHl8OLaVaQNX2.webp" alt="在这里插入图片描述" width="100%" /> ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/3sS5DWIwc1iTVFDv.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f9d29efe78d14b9f8e2e45fc1f06eeb8.png) 选择好组件之后,安装即可,如要要修改安装位置,可以在此处修改安装位置 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/dIiUzpEbhRBXJUSK.webp) 安装好了之后,将vs2022\_install设置到环境变量中,变量值就是你安装的Visual Studio路径 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/vziKb3iWLbwCQJDV.webp) # 安装Debugging Tools 在windows的应用列表中,添加Debugging Tools 选择修改 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/d7ARt9qm6eLPMkDB.webp) 选择change <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/VesqTdH094LKZcTi.webp" alt="在这里插入图片描述" width="100%" /> 选择change,等待下载安装即可 <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/czj1qr5UPiNkXeJu.webp" alt="在这里插入图片描述" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1922184057994162177/RNTkHoR90uLZfr3r.webp" alt="在这里插入图片描述" width="100%" /> # 下载和设置depot_tools 下载depot\_tools捆绑文件 下载方式一:<https://storage.googleapis.com/chrome-infra/depot_tools.zip> 下载方式二: ```sh git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git ``` ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/cn5RXbrG7dgIDhlJ.webp) 下载好之后将压缩包中的depot\_tools文件夹解压出来,随便解压到你自己能找到的地方就就可以,然后将这个depot\_tools目录设置到系统变量中 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/37475c3a776e43a9a12667e14477f730.png) 在系统变量中设置DEPOT\_TOOLS\_WIN\_TOOLCHAIN,值为0 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/PXeZvlQCULDEwqgQ.webp) # 修改depot_tools中的cipd.bat文件 打开cipd.bat文件,然后搜索关键字":CLEAN\_BOOTSTRAP" 将这个位置的powershell修改为下面的pwsh,修改完成保存即可 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/XhViOa5s2R0qq7Pk.webp) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d9deb15324aa484fb247cc38f83fc27e.png) ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/lcCaJJpLJ8ba0Aje.webp) # 初始化依赖 以上都设置好了之后,就可以开始初始化依赖,运行以下命令,本地代理的端口号改成自己的就可以 ```sh $env:HTTP_PROXY="http://127.0.0.1:10808"; $env:HTTPS_PROXY="http://127.0.0.1:10808" gclient ``` 初始化完成之后,就会出现以下结果,说明环境已经基本搭建完成,下一节内容就是如何拉取chromium代码 ![在这里插入图片描述](https://pic.code-nav.cn/post_picture/1922184057994162177/bBx5TWeBWJfLfaTw.webp)

公司想要做一个网络爬虫相关的项目,需要注意什么

### 个人情况 公司想要做一个网络爬虫相关的项目,公司官网的pc网站,然后会涉及到网络爬虫,想请问一下,做这个东西需要注意什么,想请教一下鱼皮大佬(目前看来爬虫的文章是不做商业化,也会表明爬取来源) ### 具体问题 需要注意什么,该不该给公司做这个爬虫

逮到一个爬我们网站的用户,手法拙劣到我想笑

大家好,我是程序员鱼皮。昨天逮到一个爬取我们 [程序员面试刷题网站 - 面试鸭](https://www.mianshiya.com/) 的题目的用户,小伙子可能自以为很聪明,实际上手法非常拙劣!我一分钟不到就把他揪出来了。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/9bd0ac9ae9d7c3b2e560005e4dd1057b.webp" alt="" width="414px" /></p> 下面聊一聊我是怎么做的,也借这个事情,给大家分享一下如何快速定位爬虫和攻击者的方法。 ## 如何纠出攻击者? ### 发现猫腻 生活中有人偷了你的东西,肯定要看监控。抓爬虫用户也是一样,先看监控。 像我是怎么发现网站被爬了呢?首先我们系统内部有一套识别爬虫的策略,其次我们每天都会关注网站的流量情况,有些异常情况一眼便可看出。 比如这次事件当天,我先通过系统云服务的监控看了下近 6 个小时向我们发送请求的客户端 IP,除去第一个是我们自己的 IP 外,其他几个 IP 的请求数都远超我们的业务平均值,显然这些 IP 不对劲! <img src="https://pic.code-nav.cn/migrate_image/873df95b91952879d1667b82494c829f.webp" alt="" width="100%" /> 那这些 IP 都在请求什么资源呢?通过监控查看 URL 路径访问排行,发现获取题目信息的接口被调用了 10 万多次! <img src="https://pic.code-nav.cn/migrate_image/afb3a6cb89619656206ece1d7f34df69.webp" alt="" width="100%" /> 然后我就根据这个路径进行筛选,查看访问它的客户端 IP 排行,除去第一个是我们自己的 IP 外,第二个 IP 的请求数显然有问题! <img src="https://pic.code-nav.cn/migrate_image/9bef1fddd8bf36e15a20fc32aae2ef57.webp" alt="" width="100%" /> 然后根据这个 IP 进行筛选,分析他的请求情况: <img src="https://pic.code-nav.cn/migrate_image/a72af04feaa245a2a4f141c3ffc525a6.webp" alt="" width="100%" /> 看到这个请求图,一眼就知道是爬虫了!我反正不信有正常用户半夜连续刷题 6 个多小时,你见过凌晨四点的面试鸭么? 看到这个 “纵享丝滑” 的曲线,我有点哭笑不得。虽然小伙子应该是在爬取的时候加了一些随机数导致每分钟的请求有一些细微的区别,但在监控面前,显得过于拙劣了。 **识别爬虫的基本方法,就是看爬虫的特征、请求频率和模式。** 哪怕你自以为很聪明,每天只爬一点题目,只要筛选系统一段时间的访问情况、或者用系统自动分析一波访问记录,是否为正常用户还是一目了然。 不过其实我有点小感动的,选择在半夜这个时间段下手,你人还怪好哩,这是不想给我们系统施加压力吧。 <p align="center"> <img src="https://pic.code-nav.cn/migrate_image/7c5ba13478a42b2634fef17d5bb023f6.webp" alt="" width="214px" /></p> ### 找到真凶 发现这个 IP 不对劲后,怎么知道他是我们面试鸭的哪个用户呢? 当然是看日志了!开发线上系统,肯定要有 IP 访问请求的日志记录。我直接在业务服务器的日志中根据 IP 和时间段进行筛选,就看到了该用户发送的请求和参数,什么时间访问了哪道题目都很清楚: <img src="https://pic.code-nav.cn/migrate_image/5ba04d89e16549097793c42f51b95650.webp" alt="" width="100%" /> 这个时候,再去业务系统里看下是哪个 id 的用户在这些时间段访问了这些题目,就能查出来了。 还有一种方法,他想要获取到题目信息,一定要先登录,在日志中搜索该 IP 请求 “登录接口” 或者 “获取登录用户信息接口” 的记录,查看接口的返回值,就知道是哪个用户了。 <img src="https://pic.code-nav.cn/migrate_image/9c1417179f6bca712d3a4190cd973424.webp" alt="" width="100%" /> 豁,竟然还是个永久会员用户!何必呢? <p align="center"><img src="https://pic.code-nav.cn/migrate_image/75948bad1267448297b937b6797e7266.webp" alt="" width="228px" /></p> --- 通过本次事件,再给大家一些系统开发时的建议: 1. 一定要做好请求的日志记录,不要嫌麻烦、不要嫌占用空间,关键时刻它是帮你定位业务异常的核心线索。 2. 要对请求经过的每个环节(比如 CDN、网关、业务服务器)进行监控,如果没有监控,除了问题时你会毫无头绪,只能从海量日志中去大海捞针找线索。一般第三方云服务的监控都比较成熟,直接接入就好了。 3. 要养成看监控的习惯,不是说系统挂了才要看监控,很多潜在的风险只有通过监控(或者告警)才能发现。 4. 可以给获取重要数据的接口增加更多的信息记录,最好跟系统的某个用户关联起来,便于定位非法用户。 最后,弱弱地呼吁一下,不要再爬我们的[刷题网站](https://www.mianshiya.com/)了,本来小程序和网页端就都是支持刷题的,很快我们还会上线 IDEA 摸鱼刷题插件,尽力保证大家的刷题体验! ![msyscreen.png](https://pic.code-nav.cn/post_picture/1626574509983178753/C0SGT6503vekiHQl.webp) ## 更多 💻 编程学习交流:[编程导航](https://www.codefather.cn) 📃 简历快速制作:[老鱼简历](https://laoyujianli.com) ✏️ 面试刷题神器:[面试鸭](https://mianshiya.com)

下载 APP