# S7 Projektdateien -> IP Adressen auslesen...



## Jochen Kühner (7 Oktober 2014)

Hallo,

Hab mich heute auf anfrage mal wieder ein bisschen mit dem Dateiformat von Step7 beschäftigt! Ich würde gerne zu einer CPU oder einem CP die Zugehörigen Schnittstellen und die zugehörige IP auslesen.

Die Schnittstellen scheinen in s7hssiox / hobject.dbf zu stecken, aber hat einer ne Ahnung wie/wo die IP gespeichert ist?


----------



## Thomas_v2.1 (8 Oktober 2014)

Ich habe nur mal nachgesehen welche Dateien sich ändern wenn man die IP-Adresse ändert und speichert (ohne übersetzen):
- ApiLog/LogIDs
- ApiLog/Types
- Global/ -> eine Textdatei
- hOmSave7/s7hssiox/HATTARY.DBT
- hOmSave7/s7hssiox/HOBJECT.DBF
- hOmSave7/s7hstatx/HATTARY.DBT
- hOmSave7/s7hstatx/HOBJECT.DBF
- S7Netze/S7NONFGX.tab

Beim Speichern und Übersetzen wird es sicher noch weitere Änderungen geben, da dann auch die Systemdaten der SPS erzeugt werden.
Mit einem Detailvergleich der Dateien sollte man eigentlich dahinterkommen wo die IP-Adressen stecken.


----------



## Jochen Kühner (8 Oktober 2014)

Genau das hatte ich auch gemacht, aber nichts gefunden...
Hab immer nach der Ip als Hex Wert gesucht... Das Problem, die wird als Hex String in die S7nonfgx.tab eingeteagen... Jetzt muss ich "nur" noch das Format dieser Datei verstehen...

Danke auch an SvenMag dafür... https://github.com/jogibear9988/DotNetSiemensPLCToolBoxLibrary/issues/18#issuecomment-58299029


----------



## Jochen Kühner (8 Oktober 2014)

Nochwas: ob sie vielleicht auch in den hattary.dbt steckt muss ich noch rausfinden, wobei ich das format der hattary.dbf noch gar nicht rausgefunden habe, falls es darin überhaupt ein Standartformat gibt...


----------



## klaly (10 Oktober 2014)

Hallo Jochen, 

ich kann dir zwar nicht sagen wo in den dbf Files so was abgelegt wird. 
Aber ich weiß dass diese Info in den SDBs im Unterordner .\sdb abgelegt wird. 
In diesem Ordner liegen wiederum Unterordner mit wechselnden Dateinamen. 
Die SDBs und somit auch die gewünschte IP-Adresse werden in darin enthaltenen Files gespeichert. 
Leider liegen da nicht die vollständigen SDBs drinnen, zumindest nicht dem Format wie sie in ein WLD File oder in die SPS kommen. 
Aber meines Wissen liegen hier alle SDBs die auch im Systemdatenordner zu sehen sind. 
Vermutlich werden hieraus die "echten" SDBs generiert, wenn sie zur Steuerung geschickt werden. 
Die Daten liegen da irgendwie etwas verdreht, da kommt zuerst der Trailer und dann der "Baustein", dieser wiederum ohne 7070 Kennung. 
Außerdem ist irgendwie die Byteorder anders. 

Aber mit ein wenig Mühe kann man daraus wohl die gewünschten daten gwinnen. 
Mehr weiß ich dazu nicht. 

mfG. klaly


----------



## Jochen Kühner (10 Oktober 2014)

Das würde schon funktionieren, aber nur wenn auch die Systemdaten erzeugt wurden. Wenn man einfach nur auf speichern drückt, wird an den SDB's nichts geändert. Von daher würde Ich lieber die S7Tab Datei parsen. Beim SDB steh Ich ja im Moment genau so auf dem schlauch welche IP zu welcher Hardware gehört, gibts ja auch keine Doku... Mal schaun ob Ich irgendwie noch weiterkomm, oder ob Ichs sein lass...


----------



## Thomas_v2.1 (11 Oktober 2014)

Wenn du Sinaut ST7 installiert hast, gibt es dort eine SDB-Anzeige. Dieser entschlüsselt dir zwar nur die Sinaut-spezifischen SDBs, aber auch z.B. den Routing SDB 999 falls dich das interessiert. Alle anderen SDBs werden nur mit Hexadezimalwerten dargestellt.

Aber auch wenn ich mir nur die Hex-Werte ansehe, scheint die IP-Adresse im SDB 1000 gespeichert zu werden. Zumindest bei der Sinaut-SDB Anzeige beginnt der SDB1000 immer mit 0x0e 0x0c, und ab Bytes 22 und auch 38 folgt dann die IP-Adresse, zumindest bei einer PN-CPU. Netzwerkmaske steht dort auch drin.


----------



## klaly (13 Oktober 2014)

Hallo Jochen, 

ich hab eben mal was probiert: 
CPU317PN, dort habe ich nur speichern gemacht, dannn Proj. weg kopiert, die IP geändert und wieder speichern. 
Dann geschaut welche Dateien sich unterscheiden. Die interessanteste war: .\S7Netze\S7NONFGX.tab, ca. 10kB
In dieser Datei ändert sich immer was wenn du speicherst. Leider ist der Inhalt sehr komplex, bzw. caotisch. 
Wenn du die Datei löscht (zuvor Backup davon machen), wenn du dann den HW-Konfigurator öffnest findet er die CPU (317PN), aber die IP ist nun auf 0.0.0.0 
Sobald der HW-Konfig startet legt er die Datei wieder neu an. Speichern geht, aber speichern und übersetzen geht nicht mehr, böse Fehler ...
Wenn du ihm aber seine Datei wieder zurück schiebst und den HW-Konfig dann startes, dann ist alles wieder gut. 

D.h. in dieser Datei stehen die Sachen für Vernetzung der CPUs bzw. evtl. auch der CPs. Auch wenn die PN Schnittstelle noch gar nicht vernetzt war. 

Vieleicht hilft dir das weiter. 

mfG. klaly


----------



## Jochen Kühner (13 Oktober 2014)

klaly schrieb:


> Hallo Jochen,
> 
> ich hab eben mal was probiert:
> CPU317PN, dort habe ich nur speichern gemacht, dannn Proj. weg kopiert, die IP geändert und wieder speichern.
> ...



Das es diese Datei ist hab Ich ja auch schon rausgefunden (http://www.sps-forum.de/hochsprache...tdateien-ip-adressen-auslesen.html#post508523) aber das Format... das ist halt die harte Nuss


----------



## klaly (13 Oktober 2014)

Jochen, sorry, 
ich hatte deine mail gelsen wie Datei blabla, steh auf schlauch, ...
Dann hab ich ein wenig probiert und nicht gemerkt, dass du genau die gleiche Datei meintest. 
Fakt ist aber, dass da genau die gewünschten Infos enthalten sein müssen. 
Blos wie codiert ?

mfg. klaly


----------



## Jochen Kühner (13 Oktober 2014)

Ja, hab die Datei mal mit nem Hex Editor angeschaut, aber noch nicht groß analysiert. Man müsste einträge ändern, schauen wie sich die Datei dann ändert. So wies aussieht ist das erste Byte vor nem String immer die Länge. Dann sollten für die Teilnehmer ja IP, Subnet, MacAdresse, ... enthalten sein. Was mich im Moment wundert, warum ist die IP 3 mal in der Datei...


----------



## funkey (14 Oktober 2014)

Hab mir das mal angeschaut, weil mich das ganze auch interessiert hat. Hab zwar auch keine Ahnung vom Aufbau der Datei, aber mit folgendem C-Programm können alle IP-Adressen aus einem Step7-Projekt ausgelesen werden. Der Code ist nicht gerade schön, nur schnell zusammengestöpselt, aber funktioniert bei mir bei allen Projekten. Man müsste aber noch einiges verbessern und einen Weg suchen, bei dem nur die IP-Adressen der CPU zurückgegeben werden. Vielleicht nehme ich mir noch mal Zeit, um das ganze genauer zu untersuchen.


```
[FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]#include <stdio.h>

#include <windows.h>

#include <stdarg.h>


int FileOpenDialog(LPSTR szDest, LPSTR szTitle, LPCSTR szStartDir, LPCSTR szFilter);

int search(LPSTR lpszPath, LPSTR lpszFilename);

int CheckFile(LPSTR lpszFilename);

void RestoreS7Ip(char* pwd);

int Win_Debug(const char *szFormat, ...);


int iFound = 0;


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

    char Dest[MAX_PATH];

    LPCSTR szStartPath = NULL;  //"U:\\S7_daten"

    int iFileOffset = FileOpenDialog(Dest, "Choose S7 project file", szStartPath, "S7 project file\0*.s7p\0");

    if (iFileOffset == 0) return 0;


    Dest[iFileOffset] = '\0';

    strcat_s(Dest, "S7Netze\\S7NONFGX.tab");

    

    CheckFile(Dest);


    if (iFound == 0) Win_Debug("No IP address found!");


    return 0;

}


int CheckFile(LPSTR lpszFilename)

{

    HANDLE hFile;

    hFile = CreateFile(lpszFilename, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);

    int iLen = GetFileSize(hFile, NULL);

    CloseHandle(hFile);


    unsigned char *buff = (unsigned char*)malloc(iLen + 1);


    FILE *fp;

    fopen_s(&fp, lpszFilename, "rb");

    fread(buff, iLen, 1, fp);


    int i, j;

    char sIp[16] = { 0 };


    for (i = 0; i < iLen - 8; i++)

    {

        if (

            (buff[i + 0] == 0xe0) &&

            (buff[i + 1] == 0x0f) &&

            (buff[i + 2] == 0x00) &&

            (buff[i + 3] == 0x00) &&

            (buff[i + 4] == 0xe0) &&

            (buff[i + 5] == 0x0f) &&

            (buff[i + 6] == 0x00) &&

            (buff[i + 7] == 0x00) &&

            (buff[i + 8] == 0x00))

        {

            iFound += 1;

            for (j = 20; j <28; j++)

            {

                sIp[j - 20] = buff[i + j];

            }

            sIp[8] = '\0';


            RestoreS7Ip(sIp);

            Win_Debug("IP: %s", sIp);

        }

    }


    fclose(fp);

    free(buff);

    return 0;

}

int FileOpenDialog(LPSTR szDest, LPSTR szTitle, LPCSTR szStartDir, LPCSTR szFilter)

{

    OPENFILENAME ofn;

    ZeroMemory(&ofn, sizeof(ofn));


    ofn.lStructSize = sizeof(ofn);

    ofn.hwndOwner = NULL;

    ofn.lpstrFile = szDest;

    ofn.lpstrFile[0] = '\0';

    ofn.nMaxFile = MAX_PATH;

    ofn.lpstrFilter = szFilter;

    ofn.nFilterIndex = 1;

    ofn.lpstrTitle = szTitle;

    ofn.lpstrFileTitle = NULL;

    ofn.nMaxFileTitle = 0;

    ofn.lpstrInitialDir = szStartDir;

    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;


    if (!GetOpenFileName(&ofn)) return 0;


    strcpy_s(szDest, MAX_PATH, ofn.lpstrFile);

    return ofn.nFileOffset;

}


int Win_Debug(const char *szFormat, ...)

{

    char szBuffer[1024];

    va_list    pArgList;

    va_start(pArgList, szFormat);

    _vsnprintf_s(szBuffer, sizeof(szBuffer), szFormat, pArgList);

    va_end(pArgList);

    return MessageBox(NULL, szBuffer, "S7 IP address", MB_ICONINFORMATION | MB_TOPMOST);

}


void RestoreS7Ip(char* sIp)

{

    int i, j;

    char part[4] = { 0 };

    int num[4];

    for (i = 0, j = 0; i < 7; i+=2, j++)

    {

        memcpy_s(part, sizeof(part), &(sIp[i]), 2);

        num[j] = (int)strtol((const char*)part, NULL, 16);

    }

    sprintf_s(sIp, 16, "%i.%i.%i.%i", num[0], num[1], num[2], num[3]);

}


[/SIZE][/FONT][/SIZE][/FONT]
```


----------



## Jochen Kühner (14 Oktober 2014)

Man müsste mal versuchen einzugrenzen wo die Struktur für eine IP Adresse in dem File beginnt, vor den durch deinen Code ausgelsenen werten scheint ja auch noch die MAC Adresse zu stehen... Ich werd mir ml ein file mit zick IP's und CP's anlegen und schauen was Ich rausfinde!

Könnte ja sein das die verheiratung zwischen CP und IP auch z.B. über das LNK File in dem Ordner geht.


----------



## Jochen Kühner (14 Oktober 2014)

Also der Abstand zwischen 2 Ip Adressen, wenn Ich sie so wie funkey ermittle beträgt in der Datei rund 1900 Bytes (bei einem Lean CP). Jetzt müsste man rausfinden wo die einzelnen Strukturen beginnen, was steht überhaupt drinn, wo steht die Länge, gibts einen festen Aufbau oder ist er abhängig von der Hardware (CP, PN/CPU, ...). Mal schauen wie weit man hier noch kommen kann...


----------



## Thomas_v2.1 (14 Oktober 2014)

Du kannst ja mal mit dem Sysinternals Process Monitor mitloggen, auf welche Dateien Step7 überhaupt zugreift wenn du in der HW-Konfig die IP-Adresseinstellungen aufrufst. Theoretisch kann sich Step7 die Informationen auch wieder aus den SDBs zurückübersetzen - die SPS kanns ja auch.

Damit habe ich damals zumindest diese "spezielle" Verbindung zwischen RSRVD4_L und linkhrs herausgefunden.


----------



## funkey (15 Oktober 2014)

Jochen Kühner schrieb:


> Also der Abstand zwischen 2 Ip Adressen, wenn Ich sie so wie funkey ermittle beträgt in der Datei rund 1900 Bytes (bei einem Lean CP). Jetzt müsste man rausfinden wo die einzelnen Strukturen beginnen, was steht überhaupt drinn, wo steht die Länge, gibts einen festen Aufbau oder ist er abhängig von der Hardware (CP, PN/CPU, ...). Mal schauen wie weit man hier noch kommen kann...


Ich habe noch keinen Zusammenhang gefunden, bei mir sind die Strukturen alle unterschiedlich aufgebaut und auch die Informationen sind in unterschiedlicher Reihenfolge enthalten. MMn fangen die Strukturen irgendwo bei 'AddressIsValid' an. Aber dieser String ist auch für Profibus-Strukturen ohne IP vorgesehen und nicht nur für ProfiNet /IP-Adress-Strukturen. MAC-Adressen sind auch nicht immer an der gleichen Stelle.
Hier noch ein überarbeiteter C-Code von mir, mit dem man die IP-Adressen, den Namen und die Type auslesen kann:


```
#include <stdio.h>

#include <windows.h>

#include <stdarg.h>


struct _S7NetInfo

{

    char szMac[18];

    char szIP[16];

    char szSubNetMask[16];

    char szGateway[16];

    char szName[32];

    char szType[32];

};


typedef _S7NetInfo S7NetInfo;


int search(LPSTR lpszPath, LPSTR lpszFilename);

int CheckFile(LPSTR lpszFilename);

int GetS7NetInfos(LPSTR lpszFilename, S7NetInfo *aInfos, int iInfoCount);

void RestoreS7Ip(char* pwd);

int Win_Debug(const char *szFormat, ...);

int GetBufferOffsetFromSearchArray(unsigned char *buff, int iBuffLen, int iStart, int iStop, unsigned char* search, int iSearchLen);


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

    int i, iFound;

    char szPath[1024];


    GetModuleFileName(NULL, szPath, sizeof(szPath));

    char* ptr = strrchr(szPath, '\\');

    ptr[1] = '\0';

    strcat_s(szPath, "S7NONFGX.tab");


    iFound = GetS7NetInfos(szPath, NULL, 0);

    switch (iFound)

    {

    case -1:

        Win_Debug("Error file not found!");

        break;

    case 0:

        Win_Debug("No IP address found!");

        break;

    default:

        S7NetInfo *Infos = (S7NetInfo*)calloc(iFound, sizeof(S7NetInfo));

        iFound = GetS7NetInfos(szPath, Infos, iFound);

        for (i = 0; i < iFound; i++)

        {

            Win_Debug("IP : %s\nName : %s\nType : %s", Infos[i].szIP, Infos[i].szName, Infos[i].szType);

        }

        free(Infos);

    }


    return 0;

}


int GetS7NetInfos(LPSTR lpszFilename, S7NetInfo *aInfos, int iInfoCount)

{

    HANDLE hFile;

    DWORD dwBytesRead;

    

    int iOffset = 0, iOffsetName = 0, iOffsetValid = 0, iOffsetIp = 0, iFound = 0;

    hFile = CreateFile(lpszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

    int iFileSize = GetFileSize(hFile, NULL);

    if (iFileSize <= 0)

    {

        CloseHandle(hFile);

        return -1;

    }

    unsigned char *buff = (unsigned char*)malloc(iFileSize + 1);

    ReadFile(hFile, buff, iFileSize, &dwBytesRead, NULL);

    CloseHandle(hFile);


    unsigned char searchValid[] = { 'A', 'd', 'd', 'r', 'e', 's', 's', 'I', 's', 'V', 'a', 'l', 'i', 'd' };

    unsigned char searchName[] = { 'B', 'a', 'u', 'g', 'r', 'u', 'p', 'p', 'e', 'n', 'n', 'a', 'm', 'e' };

    unsigned char searchIp[] = { 0xe0, 0x0f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00 };


    while ((iOffsetIp = GetBufferOffsetFromSearchArray(buff, iFileSize, iOffset, iFileSize, searchIp, sizeof(searchIp))) >= 0)

    {

        iOffset = iOffsetIp + 1;

        iFound += 1;


        if (aInfos != NULL)

        {

            if (iInfoCount < iFound) break;


            iOffsetValid = GetBufferOffsetFromSearchArray(buff, iFileSize, iOffsetIp, iOffsetIp - 2000, searchValid, sizeof(searchValid));


            iOffsetName = GetBufferOffsetFromSearchArray(buff, iFileSize, iOffsetValid, iOffsetIp + 2000, searchName, sizeof(searchName));

            iOffsetName += 24;


            memcpy_s(aInfos[iFound - 1].szName, sizeof(aInfos[iFound - 1].szName), &(buff[iOffsetName + 1]), buff[iOffsetName]);


            iOffsetName += buff[iOffsetName] + 1;

            iOffsetName += buff[iOffsetName] + 1;

            memcpy_s(aInfos[iFound - 1].szType, sizeof(aInfos[iFound - 1].szType), &(buff[iOffsetName + 1]), buff[iOffsetName]);


            //memcpy_s(aInfos[iFound - 1].szMac, sizeof(aInfos[iFound - 1].szMac), &(buff[iOffsetIp - 254]), 12);

            iOffsetIp += 20;

            memcpy_s(aInfos[iFound - 1].szIP, sizeof(aInfos[iFound - 1].szIP), &(buff[iOffsetIp]), 8);

            RestoreS7Ip(aInfos[iFound - 1].szIP);


        }

    }

    return iFound;

}


int Win_Debug(const char *szFormat, ...)

{

    char szBuffer[1024];

    va_list    pArgList;

    va_start(pArgList, szFormat);

    _vsnprintf_s(szBuffer, sizeof(szBuffer), szFormat, pArgList);

    va_end(pArgList);

    return MessageBox(NULL, szBuffer, "S7 IP address", MB_ICONINFORMATION | MB_TOPMOST);

}


void RestoreS7Ip(char* sIp)

{

    int i, j;

    char part[3] = { 0 };

    int num[4];

    for (i = 0, j = 0; i < 7; i+=2, j++)

    {

        memcpy_s(part, sizeof(part), &(sIp[i]), 2);

        num[j] = (int)strtol((const char*)part, NULL, 16);

    }

    sprintf_s(sIp, 16, "%i.%i.%i.%i", num[0], num[1], num[2], num[3]);

}


int GetBufferOffsetFromSearchArray(unsigned char *buff, int iBuffLen, int iStart, int iStop, unsigned char* search, int iSearchLen)

{

    int i, j, ok;


    if (iStart > iStop)

    {

        for (i = iStart; i > iStop && i > 0; i--)

        {

            ok = 1;

            for (j = 0; j < iSearchLen; j++)

            {

                if (buff[i + j] != search[j])

                {

                    ok = 0;

                    break;

                }

            }

            if (ok == 1) return i;

        }

    }

    else

    {

        for (i = iStart; i < iStop && i < iBuffLen; i++)

        {

            ok = 1;

            for (j = 0; j < iSearchLen; j++)

            {

                if (buff[i + j] != search[j])

                {

                    ok = 0;

                    break;

                }

            }

            if (ok == 1) return i;

        }

    }

    return -1;

}
```


----------



## Jochen Kühner (15 Oktober 2014)

SvenMag hat auf GitHub auch schon etwas Code gepostet. Damit kann man auch schon die Zuordnung zum CP auslesen...https://github.com/jogibear9988/DotNetSiemensPLCToolBoxLibrary/pull/20/files


----------



## Jochen Kühner (15 Oktober 2014)

Hab nun etwas in meine ToolBox gebaut. IP's von manchen CPs werden schon ausgelesen, CPUs noch nicht... Der meiste Code ist von SvenMag, hab aber das auslesen des Namens funkey übernommen...


----------



## Jochen Kühner (27 Oktober 2014)

Also SvenMag scheint es nun zuverlässig implementiert zu haben, bei mir lassen sich CP's und CPU's nun auslesen... Falls es jemand braucht, der Code ist im Haupt-Branch auf Github eingecheckt...


----------

