当前位置: 首页 > news >正文

hikivision 考勤机数据提取

海康的某考勤机可以根据网上的 api 接口提取数据,而不必用优盘拷贝。
经过观察,有如下的流程——

graph TDA[开始] --> B[Step 1: GET sessionLogin/capabilities with random]B --> C[Step 2: POST sessionLogin with timestamp]C --> D[Step 3: PUT sessionHeartbeat]D --> E[Step 4: GET Security/capabilities]E --> F[Step 5: 计算固定AES密钥]F --> G[Step 6: GET Security/users 验证密钥]G --> H[Step 7: POST AcsEvent 获取数据]H --> I[Step 8: AES-CBC + Base64 解密]I --> J[保存为CSV或JSON]J --> K[结束]

考勤等数据存在 AES CBC 加密和 Base64 编码。
aes key是固定不变的,可以调试得到(或者由固定的盐值按算法计算而来)
image
image
image

image
searchId 可以选择某个固定的值,或者使用 uuidv4

image
image

#!/usr/bin/env python3
"""
Hikvision Attendance Data Extractor (Cleaned & Enhanced)
- Uses fixed AES key
- Splits ISO8601 time into 'date' and 'time' (HH:MM:SS)
- Removes redundant comments/logic
"""import json
import binascii
import base64
import hashlib
import time
import re
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad# ------------------- Config -------------------
HOST = "192.168.1.192"
USERNAME = "admin"
PASSWORD = "password"
BASE_URL = f"http://{HOST}"
AES_KEY = "90c5ba363d3a4d7db66d2564c5697319"  # 32 hex chars = 16 bytessession = requests.Session()# ------------------- Auth -------------------
def login():# Step 1: Get session challengerandom_param = str(int(hashlib.md5(str(time.time()).encode()).hexdigest()[:8], 16))[:8]cap_url = f"{BASE_URL}/ISAPI/Security/sessionLogin/capabilities?username={USERNAME}&random={random_param}"try:resp = session.get(cap_url, timeout=10)resp.raise_for_status()session_id = re.search(r"<sessionID>(\w+)</sessionID>", resp.text).group(1)challenge = re.search(r"<challenge>([a-f0-9]+)</challenge>", resp.text).group(1)iterations = int(re.search(r"<iterations>(\d+)</iterations>", resp.text).group(1))salt = re.search(r"<salt>(\w+)</salt>", resp.text).group(1)except Exception as e:print(json.dumps({"error": f"Get capabilities failed: {e}"}))return None# Step 2: Compute password hashpwd = hashlib.sha256((USERNAME + salt + PASSWORD).encode()).hexdigest()pwd = hashlib.sha256((pwd + challenge).encode()).hexdigest()for _ in range(2, iterations):pwd = hashlib.sha256(pwd.encode()).hexdigest()# Step 3: Loginlogin_xml = f"""<SessionLogin><userName>{USERNAME}</userName><password>{pwd}</password><sessionID>{session_id}</sessionID><isSupportSessionTag>false</isSupportSessionTag><isSessionIDValidLongTerm>false</isSessionIDValidLongTerm><sessionIDVersion>2</sessionIDVersion>
</SessionLogin>"""ts = int(time.time() * 1000)login_url = f"{BASE_URL}/ISAPI/Security/sessionLogin?timeStamp={ts}"try:resp = session.post(login_url, data=login_xml, timeout=10)if "<statusValue>200</statusValue>" not in resp.text:print(json.dumps({"error": "Login failed"}))return Nonereturn tsexcept Exception as e:print(json.dumps({"error": f"Login error: {e}"}))return None# ------------------- Decrypt -------------------
def aes_decrypt_base64(ciphertext_hex, key_hex, iv_hex):if not ciphertext_hex:return ""try:key = binascii.unhexlify(key_hex)iv = binascii.unhexlify(iv_hex)ct = binascii.unhexlify(ciphertext_hex)cipher = AES.new(key, AES.MODE_CBC, iv)decrypted = unpad(cipher.decrypt(ct), AES.block_size)b64_str = decrypted.decode('utf-8')return base64.b64decode(b64_str).decode('utf-8')except Exception:return ciphertext_hex  # fallback to original if decryption fails# ------------------- Fetch -------------------
def fetch_attendance(start_time, end_time, login_ts):iv = hashlib.md5(str(login_ts).encode()).hexdigest()url = f"{BASE_URL}/ISAPI/AccessControl/AcsEvent?format=json&security=1&iv={iv}"records = []position = 0while True:payload = {"AcsEventCond": {"searchID": "c6b8f41a-d75a-4e43-a0c9-8e032f636bb1","searchResultPosition": position,"maxResults": 24,"major": 0,"minor": 0,"startTime": start_time,"endTime": end_time,}}try:resp = session.post(url, json=payload, timeout=30)resp.raise_for_status()data = resp.json()info_list = data.get("AcsEvent", {}).get("InfoList", [])if not info_list:breakfor item in info_list:name_enc = item.get("name", "")emp_enc = item.get("employeeNoString", "")time_raw = item.get("time", "")# Decrypt fields if long (likely encrypted)name = aes_decrypt_base64(name_enc, AES_KEY, iv) if len(name_enc) > 20 else name_encemp_no = aes_decrypt_base64(emp_enc, AES_KEY, iv) if len(emp_enc) > 20 else emp_enc# Split ISO8601 time: "2025-10-09T08:16:09+08:00" → date="2025-10-09", time="08:16:09"date_part = time_part = ""if time_raw:if "T" in time_raw:dt_part = time_raw.split("T")[0]t_part = time_raw.split("T")[1]time_part = t_part.split("+")[0].split(".")[0]  # Remove timezone and millisdate_part = dt_partelse:date_part = time_part = time_rawrecords.append({"employeeNo": emp_no,"name": name,"date": date_part,"time": time_part,"cardNo": item.get("cardNo", "")})position += len(info_list)total = data.get("AcsEvent", {}).get("totalMatches", 0)if position >= total:breakexcept Exception as e:print(json.dumps({"error": f"Fetch error: {e}"}))breakreturn records# ------------------- Main -------------------
def main():login_ts = login()if not login_ts:returnstart = "2025-10-01T00:00:00+08:00"end = "2025-10-31T23:59:59+08:00"records = fetch_attendance(start, end, login_ts)with open("attendance_simple.json", "w", encoding="utf-8") as f:json.dump(records, f, ensure_ascii=False, indent=2)print(f"✅ Successfully saved {len(records)} records to attendance_simple.json")if __name__ == "__main__":main()

参考阅读:

http://www.rkmt.cn/news/60678.html

相关文章:

  • [python] Python数据类使用指北
  • 深入解析:iOS 26 App 开发阶段性能优化 从多工具协作到数据驱动的实战体系
  • 小程序开发使用vant ui 组件快速开发
  • 课后作业8
  • 2025年11月25日加班
  • 租房买房必看1为什么户型不方正,会让你越住越穷?
  • 实用指南:Stable Diffusion 短视频制作算力需求与优化策略研究
  • 如何高效地学习Java编程?
  • 实用指南:【底层机制】深入浅出地、系统地剖析 Appium 的原理
  • 容错量子电路大幅降低资源开销
  • 详细介绍:【C基本功】类型转换的奇幻漂流
  • 点灯笔记:CW32L010
  • 过山车
  • day07 spark sql - 详解
  • 深入解析:系统架构设计师备考第57天——云原生架构相关技术
  • 【ArcMap】将一个线图层的属性字段连接到另一个线图层
  • 人工智能之数据分析 numpy:第十四章 知识总结
  • 信息的建筑学:MyBatis Log Panda 如何重构开发者的认知地图
  • ai论文软件推荐:智能工具助力学术写作效率提升
  • 2025年11月软瓷厂家推荐榜:3D软瓷/软瓷砖/mcm软瓷/3D打印软瓷厂家批发环保品质深度解析!
  • 降ai率免费网站:助力内容原创性提升的实用工具
  • PVC地板厂家天津航美国际贸易有限公司:华北平价基地核心成员,规模化降本,耐磨防滑产品适配多场景
  • PVC地板厂家天津航美:2016年成立深耕行业,同质透心/地板革等全品类,防火阻燃符合国际标准
  • 云拨测:当“正常变更”摧毁全球网络时,谁来守护你的业务可用性?
  • 江苏省刑事律所推荐:专业法律服务机构参考
  • 苏州婚姻家庭纠纷律所推荐:专业法律服务机构选择参考
  • 2025年下半年奖杯奖牌/水晶奖杯/奖杯定制/定制厂家前十推荐
  • 压力大失眠吃的睡眠益生菌有哪些?成分与效果解析
  • 江苏省比较好的律师事务所推荐及服务能力解析
  • 提升免疫力的靠谱保健品推荐:这些品牌值得关注