题主非逆向专业,如有不足请多多海涵。这是题主第一次发帖,这里描述的内容可能过于抽象或不详细,如果有其他问题我将继续更新以供学习交流。
似乎所有代==理,都被加了 {过}{滤}
,第一次用 52,额
最近发现一个付费软件使用 HTTP 验证是否购买,所以想绕过直接使用。考虑到软件方商业权益和本帖合法性,暂时不提供相关软件的任何信息,本帖只提供思路。
这里提醒一下,如果你没有已经购买过的样本来学习,那这一点还是很难实现的,还是老老实实脱壳破解吧。题主有幸得到一份已经购买过朋友的样本,所以学到了验证方法。
首先题主通过 Fiddler 拦截请求,实现了上述过程,把有效的返回替换了原有返回内容,成功实现了注册。然后在虚拟机中试验成功了。
软件使用了 HTTP 没用 HTTPS,直接访问 IP 地址的。现在的思路是使用 Proxifier 强制软件走代{过}{滤}理,然后自己编写一个 HTTP 服务作为代{过}{滤}理服务器。Proxifier 默认不允许 HTTP 代{过}{滤}理,需要手动开启一下。
本来想软件如果通过域名访问,直接修改 hosts 把域名重定向到本地就好了,发现只访问 IP 还挺麻烦,目前没想到能不使用 Proxifier 的方法,如有方法还请评论区建议。
题主主修人工智能,因此擅长 Python,通过 Python aiohttp 框架编写一个 HTTP 服务器,并代{过}{滤}理一个 IP 下的服务:
import asyncio
import re
import socket
from datetime import datetime, timedelta
from multiprocessing import Queue
from aiohttp import ClientSession
from aiohttp.web import Application, Request, Response, RouteTableDef, run_app
from src.config import LOCK_VERSION, TARGET_HOST, TARGET_PORT, TEMPLATE
routes = RouteTableDef()
def is_port_available(host: str, port: int) -> bool:
"""检查端口是否可用"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex((host, port))
sock.close()
return result != 0
async def _make_proxy(request: Request) -> Response:
"""返回代{过}{滤}理数据"""
async with ClientSession() as session:
async with session.request(
method=request.method,
url=f"http://{TARGET_HOST}:{TARGET_PORT}{request.path}",
headers=request.headers,
params=request.query,
data=await request.read() if request.can_read_body else None,
) as response:
headers = {
"Accept": response.headers.get("Accept", ""),
"Host": response.headers.get("Host", ""),
"Content-type": response.headers.get("Content-type", ""),
"Set-Cookie": response.headers.get("Set-Cookie", ""),
"Expires": response.headers.get("Expires", ""),
"Referer": response.headers.get("Referer", ""),
"Location": response.headers.get("Location", ""),
"User-Agent": response.headers.get("User-Agent", ""),
"Cookie": response.headers.get("Cookie", ""),
}
headers.pop("Transfer-Encoding", None)
return Response(
body=await response.read(),
headers=headers,
status=response.status,
)
软件通过字符串匹配获取软件的余剩使用日期(不得不说太 low 了),直接注册到两年后。
这就是一个基本的 HTTP 代{过}{滤}理服务,下面是代{过}{滤}理 GET 请求:
@routes.get("/{tail:.*}")
async def proxy_get(request: Request):
"""代{过}{滤}理所有 GET 请求"""
queue: Queue = request.app["queue"]
current_date = datetime.now().strftime("%Y-%m-%d")
code = request.query.get("s_code", "")
# 注册拦截器
if "/reg.php":
end_date = (datetime.now() + timedelta(days=730)).strftime("%Y-%m-%d")
queue.put(f"获取到机器码:{code},正在注册到 {end_date}。")
return Response(
text=TEMPLATE.format(
code=code,
current_date=current_date,
end_date=end_date,
),
headers={
"Content-type": "text/html; charset=UTF-8",
},
)
is_locked = request.app.get("is_locked", False)
# 检查是否锁版本
if "/update.php" in request.path and is_locked:
queue.put(f"检测到版本请求,已锁定版本")
return Response(
text=LOCK_VERSION.format(
current_date=current_date,
),
headers={
"Content-type": "text/html; charset=UTF-8",
},
)
return await _make_proxy(request)
解释一下:
queue
:进程队列
LOCK_VERSION
:是否锁定版本,如果有需求可以自己设计
TARGET_HOST
:目标主机 IP
TARGET_PORT
:代{过}{滤}理目标端口
TEMPLATE
:返回的模板代码,自己根据需要写
锁定版本是防止软件强制更新,我已经抓到以前的响应模板,这样如果需要锁定版本可以直接发送之前的更新请求的响应。
这里进程队列用于和其他进程通信,这里我使用 Python 标准库 TK 来设计 UI。可以通过 UI 查看日志信息。
下面是运行服务的代码:
@routes.post("/{tail:.*}")
async def proxy_post(request: Request):
"""代{过}{滤}理所有 POST 请求"""
return await _make_proxy(request)
def run_simple_server(queue: Queue, host: str, port: int):
"""运行服务器"""
app = Application()
app["queue"] = queue
app.add_routes(routes)
if not is_port_available(host, port):
queue.put(f"端口 {port} 被占用!请勿重复打开程序,现在请关闭全部程序后重试。")
return
try:
queue.put(f"注册机启动成功!")
queue.put({"msg": "ready"})
run_app(app, host=host, port=port)
except RuntimeError:
pass
if __name__ == "__main__":
run_simple_server(Queue(), "127.0.0.1", 8899)
我这里还使用多进程以便能通过进程通信:
import multiprocessing as mp
from src.server import run_simple_server
from src.ui import simple_tkinker_ui
HOST = "127.0.0.1"
PORT = 8899
if __name__ == "__main__":
mp.freeze_support()
queue = mp.Queue()
p1 = mp.Process(target=run_simple_server, args=(queue, HOST, PORT))
p1.start()
p2 = mp.Process(target=simple_tkinker_ui, args=(queue, HOST, PORT))
p2.start()
如果你觉得你不需要 UI,可以删除对应部分运行,直接打印日志。
UI 的代码太长,没有放出来。其实也无关紧要,以上代码用来学习测试即可。
这里使用 Nuitka 打包代码:
@echo off
python -m nuitka --standalone --onefile ^
--mingw64 --show-progress --disable-console ^
--plugin-enable=tk-inter --output-dir=dist ^
--assume-yes-for-downloads main.py
这样每次使用软件之前启动一下脚本和 Proxifier,软件就以为自己已经注册过了。
不知道有没有更简单的,在 Windows 上实现 HTTP 拦截的方法。因为请求是 IP 地址没有加密,理论上可以在网关上拦截然后不再需要注册机和 Proxifier,但是有点复杂,Linux 似乎有工具直接支持拦截 HTTP 包。
先写到这,如果有不详细的地址再更新。