ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 침입 감지 시스템 (IDS) - 타임스탬프 보완
    application 2025. 1. 5. 22:12

    시작하기 전에 

    침입 감지 시스템 (IDS: Intrusion Detection System) :: hsl's tsmaster 사용기에서 실제로 감지된 침입 건수가 내 예상보다 많았다. 원인을 찾다가 아래의 생각이 났다.  

     

    • TSMaster의 Rx 이벤트가 발생할 때 ts(timestamp)를 메시지 수신 시각으로 삼았다.
    • 하드웨어에서 메시지를 수신한 시각과 이벤트가 발생한 시각 사이에 차이가 있다면? 그리고 그 차이가 일정하지 않다면?
    • 하드웨어가 메시지 수신 시각을 메시지 데이터에 추가하지 않았을까?

     

    TSMaster의 메시지는 RawCAN 클래스이다. RawCAN 클래스의 정의에서 time_us (timestamp in microseconds) 를 찾았다. 참고로 RawCAN 클래스는 아래 코드와 같다.

    class RawCAN():
        """
        TSMaster TRawCAN
        """
        def __init__(self, AId: int = 0, ADLC: int = 8, AIdxChn: int = 0, ATimeUs: int = 0, ADatas: typing.List[int] = [], AFlags: int = 6):
            '''
            Create a raw CAN frame with identifier and dlc
            '''
        def init_w_std_id(self, AId: int, ADLC: int) -> None: "initialize a RawCAN object with standard identifier and dlc"
        def init_w_ext_id(self, AId: int, ADLC: int) -> None: "initialize a RawCAN object with extended identifier and dlc"
        def set_data(self, AIdxByte: int, AData: int) -> None: "set raw data byte by index"
        is_tx: bool = "whether are tx signal"
        is_data: bool = "whether are data"
        is_std: bool = "whether status code are correct"
        is_err: bool = "whether are error"
        is_edl: bool = "whether is EDL function"
        is_brs: bool = "whether is BRS function"
        is_esi: bool = "whether is ESI function"
        idx_chn: int = "channel index"
        dlc: int = "dlc length of data"
        id: int = "identifier of frame"
        time_us: int = "Timestamp in microseconds"
        data: typing.List[int] = "Data bytes, Note: you cannot write single value here, use set_data or write whole array instead: data = your_array_object"

     

    • Rx 이벤트가 발생한 타임스탬프가 아닌, 메시지를 수신한 타임스탬프를 이용하도록 코드를 수정하고 시험하였다.

     

     

    개요

    • 코드 수정
    • 실행 결과

     

    코드 수정

    • 간단하다. 아래와 같이 app.get_timestamp_us()로 이벤트가 발생한 시각을 ts로 하는 대신 메시지(ACAN)의 time_us를 ts로 했다.
        # 메시지 수신 이벤트가 발생한 시각
    #     ts = app.get_timestamp_us()
    
        # 하드웨어가 찍은 메시지 수신 타임스탬프
        ts = ACAN.time_us
    • 침입 감지 건수를 세기위해 Global Definition에 아래 코드를 추가하였다. 
    # 침입 감지 건수를 저장할 변수를 선언한다.
    n_intrusion_detection = {
        'dlc': 0,
        'd_ts_min': 0,
        'white_list': 0,
    }
    
    # 침입 감지 건수를 Numeric Display로 표시하기 위해서 시스템 변수를 선언하였다. 
    # 시스템 변수를 초기화한다.   
    for k, v in n_intrusion_detection.items():
        app.set_system_var_int32(f'calc.n_id_{k}', v)

     

    • detect_intrusion 함수에 침입 감지 카운터 관련 코드를 추가하였다.
        # d_ts가 기준보다 작으면, 즉, 메시지 간격이 너무 좁으면, 침입으로 감지한다. 
        if d_ts < x[ACAN.id]['d_ts_min']:
            # 침입이 감지되어 카운터를 증가한다.
            n_intrusion_detection['d_ts_min'] += 1
    
            # 침입 카운터 시스템 변수를 업데이트한다.
            app.set_system_var_int32('calc.n_id_d_ts_min', n_intrusion_detection['d_ts_min'])
    
            # 기준을 얼마나 초과하였는가?
            d_ts_over_by = x[ACAN.id]['d_ts_min'] - d_ts
    
            app.log_text(f'{ACAN.id:x} d_ts too short d_ts={d_ts:,} d_ts_min={x[ACAN.id]["d_ts_min"]:,} {d_ts_over_by=:,}', lvlOK)

     

    • 침입 감지 건수를 표시할 시스템 변수들을 추가하였다. 
      • 메인 메뉴/ Simulation/ System Variables 버튼을 클릭하여 System Variable Management 창을 연다. 
      • 시스템 변수를 정의한다. 카운터이므로 데이터 타입을 UInt32으로 한다. 

    침입 건수를 표시할 시스템 변수를 정의한다.

     

    예) n_id_d_ts_min 시스템 변수의 정의. 나머지 변수들도 같은 방식으로 정의한다.

     

    • 침입 건수를 표시할 숫자 표시창 (Numeric Display)을 연다. (메인 메뉴/ Analysis/ Numeric Display 버튼을 클릭한다.)
      • 창이 열리면 시스템 변수 아이콘을 클릭한다. 시스템 변수 창이 열린다. 추가할 변수를 더블 클릭한다. 

    침입 건수를 표시할 숫자 표시창을 준비한다.

    • 수정된 코드는 아래와 같다.

    intrusion_detection.py
    0.01MB

     

     

     

    실행 결과

    • 동일한 blf를 온-라인 재생하면서 침입 감지 건수를 비교하였다. 

    왼쪽은 미니프로그램의 Rx 이벤트가 발생한 순간의 타임스탬프를 기준으로, 오른쪽은 하드웨어가 찍은 메시지 수신 타임스탬프를 기준으로 감지한 침입 건수이다.

    • 수정 전, 후의 코드를 각각 3회씩 실행하였다. 결과는 아래 표와 같다.
    수정 전 수정 후
    18 6
    20 1
    6 2
    • 침입이 없는 데이터이므로 0건 검출되어야 한다. 침입 판정 기준값이 너무 민감하다고 할 수 있다. 
    • 주로 0x300번대 메시지들이 검출되었다. 

    0x300번대 메시지들이 검출되었다.

    • 0x300번대 메시지들의 주기는 10msec 혹은 20msec이다.
    m_id 주기 [sec]
    316       0.010
    329       0.010
    340       0.010
    346       0.010
    34C       0.010
    374       0.010
    381       0.020
    383       0.020
    386       0.020
    387       0.020
    38D       0.020
    394       0.020
    • 1msec는 주기의 10% 내지 5%에 해당한다. 기준값을 5% ~ 10%를 더 늘리는 것이 너무 느슨한 것인지 아닌지 쉽게 판단이 서지 않는다. 이럴 경우 다른 방법을 찾아야 한다고 생각한다.
    • 단위당 (예를 들어, 100 메시지당, 1초당) 침입 감지 건수를 이용하여 침입 여부를 "확정"하는 로직을 추가하는 방법이 있을 수 있다. (그 외에 다양한 방법이 있을 수 있다. 개발하는 사람의 상상력이 한계이다.) 

     

     

    결론

    • RawCAN 클래스에 하드웨어가 찍은 메시지 수신 타임스탬프 정보가 있다. time_us.
    • 하드웨어의 메시지 수신 타임스탬프를 이용하는 방법이 미니프로그램의  메시지 수신 이벤트 타임스탬프를 이용하는 것보다 침입 감지 건수가 적다. 소프트웨어(윈도, TSMaster, 파이썬)의 영향을 배제하여 그런 것이 아닐까 추정한다.
    • 메시지 주기만 보고 침입을 감지하는 방식은 민감하여 오감지가 많이 발생할 수 있다. 감지 성능을 크게 저하시키지 않으면서 오감지를 적정 수준으로 줄일 수 있는 추가 로직 개발이 필요하다고 생각한다. (IDS 개발이 그렇게 간단하지는 않을 것으로 짐작했었다. IDS를 개발하는 분들께 경의를 표한다. ;-)