Monitor for PLC - ActiveX Object

Posted on 2003-03-04
Medium Priority
Last Modified: 2013-11-13

I am communicating with an ActiveX Control over Ethernet to an Allen Bradley PLC.

Since I want to write a monitor application to see the ecapsulated packages in the data-packets and other things on the same computer, I need to create a Socket with the same port as the AcitveX Control (and the PLC).

I found out, that both are communicating over Port 2222 - is it possible to change this at the ActiveX Control and can I just forward in my monitor all packages directly to Port 2222 of the PLC?

Thanks very much,
J Priebe
Question by:jpriebe
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3

Expert Comment

ID: 8065018
Maybe I dont understand you, but are you just needing to 'peek' onto a port and see data coming across it without disturbing the original flow?
You could just open a raw socket and call ioctlsocket to set SIO_RCVALL to true on the socket. That way you get all packets coming into the computer. Then just check the IP header to make sure your computer is the destination IP and the port is correct, and if so have your way with the packet. The original packet flow is undisturbed.

This may not be what you want.. If so, let me know if you need code to do it.

Author Comment

ID: 8065075
This would be working and besides this it would be nice getting a sample code for this (I am developing in Delphi, but actually I dont care).

The ActiveX control goes through the Windows winsock object and grabs TCP/IP sockets as necessary so I don't know on what port I have to listen, since it may be varey?


Accepted Solution

colmstea earned 300 total points
ID: 8065812
I just threw together some code that will receive all packets going through your computers ethernet card and do some low level processing..  I know you use delphi but I dont so all I can suggest IF you want to use this code is to Create a Blank ActiveX project (C++), and paste the following code RIGHT after the last #include in your xxxxCtrl.cpp file. You will also have to add the following line to your Constructor:
_beginthread(MainThread ,0,0);
You will also have to add a reference to the "ws2_32.lib" in your project library dependency settings.

Note: This code was quickly put together and is not intended to be final code. Im just hoping you can get the idea of what the basics should be. There is no error checking or anything like that.

#include "process.h"
#include "winsock2.h"

// * socket io setting for promiscuous mode
// * Log file to write some info to for this demonstration
#define LOG_FILE_NAME      "c:\\socket.log"

// * Global variable to hold my IP address

// * Structures
typedef struct
      unsigned char VerIHL; // version/iplength
      unsigned char Tos;
      unsigned short Total_len;
      unsigned short ID;
      unsigned short Flags_and_Frags; //flags
      unsigned char TTL;                        // time to live if applies
      unsigned char Protocol;                  // protocol used
      unsigned short Checksum;
      unsigned long SrcIP;                // source ip
      unsigned long DstIP;                  // destination ip
        // some options and padding follow

} ip_header_type;

typedef struct
       WORD srcport;   // src port
      WORD dstport;   // dest port
      DWORD      seqnum;
      DWORD      acknum;
      BYTE      dataoffs;
      BYTE      control;
      WORD      window;
      WORD      checksum;
      WORD      urgentp;
      // options and padding may follow
} tcp_header_type;

typedef struct {
      WORD      srcport;  // src port
      WORD      destport; // destination port
      WORD      length;        // size of the UDP packet (including 8 byte header)
      WORD      checksum; // checksum
} udp_header_type;

// Initialize Winsock, setup raw sockets and bind, set promiscuous mode
SOCKET InitPromiscuousRawSockets() {
    // * Initialize WinSock
    WSAData wsaData;
    WSAStartup(0x0101, &wsaData);

      // * First thing: Grab my IP address
    char hostname[100];
    gethostname(hostname, 100);
    struct hostent * he;
    he = gethostbyname(hostname);
    DWORD dwIP;
    memcpy(&dwIP, he->h_addr_list[0], 4);
    g_dwMyIP = dwIP;

      // * Initialize RAW SOCKETS for any port
    SOCKET socketraw;
    sockaddr_in addr;                  
    memset(&addr, 0, sizeof(sockaddr_in));
    addr.sin_family = AF_INET;            
    addr.sin_port = 0; // all port action
    addr.sin_addr.s_addr = dwIP;  
    socketraw = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
    bind(socketraw, (LPSOCKADDR)&addr, sizeof(addr));
    unsigned long val;

    // * Set Promiscuous mode
    ioctlsocket(socketraw, SIO_RCVALL,&val);

    return socketraw; // no errors for now

// * After receive of packet call this function to get packet attributes/data
BOOL ProcessPacket(char * packetbuf, int packetsize) {
    ip_header_type * iph;
    unsigned short iphlen;
    char C;

    // * Grab header
    iph = (ip_header_type*)packetbuf;

    // * Calculate length of ip_header based on verihl
    iphlen = (iph->VerIHL << 4);
    memcpy(&C, &iphlen, 1);
    iphlen = (C >> 4) * 4; //20

    // We only want packets destined for OUR machine
    // since we see all packets moving across the
    // ethernet card, we have to check this
    if (iph->DstIP != g_dwMyIP)
            return false;

      // vars for our log:
      char arr[256];
      FILE * f;

      switch (iph->Protocol)
            case 1:

                  // THIS IS PACKET TYPE ICMP
            case 2:
                  // this is IGMP
            case 6:
                  // THIS IS A TCP packet
                  unsigned short tcphlen;
                  // * Grab the header (If you need to check the port, look in tcp_header->dstport
                  tcp_header_type * tcp_header;
                  tcp_header = (tcp_header_type*)(packetbuf+iphlen);
                  // determine length of the TCP header
                  tcphlen = (tcp_header->dataoffs >> 4)*4;
                  // tcp_data contains the actual TCP data
                  BYTE * tcp_data;
                  tcp_data = (BYTE*)(packetbuf + tcphlen + iphlen);
                  // tcp_data_length contains the size of the data
                  int tcp_data_length;
                  tcp_data_length = packetsize - tcphlen - iphlen;

                  // DO SOMETHING WITH IT!
                  // For now I write a log
                  f = fopen(LOG_FILE_NAME, "a");
                  if (!f)
                        f = fopen(LOG_FILE_NAME,"wb");
                  sprintf(arr, "TCP Packet:\n   Source IP: %s,  Dest IP/Port: %s/%d\n   Data Size: %d\n\n", inet_ntoa(*((in_addr*)&iph->SrcIP)), inet_ntoa(*((in_addr*)&iph->DstIP)), tcp_header->dstport, tcp_data_length);
                  fwrite(arr, strlen(arr), 1, f);
            case 17:
                  // UDP PACKET
                  udp_header_type * udp_header;
                  udp_header = (udp_header_type*)(packetbuf + iphlen);
                  // To check the port check udp_header->dstport
                  BYTE * udp_data;
                  udp_data = (BYTE*)packetbuf + iphlen + sizeof(udp_header_type);
                  int udp_data_length;
                  udp_data_length = packetsize - iphlen - sizeof(udp_header_type);

                  // DO SOMETHING WITH IT!
                  // For now I write a log
                  f = fopen(LOG_FILE_NAME, "a");
                  if (!f)
                        f = fopen(LOG_FILE_NAME,"wb");
                  sprintf(arr, "UDP Packet:\n   Source IP: %s,  Dest IP/Port: %s/%d\n   Data Size: %d\n\n", inet_ntoa(*((in_addr*)&iph->SrcIP)), inet_ntoa(*((in_addr*)&iph->DstIP)), udp_header->destport, udp_data_length);
                  fwrite(arr, strlen(arr), 1, f);

            return true;

// The main thread.. NOTE: This is just for demonstration,
// no error checking or nice looking c++ code exists here
void MainThread(LPVOID nothingyet) {
      SOCKET socketraw;
      fd_set fd;                  // for select statement for 2 second blocking
      // * Create the raw, promiscuous socket
      socketraw = InitPromiscuousRawSockets();
      // set packet blocking for 2 seconds
      timeval timeout;
      timeout.tv_sec = 2;
      timeout.tv_usec = 0;
      int num, fromlength;
      char * recvbuf = (char*)malloc(10000);
      sockaddr_in addrFrom;
      fromlength = sizeof(sockaddr_in);

      // runs forever (eeeeehhh)
      while (1)
            // Wait for some activity and then receive the packets
            FD_SET(socketraw, &fd);
            if ((num = select(NULL, &fd, NULL, NULL, &timeout)) > 0) {
                  if ((num = recvfrom(socketraw,recvbuf, 10000, 0, (struct sockaddr *)&addrFrom, &fromlength)) > 0) {
                        ProcessPacket(recvbuf, num);

If you have an email address I can just send you a zipped up project that does something more useful than this in an activex control.
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!


Author Comment

ID: 8065842
My email is jawsi@gmx.net

But is there a way to find out what sockets (with what port) the ActiveX control created? In my example i found out it was port 2222 - but this can varey I read.

Expert Comment

ID: 8066130
The above code does not require a port. So no matter what port (even if its dynamic) the data comes in on, you can isolate the PLC data by either:
1. Comparing the srcIp address in the ip header to the PLC ip, and only using the packets from the PLC.
2. Searching for some other unique "keyword" info in the packet data that disinguishes PLC packets and only using those packets that match the PLC packet format.

Ill email you a project when I get a chance.


Author Comment

ID: 8066192
Thanks! Would be nice if you would contact me by email for the zipped project and one or two other questions.


Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Entering a date in Microsoft Access can be tricky. A typo can cause month and day to be shuffled, entering the day only causes an error, as does entering, say, day 31 in June. This article shows how an inputmask supported by code can help the user a…
In this post we will learn how to make Android Gesture Tutorial and give different functionality whenever a user Touch or Scroll android screen.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
Six Sigma Control Plans
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question