|
|
""" |
|
|
IVT 봇 트래픽 생성기 |
|
|
- 다양한 IVT 패턴 시뮬레이션 |
|
|
- 테스트 목적 전용 |
|
|
""" |
|
|
|
|
|
import gradio as gr |
|
|
import requests |
|
|
import random |
|
|
import string |
|
|
import time |
|
|
import threading |
|
|
from datetime import datetime |
|
|
from concurrent.futures import ThreadPoolExecutor |
|
|
import json |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOT_USER_AGENTS = { |
|
|
"googlebot": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", |
|
|
"bingbot": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", |
|
|
"yandex": "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", |
|
|
"baidu": "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", |
|
|
"curl": "curl/7.68.0", |
|
|
"wget": "Wget/1.20.3 (linux-gnu)", |
|
|
"python_requests": "python-requests/2.28.0", |
|
|
"scrapy": "Scrapy/2.7.0 (+https://scrapy.org)", |
|
|
"selenium": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Selenium", |
|
|
"puppeteer": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36", |
|
|
"phantomjs": "Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1", |
|
|
"java": "Java/17.0.1", |
|
|
"go_http": "Go-http-client/1.1", |
|
|
"node_fetch": "node-fetch/1.0 (+https://github.com/node-fetch/node-fetch)", |
|
|
"axios": "axios/1.4.0", |
|
|
"httpx": "python-httpx/0.24.0", |
|
|
"postman": "PostmanRuntime/7.32.0", |
|
|
} |
|
|
|
|
|
NORMAL_USER_AGENTS = [ |
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", |
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", |
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", |
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15", |
|
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1", |
|
|
"Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", |
|
|
] |
|
|
|
|
|
SUSPICIOUS_REFERRERS = [ |
|
|
"https://traffic-exchange.com/surf", |
|
|
"https://autosurf.pro/visit", |
|
|
"https://free-traffic-bot.com/", |
|
|
"https://paid-to-click.net/earn", |
|
|
"https://hit-exchange.org/view", |
|
|
"https://buy-cheap-traffic.com/", |
|
|
"https://cashsurf.io/browse", |
|
|
] |
|
|
|
|
|
DATACENTER_IPS = [ |
|
|
"52.94.76.0", |
|
|
"35.192.0.0", |
|
|
"20.36.0.0", |
|
|
"104.16.0.0", |
|
|
"159.89.0.0", |
|
|
"172.104.0.0", |
|
|
"45.32.0.0", |
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
traffic_log = [] |
|
|
log_lock = threading.Lock() |
|
|
|
|
|
def add_log(message: str, status: str = "info"): |
|
|
with log_lock: |
|
|
timestamp = datetime.now().strftime("%H:%M:%S") |
|
|
icon = {"success": "✅", "error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(status, "📝") |
|
|
traffic_log.append(f"[{timestamp}] {icon} {message}") |
|
|
if len(traffic_log) > 100: |
|
|
traffic_log.pop(0) |
|
|
|
|
|
def get_logs(): |
|
|
with log_lock: |
|
|
return "\n".join(traffic_log[-50:]) if traffic_log else "로그 없음" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_fingerprint(pattern_type: str, custom_settings: dict = None) -> dict: |
|
|
"""IVT 패턴에 따른 핑거프린트 생성""" |
|
|
|
|
|
fp = { |
|
|
"timestamp": datetime.now().isoformat(), |
|
|
"sessionToken": f"bot_{int(time.time())}_{random.randint(1000, 9999)}", |
|
|
} |
|
|
|
|
|
|
|
|
settings = custom_settings or {} |
|
|
|
|
|
if pattern_type == "bot_ua": |
|
|
|
|
|
bot_type = settings.get("bot_type", random.choice(list(BOT_USER_AGENTS.keys()))) |
|
|
fp["userAgent"] = BOT_USER_AGENTS.get(bot_type, BOT_USER_AGENTS["curl"]) |
|
|
fp["platform"] = "Linux x86_64" |
|
|
fp["webdriver"] = False |
|
|
fp["pluginsCount"] = "0" |
|
|
fp["hasMouseMove"] = False |
|
|
fp["hasScroll"] = False |
|
|
fp["hasClick"] = False |
|
|
fp["dwellTime"] = random.uniform(0.1, 0.5) |
|
|
|
|
|
elif pattern_type == "headless": |
|
|
|
|
|
fp["userAgent"] = random.choice([ |
|
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36", |
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36" |
|
|
]) |
|
|
fp["webdriver"] = True |
|
|
fp["webGLVendor"] = "Google Inc. (Google)" |
|
|
fp["webGLRenderer"] = "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)), SwiftShader driver)" |
|
|
fp["platform"] = "Linux x86_64" |
|
|
fp["pluginsCount"] = "0" |
|
|
fp["hasMouseMove"] = False |
|
|
fp["hasScroll"] = False |
|
|
fp["dwellTime"] = random.uniform(0.05, 0.3) |
|
|
|
|
|
elif pattern_type == "rapid_click": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = random.choice(["Win32", "MacIntel", "Linux x86_64"]) |
|
|
fp["webdriver"] = False |
|
|
fp["hasMouseMove"] = True |
|
|
fp["hasScroll"] = False |
|
|
fp["hasClick"] = True |
|
|
fp["dwellTime"] = random.uniform(0.1, 0.8) |
|
|
fp["pluginsCount"] = str(random.randint(3, 10)) |
|
|
|
|
|
elif pattern_type == "no_interaction": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = random.choice(["Win32", "MacIntel"]) |
|
|
fp["webdriver"] = False |
|
|
fp["hasMouseMove"] = False |
|
|
fp["hasScroll"] = False |
|
|
fp["hasClick"] = False |
|
|
fp["hasKeypress"] = False |
|
|
fp["dwellTime"] = random.uniform(0.5, 2.0) |
|
|
fp["pluginsCount"] = str(random.randint(0, 2)) |
|
|
|
|
|
elif pattern_type == "datacenter_ip": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = "Linux x86_64" |
|
|
fp["webdriver"] = False |
|
|
fp["hasMouseMove"] = random.choice([True, False]) |
|
|
fp["dwellTime"] = random.uniform(1, 5) |
|
|
|
|
|
fp["_simulated_datacenter"] = True |
|
|
|
|
|
elif pattern_type == "suspicious_referrer": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = random.choice(["Win32", "MacIntel"]) |
|
|
fp["referrer"] = random.choice(SUSPICIOUS_REFERRERS) |
|
|
fp["webdriver"] = False |
|
|
fp["hasMouseMove"] = True |
|
|
fp["dwellTime"] = random.uniform(2, 10) |
|
|
|
|
|
elif pattern_type == "device_mismatch": |
|
|
|
|
|
fp["userAgent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 Mobile Safari/604.1" |
|
|
fp["platform"] = "Win32" |
|
|
fp["screenWidth"] = "1920" |
|
|
fp["screenHeight"] = "1080" |
|
|
fp["maxTouchPoints"] = "0" |
|
|
fp["webdriver"] = False |
|
|
fp["dwellTime"] = random.uniform(3, 10) |
|
|
|
|
|
elif pattern_type == "geo_mismatch": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = random.choice(["Win32", "MacIntel"]) |
|
|
fp["languages"] = "zh-CN,zh" |
|
|
fp["timezone"] = "-540" |
|
|
fp["webdriver"] = False |
|
|
fp["dwellTime"] = random.uniform(5, 15) |
|
|
|
|
|
elif pattern_type == "abnormal_screen": |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = "Win32" |
|
|
fp["screenWidth"] = random.choice(["0", "1", "100"]) |
|
|
fp["screenHeight"] = random.choice(["0", "1", "100"]) |
|
|
fp["webdriver"] = False |
|
|
fp["dwellTime"] = random.uniform(1, 5) |
|
|
|
|
|
elif pattern_type == "mixed_attack": |
|
|
|
|
|
fp["userAgent"] = BOT_USER_AGENTS["selenium"] |
|
|
fp["platform"] = "Linux x86_64" |
|
|
fp["webdriver"] = True |
|
|
fp["webGLRenderer"] = "SwiftShader" |
|
|
fp["pluginsCount"] = "0" |
|
|
fp["hasMouseMove"] = False |
|
|
fp["hasScroll"] = False |
|
|
fp["hasClick"] = False |
|
|
fp["dwellTime"] = 0.1 |
|
|
fp["referrer"] = random.choice(SUSPICIOUS_REFERRERS) |
|
|
fp["screenWidth"] = "800" |
|
|
fp["screenHeight"] = "600" |
|
|
|
|
|
else: |
|
|
|
|
|
fp["userAgent"] = random.choice(NORMAL_USER_AGENTS) |
|
|
fp["platform"] = random.choice(["Win32", "MacIntel", "Linux x86_64"]) |
|
|
fp["webdriver"] = False |
|
|
fp["hasMouseMove"] = True |
|
|
fp["hasScroll"] = True |
|
|
fp["hasClick"] = True |
|
|
fp["hasKeypress"] = random.choice([True, False]) |
|
|
fp["dwellTime"] = random.uniform(10, 60) |
|
|
fp["pluginsCount"] = str(random.randint(3, 15)) |
|
|
fp["languages"] = random.choice(["ko-KR,ko,en-US,en", "en-US,en", "ja-JP,ja,en"]) |
|
|
|
|
|
|
|
|
fp.setdefault("platform", "Win32") |
|
|
fp.setdefault("timezone", str(random.choice([-540, -480, -420, 0, 60, 120]))) |
|
|
fp.setdefault("deviceMemory", str(random.choice([2, 4, 8, 16]))) |
|
|
fp.setdefault("screenWidth", str(random.choice([1366, 1920, 2560, 1440]))) |
|
|
fp.setdefault("screenHeight", str(random.choice([768, 1080, 1440, 900]))) |
|
|
fp.setdefault("screenDepth", "24") |
|
|
fp.setdefault("hardwareConcurrency", str(random.choice([2, 4, 8, 12, 16]))) |
|
|
fp.setdefault("languages", "en-US,en") |
|
|
fp.setdefault("doNotTrack", random.choice(["1", "0", "NC"])) |
|
|
fp.setdefault("maxTouchPoints", "0") |
|
|
fp.setdefault("pluginsCount", str(random.randint(0, 10))) |
|
|
fp.setdefault("referrer", "direct") |
|
|
fp.setdefault("currentUrl", "https://test-site.com/page") |
|
|
fp.setdefault("currentHost", "test-site.com") |
|
|
fp.setdefault("webGLVendor", "Google Inc. (NVIDIA)") |
|
|
fp.setdefault("webGLRenderer", "ANGLE (NVIDIA, NVIDIA GeForce RTX 3080)") |
|
|
fp.setdefault("canvasHash", ''.join(random.choices(string.hexdigits, k=50))) |
|
|
fp.setdefault("audioSampleRate", "48000") |
|
|
fp.setdefault("videoFormats", '{"mp4":"probably","webm":"probably"}') |
|
|
|
|
|
return fp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def send_single_request(target_url: str, fingerprint: dict, request_num: int) -> bool: |
|
|
"""단일 요청 전송""" |
|
|
try: |
|
|
|
|
|
if not target_url.endswith('/'): |
|
|
target_url += '/' |
|
|
|
|
|
|
|
|
api_url = target_url.rstrip('/') + "/api/collect" |
|
|
|
|
|
headers = { |
|
|
"Content-Type": "application/json", |
|
|
"User-Agent": fingerprint.get("userAgent", "python-requests/2.28.0"), |
|
|
"Referer": fingerprint.get("referrer", ""), |
|
|
} |
|
|
|
|
|
response = requests.post( |
|
|
api_url, |
|
|
json=fingerprint, |
|
|
headers=headers, |
|
|
timeout=10 |
|
|
) |
|
|
|
|
|
if response.status_code == 200: |
|
|
add_log(f"요청 #{request_num} 성공 - {fingerprint.get('sessionToken', 'unknown')[:20]}", "success") |
|
|
return True |
|
|
else: |
|
|
add_log(f"요청 #{request_num} 실패 (HTTP {response.status_code})", "warning") |
|
|
return False |
|
|
|
|
|
except requests.exceptions.Timeout: |
|
|
add_log(f"요청 #{request_num} 타임아웃", "error") |
|
|
return False |
|
|
except requests.exceptions.ConnectionError: |
|
|
add_log(f"요청 #{request_num} 연결 실패", "error") |
|
|
return False |
|
|
except Exception as e: |
|
|
add_log(f"요청 #{request_num} 오류: {str(e)[:50]}", "error") |
|
|
return False |
|
|
|
|
|
|
|
|
def generate_traffic( |
|
|
target_url: str, |
|
|
pattern_type: str, |
|
|
request_count: int, |
|
|
delay_ms: int, |
|
|
concurrent: int, |
|
|
bot_type: str = "random", |
|
|
progress=gr.Progress() |
|
|
) -> tuple: |
|
|
"""트래픽 생성 메인 함수""" |
|
|
|
|
|
if not target_url: |
|
|
return "❌ 대상 URL을 입력하세요", get_logs() |
|
|
|
|
|
if not target_url.startswith("http"): |
|
|
target_url = "https://" + target_url |
|
|
|
|
|
add_log(f"트래픽 생성 시작: {target_url}", "info") |
|
|
add_log(f"패턴: {pattern_type}, 요청 수: {request_count}, 딜레이: {delay_ms}ms", "info") |
|
|
|
|
|
success_count = 0 |
|
|
fail_count = 0 |
|
|
|
|
|
custom_settings = {} |
|
|
if bot_type != "random" and pattern_type == "bot_ua": |
|
|
custom_settings["bot_type"] = bot_type |
|
|
|
|
|
|
|
|
if pattern_type == "rapid_click": |
|
|
delay_ms = min(delay_ms, 100) |
|
|
|
|
|
with ThreadPoolExecutor(max_workers=concurrent) as executor: |
|
|
futures = [] |
|
|
|
|
|
for i in range(request_count): |
|
|
progress((i + 1) / request_count, desc=f"요청 {i+1}/{request_count}") |
|
|
|
|
|
fp = generate_fingerprint(pattern_type, custom_settings) |
|
|
future = executor.submit(send_single_request, target_url, fp, i + 1) |
|
|
futures.append(future) |
|
|
|
|
|
if delay_ms > 0 and i < request_count - 1: |
|
|
time.sleep(delay_ms / 1000) |
|
|
|
|
|
for future in futures: |
|
|
if future.result(): |
|
|
success_count += 1 |
|
|
else: |
|
|
fail_count += 1 |
|
|
|
|
|
result = f"""## 📊 트래픽 생성 완료 |
|
|
|
|
|
| 항목 | 결과 | |
|
|
|------|------| |
|
|
| **대상 URL** | `{target_url}` | |
|
|
| **패턴** | {pattern_type} | |
|
|
| **총 요청** | {request_count}회 | |
|
|
| **성공** | ✅ {success_count}회 | |
|
|
| **실패** | ❌ {fail_count}회 | |
|
|
| **성공률** | {success_count/request_count*100:.1f}% | |
|
|
""" |
|
|
|
|
|
add_log(f"완료: 성공 {success_count}, 실패 {fail_count}", "success" if fail_count == 0 else "warning") |
|
|
|
|
|
return result, get_logs() |
|
|
|
|
|
|
|
|
def clear_logs(): |
|
|
global traffic_log |
|
|
with log_lock: |
|
|
traffic_log = [] |
|
|
return "로그가 초기화되었습니다." |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks(title="IVT Bot Traffic Generator") as demo: |
|
|
gr.Markdown("""# 🤖 IVT 봇 트래픽 생성기 |
|
|
|
|
|
**⚠️ 주의: 테스트 목적으로만 사용하세요. 본인 소유 서버에서만 테스트하세요.** |
|
|
|
|
|
IVT 탐지 시스템 테스트를 위한 다양한 부정 트래픽 패턴을 생성합니다. |
|
|
""") |
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.TabItem("🚀 트래픽 생성"): |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=2): |
|
|
target_url = gr.Textbox( |
|
|
label="🎯 대상 URL", |
|
|
placeholder="https://your-ivt-server.hf.space", |
|
|
info="IVT 탐지 서버의 URL을 입력하세요" |
|
|
) |
|
|
|
|
|
pattern_type = gr.Dropdown( |
|
|
label="📋 IVT 패턴 선택", |
|
|
choices=[ |
|
|
("🤖 봇 User-Agent", "bot_ua"), |
|
|
("👻 Headless 브라우저 (WebDriver)", "headless"), |
|
|
("⚡ 빠른 반복 클릭", "rapid_click"), |
|
|
("🚫 무상호작용 (No Interaction)", "no_interaction"), |
|
|
("🏢 데이터센터 IP 시뮬레이션", "datacenter_ip"), |
|
|
("🔗 의심스러운 Referrer", "suspicious_referrer"), |
|
|
("📱 디바이스 불일치 (Mobile UA + Desktop)", "device_mismatch"), |
|
|
("🗺️ 지역-언어 불일치", "geo_mismatch"), |
|
|
("📐 비정상 해상도", "abnormal_screen"), |
|
|
("💀 복합 공격 (다중 플래그)", "mixed_attack"), |
|
|
("✅ 정상 트래픽", "normal"), |
|
|
], |
|
|
value="bot_ua", |
|
|
info="생성할 IVT 패턴을 선택하세요" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
request_count = gr.Slider( |
|
|
label="요청 수", |
|
|
minimum=1, |
|
|
maximum=100, |
|
|
value=5, |
|
|
step=1, |
|
|
info="생성할 요청 수" |
|
|
) |
|
|
delay_ms = gr.Slider( |
|
|
label="딜레이 (ms)", |
|
|
minimum=0, |
|
|
maximum=5000, |
|
|
value=500, |
|
|
step=100, |
|
|
info="요청 간 딜레이" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
concurrent = gr.Slider( |
|
|
label="동시 요청 수", |
|
|
minimum=1, |
|
|
maximum=10, |
|
|
value=1, |
|
|
step=1, |
|
|
info="병렬 처리 수" |
|
|
) |
|
|
|
|
|
|
|
|
bot_type = gr.Dropdown( |
|
|
label="🤖 봇 유형 (봇 UA 패턴 전용)", |
|
|
choices=[ |
|
|
("랜덤", "random"), |
|
|
("Googlebot", "googlebot"), |
|
|
("Bingbot", "bingbot"), |
|
|
("curl", "curl"), |
|
|
("wget", "wget"), |
|
|
("Python Requests", "python_requests"), |
|
|
("Scrapy", "scrapy"), |
|
|
("Selenium", "selenium"), |
|
|
("Puppeteer", "puppeteer"), |
|
|
("PhantomJS", "phantomjs"), |
|
|
("Java", "java"), |
|
|
("Go HTTP", "go_http"), |
|
|
("Axios", "axios"), |
|
|
("Postman", "postman"), |
|
|
], |
|
|
value="random", |
|
|
visible=True |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
generate_btn = gr.Button("🚀 트래픽 생성", variant="primary", scale=2) |
|
|
clear_btn = gr.Button("🗑️ 로그 초기화", scale=1) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
result_output = gr.Markdown(value="**설정 후 '트래픽 생성' 버튼을 클릭하세요**") |
|
|
|
|
|
gr.Markdown("### 📜 실행 로그") |
|
|
log_output = gr.Textbox( |
|
|
label="", |
|
|
value="로그 없음", |
|
|
lines=15, |
|
|
max_lines=20, |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
generate_btn.click( |
|
|
fn=generate_traffic, |
|
|
inputs=[target_url, pattern_type, request_count, delay_ms, concurrent, bot_type], |
|
|
outputs=[result_output, log_output] |
|
|
) |
|
|
|
|
|
clear_btn.click(fn=clear_logs, outputs=log_output) |
|
|
|
|
|
|
|
|
with gr.TabItem("📖 패턴 설명"): |
|
|
gr.Markdown(""" |
|
|
## 🛡️ IVT 패턴 상세 설명 |
|
|
|
|
|
각 패턴이 어떤 부정 트래픽을 시뮬레이션하는지 설명합니다. |
|
|
|
|
|
--- |
|
|
|
|
|
### 🤖 봇 User-Agent (`bot_ua`) |
|
|
**탐지 점수: 40점** |
|
|
|
|
|
웹 크롤러, 스크레이퍼 등의 봇 UA를 사용합니다. |
|
|
|
|
|
| 봇 유형 | User-Agent 예시 | |
|
|
|---------|----------------| |
|
|
| Googlebot | `compatible; Googlebot/2.1` | |
|
|
| curl | `curl/7.68.0` | |
|
|
| Python | `python-requests/2.28.0` | |
|
|
| Scrapy | `Scrapy/2.7.0` | |
|
|
|
|
|
--- |
|
|
|
|
|
### 👻 Headless 브라우저 (`headless`) |
|
|
**탐지 점수: 50-85점** |
|
|
|
|
|
Selenium, Puppeteer 등 자동화 도구를 탐지합니다. |
|
|
|
|
|
- `navigator.webdriver = true` |
|
|
- WebGL Renderer: `SwiftShader` (소프트웨어 렌더링) |
|
|
- 플러그인 수: 0개 |
|
|
|
|
|
--- |
|
|
|
|
|
### ⚡ 빠른 반복 클릭 (`rapid_click`) |
|
|
**탐지 점수: 25-45점** |
|
|
|
|
|
클릭 팜, 자동 클릭 봇을 시뮬레이션합니다. |
|
|
|
|
|
- 5초 내 2회 이상 클릭 |
|
|
- 1분 내 5회 이상 클릭 |
|
|
- 체류시간 1초 미만 |
|
|
|
|
|
--- |
|
|
|
|
|
### 🚫 무상호작용 (`no_interaction`) |
|
|
**탐지 점수: 20점** |
|
|
|
|
|
페이지 로드 후 아무 동작도 하지 않는 패턴입니다. |
|
|
|
|
|
- 마우스 이동 없음 |
|
|
- 스크롤 없음 |
|
|
- 클릭 없음 |
|
|
- 키보드 입력 없음 |
|
|
|
|
|
--- |
|
|
|
|
|
### 🏢 데이터센터 IP (`datacenter_ip`) |
|
|
**탐지 점수: 25-30점** |
|
|
|
|
|
AWS, GCP, Azure 등 클라우드 서버에서 오는 트래픽입니다. |
|
|
|
|
|
- ASN에 'amazon', 'google', 'microsoft' 포함 |
|
|
- 호스팅/서버 IP로 분류됨 |
|
|
|
|
|
--- |
|
|
|
|
|
### 🔗 의심스러운 Referrer (`suspicious_referrer`) |
|
|
**탐지 점수: 35점** |
|
|
|
|
|
트래픽 교환, PTC(Paid-to-Click) 사이트에서 오는 트래픽입니다. |
|
|
|
|
|
- `traffic-exchange.com` |
|
|
- `paid-to-click.net` |
|
|
- `autosurf.pro` |
|
|
|
|
|
--- |
|
|
|
|
|
### 📱 디바이스 불일치 (`device_mismatch`) |
|
|
**탐지 점수: 20점** |
|
|
|
|
|
UA와 실제 디바이스 정보가 맞지 않는 경우입니다. |
|
|
|
|
|
- 모바일 UA + 데스크톱 플랫폼 |
|
|
- 모바일 UA + 터치 미지원 |
|
|
|
|
|
--- |
|
|
|
|
|
### 🗺️ 지역-언어 불일치 (`geo_mismatch`) |
|
|
**탐지 점수: 15점** |
|
|
|
|
|
IP 위치와 브라우저 언어 설정이 맞지 않습니다. |
|
|
|
|
|
- 한국 IP + 중국어 언어 |
|
|
- 미국 IP + 일본어 언어 |
|
|
|
|
|
--- |
|
|
|
|
|
### 📐 비정상 해상도 (`abnormal_screen`) |
|
|
**탐지 점수: 20-30점** |
|
|
|
|
|
화면 해상도가 비정상적인 경우입니다. |
|
|
|
|
|
- 0x0, 1x1 해상도 |
|
|
- 300 미만의 너비/높이 |
|
|
|
|
|
--- |
|
|
|
|
|
### 💀 복합 공격 (`mixed_attack`) |
|
|
**탐지 점수: 70점 이상 (GIVT)** |
|
|
|
|
|
여러 IVT 패턴을 동시에 포함합니다. |
|
|
|
|
|
- WebDriver 감지 |
|
|
- 봇 UA |
|
|
- SwiftShader |
|
|
- 무상호작용 |
|
|
- 의심스러운 Referrer |
|
|
|
|
|
--- |
|
|
|
|
|
### ✅ 정상 트래픽 (`normal`) |
|
|
**탐지 점수: 0-10점** |
|
|
|
|
|
일반 사용자 행동을 시뮬레이션합니다. |
|
|
|
|
|
- 정상 브라우저 UA |
|
|
- 마우스/스크롤/클릭 상호작용 |
|
|
- 10-60초 체류시간 |
|
|
- 정상 플러그인 수 |
|
|
""") |
|
|
|
|
|
|
|
|
with gr.TabItem("⚡ 빠른 테스트"): |
|
|
gr.Markdown(""" |
|
|
### 원클릭 테스트 |
|
|
|
|
|
각 버튼을 클릭하면 해당 패턴으로 5회 요청을 즉시 전송합니다. |
|
|
""") |
|
|
|
|
|
quick_url = gr.Textbox( |
|
|
label="🎯 대상 URL", |
|
|
placeholder="https://your-ivt-server.hf.space" |
|
|
) |
|
|
|
|
|
quick_result = gr.Markdown() |
|
|
quick_log = gr.Textbox(label="로그", lines=10, interactive=False) |
|
|
|
|
|
with gr.Row(): |
|
|
btn_bot = gr.Button("🤖 봇 UA", variant="secondary") |
|
|
btn_headless = gr.Button("👻 Headless", variant="secondary") |
|
|
btn_rapid = gr.Button("⚡ 반복클릭", variant="secondary") |
|
|
btn_noact = gr.Button("🚫 무상호작용", variant="secondary") |
|
|
|
|
|
with gr.Row(): |
|
|
btn_referrer = gr.Button("🔗 의심 Referrer", variant="secondary") |
|
|
btn_device = gr.Button("📱 디바이스불일치", variant="secondary") |
|
|
btn_mixed = gr.Button("💀 복합공격", variant="stop") |
|
|
btn_normal = gr.Button("✅ 정상", variant="primary") |
|
|
|
|
|
|
|
|
btn_bot.click( |
|
|
fn=lambda url: generate_traffic(url, "bot_ua", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_headless.click( |
|
|
fn=lambda url: generate_traffic(url, "headless", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_rapid.click( |
|
|
fn=lambda url: generate_traffic(url, "rapid_click", 10, 50, 2, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_noact.click( |
|
|
fn=lambda url: generate_traffic(url, "no_interaction", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_referrer.click( |
|
|
fn=lambda url: generate_traffic(url, "suspicious_referrer", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_device.click( |
|
|
fn=lambda url: generate_traffic(url, "device_mismatch", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_mixed.click( |
|
|
fn=lambda url: generate_traffic(url, "mixed_attack", 5, 200, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
btn_normal.click( |
|
|
fn=lambda url: generate_traffic(url, "normal", 5, 500, 1, "random"), |
|
|
inputs=[quick_url], |
|
|
outputs=[quick_result, quick_log] |
|
|
) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |