本記事は筆者の考えをベースに、ChatGPT 5.5 を用いて構成・推敲したものです。
内容の主張と判断は筆者によるものであり、文章表現の整理に ChatGPT 5.5 を活用しています。
背景
Web ページを解析していると、DevTools を開いた瞬間に alert が出たり、コンソールが消されたり、ページ遷移が発生したりすることがあります。
一度だけ調べるなら、Chrome で JS を整形して、ブレークポイントを置いて、手動で回避しても構いません。
しかし、似たようなページを何度も分析するなら、毎回手作業で対応するよりも、分析用の小さなローカルツールにしてしまった方が効率的です。
今回は ZYC.Framework で最小構成の WebView2 Host を作り、ページ自身のスクリプトが動く前に反アンチデバッグ用の JS を注入します。
現象の再現
仮に、以下のような検証用ページを分析しているとします。
https://tv.verizon.com/watch/home/
通常表示では問題ありませんが、Chrome DevTools を開くと、ページがすぐに反応します。
JS を整形して追っていくと、次のような処理にたどり着きます。
key: "detect",
value: function() {
var e = this,
t = b(function() {
V(e.largeObjectArray)
}),
n = b(function() {
U(e.largeObjectArray)
});
if (this.maxPrintTime = Math.max(this.maxPrintTime, n),
f(),
0 === t || 0 === this.maxPrintTime)
return !1;
t > 10 * this.maxPrintTime && (
2 <= this.count
? this.onDevToolOpen()
: (this.count++, this.detect())
)
}
このコードは何を検出しているのか
この種のアンチデバッグ処理で重要なのは、alert そのものではありません。
本質は「DevTools が開いている時だけ、特定の処理が遅くなる」という差分です。
上のコードでは、largeObjectArray のような大きなオブジェクトまたは配列を console 系の処理に渡しています。
DevTools が閉じている場合、console への出力コストは比較的小さいです。
しかし DevTools が開いていると、ブラウザーはそのオブジェクトをプレビュー表示したり、展開可能な形に整えたりするため、処理時間が大きく増えることがあります。
その差分を使って、次のような条件で DevTools の起動を推定しています。
t > 10 * this.maxPrintTime
この条件を何度か満たすと、最終的に以下のような処理が呼ばれます。
this.onDevToolOpen()
実際のページでは、これ以外にも次のような検出が組み合わされていることがあります。
- ウィンドウサイズ検出
-
debugger文による停止検出 -
setIntervalによる周期的チェック -
Date.now()/performance.now()の時間差検出 - 関数の
toString()改ざん検出
ただし、この記事ではまず console に大きなオブジェクトを出力するタイプの検出に絞ります。
最小の回避方針
検出の仕組みが「大きな配列を console に出して、その処理時間を見る」のであれば、対策は単純です。
ページの JS より先に console.log などを hook し、明らかに怪しい大きな配列が渡された場合だけ、その出力を握りつぶします。
通常のログはそのまま出力します。
また、ページ側に console.clear() を呼ばれると解析中のログが消えてしまうため、これも無効化しておきます。
const methods = ["log", "debug", "info", "warn", "error", "dir", "table"];
for (const name of methods) {
const raw = console[name];
console[name] = function (...args) {
if (args.some(x => Array.isArray(x) && x.length > 20)) {
return;
}
return raw.apply(this, args);
};
}
console.clear = function () {};
この程度なら Tampermonkey でも実現できます。
ただし、.NET 側から WebView2 を制御し、ページ起動・スクリプト注入・拡張機能・プロキシをまとめて扱いたい場合は、ZYC.Framework を使うと構成しやすくなります。
なぜ ZYC.Framework を使うのか
ZYC.Framework は WebView2 をラップしており、WPF / .NET のコードから Web ページ分析用の Host を作れます。
この用途で特に便利なのは次の点です。
- WebView2 の文書作成段階で JS を注入できる。
- WebView2 のブラウザー拡張機能を扱える。
- ローカル HTTP プロキシ付きの WebView Host を構成できる。
- WPF アプリとして UI、設定、ログ、タブ管理などを組み合わせやすい。
つまり、単にブラウザーでページを開くのではなく、分析対象ページの実行環境そのものをコードで組み立てられます。
最小プロジェクトを作成する
まず ZYC.Framework CLI をインストールします。
dotnet tool install --global ZYC.Framework.CLI --version 1.3.6
すでにインストール済みの場合は更新します。
dotnet tool update --global ZYC.Framework.CLI --version 1.3.6
次に、最小テンプレートでプロジェクトを作成します。
zyc new JsAntiAntiDebugDemo --template minimal
View を WebView2 Host に変更する
minimal テンプレートで生成される View は、デフォルトでは通常の WPF UserControl です。
今回は WebView2 を使いたいので、View を WebViewHostBase 継承に変更します。
XAML 側のルートも、通常の UserControl ではなく webView2:WebViewHostBase にします。
<webView2:WebViewHostBase x:Class="JsAntiAntiDebugDemo.UI.JsAntiAntiDebugDemoView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:webView2="clr-namespace:ZYC.Framework.WebView2;assembly=ZYC.Framework.WebView2">
</webView2:WebViewHostBase>
次に C# 側です。
InternalWebViewHostLoadedAsync() で、ページに移動する前に AddScriptToExecuteOnDocumentCreatedAsync() を呼び出します。
using Autofac;
using ZYC.CoreToolkit.Extensions.Autofac.Attributes;
using ZYC.Framework.WebView2;
namespace JsAntiAntiDebugDemo.UI;
[Register]
internal partial class JsAntiAntiDebugDemoView : WebViewHostBase
{
public JsAntiAntiDebugDemoView(ILifetimeScope lifetimeScope) : base(lifetimeScope)
{
}
protected override async Task InternalWebViewHostLoadedAsync()
{
await CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("""
const methods = ["log", "debug", "info", "warn", "error", "dir", "table"];
for (const name of methods) {
const raw = console[name];
console[name] = function (...args) {
if (args.some(x => Array.isArray(x) && x.length > 20)) {
return;
}
return raw.apply(this, args);
};
}
console.clear = function () {};
""");
await NavigateAsync("https://tv.verizon.com/watch/home/");
}
}
ここで重要なのは、次の API です。
AddScriptToExecuteOnDocumentCreatedAsync
これはページの読み込み完了後にスクリプトを実行する API ではありません。
文書が作成される段階でスクリプトを注入します。
アンチデバッグ処理はページのかなり早い段階で動き始めることが多いため、後から ExecuteScriptAsync() で注入するより、このタイミングの方が適しています。
注入前後の違い
注入前は、DevTools を開くと次のような挙動が発生します。
-
alertが表示される。 - コンソールが消される。
- ページが遷移する。
- 解析用のログが追えなくなる。
- 一部の処理が意図的に重くなる。
注入後は、console に渡される大きな配列だけを握りつぶします。
その結果、console のレンダリング時間を使った検出が成立しにくくなります。
通常のログはそのまま残るため、分析作業への影響も小さくできます。
さらに拡張するなら
今回の対策は、あくまで console 大オブジェクト出力による検出への最小対応です。
実際のページでは、他の検出方法も組み合わされることがあります。
例えば、debugger 文を動的に生成するコードには、次のような対策が考えられます。
const rawFunction = Function;
Function = function (...args) {
if (args.some(x => typeof x === "string" && x.includes("debugger"))) {
return function () {};
}
return rawFunction.apply(this, args);
};
ほかにも、用途に応じて次のような機能を追加できます。
- ウィンドウサイズ検出の緩和
-
setIntervalによる高頻度チェックの制御 -
performance.now()の差分検出対策 -
console関数のtoString()偽装 - 特定ドメインだけに注入するルール
- ログ出力の保存
- WebView2 拡張機能との連携
- プロキシ経由でレスポンス内の JS を書き換える処理
ZYC.Framework のプロキシ付き WebView Host を使えば、ページ実行後の hook だけでなく、レスポンス段階で JS ファイルを書き換える構成も作れます。
まとめ
この記事で紹介したのは、特定サイト専用の小技ではありません。
重要なのは、次のような分析フローです。
- まずアンチデバッグの現象を再現する。
- 整形した JS から検出箇所を探す。
- 検出原理を確認する。
- 最小限の回避スクリプトを書く。
- ページ自身の JS より前に注入する。
- その流れを ZYC.Framework 上のローカルツールにする。
一回限りの調査なら、ブラウザー上で手作業しても十分です。
しかし、同じような分析を繰り返すなら、WebView2、拡張機能、ローカルプロキシ、WPF UI をまとめて扱える環境を作った方が楽になります。
ZYC.Framework を使うと、.NET 開発者にとって扱いやすい形で Web 分析用のデスクトップツールを組み立てられます。
ZYC.Framework について
ZYC.Framework は、.NET 10 と WPF をベースにした、モジュール型・拡張可能なデスクトップ自動化開発フレームワークです。モジュールアーキテクチャを中核に、マルチワークスペース/マルチタブ UI、WebView2、Blazor、.NET Aspire との連携を備えており、モダンなデスクトップアプリ、社内ツール、複雑な自動化システムの構築に適しています。
- GitHub: ZYC.Framework
- NuGet: ZYC.Framework.Alpha
- License: MIT




