问题如下图如何断点重续打开\venv\Lib\site-packages\labelImg\labelImg.py 文件在labelimg文件1252行 self.default_save_dir = dir_path 下面加入以下代码 if dir_path is not None and len(dir_path) > 1: self.default_save_dir = dir_path # 加入以下代码 files = os.listdir(dir_path) # 判断是否存在名为 class.txt 的文件 if "classes.txt" in files: self.label_hist.clear() # 不清空继续打开其他标注文件不会删除原来的 with open(dir_path + "/classes.txt", 'r') as f: for line in f.readlines(): line = line.strip() if self.label_hist is None: self.label_hist = [line] else: self.label_hist.append(line) 加入后重新按照 Open dir->Change Save Dir顺序打开文件,即可重新读取Change Save Dir下的classes标注信息使用前建议备份一下 目前我用是没啥问题 不知道有啥隐藏bug。os:因为这个问题导致我标了一天的信息全被覆盖了。干!
基于中国铁路12306公布的火车信息等数据进行数据分析与数据可视化
项目概要项目名称基于中国铁路12306公布的火车信息等数据进行数据分析与数据可视化项目背景随着中国经济的持续发展和城市化进程的加速，人口流动日益频繁，铁路运输作为中国最重要的公共交通方式之一，承载了巨大的客运量。本项目旨在利用中国铁路12306公布的火车票价格等公开数据，通过数据分析与可视化技术，探索票价趋势、出行模式，提供直观的数据洞察，辅助决策优化和服务提升。分析范围本项目主要分析中国铁路12306公布的较为繁忙的铁路线路的火车车次、火车类型、火车出发时刻/到站时刻、火车始发站/终点站、火车票价等进行数学分析和数据可视化。实现思路[!NOTE]为确保项目实现过程与课程教学内容紧密对应，本项目将依据课程中讲授的知识点逐步实现相应功能。尽管实现方法可能非最高效途径，但旨在强化理论与实践的结合，确保学习效果。编写开发文档一个好的项目避免不了需要一个好的开发文档，为了实现此项目，特编写了这篇开发文档，因为了保持和课程教学内容紧密对应，本篇文档将采用MarkDown编写。数据采集本次选取了piao.gaotie.cn网站进行火车数据的数据采集，并加入了代理IP进行高效爬取，谨防出现IP被封禁等情况。为了更好的实现数据采集的功能，我将数据采集(GetData)与代理(Proxy)制作成了两个软件包，供主函数调用。为了更高效的使用IP代理，减少数据采集带来的麻烦。我将代理IP的软件包制作成了一个Class(类)对象，进行代理调度让采集更高效，以更好的实现数据采集。通过上述步骤，将采集好的数据通过Python中的List(列表)特有的数据类型返回给主函数进行调用。[!WARNING]本项目侧重点为数据分析与可视化，故数据采集的具体实现方法与过程将不再过多讲述，详情可查看源代码。数据写入与保存将数据采集返回的数据保存到运行目录下的/data文件夹下（在保存前，应先判断此路径是否存在，如不存在应先创建），将为每一条火车线路单独创建一个csv文件，实现数据的分别保存。 # 获取火车数据并写入csv文件，准备数据处理 # 注意：如是火车，则动车组二等座的价格为硬座价格。 # TODO 数据获取与写入 # START------------------------------------------------ data, city = GetData_Package.get_data() # 获取数据的函数 for index, item in enumerate(data): #循环取值 city_from = city[index]["from"][0] #获取火车的出发地 city_to = city[index]["to"][0] #获取火车的目的地 with open(r"data/" + city_from + "-" + city_to + ".csv", "w", newline="", encoding="gbk") as files: #创建文件对象 writer = csv.writer(files) # 创建csv对象 writer.writerow( ['车次', '空白值1', '类型', '出发站', '空白值2', '到达站', '出发时间', '到达时间', '运行时长', '动车组票价', '火车组票价', '空白值3']) # 在数据爬取中，有空白列，在后续处理 writer.writerows(item) # 写入数据 # END------------------------------------------------[!CAUTION]至此，数据采集与文件写入已完成，下面将开始进行数据分析。数据分析前提条件：读入csv文件进行数据分析的前提需要读入csv文件，在csv文件中进行分析。因本项目中，将每一条火车线路都单独创建了一个csv文件，所以在读入时，我采用了循环读入文件。 data, city = GetData_Package.get_data() # 获取采集后的数据 for index, item in enumerate(data): city_from = city[index]["from"][0] #获取火车的出发地 city_to = city[index]["to"][0] #获取火车的目的地 file_path = f"data/{city_from}-{city_to}.csv" # 读取CSV文件到 DataFrame df = pd.read_csv(file_path, encoding='gbk', na_values=' - ') # 后续数据分析的代码都在此循环内依次进行数据分析_第一步：空白列删除（缺失值处理）因数据采集后的数据有多余无意义的数据，为了保证数据分析的可靠性，我们需要先将多余无意义的数据进行删除。在本项目中，数据采集之后生成了多余的空白列，所以在此处我们先进行空白列的删除。我们定义了一个List(columns_to_keep)，将需要保留的列赋值给了它。运用pandas库中的函数删除不存在上述列表中的列，达到空白列的删除。# 处理数据第一步：删除空白列 # 背景：在数据爬取中，有空白列，我们在这里删除 # TODO 数据处理：删除空白列 # START------------------------------------------------ columns_to_keep = ['车次', '类型', '出发站', '到达站', '出发时间', '到达时间', '运行时长', '动车组票价', '火车组票价'] # 定义需要保留的列 df = df[columns_to_keep] # 删除不在 columns_to_keep 列表中的所有列 # END------------------------------------------------数据分析_第二步：火车价格的数据处理（分割数据）在本项目中，数据采集后的数据将火车的火车价格都放在一个单元格中（例如在火车中，爬取后的数据将卧铺的价格放在了一个单元格内，但原本火车价格应有卧铺上铺、中铺、下铺三种席位，这三种席位，每种席位有单独的价格，故我们应该将这三个价格分别放置在三个不同单元格中），需要对价格进行分割，否则会造成后续进行票价分析中出现问题。# 处理数据第二步：火车价格的数据处理 # 背景：目前火车所有坐席的价格在同一个单元格内，我们需要把不同坐席的价格放在不同的单元格内进行展示 # TODO 数据处理：分割数据 # START------------------------------------------------ split_columns = df['动车组票价'].str.split('/', expand=True)# 分割'动车组票价'、"火车组票价"列，并创建新的列名 split_columns.columns = ['动车组票价_二等座', '动车组票价_一等座'] df = df.join(split_columns).drop('动车组票价', axis=1)# 将新列加入原始 DataFrame并删除旧的'动车组票价'、"火车组票价"列 split_columns = df['火车组票价'].str.split('/', expand=True) split_columns.columns = ['火车组票价_硬卧上', '火车组票价_硬卧中','火车组票价_硬卧下'] df = df.join(split_columns).drop('火车组票价', axis=1) # END------------------------------------------------数据分析_第三步：缺失值处理在数据采集中，有部分火车的价格没有获取到，造成了火车的价格为null，为了为了保证数据分析数据真实性，我们将没有获取到火车价格的车次信息进行整行删除。# 处理数据第三步：火车价格的缺失值处理 # 背景：有些火车数据非常扯淡！它没有获取到火车票价格，那我们就把没有获取到火车票价格的车次删除掉！ # TODO 数据处理：缺失值处理 # START------------------------------------------------ columns_of_interest = ['火车组票价_硬卧上', '火车组票价_硬卧中','火车组票价_硬卧下','动车组票价_二等座', '动车组票价_一等座'] #指定列名 for column in columns_of_interest:# 在csv中，缺失值是使用'-'代替，我们在指定列中将其替换为NaN以便更好处理缺失值 df[column] = df[column].apply(lambda x: np.nan if x == ' - ' else x) mask_all_na_in_columns = df[columns_of_interest].isna().all(axis=1)# 找出所有在指定列中全部是NaN的行 df = df[~mask_all_na_in_columns]# 删除那些在指定列中全部是NaN的行 # END------------------------------------------------数据分析_第四步：数据清洗在csv文件中读取的火车价格的数据类型为String(字符串)类型，为了实现后续的数学运算和数学分析，在此处我们需要将火车价格的数据类型从字符串转为float(浮点型)。否则会报错！# 处理数据第四步：火车价格的数据清洗 # 背景：有些火车价格数据为string，我们需要把它们统一转换为float，才能进行下一步的数据最大/小值的计算，否则会报错！ # TODO 数据处理：火车价格的数据清洗 # START------------------------------------------------ df['动车组票价_二等座'] = pd.to_numeric(df['动车组票价_二等座'], errors='coerce') df['动车组票价_一等座'] = pd.to_numeric(df['动车组票价_一等座'], errors='coerce') df['火车组票价_硬卧上'] = pd.to_numeric(df['火车组票价_硬卧上'], errors='coerce') df['火车组票价_硬卧中'] = pd.to_numeric(df['火车组票价_硬卧中'], errors='coerce') df['火车组票价_硬卧下'] = pd.to_numeric(df['火车组票价_硬卧下'], errors='coerce') # END------------------------------------------------数据分析_第五步：数据排序与Xlsx文件写入我们将根据火车的价格从高到低(AESC)进行排序，并将排序好的数据写入到/data/sort文件夹下（在写入前，应先判断此路径是否存在，如不存在应先创建）。通过上述操作可形成一个具有价格梯度的火车信息，帮助我们分析和查看。注意：本次排序将通过动车组票价_二等座的票价数据为Key进行排序，其他字段不涉及排序。# 处理数据第五步：根据火车价格进行AESC排序以及保存至XLSX文件中 # 背景：我们需要根据火车价格进行排序，形成一个具有价格梯度的火车信息，帮助我们分析和查看。 # 注意：在此处我们统一按照 动车组票价_二等座 进行排序，其他字段不涉及排序，将排序后的结果生成到 data/sort 文件夹下。 # TODO 数据处理：根据火车价格进行排序以及保存至XLSX文件中 # START------------------------------------------------ sorted_df = df.sort_values(by=['动车组票价_二等座'], ascending=False) sorted_df.to_excel('data/sort/' + city_from + '-' + city_to + '.xlsx', index=False, engine='openpyxl') # END------------------------------------------------数据分析_第六步：重复值处理在本项目中，数据本身没有重复的现象，但为确保项目实现过程与课程教学内容紧密对应，此处仍然进行重复值处理。如果发现有重复值情况，则删除重复值，谨防对后续数学运算带来影响。# 处理数据第六步：数据清洗，检测并删除重复值（重复数据） # 背景：如果火车数据是重复的，那我们就需要删除重复数据，防止数据有多个造成数据不准确。 # TODO 数据处理：数据清洗，检测并删除重复值 # START------------------------------------------------ df.drop_duplicates(inplace=True) # END------------------------------------------------数据分析_第七步：运用max、min、mean进行数学统计计算火车价格的最大值、最小值、平均值进行数据统计，方便我们查看数据。在计算好数据之后，我们将这些数据写入到csv文件中（在文件的最后一行中再新起三行分别写入最小值、最大值、平均值）。# 处理数据第七步：计算火车价格的最大值、最小值、平均值进行数据统计，将计算的结果新起一列写入csv文件中 # 背景：数据统计后，能发现乘坐哪个火车价格最实惠。 # TODO 数据处理：火车价格的数据统计 # START------------------------------------------------ new_row_data = {'车次': '车票价格总计_最大值', '动车组票价_二等座': df['动车组票价_二等座'].max(), '动车组票价_一等座': df['动车组票价_一等座'].max(), '火车组票价_硬卧上': df['火车组票价_硬卧上'].max(), '火车组票价_硬卧中': df['火车组票价_硬卧中'].max(), '火车组票价_硬卧下': df['火车组票价_硬卧下'].max(), } df.loc[len(df)] = new_row_data # 将最大值的数据添加到 df末尾 # ---分隔符--- new_row_data = {'车次': '车票价格总计_最小值', '动车组票价_二等座': df['动车组票价_二等座'].min(), '动车组票价_一等座': df['动车组票价_一等座'].min(), '火车组票价_硬卧上': df['火车组票价_硬卧上'].min(), '火车组票价_硬卧中': df['火车组票价_硬卧中'].min(), '火车组票价_硬卧下': df['火车组票价_硬卧下'].min(), } df.loc[len(df)] = new_row_data # 将最小值的数据添加到 df末尾 # ---分隔符--- new_row_data = {'车次': '车票价格总计_平均值', '动车组票价_二等座': df['动车组票价_二等座'].mean(), '动车组票价_一等座': df['动车组票价_一等座'].mean(), '火车组票价_硬卧上': df['火车组票价_硬卧上'].mean(), '火车组票价_硬卧中': df['火车组票价_硬卧中'].mean(), '火车组票价_硬卧下': df['火车组票价_硬卧下'].mean(), } df.loc[len(df)] = new_row_data # 将平均值的数据添加到 df末尾 # END------------------------------------------------数据分析：运用describe进行数据输出并进行数据写入我将前面七步执行完后，将通过print语句输出describe可更好查看具体概括数据。# 输出df和desc print(df) print(df.describe())输出后，我们需要将前面七步进行更改后的数据写到csv文件中。# 将清理后的数据写回CSV文件 df.to_csv(file_path, index=False, encoding='gbk')数据分析_第八步：合并csv文件在前面的讲述中，我将每一条火车线路都单独创建了一个csv文件。于是我们将这些csv文件合并到一个csv文件里面，方便我们查看所有的数据。# 处理数据：合并数据，将所有城市的火车数据合并到一个csv文件中 # 背景：我们需要将所有城市的火车数据合并到一个csv文件中，便于后续分析。 # TODO 数据处理：合并数据 # START------------------------------------------------ all_csv_files = glob.glob(os.path.join('data', "*.csv")) all_csv_files_list = [] for csv_files_item in all_csv_files: all_csv_df = pd.read_csv(csv_files_item, encoding='gbk') all_csv_df = all_csv_df[ ~(all_csv_df['车次'].isin(['车票价格总计_最大值','车票价格总计_最小值','车票价格总计_平均值'])) ] # 这些值不用合并 all_csv_files_list.append(all_csv_df) all_cities_df = pd.concat(all_csv_files_list, axis=0, ignore_index=True) all_cities_df.to_csv('所有城市的火车信息.csv', index=False, encoding='gbk') # 将所有城市的火车数据写回一个csv文件 # END------------------------------------------------[!CAUTION]至此，数据分析已完成，下面将开始进行数学分析。数学运算经过数据分析的八个步骤后，我们的数据样本已经得到了大致的了解，下面我们通过数学运算来更好的进行数据层面的分析：[!TIP]在数学运算中，我们统一通过所有城市的火车信息.csv此文件进行数学运算的分析。前提条件：读入csv文件# 这里读入的是'所有城市的火车信息.csv'，也就是合并后的数据进行检验 df = pd.read_csv('所有城市的火车信息.csv', encoding='gbk', na_values=' - ')数学运算_第一步：假设检验（T检验）假设检验可以用来比较不同类型的票价，看看各票价之间是否存在显著差异。# 数学运算第一步：假设检验（T检验） # 背景：假设检验可以用来比较不同类型的票价，看看是否存在显著差异。 # TODO 数学运算：假设检验 # START------------------------------------------------ t_stat, p_value = stats.ttest_ind( df['动车组票价_二等座'].dropna(), df['火车组票价_硬卧上'].dropna(), nan_policy='omit')# 比较动车组二等座和火车组硬卧上座的票价 print(f"假设检验检测_动车组票价_二等座 or 火车组票价_硬卧上 - T-statistic: {t_stat}, P-value: {p_value}") # END------------------------------------------------数学运算_第二步：分布拟合我们可以拟合票价数据到常见的统计分布（例如正态分布），看数据是否符合这些分布。# 数学运算第二步：分布拟合 # 背景：我们可以拟合票价数据到常见的统计分布（例如正态分布），看数据是否符合这些分布。 # TODO 数学运算：分布拟合 # START------------------------------------------------ params = stats.norm.fit(df['动车组票价_二等座'].dropna()) # 拟合正态分布 print(f"分布拟合检测_动车组票价_二等座 - 均值(Mean): {params[0]}, 标准差(Std): {params[1]}") params = stats.norm.fit(df['火车组票价_硬卧上'].dropna()) # 拟合正态分布 print(f"分布拟合检测_火车组票价_硬卧上 - 均值(Mean): {params[0]}, 标准差(Std): {params[1]}") params = stats.norm.fit(df['火车组票价_硬卧中'].dropna()) # 拟合正态分布 print(f"分布拟合检测_火车组票价_硬卧中 - 均值(Mean): {params[0]}, 标准差(Std): {params[1]}") params = stats.norm.fit(df['火车组票价_硬卧下'].dropna()) # 拟合正态分布 print(f"分布拟合检测_火车组票价_硬卧下 - 均值(Mean): {params[0]}, 标准差(Std): {params[1]}") # END------------------------------------------------数学运算_第三步：方差分析（ANOVA）利用数据中的方差数据，帮助我们更好地理解数据。# 数学运算第三步：方差分析（ANOVA） # 背景：利用方差，帮助我们更好地理解数据。 # TODO 数学运算：方差分析（ANOVA） # START------------------------------------------------ # 方差分析（ANOVA） f_stat, p_value = stats.f_oneway( df['动车组票价_二等座'].dropna(), df['动车组票价_一等座'].dropna() ) print(f"方差分析（ANOVA） - F-statistic: {f_stat}, P-value: {p_value}") # END------------------------------------------------数学运算_第四步：数据的相关性分析我们可以通过计算数据的相关系数，来判断数据之间的相关性。(此处火车和火车比较、动车和动车比较)# 数学运算第四步：数据的相关性分析 # 背景：我们可以通过计算数据的相关系数，来判断数据之间的相关性。 # TODO 数学运算：数据的相关性分析 # START------------------------------------------------ # 相关性分析 corr_1 = df['动车组票价_二等座'].corr(df['动车组票价_一等座']) corr_2 = df['火车组票价_硬卧上'].corr(df['火车组票价_硬卧中']) print(f"动车组二等座和一等座票价的相关性: {corr_1}") print(f"火车组硬卧上和硬卧中的票价相关性: {corr_2}") # END------------------------------------------------
组装硬件将提供的设备进行组装，有相机组件、机械臂组件、传送带组件。其中，机械臂组件还需要与吸盘、夹爪组装。(如果是螺丝检测，则应该组装夹爪；如果是PCB检测，则应该组装吸盘)传送带组件中，还有红外线检测仪器。项目文件螺丝加工件处理数据采集与标注数据采集本项目中，数据采集使用"MAIN_DH.py"文件，直接在终端内执行(执行命令前需要cd至代码主路径下)python3 MAIN_DH.py执行命令后，会打开GUI窗口，如图所示：其中具体功能详情如下：打开相机关闭相机采集图像数据连接机械臂断开机械臂机械臂回零关闭真空发生器显示机械臂现在的笛卡的坐标XYZR的值控制机械臂XY方向运动控制机械臂Z方向与R旋转设置中转点将8现在的坐标值设置到中转点位运动到中转点设置抓取点将8现在的坐标值设置到抓取点位运动到抓取点设置机械臂抓取机械臂在XY方向的偏差显示日志信息选择放置物料的类别开始零件分类开始PCB分类调用执行开始保存设置的点位信息调试完成后勾选记录比赛根据17设置的类别设置对应类别的放置点运动到17选择的点位数据采集后，数据保存到主路径的上一级目录下的"save_image"文件夹下。数据标注此时，应该在项目主目录下新建一个文件夹，然后把"save_image"文件夹下的文件复制到新建的文件夹下。cd进入这个文件夹后，在终端执行：labelimg会打开数据标注工具"labelimg"，如下图所示：打开标注照片的文件夹设置标注完成保存位置（将采集的图片.jpg文件和打完标签的.txt文件保存在一个文件夹里）打完标签完成后可以打开该文件里的classes.txt文件，若里面的东西只有本次打标签的名字，则正确。格式选YOLO可以在 Edit 里面选择"auto save"（自动保存）注意事项：标注PCB缺陷数据集的时候必须标注一个名为PCB的类，为pcb抓取定位位置（没有这个类无法定位抓取）。需要将有问题的PCB板的问题处特别标定。模型训练在数据采集和训练完成后，我们需要训练数据，训练数据的python代码是项目主路径下的"train.py"文件。但是在训练之前，我们需要调整训练参数，具体要调整的内容如下：训练的模型配置文件和标签数据文件：在 train.py的大概430行左右，有下述代码： parser.add_argument('--cfg', type=str, default='models/yolo5s_part.yaml', help='model.yaml path') parser.add_argument('--data', type=str, default=ROOT /'data/part.yaml', help='dataset.yaml path')依次点击上述两个yaml文件，修改nc(标签个数)与name(标签名称)。训练的GPU与训练的轮数、一轮中每秒训练的图片张数：在 train.py的大概440行左右，有下述代码： parser.add_argument('--epochs', type=int, default=1000, help='total training epochs') parser.add_argument('--batch-size', type=int, default=100, help='total batch size for all GPUs, -1 for autobatch')修改第一行的轮数次数，修改第二行的一轮中每秒训练的图片张数（一般为6 or 8）。在 train.py的大概450行左右，有下述代码： parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')修改需要使用的GPU索引，一般填0，即默认使用第一张显卡训练。修改完训练参数后，我们开始训练模型。我们使用终端执行下述命令打开它。python3 train.py然后等待训练的模型完成。会生成best.pt与last.pt文件，路径是在"/runs/train/xxx"(这个路径在训练的python文件中可自定义)。我们把这两个pt文件复制到\my_weights\nut路径下。九点标注运行项目主路径线下的"arm_机械臂手眼标定\MAIN_GUI_daheng.py"文件，进行九点标注，实现九点标注是为了把机械臂与摄像头的点位匹配，已达到获取后续抓取的准确性。开始测试本项目中，数据测试使用"MAIN_DH.py"文件，直接在终端内执行(执行命令前需要cd至代码主路径下)python3 MAIN_DH.py打开GUI页面后，开始测试。PCB板加工件处理数据采集与标注同上文一致。模型训练同上文一致。九点标注上文标注后PCB板可不再标注，但是不能动 摄像头与机械臂的位置。开始测试同上文一致。
物流信息批量查询之JS混淆逆向跟栈分析
前情概要近几天有客户联系到我接单，客户要求：做一个批量查询物流信息的软件，要求一天能够查询上十万单的物流信息，并且从这十几万单的物流信息中提取出有效物流，并且能够导出物流揽收时间、寄出地（并且要求提取出省、市）。奈何现在的反爬技术愈来愈强，各大快递公司对于物流信息查询的接口鉴权已做的越来越全面。通过提取物流公司提供的快递单号查询接口已不太现实。但通过物流公司提供的开放平台“物流轨迹识别API”实现需要对API按次收费，也不是最优的方案。我的想法经过几天的查找，发现有个别网站正在做“批量查询物流信息”的平台，虽然是收费平台，但是看到优惠信息“每个账号每天能免费查询100单物流信息”，于是一个非常大胆的想法油然而生，制作一个自动化程序，首先注册一万个账号写入“账号库”，在之后查询时调用“账号库”中的账号进行查询，理论上可以实现。开始分析PS：平台网址：aHR0cHM6Ly95dW4uemh1emh1ZmFubGkuY29tLw==想要实现自动化注册流程，那么必须抓到网站在注册时候发送的包，首先进入网站的注册网页，再进入Web dev，后面发现跳“debugger”。那么，首先要解决这个问题，这个问题在JS逆向中最常见，一般出现这个问题我们是要通过“替换JS”文件的方法来解决，即把调用这个“debugger”的方法通过JS替换文件的方式给注释掉，就不会弹“debugger”了。JS逆向第一步：注释debugger方法那么，我们通过跟栈分析，在调用"debugger"方法中，有两个匿名函数，那么我们跳过，点击第一个堆栈函数。发现(_0x4a3118['LlPtj'])('de'));这一个函数调用了“debugger”方法，那么对函数参数置空，即(_0x4a3118[''])('de'));即可注释掉“debugger”方法。这是最基础的一个步骤，因为JS做了逆向和混淆，同时也可见JS代码中，所有的参数、函数名称，都已经混淆，完全代码不可读。JS逆向第二步：通过网络模块，找出注册账号的接口通过分析，发现注册的脚本内，除了我们填写的值“username”、“password”、“qq”等，还有一个“zs”的值，那很明显接口被加密了，首先遇到这个情况怎么办呢？如何知道这个加密的值是怎么计算的呢？发现加密参数首先，我们观察一下这个值的构成，他是由xxxx + “|” + 时间戳 构成，那么这个加密的值是不是动态的？（即用户名和密码和qq都填写的一样，这个加密的值会变化吗）？我们多尝试几次，发现加密出的这个参数的值每次都会发生变化，因此我们考虑是否和时间戳有关。其实这个参数的构成中带有时间戳，已经暗示的很明显了，生成这个参数的值和时间戳是有关的，因为时间戳每时每刻都在发生变化，所以无论我们注册的值是否发生变化，时间戳发生变化后，这个加密的值都会发生变化。找到计算加密参数的JS代码我们全局搜索这个参数的key，即“zs”。现在，我们找到和有关“zs”的JS代码。 submit: function (formfilter, reload, addsign, sgname) { //监听提交 form.on('submit(' + formfilter + ')', function (data) { var ld = layer.load(); //算sign if (addsign === true) { var s = md5.Do(data.field[sgname]); data.field["zs"] = s.toString(); } $.post('', data.field, function (result) { layer.close(ld); //关闭load lyresult.show(result, reload); }, 'json').error(function () { layer.close(ld); //关闭load lyresult.syserr(); }); return false; }); },现在，可见，有“md5”字样，可能是“MD5加密”，我们打断点，分析他调用的函数的内容。跟栈分析，发现在一串函数中，有一个返回值： return _0x4542ec['Abmuj'](_0x4cb122['MD5'](_0x5ca529 + _0x4542ec + [_0x4481('‫129'很明显，他是字符串拼接，然后进行MD5加密的，可以打断点，分析每个参数里面的值是什么？经过断点分析，“_0x5ca529”是用户名，“_0x4542ec”是一个常量，用于字符串的加密拼接所需要的，至此，加密逻辑都完成了。我们知道了他的加密逻辑，放在python等语言中，计算加密内容调用即可！登录之后，推送物流查询信息这儿也和之前一样，通过dev获取相关接口，虽然也有加密，但是加密的方法也是一样的，一样的分析即可。这里不再赘述。本篇完。
本次分析的是“网站测速”模块。进入页面后，通过F12开发者工具，进行网页抓包，发现点击“开始测速”后，发现页面进行了一个 POST 请求，如图，经过请求页面后，发现有一段JS代码进行WSS（websockets）协议连接；通过分析 POST后的页面源码，发现一段疑似进行WSS协议链接的网页代码：$(function(){ //新增历史记录 add_history('http://code.chboy.top'); //开始分发任务 //连接websocket var ws; var wss_url='wss://www.itdog.cn/websockets'; var task_id='20240627153112mtm0f2jx561tfo0e0g'; var check_mode='fast'; create_websocket(wss_url,task_id,check_mode); });通过溯源这个JS代码中的 create_websocket 函数，后发现于一个经过混淆的JS代码内（https://www.itdog.cn/frame/js/pages/http_speed.js）。通过JS代码整体情况来看，疑似ob混淆，参考文献：https://blog.csdn.net/weixin_43411585/article/details/123437953于是我们使用 v_jstools 工具进行解密。通过原JS脚本下断点，发现WSS协议中的加密参数 ws.onopen = function () { ws.send("{\"task_id\":\"" + _0x261ac6 + "\",\"task_token\":\"" + md5(_0x261ac6 + "token_20230313000136kwyktxb0tgspm00yo5", 16) + "\"}"); };可见，通过task_id字段和"token_20230313000136kwyktxb0tgspm00yo5"字符串进行拼接，然后MD5加密，之后取MD5的前16个字符。然而，在POST接口时，task_id字段已经明文告知，此处不需要解密，直接调用这个解密即可解密。详情解密结果，可见附件。itdog.zip