Hikvision 考勤机数据提取(3)

news/2025/12/8 23:28:56/文章来源:https://www.cnblogs.com/geyee/p/19323960

同样使用 HTTPDigestAuth

import json
import binascii
import base64
import hashlib
import time
import requests
import argparse
import uuid
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
from requests.auth import HTTPDigestAuth# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
DEFAULT_IP = "192.168.1.192"
DEFAULT_PORT = "8080"
USERNAME = "admin"
PASSWORD = "password"# 认证对象
DIGEST_AUTH = HTTPDigestAuth(USERNAME, PASSWORD)# ---------------------------------------------------------------------------
# Key Derivation
# ---------------------------------------------------------------------------
def get_aes_key(host_str, username, password, auth, proxies=None):"""Derive AES Key dynamically from device capabilities."""try:url = f"http://{host_str}/ISAPI/Security/capabilities?username={username}"print(f"Fetching capabilities from {url}...")resp = requests.get(url, auth=auth, timeout=10, verify=False, proxies=proxies)resp.raise_for_status()root = ET.fromstring(resp.text)ns = {'ns': 'http://www.isapi.org/ver20/XMLSchema'}salt_node = root.find('ns:salt', ns)if salt_node is None:print("Error: Could not find <salt> in capabilities response.")return Nonesalt = salt_node.textprint(f"Got Salt: {salt}")# 1. Calculate IrreversibleKeycombined_string = f"{username}{salt}{password}"irreversible_key = hashlib.sha256(combined_string.encode('utf-8')).hexdigest()# 2. Calculate Final Key with Challenge# Note: The challenge "AaBbCcDd1234!@#$" appears to be hardcoded or specific to this auth modechallenge = "AaBbCcDd1234!@#$" combined_for_hash = f"{irreversible_key}{challenge}"key = hashlib.sha256(combined_for_hash.encode('utf-8')).hexdigest()# 3. Iterate hashingiterations = 100for _ in range(1, iterations):key = hashlib.sha256(key.encode()).hexdigest()aes_key = key[:32]print(f"Derived AES Key: {aes_key}")return aes_keyexcept Exception as e:print(f"Key derivation failed: {e}")return None# ---------------------------------------------------------------------------
# Encryption / Decryption
# ---------------------------------------------------------------------------
def aes_encrypt(plaintext, key_hex, iv_hex):try:key_bytes = binascii.unhexlify(key_hex)iv_bytes = binascii.unhexlify(iv_hex)b64_str = base64.b64encode(plaintext.encode('utf-8')).decode('utf-8')data_bytes = b64_str.encode('utf-8')padded_data = pad(data_bytes, AES.block_size)cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)ciphertext_bytes = cipher.encrypt(padded_data)return binascii.hexlify(ciphertext_bytes).decode('utf-8')except Exception as e:print(f"Encryption error: {e}")return Nonedef aes_decrypt_base64(ciphertext_hex, key_hex, iv_hex):if not ciphertext_hex:return ""try:key_bytes = binascii.unhexlify(key_hex)iv_bytes = binascii.unhexlify(iv_hex)ct_bytes = binascii.unhexlify(ciphertext_hex)cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)decrypted = cipher.decrypt(ct_bytes)decrypted = unpad(decrypted, AES.block_size)b64_str = decrypted.decode('utf-8')plain_bytes = base64.b64decode(b64_str)return plain_bytes.decode('utf-8')except Exception:return ""def parse_time_arg(time_str):if not time_str:return Noneif 'T' in time_str:if '+' not in time_str and 'Z' not in time_str:return time_str + "+08:00"return time_strtry:dt = Nonetime_str = time_str.strip()if len(time_str) == 10:dt = datetime.strptime(time_str, "%Y-%m-%d")elif len(time_str) == 16:dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M")elif len(time_str) == 19:dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")if dt:return dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")except ValueError:passreturn time_str# ---------------------------------------------------------------------------
# Data Fetching
# ---------------------------------------------------------------------------
def fetch_attendance_v3(start_time, end_time, aes_key, base_url, employee_no=None, proxies=None, fixed_iv=None):all_records = []# Determine IVif fixed_iv:iv = fixed_ivprint(f"Using Fixed IV: {iv}")else:# Dynamic IV based on timestamplogin_timestamp = int(time.time() * 1000)iv = hashlib.md5(str(login_timestamp).encode()).hexdigest()print(f"Using Dynamic IV: {iv}")url = f"{base_url}/ISAPI/AccessControl/AcsEvent?format=json&security=1&iv={iv}"position = 0batch_size = 24search_id = str(uuid.uuid4())print(f"Fetching data from {start_time} to {end_time}...")target_encrypted_emp = Noneif employee_no:if len(employee_no) > 20 and all(c in '0123456789abcdefABCDEF' for c in employee_no):target_encrypted_emp = employee_noelse:target_encrypted_emp = aes_encrypt(employee_no, aes_key, iv)print(f"  -> Encrypted Employee No: {target_encrypted_emp}")while True:acs_event_cond = {"searchID": search_id,"searchResultPosition": position,"maxResults": batch_size,"major": 0,"minor": 0,"startTime": start_time,"endTime": end_time,}if target_encrypted_emp:acs_event_cond["employeeNoString"] = target_encrypted_emppayload = {"AcsEventCond": acs_event_cond}headers = {"Content-Type": "application/json","X-Requested-With": "XMLHttpRequest",}try:resp = requests.post(url, json=payload, headers=headers, auth=DIGEST_AUTH, proxies=proxies,timeout=30)resp.raise_for_status()data = resp.json()acs = data.get('AcsEvent', {})info = acs.get('InfoList', [])if not info:breakfor item in info:name_enc = item.get('name', '')emp_enc = item.get('employeeNoString', '')name = aes_decrypt_base64(name_enc, aes_key, iv) if len(name_enc) > 20 else name_enccurr_employee_no = aes_decrypt_base64(emp_enc, aes_key, iv) if len(emp_enc) > 20 else emp_encrecord = {'employeeNo': curr_employee_no,'name': name,'time': item.get('time', ''),'cardNo': item.get('cardNo', ''),'raw_employeeNoString': emp_enc }all_records.append(record)position += len(info)total_matches = acs.get('totalMatches', 0)print(f"Fetched {position}/{total_matches} records...")if position >= total_matches:breakexcept Exception as e:print(json.dumps({"error": f"Fetch error: {e}"}, ensure_ascii=False))breakreturn all_records# ---------------------------------------------------------------------------
# Main Function
# ---------------------------------------------------------------------------
def main_v3():global DIGEST_AUTHparser = argparse.ArgumentParser(description="Hikvision Attendance Fetcher V3 (Dynamic Key)")parser.add_argument("--start", help="Start time (e.g. 2025-12-08 08:00)", default=None)parser.add_argument("--end", help="End time (e.g. 2025-12-08 23:59)", default=None)parser.add_argument("--employeeNo", help="Employee Number (e.g. 16128 or encrypted hex)", default=None)parser.add_argument("--out", help="Output JSON file", default="attendance_v3.json")parser.add_argument("--ip", help=f"Target IP (default: {DEFAULT_IP})", default=DEFAULT_IP)parser.add_argument("--port", help=f"Target Port (default: {DEFAULT_PORT})", default=DEFAULT_PORT)parser.add_argument("--proxy", help="Proxy URL (e.g. http://127.0.0.1:8899)", default=None)parser.add_argument("--iv", help="Fixed IV (optional, for encrypted employeeNo)", default=None)args = parser.parse_args()host_str = f"{args.ip}:{args.port}"base_url = f"http://{host_str}"# Configure Proxiesproxies = Noneif args.proxy:proxies = {"http": args.proxy,"https": args.proxy,}print(f"Using Proxy: {args.proxy}")DIGEST_AUTH = HTTPDigestAuth(USERNAME, PASSWORD)# 1. Derive Keyprint("Deriving AES Key...")aes_key = get_aes_key(host_str, USERNAME, PASSWORD, DIGEST_AUTH, proxies)if not aes_key:print("Failed to derive key. Exiting.")return# 2. Parse Timesif args.start:args.start = parse_time_arg(args.start)else:now = datetime.now()start_dt = datetime(now.year, now.month, now.day, 0, 0, 0)args.start = start_dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")if args.end:args.end = parse_time_arg(args.end)else:now = datetime.now()end_dt = datetime(now.year, now.month, now.day, 23, 59, 59)args.end = end_dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")# 3. Fetch Datarecords = fetch_attendance_v3(args.start, args.end, aes_key, base_url, args.employeeNo, proxies, args.iv)# 4. Savewith open(args.out, 'w', encoding='utf-8') as f:json.dump(records, f, ensure_ascii=False, indent=2)print(f"Data successfully saved to {args.out} ({len(records)} records)")if __name__ == "__main__":main_v3()

imageimage

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/993706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12306爬取基本车次信息(需下载chromedriver)

from selenium import webdriver from selenium.webdriver.common.by import By import re import time import csv import pandas as pd import sys from selenium.webdriver.chrome.service import Service from sel…

微信小程序渗透测试

https://mp.weixin.qq.com/s/hTEjuNHF-k8053nejSZ1Jw

Day14-20251208

本文摘要:介绍了使用IDEA生成JavaDoc文档的详细步骤,包括设置作用域、输出目录、语言编码等参数 列举了常用JavaDoc标签,分为基础信息类、功能说明类和状态标识类三大类 讲解了Java流程控制中的Scanner对象使用,比…

从纯数学到应用AI科学的职业转变

本文讲述了Giovanni Paolini从纯数学研究转向在某中心担任应用科学家的历程,其工作涉及计算机视觉、自然语言处理等机器学习技术,并探讨了数学背景对AI研究的独特价值。从纯数学家到某中心应用科学家 Giovanni Paoli…

Flask集成MCP的AI Agent

基于Flask开发集成MCP Client和MCP Server的Ai Agent.前言 近年来,大量新兴的 AI 相关第三方库都提供了异步接口,有些甚至出于性能考虑仅支持异步调用,例如 MCP SDK。伴随着 Python 异步编程的发展,FastAPI 等框架…

threadDay01

#include <iostream> #include <thread> #include <condition_variable> #include <queue> #include <mutex>std::queue<int> g_queue; std::condition_variable g_cv; std::mute…

大数据数仓设计:分层架构与维度建模 - Binge

引言 大数据数据仓库(数仓)是企业数据管理的核心,用于高效存储、处理和分析海量数据。基于Hive的设计结合了分层架构和维度建模,能提升数据查询性能、可维护性和业务价值。下面我将逐步介绍分层架构的原理、维度建…

2025年折弯机上下模实力厂家推荐榜

近年来,随着智能制造在钣金加工领域的深度渗透,折弯机上下模产品正经历显著的价格结构优化。一方面,高精度、长寿命模具的制造成本因自动化产线普及而逐步降低;另一方面,用户在采购决策中愈发重视“性能与价格比”…

遇到的前端ts语法问题记录 - wuzx

遇到的前端ts语法问题记录const cities = ref([]); //List转指定字段为数组const getRoomList = async () => { const res = await listRoom(roomData);    roomList.value = res.rows; const arr_names = room…

2025.12.7 百度之星决赛 2025

Solved:6/12B. 0:45 H. 1:35(-2) E. 2:00(-3) G. 2:45(-2) J. 3:50(-4) C. 4:30(-1)Rank:33(大学组)/ 122(全部)E. 题意 棋盘有 \(L\) 个格子,从左到右编号为 \(1,2,\dots, L\)。初始 \(n\) 个棋子位于 \(1,2,\…

环境配置

Pytorch配置 创建python环境 第一步:打开anaconda prompt(官网下载)第二步:创建python环境(python=3.10) conda create -n pytorch python=3.10第三步:激活环境 conda activate pytorch下载conda 当前pytorch官网只…

rustfs

docker run -d --name rustfs --restart always -p 9000:9000 -p 9001:9001 -v $(pwd)/rustfs/data:/data -e RUSTFS_ACCESS_KEY=rustfsadmin -e RUSTFS_SECRET_KEY=rustfsadmin -e RUSTFS_CONSOLE_ENABLE=true rustfs…

rustfs

docker run -d --name rustfs --restart always -p 9000:9000 -p 9001:9001 -v $(pwd)/rustfs/data:/data -e RUSTFS_ACCESS_KEY=rustfsadmin -e RUSTFS_SECRET_KEY=rustfsadmin -e RUSTFS_CONSOLE_ENABLE=true rustfs…

深入解析:OpenAI 新推 GPT-5-Codex-Mini:一款针对开发者的轻量级编码助手

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:OpenAI 新推 GPT-5-Codex-Mini:一款针对开发者的轻量级编码助手

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Python数据可视化全攻略:Matplotlib/Seaborn从入门到实战

本文聚焦Python两大主流可视化库——Matplotlib(基础绘图库)和Seaborn(高级统计绘图库),从实战场景出发,讲解折线图、柱状图、散点图、热力图等高频图表的绘制方法,适合数据分析新手快速上手。数据可视化是数据…

深入设计模式

《深入设计模式》 对 22 个经典设计模式以及这些模式背后的 8 个基本设计原则进行了说明。官方网站 https://refactoringguru.cn/design-patterns资料下载http://book.p.starxy.cc/book/177 在线阅读 http://book.p.st…

工程模拟分析软件 Abaqus 2024 免费下载安装教程(含中文版设置+ 激活步骤)

目录一、Abaqus 2024 软件核心介绍二、Abaqus 2024 安装前准备三、Abaqus 2024 详细安装步骤(含 激活 + 中文版)第一步:解压安装包第二步:安装 JDK 运行环境第三步:部署 激活许可服务器第四步:配置系统环境变量第…

RustFS是国产的吗?有人用吗?深度解析这款新兴对象存储

RustFS是国产的吗?有人用吗?深度解析这款新兴对象存储在对象存储领域被MinIO、Ceph等国际开源项目主导的当下,一个名为RustFS的项目悄然崛起。它真的是国产存储界的新星吗?在实际生产环境中有人敢用吗?本文将为你…

软件工程学习日志2025.12.8

📊 今日学习内容概览 今天系统学习了Hadoop HDFS的编程接口使用,通过Java API实现了完整的HDFS文件管理系统,并对比学习了相应的Shell命令操作。以下是核心学习成果总结: 🔧 第一部分:HDFS文件操作编程实现 成…