environment
- 2022-05-10
- Windows 10
- Development Tools VS 2022
- Development language C#
- a .pcap file
Task
-
任务
: pcap file parsing. -
参考资料
:- pcap file format learning: Detailed explanation of Pcap files_Keep-fight's blog-CSDN blog_pcap
-
作业
: Programming to parse the pcap file on the third day, traverse all data packets in the file, and print all packet time and length information.Example:
- 124 packets in total:
- [2022-01-12 15:00:01] 128 Bytes
- [2022-01-12 15:00:02] 1064 Bytes
- [2022-01-12 15:00:03] 1460 Bytes
- ······
Operation
思路分析
1. 文件读取用FileStream 2. 读取/解析pcap数据报存储格式的数据 2.1 读取Pcap Header的Magic(4B) 判断大小端 2.1.1 如果是小端数据,则需要Reverse函数,交换字节顺序。 2.2 读取数据包的时间戳(高低都要) 2.3 读取数据包长度 2.4 通过定位来截取需要的字节Seek;或者通过 字节数组 的截取 2.5 将读取的2进制文件转成16进制(方便对照) 2.6 将数据转换成10进制 3. 用FileStream写入解析出来的数据
难点
时间戳:格林威治时间1970年01月01日00时00分00秒起至当下的总秒数。(这是Unix的) 时间戳:DateTime.Now.Ticks;是从0001 年 1 月 1 日午夜 12:00:00 开始,单位是微秒。(这是C#系统的) pcap的Timestamp(4B):时间戳高位,精确到seconds,这是Unix时间戳。捕获数据包的时间一般是根据这个值。 我是在C#里,所以我需要将解析出来的这个时间戳*1000,
问题
The timestamp calculation was unsuccessful. After I converted it to integer type int(int32)*1000, the timestamp became negative, and then changed it to long integer type long(int64)*1000; and the start time must be set to Greenway. Calculate the time of [DateTime dateTimeStart = new DateTime(1970, 1, 1, 0, 0, 0);], and then the real time can be calculated.
code
using System.Text;
using ReadAndPrint;
var path = @"D:/day3.pcap";
var filePath = $@"D:/Log/{
DateTime.Now.ToString("yyyyMMdd-HHmmss")}.txt";
PcapHelper pcap = new PcapHelper();
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
// 将文件中的数据暂存至 字节数组b 中
int len = (int)fs.Length;
byte[] b = new byte[fs.Length];
fs.Read(b, 0, len);
// 截取前4B 判断大小端 识别文件和字节顺序
byte[] bMagic = b.Skip(0).Take(4).ToArray();
pcap.isSmall(bMagic);
long icapLen = 0;
int position = 24;
int counter = 1;
do
{
// 数据包 时间戳 s:高精度 ms:低精度
byte[] btimeStamp = b.Skip(position).Take(4).ToArray();
// long s = pcap.Get10By2(btimeStamp);//由于写入的时间里也加入了这个方法,导致字节进行了两次交换,所以屏蔽了这一句;或者可修改时间的方法或者重新写一个参数为long类型的得到时间的方法。
long ms = pcap.Get10By2(b.Skip(position + 4).Take(4).ToArray());
// 数据包 长度信息
byte[] bcapLen = b.Skip(position + 8).Take(4).ToArray();
icapLen = pcap.Get10By2(bcapLen);
// pcap.ByteToDate(btimeStamp) 解析这个时间戳,转换为抓包的时间
pcap.WritePrint(filePath, $"[{
pcap.ByteToDate(btimeStamp)}.{
ms}] [{
counter}] {
icapLen} Bytes ");
position += (int)icapLen + 16;
counter++;
// 当icapLen<=0且position>=len的时候,结束循环
} while (icapLen > 0 && position < len);
Console.WriteLine();
}
// 辅助类
using System.Text;
/// <summary>
/// 若isSmall为true,则字节数组进行翻转
/// </summary>
private bool bisSmall {
get; set; } = false;
/// <summary>
/// 根据 16进制字符串 返回 10进制数字
/// 16 => 10
/// </summary>
/// <param name="str16">16进制字符串</param>
/// <returns>10进制数字</returns>
public int Get10By16(string str16)
{
return System.Convert.ToInt32(str16, 16);
}
/// <summary>
/// 将字节数组 转成 10进制数字
/// </summary>
/// <param name="b"></param>
/// <returns>10进制数字</returns>
public long Get10By2(byte[] b)
{
return System.Convert.ToInt64(Get16By2(b), 16);
}
/// <summary>
/// 将字节数组 转化为 16进制的字符串
/// 2 => 16
/// </summary>
/// <param name="b">字节数组</param>
/// <returns>16进制的字符串</returns>
public string Get16By2(byte[] b)
{
string str = "", str1 = "";
// 判断数据是 大端读取 还是 小端读取
// 是小端则进行翻转
if (bisSmall)
Array.Reverse(b);
for (int i = 0; i < b.Length; i++)
{
str += string.Format("{0:X2}", b[i]);// D4C3B2A1
str1 += string.Format("{0:x}", b[i]);// d4c3b2a1
}
return str;
}
public string ByteToDate(byte[] b)
{
// C# 需要精确到毫秒,而解析出来的高精度是到秒,所以*1000
long unixDate = Get10By2(b) * 1000;
// 相当于时间定位到格林威尔
DateTime dateTimeStart = new DateTime(1970, 1, 1, 0, 0, 0);
// 格林威尔时间+时间戳 ==》数据包抓包时间
DateTime date = dateTimeStart.AddMilliseconds(unixDate).ToLocalTime();
return date.ToString("yyyy-MM-dd HH:mm:ss");
}
/// <summary>
/// 判断数据是 大端读取 还是 小端读取
/// </summary>
/// <param name="b">字节数组</param>
public void isSmall(byte[] b)
{
string sMagic = Get16By2(b);
if (sMagic == "D4C3B2A1")
bisSmall = true;
else if (sMagic == "A1B2C3D4")
bisSmall = false;
}
/// <summary>
/// 将解析出来的数据写入Log
/// </summary>
/// <param name="b"></param>
public void WritePrint(string filePath, string b)
{
// 创建文件夹
CreateFolder(filePath);
using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
fs.Write(Encoding.UTF8.GetBytes(b), 0, b.Length);
fs.Write(Encoding.UTF8.GetBytes("\r\n")); // 数据换行
}
}
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="filePath"></param>
public void CreateFolder(string filePath)
{
//var fileName = Path.GetFileName(filePath); // 20220518-134243.txt
var logPath = Path.GetDirectoryName(filePath);// D:\\Log
if (Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
// 创建文件
CreateFile(filePath);
}
/// <summary>
/// 创建文件
/// </summary>
/// <param name="filePath"></param>
public void CreateFile(string filePath)
{
if (!File.Exists(filePath))
{
using var fs = new FileStream(filePath, FileMode.Create); }
//File.Create(filePath);
}
result
[1] [2022-05-10 09:44:27.650676] 66 Bytes
[2] [2022-05-10 09:44:27.702689] 66 Bytes
[3] [2022-05-10 09:44:27.702731] 54 Bytes
···
[57] [2022-05-10 09:44:28.504467] 1039 Bytes
[58] [2022-05-10 09:44:28.545317] 54 Bytes