Docker를 이용한 cross compile

곰개구리·2024년 8월 18일
0
post-thumbnail

개요

  • clamav를 개발 환경에 맞춰 빌드하고 가져오는 것이 필요했다
  • 환경은 기본적으로 openwrt buildroot를 이용한 크로스 컴파일이 지원되었다
  • 그러나 clamav는 공유 라이브러리(so) 형식으로 종속 모듈들과 관리되어야 한다
  • clamav는 1.x 이상 버전을 사용해야 한다

환경구성

  • glibc 2.31
  • openssl 3.0.13

방안

  • docker를 사용해 cross compile을 시도했다
  • glibc 2.31을 기본으로 사용하는 ubuntu 20.04 환경에서 clamav를 빌드했다
  • 현재 clamav lts (1.0.6) 이 openssl 3.x를 지원하지 않아 최근 릴리즈 버전인 1.3.1을 사용했다

플로우

  • ubuntu 20.04 docker 환경에서 clamav와 그 종속 모듈들을 빌드한다
  • 빌드된 clamav와 종속 모듈들의 공유 라이브러리, clamav의 종속 헤더 파일을 각각 docker에서 압축하여 가져온다

사용 도커파일

# Base image 설정 (Ubuntu 20.04)
FROM ubuntu:20.04

# tzdata 설치 시 대화형 프롬프트를 피하기 위한 환경 변수 설정
ENV DEBIAN_FRONTEND=noninteractive

# 필수 패키지 설치 및 시간대 설정
RUN apt-get update && \
    apt-get install -y build-essential wget libssl-dev libmspack-dev libbz2-dev zlib1g-dev \
                       libcurl4-openssl-dev libncurses5-dev cmake curl git check python3 \
                       python3-pip libxml2-dev libpcre2-dev libjson-c-dev libmilter-dev \
                       libclamav-dev ca-certificates tzdata

# Rust 및 Cargo 설치
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# 시간대 설정 (서울 기준)
ENV TZ=Asia/Seoul
RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && dpkg-reconfigure --frontend noninteractive tzdata

# OpenSSL 3.x 설치
RUN wget https://www.openssl.org/source/openssl-3.0.13.tar.gz && \
    tar -xzf openssl-3.0.13.tar.gz && \
    cd openssl-3.0.13 && \
    ./config --prefix=/usr/local --openssldir=/usr/local/openssl && \
    make -j$(nproc) && \
    make install && \
    cd .. && rm -rf openssl-3.0.13*

# 환경 변수 설정
ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib:/usr/local/libcurl/lib:$LD_LIBRARY_PATH
ENV PKG_CONFIG_PATH=/usr/local/libcurl/lib/pkgconfig:$PKG_CONFIG_PATH

# libcurl 설치 (OpenSSL 3.x와 함께)
RUN wget https://curl.se/download/curl-8.0.1.tar.gz && \
    tar -xzf curl-8.0.1.tar.gz && \
    cd curl-8.0.1 && \
    LDFLAGS="-L/usr/local/lib64" CPPFLAGS="-I/usr/local/include" ./configure --with-ssl=/usr/local --prefix=/usr/local/libcurl --enable-shared --disable-static && \
    make -j$(nproc) && \
    make install && \
    cd .. && rm -rf curl-8.0.1*

# ClamAV 1.3.1 설치 (OpenSSL 3.x 및 최신 libcurl 사용)
RUN wget https://www.clamav.net/downloads/production/clamav-1.3.1.tar.gz && \
    tar -xzf clamav-1.3.1.tar.gz && \
    cd clamav-1.3.1 && \
    mkdir build && cd build && \
    cmake .. -DOPENSSL_ROOT_DIR=/usr/local -DOPENSSL_INCLUDE_DIR=/usr/local/include -DOPENSSL_CRYPTO_LIBRARY=/usr/local/lib64/libcrypto.so -DOPENSSL_SSL_LIBRARY=/usr/local/lib64/libssl.so -DCURL_LIBRARY=/usr/local/libcurl/lib/libcurl.so -DCURL_INCLUDE_DIR=/usr/local/libcurl/include && \
    make -j$(nproc) && \
    make install && \
    cd ../.. && rm -rf clamav-1.3.1*

# 라이브러리 및 헤더 파일 복사 및 압축
RUN mkdir -p /tmp/clamav-libs/lib /tmp/clamav-libs/include /tmp/clamav-libs/include/openssl && \
    cp /usr/local/lib/libclamav.so* /tmp/clamav-libs/lib/ && \
    cp /usr/local/lib/libclammspack.so* /tmp/clamav-libs/lib/ && \
    cp /usr/local/include/clamav.h /tmp/clamav-libs/include/ && \
    cp /usr/local/include/clamav-types.h /tmp/clamav-libs/include/ && \
    cp /usr/local/include/clamav-version.h /tmp/clamav-libs/include/ && \
    cp /usr/local/include/openssl/* /tmp/clamav-libs/include/openssl/ && \
    cp /usr/lib/x86_64-linux-gnu/libpcre2-8.so* /tmp/clamav-libs/lib/ && \
    cp /usr/local/lib64/libcrypto.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libbz2.so* /tmp/clamav-libs/lib/ && \
    cp /usr/local/lib64/libssl.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libz.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libjson-c.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libxml2.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libicuuc.so* /tmp/clamav-libs/lib/ && \
    cp /usr/lib/x86_64-linux-gnu/libicudata.so* /tmp/clamav-libs/lib/ && \
    tar -czvf /tmp/clamav-libs.tar.gz -C /tmp clamav-libs

# 기본 명령어 설정
CMD ["clamscan", "--version"]

테스트 코드(c)

#include <stdio.h>
#include <stdlib.h>
#include <clamav.h>

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <file_to_scan>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1];
    const char *dbdir = "./clamav-db";  // 데이터베이스 디렉터리 경로
    int ret;
    unsigned long int scanned = 0;
    const char *virname = NULL;

    // Initialize ClamAV
    if ((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
        fprintf(stderr, "cl_init() error: %s\n", cl_strerror(ret));
        return 1;
    }

    // Create a new scan engine
    struct cl_engine *engine = cl_engine_new();
    if (!engine) {
        fprintf(stderr, "cl_engine_new() error: %s\n", cl_strerror(CL_EMEM));
        return 1;
    }

    // Load the virus signature database from the specified directory
    unsigned int sigs = 0;
    if ((ret = cl_load(dbdir, engine, &sigs, CL_DB_STDOPT)) != CL_SUCCESS) {
        fprintf(stderr, "cl_load() error: %s\n", cl_strerror(ret));
        cl_engine_free(engine);
        return 1;
    }

    printf("Loaded %u virus signatures from %s.\n", sigs, dbdir);

    // Compile the engine
    if ((ret = cl_engine_compile(engine)) != CL_SUCCESS) {
        fprintf(stderr, "cl_engine_compile() error: %s\n", cl_strerror(ret));
        cl_engine_free(engine);
        return 1;
    }

    // Define scan options
    struct cl_scan_options scan_options = {
        .general = CL_SCAN_GENERAL_ALLMATCHES | CL_SCAN_GENERAL_HEURISTICS | CL_SCAN_GENERAL_UNPRIVILEGED, // 표준 스캔 옵션
        .parse = CL_SCAN_PARSE_ARCHIVE | CL_SCAN_PARSE_ELF | CL_SCAN_PARSE_PDF | CL_SCAN_PARSE_MAIL,      // 추가 파싱 옵션
        .heuristic = CL_SCAN_HEURISTIC_BROKEN | CL_SCAN_HEURISTIC_MACROS,                                 // 휴리스틱 옵션
        .mail = CL_SCAN_MAIL_PARTIAL_MESSAGE,                                                            // 메일 스캔 옵션
        .dev = 0                                                                                         // 개발 옵션 (사용하지 않음)
    };

    // Scan the file
    ret = cl_scanfile(filename, &virname, &scanned, engine, &scan_options);
    if (ret == CL_VIRUS) {
        printf("Virus found: %s\n", virname);
    } else if (ret == CL_CLEAN) {
        printf("No virus found in %s\n", filename);
    } else {
        fprintf(stderr, "cl_scanfile() error: %s\n", cl_strerror(ret));
    }

    // Clean up
    cl_engine_free(engine);
    return 0;
}

빌드

  • gcc -o clamscan_example clamscan_example.c -I./include -L./lib -lclamav -Wl,-rpath=./lib

테스트

  • eicar에서 테스트 악성코드를 가져와서 테스트했다
    • wget -O eicar_com.zip "https://www.eicar.org/download/eicar_com-zip/?wpdmdl=8847&refresh=66c1ae90c82bb1723969168"
  • LD_LIBRARY_PATH=/tmp/workplace/clamav-libs/lib ./clamscan_example eicar_com.zip
  • 결과
root@4dd1c9322fad:/tmp/workplace/clamav-libs# LD_LIBRARY_PATH=/tmp/workplace/clamav-libs/lib ./clamscan_example eicar_com.zip
Loaded 8697521 virus signatures from ./clamav-db.
Virus found: Win.Test.EICAR_HDB-1

참조: 바이러스 데이터베이스

profile
개굴개굴 곰개굴

0개의 댓글