CANopenSocket CANopenCGI.c hacking

/****************************************************************************************
 *                      CANopenSocket CANopenCGI.c hacking
 * 说明:
 *     分析一下CANopenSocket中的CANopenCGI部分是怎么工作的。
 *
 *                                          2017-3-23 深圳 南山平山村 曾剑锋
 ***************************************************************************************/

/*
 * Client socket command interface (Apache CGI) for CANopenSocket.
 *
 * @file        CANopenCGI.c
 * @author      Janez Paternoster
 * @copyright   2016 Janez Paternoster
 *
 * This file is part of CANopenNode, an opensource CANopen Stack.
 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
 * For more information on CANopen see <http://www.can-cia.org/>.
 *
 * CANopenNode is free and open source software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <fnmatch.h>
#include <sys/un.h>
#include <sys/socket.h>

#ifndef BUF_SIZE
#define BUF_SIZE            100000
#endif

/* Helper functions */
static void errExitErrno(char* msg) {
    printf("%s: %s\n", msg, strerror(errno));
    exit(EXIT_FAILURE);
}

static void errExit(char* msg) {
    printf("%s\n", msg);
    exit(EXIT_FAILURE);
}

/**
 * 字符串拷贝并转换成大写
 */
static void strcpyToUpper(char *dest, const char *src) {
    char in;

    do {
        in = *(src++);
        *(dest++) = toupper(in);
    } while(in != 0);
}

/**
 * 字符串转换成大写
 */
static void strToUpper(char *str) {
    char c;

    do {
        c = *(str);
        *(str++) = toupper(c);
    } while(c != 0);
}

/**
 * 字符串转换成小写
 */
static void strToLower(char *str) {
    char c;

    do {
        c = *(str);
        *(str++) = tolower(c);
    } while(c != 0);
}

/* Decode hex string ‘str‘ of length ‘len‘ and return numerical value.
 * In case of error in string, set ‘err‘ to 1. */
/**
 * 将十六进制的字符串转成数字
 */
static unsigned int hex2dec(const char *str, int len, int *err){
    unsigned int val = 0;
    int i;

    for(i=0; i<len; i++) {
        char c = str[i];
        if(c >= ‘0‘ && c <= ‘9‘) {
            c = c - ‘0‘;
        } else if (c >= ‘A‘ && c <= ‘F‘) {
            c = c - (‘A‘ - 10);
        }
        else {
            *err = 1;
            return 0;
        }
        val = val << 4 | c;
    }
    return val;
}

static void sendCommand(int fd, int sequence, char* command);

static void printUsage(void) {
printf(
"Usage: canopen.cgi?wnniiiissdd=xxxx[&rnniiiissdd=]\n"
"  - w    - One digit - ‘W‘rite or ‘R‘ead.\n"
"  - nn   - Two hex digits of node ID.\n"
"  - iiii - Four hex digits of Object Dictionary Index.\n"
"  - ss   - Two hex digits of Object Dictionary Subindex.\n"
"  - dd   - One to three digits of data type.\n"
"  - xxxx - Value to be written.\n"
"\n"
"Datatypes:\n"
"  - b                 - Boolean.\n"
"  - u8, u16, u32, u64 - Unsigned integers.\n"
"  - i8, i16, i32, i64 - Signed integers.\n"
"  - r32, r64          - Real numbers.\n"
"  - t, td             - Time of day, time difference.\n"
"  - vs                - Visible string (between double quotes).\n"
"  - os, us, d         - Octet string, unicode string, domain."
);
}

/******************************************************************************/
int main (int argc, char *argv[], char *env[]) {
    char socketPath[260] = {0};  /* Name of the local domain socket. */

    FILE *fp;
    int fdSocket;
    struct sockaddr_un addr;
    char *queryString;
    int queryStringAllocated = 0;

    /* whitelist and blacklist are arrays of null separated strings, which
     * contains patterns for comparision with commands from query string. */
    char *whitelist;
    char *blacklist;
    int whitelistLen;
    int blacklistLen;

    /* Print mime */
    /**
     * 输出http协议的头
     */
    printf("Content-type:text/plain\n\n");

    /* Get program options from configuration file */
    /**
     * 处理配置文件
     */
    fp = fopen("canopen.conf", "r");
    if(fp == NULL) {
        errExitErrno("Can‘t open configuration file");
    }
    else {
        const char spaceDelim[] = " \t\n\r\f\v";
        char buf[1000];
        int wlSize = 1000;   /* byte length */
        int blSize = 1000;
        int wlDataSize = 0;
        int blDataSize = 0;

        whitelist = (char *) malloc(wlSize);
        blacklist = (char *) malloc(blSize);;
        // 最开始wlDataSize长度都是0,随着allow检测到的配置越来越多,长度会越来越长
        whitelistLen = 0;   /* number of tokens in list */
        blacklistLen = 0;
        if(whitelist == NULL || blacklist == NULL) {
            errExitErrno("Whitelist or Blacklist can‘t be allocated.");
        }

        // 每次读取一行
        while(fgets(buf, sizeof(buf), fp) != NULL) {
            char *token;
            token = strtok(buf, spaceDelim);

            if(token == NULL) {

            }
            /**
             * 获取socketPath配置
             */
            else if(strcasecmp(token, "socketPath") == 0) {
                if(strlen(socketPath) != 0) {
                    errExit("Duplicate ‘socketPath‘ in canopen.conf.");
                }
                strncpy(socketPath, strtok(NULL, spaceDelim), sizeof(socketPath));
                socketPath[sizeof(socketPath)-1] = 0;
            }
            else if(strcasecmp(token, "allow") == 0) {
                // 保存上一次的wlDataSize长度,随着allow检测到的配置越来越多,长度会越来越长
                int prevDataSize = wlDataSize;

                // 获取value
                token = strtok(NULL, spaceDelim);
                // 计算value长度并+1,最后一个字节用于存放字符串结束符,这个长度叠加到wlDataSize中
                wlDataSize += (strlen(token) + 1);
                // 长度大于预设字符串长度,双倍扩容并重新分配,不过从这里开看最大也就是双倍的扩容长度
                while(wlDataSize > wlSize) {
                    wlSize *= 2;
                    whitelist = (char *) realloc(whitelist, wlSize);
                    if(whitelist == NULL) {
                        errExitErrno("Whitelist can‘t be allocated.");
                    }
                }
                // 拷贝当前的匹配数据到whitelist中
                strcpyToUpper(&whitelist[prevDataSize], token);
                whitelistLen ++;
            }
            /**
             * 类是于白名单
             */
            else if(strcasecmp(token, "deny") == 0) {
                int prevDataSize = blDataSize;

                token = strtok(NULL, spaceDelim);
                blDataSize += (strlen(token) + 1);
                while(blDataSize > blSize) {
                    blSize *= 2;
                    blacklist = (char *) realloc(blacklist, blSize);
                    if(blacklist == NULL) {
                        errExitErrno("Blacklist can‘t be allocated.");
                    }
                }
                strcpyToUpper(&blacklist[prevDataSize], token);
                blacklistLen ++;
            }
        }
    }

    fclose(fp);

    /* Create and connect client socket */
    /**
     * 创建本地socket
     */
    fdSocket = socket(AF_UNIX, SOCK_STREAM, 0);
    if(fdSocket == -1) {
        errExitErrno("Socket creation failed");
    }

    /**
     * 配置本地socket
     */
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socketPath, sizeof(addr.sun_path) - 1);

    /**
     * 连接本地socket
     */
    if(connect(fdSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
        errExitErrno("Socket connection failed");
    }

    /* get query string */
    /**
     * 获取网络请求数据
     */
    queryString = getenv("QUERY_STRING"); /* HTTP GET method. */
    if(queryString != NULL && strlen(queryString) == 0) {
        queryString = malloc(BUF_SIZE);
        if(queryString == NULL) {
            errExitErrno("queryString can‘t be allocated.");
        }
        queryStringAllocated = 1;
        fgets(queryString, BUF_SIZE, stdin); /* HTTP POST method. */
    }
    if(queryString == NULL && argc >= 2) {
        queryString = argv[1];  /* If no query string, try first argument. */
    }

    /* get commands from query string */
    /**
     * 解析网络请求数据
     */
    if(queryString != NULL && strlen(queryString) > 0) {
        char *command;
        int sequence = 1;

        /* put whole query string to upper case */
        /**
         * 将请求数据转为大写的格式
         */
        strToUpper(queryString);

        command = strtok(queryString, "&");
        while(command != NULL) {
            int i;
            int offset;
            int passed = 0;

            /* Test whitelist and blacklist */
            /**
             * 一个一个偏移着找
             */
            offset = 0;
            for(i=0; i<whitelistLen; i++) {
                char *patern = &whitelist[offset];
                if(fnmatch(patern, command, 0) == 0) {
                    passed = 1;
                    break;
                }
                offset += strlen(patern) + 1;
            }
            /**
             * 检查黑名单
             */
            if(passed == 1) {
                offset = 0;
                for(i=0; i<blacklistLen; i++) {
                    char *patern = &blacklist[offset];
                    if(fnmatch(patern, command, 0) == 0) {
                        passed = -1; /* not allowed */
                        break;
                    }
                    offset += strlen(patern) + 1;
                }
            }

            /* Send command or error message */
            if(strlen(command) < 9) {
                printf("? %s [%d] ERROR: 101 - Syntax error in command.\n", command, sequence);
            }
            else if(passed == 1) {
                sendCommand(fdSocket, sequence, command);
            }
            else {
                printf("%c %c%c%c%c%c%c%c%c [%d] ERROR: 100 - Access restriction, command %s.\n",
                    command[0], command[1], command[2], command[3], command[4],
                    command[5], command[6], command[7], command[8],
                    sequence, (passed==0)?"not on whitelist":" on blacklist");
            }

            command = strtok(NULL, "&");
            sequence ++;
        }
    }
    else {
        printUsage();
    }

    close(fdSocket);
    free(whitelist);                    // 释放白名单
    free(blacklist);                    // 释放黑名单
    if(queryStringAllocated == 1) {
        free(queryString);
    }

    exit(EXIT_SUCCESS);
}

static void sendCommand(int fd, int sequence, char* command) {
    int i, err;
    char comm;
    unsigned int nodeId, idx, sidx;
    char dataType[4];
    char *value = "";

    char buf[BUF_SIZE];

    /* Parse command. It is at least 8 characters long. */
    /**
     * 解析命令
     */
    err = 0;

    comm = command[0];
    if(comm != ‘R‘ && comm != ‘W‘) {
        err = 1;
    }

    nodeId = hex2dec(&command[1], 2, &err);
    if(nodeId < 1 || nodeId > 127) {
        err = 1;
    }

    idx = hex2dec(&command[3], 4, &err);
    sidx = hex2dec(&command[7], 2, &err);

    for(i=0; i<sizeof(dataType); i++) {
        char c = command[9+i];

        if(c == ‘=‘ || c == 0) {
            dataType[i] = 0;
            if(c == ‘=‘) {
                value = &command[10+i];
            }
            break;
        }
        dataType[i] = c;
    }
    if(i > 3) {
        err = 1;
        dataType[0] = 0;
    }
    if(strlen(value) > (sizeof(buf) - 50)) {
        err = 1;
    }

    /* Write command according to CiA309-3. */
    /**
     * 命令转换,转换成canopend能接收的命令格式
     */
    if(err == 0) {
        size_t wlen, rlen;

        strToLower(dataType);

        wlen = sprintf(buf, "[%d] 0x%02X %c 0x%04X 0x%02X %s %s\n",
                sequence, nodeId, tolower(comm), idx, sidx, dataType, value);

        if (write(fd, buf, wlen) != wlen) {
            errExit("Socket write failed");
        }

        rlen = read(fd, buf, sizeof(buf));

        if(rlen == -1) {
            errExit("Socket read failed");
        }

        printf("%c %02X%04X%02X %s",
            comm, nodeId, idx, sidx, buf);
    }
    else {
        printf("? %s [%d] ERROR: 101 - Syntax error in command.\n",
                    command, sequence);
    }
}
时间: 2024-08-03 01:57:46

CANopenSocket CANopenCGI.c hacking的相关文章

移动安全初探:窃取微信聊天记录、Hacking Android with Metasploit

在这篇文章中我们将讨论如何获取安卓.苹果设备中的微信聊天记录,并演示如何利用后门通过Metasploit对安卓设备进行控制.文章比较基础.可动手性强,有设备的童鞋不妨边阅读文章边操作,希望能激发大家对移动终端的安全兴趣. (文章内容存在一定攻击性,目的在于普及终端安全知识.提高安全意识,如有非法使用,后果自负) “如何获取Android.iPhone手机上的微信聊天记录? ” 0×00 条件: 安卓设备已获取root权限,安装SSHDroid(通过ssh.ftp连接手机) Apple设备越狱,安

I.MX6 recovery mode hacking

/******************************************************************************** * I.MX6 recovery mode hacking * 说明: * 看一下i.MX6 Recovery模式是怎么工作的. * * 2017-6-12 深圳 龙华樟坑村 曾剑锋 ****************************************************************************

Redis代码阅读之Hacking Strings

Hacking Strings The implementation of Redis strings is contained in sds.c ( sds stands for Simple Dynamic Strings ).The C structure sdshdr declared in sds.h represents a Redis string: struct sdshdr { long len; long free; char buf[]; }; The buf charac

我来Hacking JDBC,你并不需要它

我们喜欢和JDBC打交道,以前从未有人这样说过.很严肃的说,JDBC是一个非常优秀的API.这可能是现在Java能够成为一个受欢迎的平台的重要原因之一. 在JDK1.1之前,ODBC出现之前(很久之前的事情了),很难去想象有平台会标准化数据库的访问.在那个时候SQL语言甚至本身还没有标准化,随后出现的面向Java的简单的JDBC API,工作中你需要也就是一下几点: connection:使之能够让领域模型与数据库交互 PreparedStatement:让你能够运行SQL语句的对象 Resul

Rootkit Hacking Technology &amp;&amp; Defence Strategy Research

目录 1. The Purpose Of Rootkit 2. Syscall Hijack 3. LKM Module Hidden 4. Network Communication Hidden 5. File Hidden 6. Process Hidden 7. Hidden Port Remote Reverse Connections 8. Programe Replacing 1. The Purpose Of Rootkit Basically, the purpose of r

google hacking for penetration testers 手册笔记

参考链接: Advanced operators reference Google hacking database 注意:以下内容涉及到大量参考图表,图表均在本人相册<google hacking参数查询表>里 第一章 谷歌搜索基础知识 1.谷歌翻译 可看作透明代理 利用: “翻译此页” 2.黄金法则: 不区分大小写 通配符*代表一个单词,非单一字母或一系列字母,在一单词开始或结尾使用星号和直接使用单词效果相同 保留查询关键字权利 强制搜索:         加引号:“words” 或 用布

OK335xS davinci mdio driver hacking

/******************************************************************************* * OK335xS davinci mdio driver hacking * 说明: * 以前一直也想对网卡驱动的工作原理进行跟踪,这次正好有机会,先跟mdio接口部分 * 的代码. * * 2016-3-1 深圳 南山平山村 曾剑锋 **************************************************

I.MX6 android BatteryService jni hacking

/**************************************************************************** * I.MX6 android BatteryService jni hacking * 声明: * 本文主要是为了知道Android的获取的电源管理的数据的jni是从Linux系统的 * 什么位置获取的,获取的机制是什么. * * 2016-2-22 深圳 南山平山村 曾剑锋 ********************************

OK335xS 网络连接打印信息 hacking

/*********************************************************************** * OK335xS 网络连接打印信息 hacking * 说明: * 当我们插入网线的时候,经常会看到对应的网卡已连接,当前属于10M. * 100M网卡工作状态等等信息,那么这些信息是如何被输出的,工作机制是什么, * 网卡的速度是由phy决定的还是由mac决定的,是不是在phy对应的中断里处理, * 等等,这些内容都需要去确认. * * 2016-