.NET6实现破解Modbus poll点表配置文件

  • 📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!

  • 📢本文作者:由webmote 原创

  • 📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !

序言

Modbus 协议是工控领域常见的一种通信协议,而Modbus Poll无疑是其中最好用的Master软件了,通过自定义的点表,可以通过查表的方式,快速的去响应主从机的动作和状态。

其中使用的点表配置文件格式为mbp,今天,我们的目标就是这个文件!

其中,对我们有意义的数据有起始地址,功能代码,点表列表数据,如何获取呢?
dbfbb4a3c23d6e596af8bf6d4d2881ca.png

1. 分析文件格式

先谷歌一下,在百度一下,并未发现有人解析过mbp格式的文件,经过在官网的浏览,没有发现任何有用的信息,可见该文件格式是私密格式,如果给作者发邮件,不知道能否得到作者大大的指点,有社牛的朋友,可以试试。

chatgpt说: Modbus Poll 是一个用于 Modbus 通信协议的 Windows 应用程序,它允许用户进行 Modbus 通信的监视和测试。Modbus Poll 使用 MBP 格式来保存配置文件,其中包含了 Modbus 通信配置和设置。要解析 Modbus Poll 的 MBP 格式文件,需要了解该文件的具体结构和存储的内容。通常来说,MBP 文件是一个二进制文件,保存了 Modbus Poll 的配置信息,如串口设置、Modbus 寄存器的地址、功能码、数据格式等。

不过Chatgpt并没有鸟用。
b032b1f2f40397eb2674279b1b4f73b2.png
好鸟一身毛,让我们打开mbp,直接查看吧。
5e28a7ae7559734cdca983a1cc07f14b.png
好家伙,这文件也太不精简了,这么多预留字段,如何下手啊?

好的思路,胜过一千行代码,来吧,让我们打开Beyond Compare屠龙软件,多加一行点表,看看HEX有啥不同。

ffea59052e37aceab7200d9d7b756872.png
经过缜密的比对,已经看出来头信息的格式定义,以及点表的定义位置,那么,让我们打开VS,开启码农生活。

2. 解析文件头

本来想采用结构体定义,不过有些字段是可变长度的,处理起来并没有那么容易,干脆一不做二不休,直接使用接口定义解析。

public interface IParser
    {
        int Size { get; }
        /// <summary>
        /// 解析buffer
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="start">开始解析的地址</param>
        /// <returns>解析读取了多少字节</returns>
        int Parse(byte[] buffer, int start =0);
    }

通过size,返回该类的所占的字节长度,通过parse的返回值,获取buffer的当前指针。

定义好接口后,那么我们就来实现头的解析。

public class MbpHeader : IParser
    {
        public int Flag;
        public int Version;
        public int FuncCode;
        public int StartAddress;
        public int PointTableSize;
        public byte[] Reserve;
        public int Size { get; private set; }
        public MbpHeader()
        {
            Flag = 0x2454;
            Version = 0x00A8;
            FuncCode = 0;
            StartAddress = 0;
            PointTableSize = 0;
            Reserve = new byte[508];
            Size = 0x20C;
        }

        public int Parse(byte[] buffer, int start = 0)
        {
            int i = start;
            var flag = BitConverter.ToInt32(buffer, i);
            //if(flag != 0x2454 && flag != 0x2648)
            //{
            //    throw new NotSupportedException("报文头不对,不能解析");
            //}
            i += 4;
            var ver  = BitConverter.ToInt32(buffer, i);
            //if(ver != Version)
            //{
            //    throw new NotSupportedException("报文版本不对,不能解析");
            //}
            i += 4;
            FuncCode = BitConverter.ToInt32(buffer, i);
            i += 4;
            StartAddress = BitConverter.ToInt32(buffer, i);
            i += 4;
            PointTableSize = BitConverter.ToInt32(buffer, i);
            i += Reserve.Length;

            this.Size = i;
            return i;
            
        }
    }

代码中的FlagVersion是我们推测来的,不过经测试,大概率和版本有关。
这里注释掉对版本的限制,可以支持多个版本的解析。

3.解析点表列表

经过排查,点表的解析定在 0x20C的位置上,从这里开始进行循环检测。
代码如下:

public class MbpPointTable : IParser
    {
        public int[] Flags { get; set; }       
        public MbpTableItem Item { get; set; }       
        public int Size { get; private set; }
        public MbpPointTable()
        {
            Flags = new int[12];
            Item = new MbpTableItem();
        }

        public int Parse(byte[] buffer, int start = 0)
        {
            Size = 0;
            if (buffer == null) return 0;

            var i = start;
            if(buffer.Length < start + 4 * Flags.Length)
            {
                return 0;
            }
            for(var j=0; i < start + 4*Flags.Length; i+=4,j++)
            {
                Flags[j] = BitConverter.ToInt32(buffer, i);
            }
            //0xFF FE FF
            i += 3;           
            var size = Item.Parse(buffer, i);           
            i += size;
            //解析值
            var flag3 = BitConverter.ToInt32(buffer, i);
            if(flag3 == 0x55555555)
            {
                Size = i + 4 - start;
                return i+4;
            }
            else
            {
                return -1;
            }               
        }

    }

为了更方便的标识点表数据,还需要增加一个tableItem类。

public class MbpTableItem : IParser
    {
        public int Size { get; private set; }
        public byte Len { get; set; }
        public string Name { get; set; }

        public Int16 Value { get; set; }

        public int Parse(byte[] buffer, int start = 0)
        {
            if (buffer == null) return 0;
            if(buffer.Length> start)
            {
                Len = buffer[start];
                if(buffer.Length >= (start +1 + 2* Len))
                {
                    if (Len > 0)
                    {
                        this.Name += Encoding.Unicode.GetString(buffer, start + 1, 2 * Len);
                    }
                    else
                    {
                        this.Name = String.Empty;
                    }

                    return 1+ 2*Len;
                }
                else
                {
                    return 0;
                }
            }

            return 0;
        }       
    }

这里采用Unicode对名称进行解析,以支持中文名称。

4.测试例子

为了测试,还需要封装一个mbpFile的类,这里利用该类读取文件内容,并送给上述类进行解析。
代码如下:

var p = @"D:\遥测v1.0.1(7).mbp";
MbpFile mbp = new MbpFile(p);

Console.WriteLine($"header: func = {mbp.Header.FuncCode}, start = {mbp.Header.StartAddress}, item={mbp.Header.PointTableSize}");
var i = 0;
foreach(var item in mbp.PointTables)
{
    Console.WriteLine($"点表定义{i++}: {item.Item.Name} {item.Item.Value}"); 
}

输出结果如下:
6f39f46e0d2ed77d4baf35c2de090742.png
啊哈哈,终于可以采用mbp文件,直接作为程序的配置文件了。
你用过modbus吗?
是不是觉得这种方式不错?
当然,自己定义一个格式,也许更加丰富!

号外

哦哦哦,神奇的一天又结束了,modbus这个协议确实不错,优秀!

👓都看到这了,还在乎点个赞吗?

👓都点赞了,还在乎一个收藏吗?

👓都收藏了,还在乎一个评论吗?

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值