[연재 1] 1. 개요 2. 주요 내용 3. 방법론 4. 자동차 관련 취약점 - 건초 더미에서 바늘 찾기4.1 심각도 vs. 관련성 4.2 악명 높은 공급망 취약점 [연재 2] 5. 노후화된 소프트웨어 관련 위험 6. 자동차 소프트웨어에 영향을 미치는 메모리 손상 7. 결론
1. 개요
커넥티드 카(Connected car)의 등장이 현실이 되었다. 커넥티드 카는 기존의 운송 수단 및 모빌리티 서비스를 혁신할 자율주행 기술과 관련된 중요한 이정표를 제공한다. 그러나 이와 같은 발전에는 도전이 따른다.
증가하고 있는 자동차 산업에 대한 사이버 보안 위험에 대한 효과적인 사이버 보안 조치가 없을 경우 공격 표면이 넓은 최신 사양의 자동차는 물리적 손상을 일으킬 수 있는 각종 악의적인 활동에 취약할 수밖에 없고, 이에 따라 운전자와 보행자 모두의 안전에 영향을 미칠 수 있다.
각종 보안 위협에 대한 업계 전반의 인식은 UNECE WP.29 및 ISO/SAE 21434와 같은 국제표준에서도 분명히 드러난다. 두 국제표준 모두 OEM이 공급업체(Suppliers), 서비스 제공업체, 기타 조직과 관련된 다양한 위험을 관리하여 자동차 분야의 사이버 보안을 책임져야 한다고 규정하고 있으며, 다음과 같은 내용을 중심으로 자동차 사이버 보안에 대한 근본적인 변화를 주도한다.
1) 제품의 설계 및 개발 과정의 초기 단계에서 보안 관련 문제를 해결하는 “시프트 레프트(shift left)” 접근방식 도입 2) 제품의 개발, 생산, 운행, 폐기 등 자동차의 라이프사이클 전반에 걸친 사이버 보안 관리의 필요성 3) 자동차 공급망 전반에 걸친 사이버 보안 책임 강화
그러나 자동차 분야의 사이버 위험을 관리하는 것은 간단하지 않다. 제품 보안팀은 제한된 인력으로 일상적으로 발생하는 다양한 보안 문제를 관리하고 자동차 분야 제조업체의 디지털 혁신까지 지원해야 하기 때문이다.
이스라엘 자동차 사이버 보안 위험 평가 전문 기업인사이벨리움(Cybellum)의 ‘2021 자동차 소프트웨어 보안 현황 보고서’는 2020년부터 2021년에 걸쳐 발견된 100개 이상의 자동차 소프트웨어 컴포넌트에 대한 취약점 분석을 기반으로 자동차 소프트웨어와 관련된 주요 보안 위험을 조명한다.
2. 주요 내용
1) “일반적인” 취약점과 다른 자동차 소프트웨어 취약점
2020년에는 그 어느 때보다 많은 취약점이 발견되었지만, 그 중 극히 일부 취약점만이 자동차 소프트웨어에 영향을 미치는 것으로 나타났다. 이는 자동차 산업이 복잡하고 다양한 공급업체와 연관되어 있기 때문이다. 자동차 산업의 보안을 위협하는 취약점을 탐지하는 것은 건초 더미에서 바늘을 찾는 것만큼 까다롭다.
2) 공급망 취약점의 영향을 받는 자동차 소프트웨어
자동차 산업의 공급망은 다수의 복잡한 공급업체로 구성되므로, 이미 오래 전에 발견된 취약점과 최근 새롭게 발견된 취약점 모두의 영향을 받는다. 사이벨리움의 연구에 따르면, 4년 전 발견된 취약점인 ‘BlueBorne’과 같이 상당히 오래된 취약점과 ‘Ripple20’과 같이 최근 새롭게 발견된 취약점 모두 자동차 소프트웨어에 영향을 미치고 있는 것으로 나타났다.
3) 노후화된 소프트웨어로 인한 운영 상의 위험이 발생할 가능성
현재 자동차 제조업체는 다수의 노후화된 소프트웨어를 사용하고 있다. 특히 사이벨리움이 분석한 자동차 ECU 중 90%가 안전한 최신 버전의 소프트웨어를 사용할 수 있음에도 개발 시점이 5년 이상 경과한 오래된 소프트웨어를 사용하고 있는 것으로 나타났다. 최신 상태로 유지 및 관리되지 않는 소프트웨어는 운영 상의 심각한 사이버 보안 문제를 초래할 수 있다.
4) 메모리 손상(Memory Corruption)으로 인한 자동차 소프트웨어 보안 위협
자동차 소프트웨어에 영향을 미치는 대부분의 코딩 관련 약점은 다양한 메모리 관리와 관련된 오류로 인해 발생한다. 버퍼 오버플로(Buffer Overflow), Null 역참조, Use-After-Free 취약점 등의 메모리 손상(Memory Corruption) 취약점은 이미 수년 전에 발견된 것으로 위험도가 낮은 수준의 약점에 해당하지만, 최근 자동차 소프트웨어의 보안에 많은 영향을 미치는 것으로 확인되었다.
3. 방법론
사이벨리움은 ‘2021 자동차 소프트웨어 보안 현황 보고서’를 통해 자동차 소프트웨어 보안의 현재 상태에 대한 심층적인 분석을 제공한다. 보고서의 핵심 내용은 2020년에서 2021년 사이에 발견된 100개 이상의 자동차 소프트웨어 컴포넌트에 대한 분석이며, 이와 관련하여 보고서에 포함된 일부 정보는 오픈소스 소프트웨어를 기반으로 작성되었다.
4. 자동차 관련 취약점
건초 더미에서 바늘 찾기
취약점으로 인한 사이버 공격 피해를 최소화하기 위해서는 취약점 완화의 우선 순위를 지정하는 것이 중요하지만, 자동차 대상의 취약점이 크게 증가하면서 심각도에 따른 취약점 우선 순위 지정 프로세스가 지연되고 있다. 특히 일반적으로 공개되는 취약점이 모두 자동차 소프트웨어에 영향을 미치는 것이 아니기에 자동차와 관련된 취약점을 명확하게 식별하는 것이 쉽지 않고, 이에 따라 적절한 시기에 취약점을 완화하고 해결하지 못하는 사례가 발생하고 있다. 이는 궁극적으로 자동차의 컴포넌트 및 자동차 산업 분야에 속한 조직의 사이버 복원력에 악영향을 미칠 수 있다.
표 1 – 공개된 CVE (Common Vulnerabilities and Exposure) 취약점 (출처: NIST NVD,https://nvd.nist.gov)
소프트웨어의 유효성에 대한 적절한 테스트가 이루어지지 않을 경우 공격자는 특정 형식을 악용하여 입력을 조작할 수 있으며, 이에 따라 응용 프로그램이 의도하지 않은 입력 값을 수신하게 된다. 이 경우 제어 흐름, 리소스에 대한 임의 제어 또는 임의 코드 실행이 이루어질 수 있기 때문에 소프트웨어에 악영향을 미칠 수 있는 취약점을 적절하게 식별할 필요가 있다.
이와 관련하여 아래표 2의 구글(Google)에서 가장 많이 검색된 10개의 CVE 취약점과 사이벨리움이 분석한 자동차 소프트웨어 관련 CVE 취약점 비교 자료를 통해 비교해 볼 때, 일반적인 취약점 목록과 자동차 소프트웨어에 영향을 미치는 취약점 목록이 큰 차이점을 보인다는 것을 알 수 있다. 구글에서 가장 많이 검색된 CVE 취약점은 대부분 엔터프라이즈 및 IT 제품에 영향을 미치는 취약점에 해당하기 때문에 자동차와 관련된 임베디드 시스템과는 차이가 있다.
가장 많이 검색된 CVE (조사 기관: Google)
가장 인기 있는 주요 자동차 CVE (조사 기관: 사이벨리움)
CVE
CVSS점수
영향을 받는 소프트웨어
CVE
CVSS점수
영향을 받는 소프트웨어
2019-0708
9.8
Remote Desktop Services
2020-11656
9.8
SQLite
2017-11882
7.8
Microsoft Office
2019-19646
9.8
SQLite
2017-0199
7.8
Microsoft Office
2019-8457
9.8
SQLite
2018-11776
8.1
Apache Struts
2019-5482
9.8
cURL
2018-5638
10
Jakarta Multipart parser
2018-16842
9.1
cURL
2019-5544
9.8
OpenSLP (ESXi / Horizon DaaS)
2018-14618
9.8
cURL
2017-0143
8.1
SMBv1 server (Microsoft Windows)
2017-12652
9.8
libpng.
2020-0549
5.5
Intel(R) Processors
2017-109189
9.8
SQLite
2020-2555
9.8
Oracle Coherence
2018-1000301
9.1
cURL
2018-7600
9.8
Drupal
2018-1000120
9.8
cURL
표 2 – 일반 CVE 및 자동차 관련 CVE 비교
4.1 심각도 vs. 관련성
표 2에서 CVSS 점수로 표시된 취약점의 심각도를 통해 각 취약점이 자동차 보안에 미치는 영향을 확인할 수 있지만, 이러한 취약점 심각도만을 기준으로 자동차에 미치는 전체적인 보안 위험과 관련된 내용을 확인하는 것에는 한계가 있다. 그러나 일반적인 자동차 OEM 및 제조업체는 보안을 위한 인력 및 시간에 제한이 있기 때문에 자동차 보안에 영향을 미칠 수 있는 모든 취약점을 분석할 수 없고 이에 따라 심각도가 높은 취약점에 대한 식별 및 완화에 집중할 수밖에 없다.
심각도가 ‘중간 단계’로 평가되는 취약점은 전체 취약점에서 약 40%에 이르는 비중을 차지하지만, 심각도가 중간 단계로 평가되기 때문에 취약점 완화를 위한 패치가 적용되지 않은 상태로 자동차 컴포넌트 내에 존재하는 경우가 대부분이다. 그러나 중간 심각도라는 것이 ‘중간 위험’을 의미하는 것이 아니기 때문에, 일부 취약점에는 특정 익스플로잇(Exploit)이 존재하지 않는 일부 심각도가 높은 취약점보다 더욱 심각한 피해를 유발할 수 있다.
CVSS 점수는 취약점 평가 시 취약점 사이의 관련성 및 잠재적 영향을 고려하지 않음
특히 자동차 소프트웨어에 대한 취약점은 전체 공격 체인의 관점에서 컴포넌트 사이의 측면 이동이 가능하도록 자동차의 내부에 진입하는 역할을 한다. 대부분의 보안팀이 심각도가 높은 취약점의 식별 및 완화에 집중하고 있어 중간 심각도를 가진 취약점을 악용하는 공격의 성공률이 높기 때문에 공격자는 중간 심각도를 가진 CVE 공격을 선호한다.
앞서 언급한 것처럼 자동차 소프트웨어에는 다양한 취약점이 내재되어 있다. 자동차 소프트웨어와 관련된 컴포넌트, 소프트웨어 사이의 상관 관계를 분석할 수 있어야 자동차의 컴포넌트에 실질적인 영향을 미칠 수 있는 CVE 취약점을 식별할 수 있으며, 이를 통해 심각도 점수와 관계없이 자동차 보안에 악영향을 미칠 수 있는 CVE까지도 제거할 수 있다.
그러나 사이벨리움의 연구 결과에 따르면, 평균적으로 자동차의 컴포넌트에서 탐지된 CVE 취약점의 80% 이상에 대한 관련성 분석이 이루어지지 않아 심각한 영향을 미칠 수 있는 취약점인데도 완화되거나 제거되지 않은 것으로 나타났다.
80%이상의 CVE 취약점이 제거되지 않음
이를 통해 알 수 있듯이 소프트웨어 기능 사이의 상호 관계에 대한 인식(Context Awareness)은 취약점 관리 프로그램의 투자 수익(Return-On-Investment, ROI)을 최적화하는 동시에 전반적인 제품의 보안성을 향상시킨다.
4.2 악명 높은 공급망 취약점
표 3의 취약점 목록에서는 일부 누락된 사항이 있다. 언론에서 수차례 헤드라인을 장식한 악명 높은 취약점은 자동차 소프트웨어 취약점을 나열한 목록의 상위 10위 안에 포함되지 않았기 때문이다.
자동차 소프트웨어를 위협하는 소위 ‘악명 높은 공급망 취약점’ 중 상위 3개에 해당하는 BlueBorne, DirtyCow, Drown 취약점은 이미 4~5년 전에 발견된 취약점이지만 여전히 자동차 소프트웨어에 영향을 미치고 있다. 체계적인 사이버 보안 관행이 뒷받침되었다면 이러한 취약점은 발견되고 얼마 지나지 않아 완화할 수 있는 해결책이 제시되었어야 한다.
순위취약점 명칭발견된 년도
01
BlueBorne
2017
02
DirtyCow
2016
03
Drown
2016
04
SweynTooth
2020
05
Ripple20
2020
06
Amnesia:33
2020
07
Urgent/11
2019
08
Ghost
2015
표 3 – 자동차 취약점을 위협하는 악명 높은 취약점 순위
Ripple20, Amnesia:33, Urgent/11와 같은 취약점은 여러 공급업체에서 널리 사용되는 통신 스택(Stack)에 내재되어 있는 취약점으로 최근 들어 발견된 공급망 취약점이다.
KEY TAKEAWAYS
공개된 CVE 취약점 중 대부분은 자동차 소프트웨어에 영향을 미치지 않지만 너무 많은 소프트웨어와 취약점이 존재하기 때문에 자동차 또는 컴포넌트에 실질적인 영향을 미치는 취약점을 식별하는 것은 건초 더미에서 바늘을 찾는 것처럼 까다롭다.
기존의 보안 관행은 수동 감지 및 대응을 토대로 수행되기 때문에 깊이 있는 통찰력을 제공할 수 없다. 신속한 문제해결을 위해서는 자동화된 프로세스가 필요하다.
취약점 관리 프로그램은 제품의 컴포넌트와 소프트웨어 사이의 상관관계에 대한 인식 및 분석을 기반으로 자동차 보안에 영향을 미칠 가능성이 높은 위험을 식별할 수 있어야 한다. 실제로 대부분의 보안문제는 제품이 생산되기 전 단계인 개발 과정에서 식별 및 해결될 수 있다.
공급망 위험은 자동차 소프트웨어에 실질적인 영향을 미친다. 몇 년 전 발견된 취약점은 견고한 보안 관행을 통해 완화되었어야 하고 완화될 수 있어야 하는 것이 정상이다.
exception vector table은 여러가지 인터럽트와 예외를 처리할수 있는 주소를 모아놓은 테이블입니다.
먼저 예외와 동작모드에 관해서 보겠습니다.
arm에서의 예외는 총 7가지가 있습니다.
Data Abort :메모리의 데이터를 읽거나 데이터를 쓰려다가 실패하는 경우 발생.
FIQ, IRQ :외부 인터럽트가 코어에 전달될 때 발생.
Prefetch Abort :명령어를 해석하다가 실패하는 경우 발생.메모리에 존재하는 명령어를 읽어오다가 오류가 생기면 발생.
SWI(Software Interrupt) :프로그램 내부에서 발생시키는 인터럽트.소프트웨어 인터럽트라고 함.시스템 콜의 핵심.
Reset : ARM코어가 리셋되었을 경우 발생
Undefined Instruction :읽어온 명령어가ARM코어와 코프로세서에 정의디지 않은 명령어일 때 발생.
OS나 임베디드OS를 만들 경우엔 저 예외들은 필수로 들어간다고 생각합니다.
(현재arm아키텍쳐를 제일 많이 사용하니 보편적으로 사용하는 최소한의 예외라고 생각합시다.)
그리고ARM에는7가지exception과 연결된 일곱가지의 프로세서 동작모드가 있습니다.
Abort(ABT)
Fast Interrupt Request(FIQ)
Interrupt Request(IRQ)
Supervisor(SVC)
System(SYS)
Undefined(UND)
USER(USR)
ABT, FIQ, IRQ, SVC, UND모드는exception이 발생할 때 자동으로 변경되는 동작모드,
SYS와USR는 프로세서의 일반적인 동작 모드입니다.
예외
동작모드
Prefetch Abort
ABT
Data Abort
FIQ
FIQ
IRQ
IRQ
Reset
SVC
SWI
Undefined
Undefined
예외가 발생할 경우 해당하는동작모드로 변경을 하려면cpsr(Current Program Status Register)의0~4번 비트를 수정해 주어야 합니다.전에3장에서 보았던LED를 끄고 켤 때 관련된 레지스터의 비트를 수정하는 것과 같습니다.
<!--[endif]-->
모드
약자
특권기능
모드 비트(cpsr[4:0])
Abort
abt
yes
10111
FIQ
fiq
yes
10001
IRQ
irq
yes
10010
Supervisor
svc
yes
10011
System
sys
yes
11111
Undefined
und
yes
11011
USER
usr
no
10000
cpsr의0~4번 비트에 위의 표에서 나오는 모드 비트값을 넣으면 해당모드로 작동합니다.
근데 자세히보면 모드 비트의 자리수는0~4번까지5개의 비트를 사용합니다.실제로 사용하는 모드는 총7개뿐인데 말이죠.
방금 보았던Prefetch Abort예외와Data Abort예외가Abort동작모드에서 처리가 되도록 설정되었지만,
동작모드를 새로 하나 만들고Prefetch Abort와Data Abort를 각각 처리를 할수도 있다는 것을 유추해볼수 있습니다.
(2^5승이니32개.총32개까지 모드를 만들 수 있겠죠.)
책에서는 리눅스에서의 동작모드에 대해 엄청 잘 설명해놨습니다.
“리눅스에서는 일반적으로 사용자 프로그램은 전부 다USER모드에서 작동한다.그러다가 프로그램 안에서 시스템 콜을 호출하면 프로그램은SWI exception을 발생시키고, ARM코어가 프로세서의 동작 모드를SVC모드로 변경시켜Software Interrupt를 처리한 후에 다시USER모드로 복귀한다.마찬가지로 키보드 입력같은 경우는 대부분의 운영체제에서IRQ로 처리한다.우리가 키보드를 누르면 키보드 케이블을 통해 메인보드에 신호가 전달되고,메인보드에 있는 여러 회로를 거쳐ARM코어에IRQ exception이 발생한다.그러면ARM코어는 프로세서 동작 모드를IRQ모드로 변경하고IRQ관련 처리를 한 다음USER모드로 복귀한다.”
근데 리눅스를 잘 사용하지 않아 조금 거부감이 있는 사람들을 위해 윈도우 측면에서 설명해 보겠습니다.
(왠만하면 위에껄로 이해하세요.제가 설명하는게 정확한지 모릅니다.)
“윈도우에서는 일반적으로 사용자 프로그램은 각각 생성된 윈도우 계정에서 작동한다.[USER모드에서 작동]그렇게 프로그램에서 작업을 하던 중,작업하던 프로그램이 멈춰버렸다.응답이 없는 프로그램을 처리하기 위해서 우측 상단의X표를 누른다.[SWI exception을 발생시킨다.]그리고 예외를 처리하기 위해서 프로세서의 모드를 프로그램을 종료시킬수 있는 모드로 변경한다.[SVC모드로 변경]마지막으로 응답없는 프로그램을 종료 시키고[Software Interrupt처리]다시 프로그램이 켜지기 전의 상태로 돌아간다.[USER모드 복귀]
이제 예외를 처리하는 방법을 봅시다.
exception을ARM에서 처리하는 순서.
1. exception이 발생함.
2. exception모드의spsr에cpsr을 저장함.
3. exception모드의lr에pc값을 저장함.
4. cpsr의 모드 비트를 변경하여,해당exception에 대응하는 프로세서 동작 모드로 진입함.
5. pc에exception핸들러의 주소를 저장하여 해당exception에서 해야 할 일을 처리함.
spsr(Saved Program Status Register) :각 동작모드에 존재하며, exception이 발생하여cpsr을 변경해야 할 때 이전 모드의cpsr을spsr에 백업한다.
링크레지스터(lr) : exception의 처리후 복귀하기 위한pc의 주소를 저장.
4번째에 보면 모드비트를 변경하고 나서,대응하는 프로세서 동작 모드로 진입한다고 되어있습니다.
각 동작모드가 있는 주소를 모아 놓은 것이exception vector table입니다.
데이터 영역에서 코드가 실행되는 것을 방지하는 기법으로스택이나 힙에서 실행 권한을 없애 쉘 코드 실행을 막아주는 기법이다. 윈도우에서는 DEP라고 부르고 리눅스에서는 NX bit라고 불러서 결국 둘 다 동일한 말
NX 비활성화 옵션
gcc -z execstack
3. Stack Canary
함수 진입 시 스택에 SFP와 return address값을 저장할 때,이 값을 공격자로부터 덮어씌워지는 것을 보호하기 위해 스택 상의 변수들의 공간과 SFP사이에 특정한 값을 추가하는데 이 값을 Canary라고 부른다.함수가 종료할 시점에 Canary값의 변조 유무로 bof를 탐지할 수 있으며 변조 시 프로그램이 종료된다.보통 SFP앞에 카나리 있음.
Canary 종류
Terminator Canary: NULL(0x00), CR(0x0d), LF(0x0a), EOF(0xff)와 같은 Terminator 문자를 포함시키는 Canary로 종료 문자들 때문에 Canary값을 알고 있어도 똑같이 작성하여 공격이 불가능
Random Canary: Canary값이 랜덤으로 계속해서 생성하는 방법
Radom XOR Canary: Canary값을 모든 제어 데이터 또는 일부를 사용해 XOR-scramble하여 생성
GOT Overwrite와 같은 공격에 대비해 ELF 바이너리 또는 프로세스의 데이터 섹션을 보호하는 기법이다.바이너리 컴파일 시 Full-RELRO 옵션을 주면 .ctors, .dtors, .jcr, .dynamic, .got섹션이 Read-Only가 됨 →GOT Overwrite 불가능
Lazy Binding(Lazy Linking, on-demand symbol resolution)
모든 함수의 주소를 한 번에 로딩하지 않고함수를 처음 호출한 시점에 공유 라이브러리에서 해당 함수의 주소를 알아오는 것을 Lazy Binding이라고 한다.
Now Binding
프로그램이 실행될 때 해당 프로그램에서 사용하는 모든 함수들의 주소를 읽어와 got영역에 저장
추가 설명)
Dynamic Linking 방식으로 컴파일된 바이너리에서 라이브러리 함수를 호출할 때 실제 함수의 주소를 모르기 때문에 동적으로 알아오기 위해 plt, got를 이용한다. 처음 함수를 호출할 때는 plt영역의 got를 참조하고 뭐 여러가지 과정을 거치고 dl_runtime_resolve()로 가서 dl_fixup()에서 드디어 실제 함수의 주소를 got에 넣어준다. 이후에 다시 함수를 호출할 때는 위와 같은 복잡한 과정을 거치지 않고 걍 got에 있는 함수를 호출한다.
[No-RELRO]
[Partial-RELRO]
[Full-RELRO]
추가 설명)
Full RELRO를 사용하면 GOT가 Read-Only인 상태여서 Partial RELRO보다 보안상 더 안전할 것 같은데 Full RELRO보다 Partial RELRO가 더 널리 사용되는 이유는Full RELRO의 경우 프로세스가 시작될 때 링커에 의해 모든 메모리에 대해 재배치 작업이 일어나 실행이 느려지기 때문이다.
5. PIE(Position Independent Executable)
PIE는 위치 독립 코드로 이루어진 실행 가능한 바이너리이며 바이너리 주소가 상대적인 주소로 랜덤하게 매핑시키는 기법이다. 모든 심볼을 상대주소로 작성하므로 Base Address가 달라지게 함으로써 모든 심볼 주소가 실행할 때마다 달라져 ASLR과 같은 효과를 가질 수 있다.(Base Address + Relative offset, 참고로 Base Address는 매번 변경되지만 offset은 동일)
참고로 PIE는 OS의 ASLR기능에 의존하므로 OS의 ASLR이 dsiable되어 잇으면 randomize 불가하며 더 구체적으로는 OS loader의 영향을 받는다.
PIE 활성화 및 비활성화 옵션
[비활성화]
gcc -no-pie
[활성화]
gcc -fPIE -pie
6. ASCII-Armor
RTL공격에 대응하기 위한 방법으로 공유 라이브러리 영역의 상위 주소에 0x00을 포함시킨다. ASCII-Armor가 적용되면 라이브러리를 공유 라이브러리 영역에 할당하지 않고 텍스트 영역의 16MB아래의 주소에 할당된다. 따라서 라이브러리 영역의 주소 최상위 1byte가 NULL로 인식된다. bof공격을 위해 string관련 함수(strcpy, strcat, ...)를 사용하게 되면 문자열을 읽다가 NULL값이 나오면 중지되므로 라이브러리 함수를 호출하는 그 때 최상위 1byte가 NULL값이므로 동작이 끝나 버린다.
CAN-TP is a transport protocol that enables large data packets to be transmitted over the CAN bus, which has a limited message size. The protocol allows for the segmentation and reassembly of data packets, so that they can be transmitted in multiple smaller frames over the CAN bus.
Types of Addressing in CAN-TP (ISO 15765) Protocol
The main purpose of addressing in CAN-TP is for how to identify, either it is a non CAN-TP message or CAN-TP message. To identify it, there ae 2 types of addressing defined in the ISO 15765-2 standard.
Basic Addressing.
Extended Addressing.
Basic Addressing in CAN-TP (ISO 15765)
The CAN-TP Protocol basic addressing mode is called a normal addressing mode were to identify the CAN message or CAN-TP, we are using the CAN Identifier. So for this, there will be some identifier will be for CAN-TP, where if any message will receive then the server will understand that this TP message. The advantages of this type of addressing are that full 8 bytes of the data packet can be sent as data.
In basic addressing, each message is addressed to a specific node on the network using a unique identifier called a “Node Address.” Each node on the network is assigned a unique Node Address, which is typically configured by the user or by a configuration tool.
When a node wants to send a message, it includes the destination Node Address in the message header. When the message is received by the other nodes on the network, they compare the destination Node Address in the message header to their own Node Address. If the Node Address matches, the node accepts the message and processes it. If the Node Address does not match, the node ignores the message.
Extended Addressing in CAN-TP (ISO 15765)
Extended Addressing is a feature of the ISO 15765-2 Transport Protocol (CAN-TP) that allows messages to be addressed to a larger number of nodes on a Controller Area Network (CAN) than is possible with Basic Addressing. In Extended Addressing, the identifier field of the CAN message header is used to transmit the destination Node Address, which can be up to 29 bits in length. This allows for a much larger address space, with up to 536,870,912 possible Node Addresses, compared to the 8-bit Node Addresses used in Basic Addressing.
Extended Addressing also allows for dynamic addressing, where nodes can be added or removed from the network without requiring reconfiguration of the Node Addresses. This is accomplished through the use of a “Dynamic Addressing” scheme, where a unique “Arbitration ID” is assigned to each node, and the Node Address is transmitted in the data portion of the message.
When a node sends a message using Extended Addressing, it includes the destination Node Address in the identifier field of the CAN message header. When the message is received by the other nodes on the network, they compare the destination Node Address in the message header to their own Node Address. If the Node Address matches, the node accepts the message and processes it. If the Node Address does not match, the node ignores the message.
This addressing mode is the CAN-TP addressing mode where the 1st byte of the CAN data field will be used for the additional elements of the address whereby it reducing the data payload by one byte. The primary task of the transport protocol is to transfer messages which cannot be transmitted as a single Protocol Data Unit (PDU) due to their length. Messages which contain more data that can be transmitted within a single PDU are segmented by means of the transport protocol and divided into multiple, separate PDUs.
This procedure can also be implemented on the data link layer. The segmentation of the message must then be carried out in PDUs of the corresponding data link protocol. So to send the data like CAN, the CAN-TP Protocol has been designed. Let us discuss how it can send multiple frames.
Different Types of Frames in CAN-TP (ISO 15765)
So to make it possible, the CAN-TP also having 2 types of frames.Single FrameandMulti-Frame. Again the Multi-frame is having 3 types of frames. So total 4-types frames are there in CAN-TP:
In the ISO 15765-2 Transport Protocol (CAN-TP), a Single Frame is a type of message that is used to transmit data that can fit within a single CAN data frame. A Single Frame consists of a header and a data field, where the header contains information about the message, and the data field contains the actual data to be transmitted.
Single Frames are used to transmit small amounts of data, typically up to 7 bytes, that can be sent in a single CAN data frame. Single Frames are the simplest and most efficient way to transmit data in CAN-TP, as they require only one message to be sent and do not require any additional message framing or flow control mechanisms.
CAN-TP: Single Frame Format
If the data payload is 7-bytes or less than that, then the single frame will be used for the data transfer in CAN-TP Protocol. Where the first byte of the data field is used for addressing. Again this byte is divided into two divisions, where the MSB 4-bit is used for addressing of TP frame type called PCI (Protocol Control Information). The LSB 4-bit is used for the DLC (Data Length Code).
Example of Single Frame in CAN-TP (ISO 15765)
The below table shows an example of how the CAN-TP implemented in CAN Data field using Single Frame.
05
01
02
03
04
05
AA
AA
CAN Datafield with CAN-TP Using Single Frame(Request: Client –> Server)
PCI Header (0x05):1 Byte Protocol Control Information Header. Where,
Last 4 MSB is “0” and it defines the Frame Type is Single Frame.
First 4 bit LSB is “5” and it defines the payload or datalength transmitted in Single Frame.
Payload:Next 5 bytes are the original data (01 – 05) sent by the TransmitterECUand 2 byte is empty (NULL).
Generally, if you are going to send less than 8 byte, you can use the Single Frame else to send the more than that you have to use the other 3 frames. 0xAAorox55is filled for NULL data bytes to make sure that no junk or corrupted data sent by the transmitter.
First Frame (FF – 0x1) in CAN-TP (ISO 15765)
In Controller Area Network – Transport Protocol (CAN-TP) ISO15765-2, the “First Frame” refers to the first message frame in a multi-frame message. This message type is used to initiate the transmission of a large data block. It includes information such as the total size of the data block and the size of each subsequent frame.
When a large message is sent over the CAN bus, it is divided into several smaller frames to ensure reliable and efficient transmission. The First Frame is used to initiate this process and contains important information about the message, such as its total length and the number of frames that will be used to transmit it.
The structure of the First Frame message is defined by the ISO15765-2 standard and includes a header that contains information about the message, such as the message length and a unique identifier (ID) that allows the recipient to associate the message with the correct application. The data field of the First Frame contains the first part of the message data, up to a maximum of4096bytes.
Once the First Frame has been successfully transmitted and received, the following frames, known as Consecutive Frames, are sent to transmit the remaining parts of the message data. The use of multiple frames allows for more efficient use of the available bandwidth on the CAN bus and ensures reliable transmission even in noisy environments.
The first frame is an initial message of the multi-frame message packet in CAN-TP Protocol. It is used when more than 6 or 7 bytes of data segmented must be communicated. The first frame contains the length of the full packet, along with the initial data.
CAN-TP: First Frame Format
In FF, the first 2-bytes are used for the PCI, where the MSB 4-bit of the 1st byte is used for the type of frame and the LSB 4-bit and next 1-byte (total 8+4 = 12 bit)of the CAN data field is used for DLC (2^12 = 4096 data bytes). so in FF, only 6-bytes of the data can be transferred for the first time. This frame is responsible for sending the information to the receiver about the information as to how many total data bytes he is going to send.
Example of First Frame in CAN-TP (ISO 15765)
The below table shows an example of how the CAN-TP implemented in CAN Data field using First Frame.
10
64
01
02
03
04
05
06
CAN Datafield with CAN-TP Using First Frame (Request: Client –> Server)
PCI Header (0x1064):In FF, it is 2 byte.
Last 4 MSB is “1” is defining the Frame Type is First Frame.
First4LSB plus next 1 byte (Total = 4 LSB + 1 Byte) is Payload.0x0 + 0x64 = 0x64in decimal, it is100byte of data.
Payload:Next6bytes (01 – 06) are the original data that is sending to the receiver from total100bytes. So remaining 94 bytes will be sent consicutively by using the Consecutive Frames.
Consecutive Frame (CF)in CAN-TP (ISO 15765)
The Consecutive Frame or message type is used to transmit the data in consecutive segments. Each CF message contains a segment of data, as well as a sequence number that identifies the position of the segment within the larger data block.
The Consecutive Frame (CF) messages are used to transmit the actual data in a large data block. The size of each CF message is typically determined by the underlying CAN bus and network topology. The sequence number included in each CF message is used to ensure that the segments of data are received and reassembled in the correct order.
The primary task of the transport protocol or you can also CAN-TP Protocol is to transfer messages which cannot be transmitted as a single Protocol Data Unit (PDU) due to their length. Messages which contain more data that can be transmitted within a single PDU are segmented by means of the transport protocol and divided into multiple, separate PDUs. This procedure can also be implemented on the data link layer. The segmentation of the message must then be carried out in PDUs of the corresponding data link protocol.
CAN-TP: Consecutive Frame Format
In CF, the 1st byte is used for PCI byte, whereas the MSB 4-bit is defined for the type of frame and the LSB 4-bit is defined for the Sequence number. The sequence number always starts at 1 and increments with each frame sent (like as 1, 2,…, 15, 0, 1,…), with which lost or discarded frames can be detected. Each consecutive frame starts at 0, initially for the first set of the data in the first frame will be considered as 0th data. So the first set of CF(Consecutive frames) starts from “1”. There afterwards when it reaches “15”, it will be started from “0” by resetting the buffer register.
Example ofConsecutive Framein CAN-TP (ISO 15765)
The below table shows an example of how the CAN-TP implemented in CAN Data field using Consecutive Frame.
20
01
02
03
04
05
06
07
CAN Datafield with CAN-TP Using Consecutive Frame(Request: Client –> Server)
PCI Header (0x20):Here 20 is the 1 Byte PCI in CF.
Last 4 MSB is the Frame Type is “2” that defines the Consecutive Frame.
First 4 LSB is the Index of number or counter of blocks data is sends by the Transmitter ECU or node.Here it is “0” for the first consecutive frame.
Payload:Next remaining 7 bytes (01 – 07) are the original data to be sent to the receiver ECU or node.
Flow Control (FC) Frame in CAN-TP (ISO 15765)
The FC frame is the third and final message type in a three-frame sequence used for transmitting large data blocks that exceed the maximum size that can be transmitted in a single CAN message. This message type is used to manage the flow of data between the transmitter and the receiver. It includes information such as the number of consecutive frames that can be transmitted before the receiver must send an acknowledgement message.
The FC frame is sent by the receiver after it receives a CF message from the transmitter. The FC frame includes information such as the sequence number of the last received CF message, the status of the flow control mechanism, and the number of consecutive frames that can be transmitted by the transmitter before the receiver must send an acknowledgement message.
The information in the FC frame is used to manage the flow of data between the transmitter and the receiver to ensure that data is transmitted efficiently and reliably. By using flow control, the receiver can indicate to the transmitter how much data it can receive at any given time, preventing the transmitter from overloading the receiver with data and causing buffer overflow or other issues.
In conclusion, the flow control mechanism of the CAN-TP Protocol is used to configure the sender to match the properties of the receiver (timing, available receive buffer, readiness to receive). The FCF is always sent by the receiver so that to inform about the receiver capability of how the transmitter is going to send the data.
CAN-TP: Flow Control Frame Format
The flow control frame contains 3 bytes which together form a PCI in CAN-TP Protocol.
The first byte begins in the upper four bits with a value of 3, indicates that there is flow control. In the four least significant bits of the first byte shows a Flow Status (FS). Thus the transmitter to the receiver can signal whether a segmented transmission Clear To Send (CTS), Wait or Overflow.
FS = 0: Clear to Send.
FS = 1: Wait.
FS = 2: Overload.
The second byteBSstands for Block Size and shows how many consecutive frames need to be sent. So the Block Size is nothing but the number of Consecutive Frame.
The last byteSTminshows the minimum separation time between consecutive frames to be noticed.
Example ofFlow Control (FC) Framein CAN-TP (ISO 15765)
30
0A
14
AA
AA
AA
AA
AA
CAN Datafield with CAN-TP Using Consecutive Frame(Request: Server –>Client)
PCI Header:The total Flow Control Frame is PCI data and it is 3 byte.
0x30:First 4 MSB is “3” and it defines the Type of Frame that is Flow Control and Last 4 LSB is “0” that defines the Flow Status that is clear to send.
0x0A:This second byte is a1byte Block Size (BS) nothing but the number of Consecutive Frames. So here the receiver is informing to transmitter ECU to send the next0x0Aor10(in decimal) blocks or consecutive frames.
0x14:This third byte is a 1 byteSTminTime period in milli Second. Here it is0x14nothing but the20(in decimal). That means the Transmitter ECU will send each CF in the delay of20milli second to the receiver ECU or node.
An ISO-TP frame is always 8 bytes long and unneeded bytes filled with the padding byte as 0xAA or 0x55.
Working Principle of CAN-TP (ISO 15765)
There are two types of data transfer methods of CAN-TP Protocol: single-frame data transmission and multiple data transmission. The single-frame data transmission (Unacknowledged Unsegmented Data Transfer) and the Multi-frame Datatransmission(Unacknowledged Segmented Data Transfer). In a CAN frame, there is a maximum of 8 data bytes of user data. The data length of the ISO TP message can reach a maximum of 4095 bytes. If an ISO TP message length exceeds the data length of 8 data bytes,the UDSmessage must be segmented.
The CAN-TP protocol works by dividing large data into smaller packets called Transport Protocol Data Units (TPDUs). These TPDUs are then transmitted over the CAN bus, with each TPDU containing a sequence number and checksum to ensure data integrity. The receiver then reassembles the TPDUs into the original data message.
Single Frame Communication
CAN-TP ISO15765 Single Frame Communication
Multi-Frame Communication
CAN-TP for Multi-Frame Communication
Below is an example of how the CAN-TP protocol could work in a client-server communication scenario:
The client sends a large data message to the server, which exceeds the maximum size of a single CAN message.
The client software breaks the large message into smaller TPDUs, each with its own sequence number and checksum.
The client software sends the TPDUs to the server over the CAN bus by using the First Frame (FF).
The server software receives the TPDUs and verifies the sequence numbers and checksums to ensure that all packets have been received and that the data is intact.
Once all TPDUs have been received and verified, the server software reassembles the TPDUs into the original data message.
The server software then sends an Flow Control message back to the client, indicating that how, when, & How much the data should be send by the Transmitter ECU.
The client software receives the acknowledgement message (FC message) and can continue with its operations by using the Consecutive Frames until the next command from Receiver ECU or as per the Flow Control data or information provided by the Transmitter ECU.
Error Handlingin CAN-TP (ISO 15765)
In CAN-TP Protocol, Both the transmitter and receiver monitors the data transmission with a timer. The receiver monitors the time required by the transmitter for sending Consecutive Frames. If this takes a long time, the transmission is aborted and data already transmitted are discarded. Similarly, the transmitter monitors the time for the receiver to send the flow control frame. Again the transmission is aborted and data already transmitted are discarded if a timeout is detected. The maximum waiting time for a frame is one second.
Generally, In addition to timing errors, errors in the message structure must be recognized. If an erroneous PDU is detected the frame is ignored or if a transmission is in progress, canceled it. Possible errors in the message structure are incorrect sequence numbers in consecutive frames, invalid message length, and invalid flow status in the flow control frames or an invalid PDU type. Unexpectedly arriving frames are always ignored, with the exception of a single frame and physically addressed the first frame. Functionally addressed first frame, consecutive frame, and flow control are always ignored.
How to send 100 bytes of data using CAN-TP Protocol?
I hope after learning this tutorial, you will be able to write this answer in below comments. Thank you everyone for giving your time to learn step by step about the CAN-TP Tutorial.
리눅스 기반 시스템의 기본 바이너리 형식인 ELF(Executable and Linkable Format)을 살펴보자.
ELF는 실행 가능한 바이너리 파일, 목적 파일, 공유 라이브러리, 코어 덤프 등에서 사용되는 형식이다. 여기서는 64비트의 실행 가능한 바이너리 파일을 중심으로 살펴보겠다.
A 64-bit ELF binary at a glance
ELF 바이너리는 ELF 파일 헤더, 프로그램 헤더, 그리고 섹션들과 섹션 헤더들로 이루어져 있다.
Executable Header
모든 ELF 파일은 executable header로 시작하며, /usr/include/elf.h에서 확인할 수 있다.
/usr/include/elf.h의Elf64_Ehdr정의
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
Elf64_Ehdr 구조체의 각 원소들의 자료형인 Elf64_Half, Elf64_Word 등은 uint16_t와 uint32_t다.
ELF 파일 헤더의 각 필드
Section Headers
ELF 바이너리의 코드와 데이터는 섹션(section)으로 나누어져 있다.
섹션의 구조는 각 섹션의 내용이 구성된 방식에 따라 다른데, 각 섹션 헤더(section header)에서 그 속성을 찾을 수 있다. 바이너리 내부의 모든 섹션에 대한 헤더 정보는 섹션 헤더 테이블(section header table)에서 찾을 수 있다.
섹션은 링커가 바이너리를 해석할 때 편리한 단위로 나눈 것이다.
링킹이 수반되지 않은 경우에는 섹션 헤더 테이블이 필요하지 않다. 만약 섹션 헤더 테이블 정보가 없다면 e_shoff 필드는 0이다.
바이너리를 실행할 때 바이너리 내부의 코드와 데이터를 세그먼트(segment)라는 논리적인 영역으로 구분한다.
세그먼트는 링크 시 사용되는 섹션과는 달리 실행 시점에 사용된다.
/usr/include/elf.h의Elf64_Shdr구조체 정의
typedef struct {
Elf64_Word sh_name; /* Section name (string tbl index) */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section flags */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Section size in bytes */
Elf64_Word sh_link; /* Link to another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
섹션 헤더의 각 필드
Sections
GNU/Linux 시스템의 ELF 파일들은 대부분 표준적인 섹션 구성으로 이루어져 있다.
$ readelf --sections --wide a.out
There are 29 section headers, starting at offset 0x1168:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 00001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 0002b8 000060 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400318 000318 00003d 00 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400356 000356 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400360 000360 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400380 000380 000018 18 A 5 0 8
[10] .rela.plt RELA 0000000000400398 000398 000030 18 AI 5 24 8
[11] .init PROGBITS 00000000004003c8 0003c8 00001a 00 AX 0 0 4
[12] .plt PROGBITS 00000000004003f0 0003f0 000030 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000400420 000420 000008 00 AX 0 0 8
[14] .text PROGBITS 0000000000400430 000430 000192 00 AX 0 0 16
[15] .fini PROGBITS 00000000004005c4 0005c4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 00000000004005d0 0005d0 000012 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000004005e4 0005e4 000034 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400618 000618 0000f4 00 A 0 0 8
[19] .init_array INIT_ARRAY0000000000600e10 000e10 000008 00 WA 0 0 8
[20] .fini_array FINI_ARRAY0000000000600e18 000e18 000008 00 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e20 000e20 000008 00 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e28 000e28 0001d0 10 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 000ff8 000008 08 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 001000 000028 08 WA 0 0 8
[25] .data PROGBITS 0000000000601028 001028 000010 00 WA 0 0 8
[26] .bss NOBITS 0000000000601038 001038 000008 00 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 001038 000034 01 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00106c 0000fc 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
.init섹션에는 초기화에 필요한 실행 코드가 포함된다. 운영체제의 제어권이 바이너리의 메인 엔트리로 넘어가면 이 섹션의 코드부터 실행된다.
.fini섹션은 메인 프로그램이 완료된 후에 실행된다. .init과 반대로 소멸자 역할을 한다.
.init_array섹션은 일종의 생성자로 사용할 함수에 대한 포인터 배열이 포함된다. 각 함수는 메인 함수가 호출되기 전에 초기화되며 차례로 호출된다. .init_array는 데이터 섹션으로 사용자 정의 생성자에 대한 포인터를 포함해 원하는 만큼 함수 포인터를 포함할 수 있다.
.fini_array섹션은 소멸자에 대한 포인터 배열이 포함된다. .init_array와 유사하다. 이전 버전의 gcc로 생성한 바이너리는 .ctors와 .dtors라고 부른다.
.text섹션에는 메인 함수 코드가 존재한다. 사용자 정의 코드를 포함하기 때문에 SHT_PROGBITS라는 타입으로 설정되어 있다. 또한 실행 가능하지만 쓰기는 불가능해 섹션 플래그는 AX다._start,register_tm_clones,frame_dummy와 같은 여러 표준 함수가 포함된다.
.rodata섹션에는상숫값과 같은 읽기 전용 데이터가 저장된다.
.data섹션은초기화된 변수의 기본값이 저장된다. 이 값은 변경되어야 하므로 쓰기 가능한 영역이다.
.bss섹션은초기화되지 않은 변수들을 위해 예약된 공간이다. BSS는 심벌에 의해 시작되는 블록 영역(Block Strarted by Symbol)이라는 의미로, (심벌) 변수들이 저장될 메모리 블록으로 사용한다.
.rel.*와.rela.*형식의 섹션들은 재배치 과정에서 링커가 활용할 정보를 담고 있다. 모두 SHT_RELA 타입이며 재배치 항목들을 기재한 테이블이다. 테이블의 각 항목은 재배치가 적용돼야 하는 주소와 해당 주소에 연결해야 하는 정보를 저장한다.동적 링킹 단계에서 수행할 동적 재배치만 남아 있다. 다음은 동적 링킹의 가장 일반적인 두 타입이다.
GLOB_DAT(global data): 이 재배치는 재배치는 데이터 심벌의 주소를 계산하고 .got의 오프셋에 연결하는 데 사용된다. 오프셋이 .got 섹션의 시작 주소를 나타낸다.이 엔트리의 오프셋은 해당 함수에서 간접 점프하는 점프 슬롯의 주소(rip로부터의 상대 주소로 계산)다.
JUMP_SLO(jump slots): .got.plt 섹션에 오프셋이 있으며 라이브러리 함수의 주소가 연결될 수있는 슬롯을 나타냅니다. 엔트리는 점프 슬롯(jump slot)이라고 부른다.
.dynamic섹션은 바이너리가 로드될 때 운영체제와 동적 링커에게 일종의 road map을 제시하는 역할을 한다. 일명 태그(tag)라고 하는 Elf64_dyn 구조의 테이블을 포함한다. 태그는 번호로 구분한다.DT_NEEDED 태그는 바이너리와 의존성 관계를 가진 정보를 동적 링커에게 알려준다.게다가 동적 링커의 수행에 필요한 중요한 정보들을 가리키는 역할을 하기도 한다. 예를 들어 동적 문자열 테이블(DT_STRTAB), 동적 심벌 테이블(DT_SYMTAB), .got.plt 섹션(DT_PLTGOT), 동적 재배치 섹션(DT_RELA) 등.
DT_VERNEED와 DT_VERNEEDNUM 태그는 버전 의존성 테이블(version dependency table)의 시작 주소와 엔트리 수를 지정한다.
.shstrtab섹션은 섹션의 이름을 포함하는 문자열 배열이다. 각 이름들을 숫자로 인덱스가 매겨져 있다.
.symtab섹션에는 Elf64_Sym 구조체의 테이블인 심벌 테이블이 포함되어 있다. 각 심벌 테이블은 심벌명을 함수나 변수와 같이 코드나 데이터와 연관시킨다.
.strtab섹션에는 심벌 이름을 포함한 실제 문자열들이 위치한다. 이 문자열들 Elf64_Sym 테이블과 연결된다. 스트립 된 바이너리에는 .symtab과 .strtab 테이블은 전부 삭제된다.
.dynsym섹션과.dynstr섹션은 동적 링킹에 필요한 심벌과 문자열 정보를 담고 있다는 점을 제외하면 .symtab이나 .strtab와 유사하다.정적 심벌 테이블은 섹션 타입이 SHT_SYMTAB이고 동적 심벌 테이블은 SHT_DYNSYM 타입이다.
Program Headers
프로그램 헤더 테이블은 바이너리를 세그먼트의 관점에서 볼 수 있게 해준다.
바이너리를 섹션 관점에서 보는 것은 정적 링킹의 목적만으로 한정하는 것이다.
세그먼트의 관점으로 본다는 것은 운영체제와 동적 링킹 과정을 통해 바이너리가 프로세스의 형태가 되고, 그와 관련된 코드와 데이터들을 어떻게 처리할 것인지를 다룬다는 의미다.
PLT는 <puts@plt-0x10>:와 같이 구성되어 있다. 다음으로 일련의 함수들이 나타나며 모두 라이브러리 함수 각각에 대한 것이고 스택에 값이 1씩 증가되며 push 한다.
PLT를 사용해 동적으로 라이브러리 함수 참조
라이브러리 함수 중 하나인 puts 함수 호출할 때 PLT 구조를 사용해 puts@plt와 같이 호출할 수 있다➊.
PLT 구문은 간접 점프 명령으로 시작하며, .got.plt 섹션에 저장된 주소로 점프한다➋. 지연 바인딩 전에는 이 주소가 puts@plt의 다음 명령어 주소를 가리키고 있다.
스택에 push 하는 값은 PLT 구문에서 함수들을 식별하기 위한 번호다➌.
다음 명령을 통해 default stub으로 이동한다➍.
default stub은 GOT에서 가져온 또 다른 식별자를 스택에 push하고, 동적 링커 부분으로 점프(GOT로 다시 간접 점프)한다➎.
PLT 구문에 의해 스택에 push된 이 식별 정보를 통해 동적 링커는 puts 함수 주소를 찾을 수 있음을 확인하고, puts 함수를 대신 호출한다. 동적 링커는 puts 함수 주소를 찾고 puts@plt와 관련된 GOT 테이블의 항목에 해당 함수의 주소를 연결(기록)한다.
이후 puts@plt를 호출하면 GOT 테이블 내부에는 패치를 통해 실제 puts 함수의 주소가 저장되어 있으므로 PLT 구문으로 점프될 때 (앞서 다룬) 동적 링커 작업을 반복하지 않고 즉시 puts 함수로 점프한다➏.
GOT를 사용하는 이유
PLT 코드에 라이브러리 주소를 바로 쓰지 않고 GOT와 함께 사용하는 이유는 보안상의 문제 때문이다. 바이너리에 취약점이 존재하는 경우 공격자가 PLT overwrite를 이용해 exploit 할 수 있기 때문이다. 물론 GOT 내부의 주소를 변경해 공격하는 GOT overwrite 기법도 발표되긴 하였으나 난이도가 더 높다.
솔라나는 최근 아주 각광받는 레이어1 코인입니다. 일단 gas fee가 이더리움에 비해 압도적으로 저렴하고, PoH라는 새로운 접근 방식을 통해 초당 트랜잭션 수도 아주 뛰어나 무려 초당 5만 개에 달하는 트랜잭션을 처리할 수 있습니다. 기존 비트코인, 이더리움의 초당 10여 개에 불과한 트랜잭션 수와 비교하면 아주 장족의 발전이죠.
트랜잭션의 gas fee도 현재 5000Lamport (0.000000001SOL) 에 불과해 20만원이 1솔라나라고 가정하면 트랜잭션당 소모되는 gas fee가 10원에 불과합니다. 해외 송금 해보신 분들은 아실거에요. 이 수치가 얼마나 대단한 것인지... 순식간에 이체되고, 아주 적은 비용으로 송금이 가능합니다.
솔라나는 당연히 최신 기술인 NFT 민팅과 Smart Contract, Defi 등을 지원합니다. 이번 글에선, 이 모든 기술들의 근간이 될 스마트 컨트랙트를 빌딩하는 방법에 대해서 알아보려고 합니다.
사실 많은 분들이 스마트 컨트랙트가 뭔지 구체적으로 알지 못하고 계시지만, 개념은 엄청 간단합니다. 그냥 자판기를 생각하시면 됩니다. 내가 어떤 일을 하면 어떤 결과가 나올지 미리 정해진 스크립트에 따라 수행되는 것이죠! 인터넷에 수행될 코드를 미리 업로드시켜놓고, 수행 조건이 만족되면 해당 코드가 자동으로 실행되고 연산됩니다. 이게 답니다!
그럼 이게 왜 혁신적이냐고요? 왜냐면, 기존에는 다들 '신뢰'에 기초해서 하던 작업들을 이젠 딱히 상대를 신뢰하지 않아도 되거든요. 예를 들면 제가 어떤 게임을 하고 이겼을 때 1000원을 받는다고 해봅시다. 종전에는 이 게임을 하고 1000원을 받는 행위 자체를 해당 사이트를 '신뢰'하지 않으면 할 수 없었습니다. 게임 사이트가 나에게 돈을 줄지, 주지 않을지 어떻게 아나요?
하지만 블록체인 스마트 컨트랙트는 다릅니다. 스마트 컨트랙트 온체인에서 일어나는 모든 일들은 스크립트를 읽을 수 있는 능력만 있다면 게임사이트가 특정 조건을 만족하면 자동으로 나에게 돈을 준다는 사실을 알 수 있습니다. 그렇지 않다면, 교묘하게 그렇지 않은 코드를 삽입했다는 사실도 알아낼 수 있죠. 현재는 일부 프로그래머만이 해당 사실을 검증할 수 있겠습니다만, 적어도 모두에게 공개되어있는 코드에 대놓고 스캠 코드를 삽입할 수는 없겠죠?
스마트 컨트랙트에 대한 대략적인 설명입니다. 먼저 프로그램된 컨트랙트 (계약)을 올려놓고, 이벤트가 진행이 되면 해당 이벤트가 실행이 됩니다. 그 실행 내역에 따라서 토큰 (뭐... 돈이죠) 이 오가는 것을 보증할 수 있습니다.
borsh는Binary Object Representation Serializer for Hashing 의 약어입니다. 바이너리로 풀어진 오브젝트를 시리얼라이징하거나 디시리얼라이징할 때 쓰이는 일종의 라이브러리라고 생각하시면 됩니다. node에도 동일하게 존재하는 라이브러리입니다.
entrypoint!() 에서 실제 스마트 컨트랙트가 선언되는데, 이 안 내용을 한번 살펴보시죠.
account.iter()는 map과 비슷한겁니다. account를 iterable하게 돌려서 account를 하나씩 살펴본다는 의미입니다. 그래서 next_account_info로 account를 가지고 온 후, 해당 account_info에 데이터를 set해줄 준비를 할 겁니다. 데이터를 저장해야 그 데이터로 무언가 로직을 돌릴테니, 이번 예제에선 데이터를 저장하는 로직만 해볼거에요. 그러니까... UPDATE, INSERT, SELECT 세 기능을 구현해 볼 겁니다.
여기가 핵심 로직인데요, account의 data를 가지고 와서, instruction_data를 해당 data에 set 해주는 부분입니다. instruction_data는 우리가 직접 넣어주는 데이터가 될 거에요. 그 다음 다시 저장해주고 Ok() 해주면, 해당 노드 블럭에 스마트 컨트랙트 내용이 저장됩니다.
`Failed to read program keypair at '${PROGRAM_KEYPAIR_PATH}' due to error: ${errMsg}. Program may need to be deployed with \`solana program deploy dist/program/helloworld.so\``,
이제 실제로 트랜잭션을 발생시킵니다. TransactionInstruction의 input이 아까 러스트랑 동일하죠? 맞습니다, 러스트에서 받는 인자들입니다. 따라서 data에 저렇게 넣으면 2라는 숫자가 들어갑니다. 중요한건, Buffer로 선언된 ㄹㅇ 바이너리 숫자가 들어간다는 사실입니다.
이렇게 솔라나로 아주 간단한 스마트 컨트랙트를 만들어 봤습니다. 노드를 통해 통신할 수 있으니, 아마 노드 웹서버를 통해 통신할 수도 있겠지요.
이런 스마트 컨트랙트를 openDB, open Logic으로도 사용할 수 있습니다. 누구나 볼 수 있는 DB, 그리고 누구나 볼 수 있는 로직이기 때문에 절대로 속일 수 없다는 특징을 가지게 된 것이죠. 누구나 검증할 수 있게 된 것입니다. 물론, 스마트컨트랙트를 누구나 발생시킬 수 있기 때문에, 실제 구현상에선 greetedPubKey의 SEED를 적절하게 잘 숨겨서 보관하는 것도 중요합니다. 해당 seed가 곧 DB의 암호가 되는 셈이니까요.
이 글이 한국어 사용자의 스마트 컨트랙트 이해도 향상에 조금이나마 도움이 되었으면 좋겠습니다! 기회가 된다면 다음 글로 곧 스마트컨트랙트의 실제 활용도 한번 보여드리겠습니다.
추가)
사실 프로그램을 만들 때엔 anchor를 이용하시는 것이 좋습니다. 해당 글도 읽어보세요!