반응형

Learning-eBPF의 pdf에 기인한 내용(chapter3)이며 개인적으로 공부하며 작성되었기에 틀린 내용이 있다면 말씀해주시면 감사하겠습니다.

 

https://cilium.isovalent.com/hubfs/Learning-eBPF%20-%20Full%20book.pdf

1. compile with clang

hello.bpf.o: %.o: %.c	
 clang \	
 -target bpf \	
 -I/usr/include/$(shell uname -m)-linux-gnu \	
 -g \	
 -O2 -c $< -o $@

 

line1: .o파일을 .c파일로부터 만듦(% : 와일드 카드)

line2: LLVM 컴파일러인 Clang을 사용하여 소스 코드를 컴파일

line3: Clang이 BPF 목표 아키텍처를 컴파일하도록 지정하는 옵션

line4: 헤더 파일을 조회할 디렉터리 지정

line5: 디버그 정보를 포함하여 컴파일

line6: O2 - 최적화 레벨,   $< - 현재 목표의 종속 항목 중 첫번째 (해당 코드에서는 .c파일) , $@ - 현재 목표 (해당 코드에서는 .c파일)  => .c를 compile하여 .o파일로 생성

https://hyeonbell.tistory.com/174

 

2. llvm-objdump -S hello.bpf.o

- eBPF IR파악을 위한 dump

https://github.com/chubin/cheat.sheets/blob/master/sheets/objdump

 

result

hello.bpf.o:    file format elf64-bpf

Disassembly of section xdp:

0000000000000000 <hello>:
;     bpf_printk("Hello World %d", counter);
       0:       18 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r6 = 0 ll
       2:       61 63 00 00 00 00 00 00 r3 = *(u32 *)(r6 + 0)
       3:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
       5:       b7 02 00 00 0f 00 00 00 r2 = 15
       6:       85 00 00 00 06 00 00 00 call 6
;     counter++; 
       7:       61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0)
       8:       07 01 00 00 01 00 00 00 r1 += 1
       9:       63 16 00 00 00 00 00 00 *(u32 *)(r6 + 0) = r1
;     return XDP_PASS;
      10:       b7 00 00 00 02 00 00 00 r0 = 2
      11:       95 00 00 00 00 00 00 00 exit

IR opcode에 대한 reference

https://github.com/iovisor/bpf-docs/blob/master/eBPF.md

 

3. load program with "bpftool" (require root previlige)

sudo bpftool prog load hello.bpf.o /sys/fs/bpf/hello

 

how to check

 ls /sys/fs/bpf

 

4. list bpf programs

sudo bpftool prog list

 

get output with prettified JSON

sudo bpftool prog show id 101 --pretty

output

{
    "id": 101,
    "type": "xdp",
    "name": "hello",
    "tag": "d35b94b4c0c10efb",
    "gpl_compatible": true,
    "loaded_at": 1706073152,
    "uid": 0,
    "bytes_xlated": 96,
    "jited": true,
    "bytes_jited": 64,
    "bytes_memlock": 4096,
    "map_ids": [4,5
    ],
    "btf_id": 159
}

-program ID: 101

-xdp event를 통한 attach

-program name: hello

-identifier

-GPL compatible license
-program이 로딩된 시점의 timestamp - 부팅이후의 시점부터

-userID 0 (root) 로 load

-eBPF bytecode로 변환된 프로그램의 크기

-JIT compiled 및 컴파일 이후 machine code의 크기

-BPF map ID들

- -g flag로 컴파일 했을시 포함되는 정보이며, 해당 BTF id 는 단일 블록의 BTF 정보를 포함

 

program ID는 load와 unload를 할때마다 바뀌지만 tag는 유지된다 -> tag가 고유한 것은 아니다.

같은 name으로 program을 생성할 수 있으며 여러 인스턴스를 가질 수 있지만, ID와 pinned path는 고유하다. 

그리고 아래의 instruction들을 통해 bpf program의 정보를 파악할 수 있다. (네가지 모두 동일한 output)

• bpftool prog show id 540
• bpftool prog show name hello
• bpftool prog show tag d35b94b4c0c10efb
• bpftool prog show pinned /sys/fs/bpf/hello

 

Translated Byte code확인

sudo bpftool prog dump xlated name hello

output

int hello(struct xdp_md * ctx):
; bpf_printk("Hello World %d", counter);
   0: (18) r6 = map[id:4][0]+0
   2: (61) r3 = *(u32 *)(r6 +0)
   3: (18) r1 = map[id:5][0]+0
   5: (b7) r2 = 15
   6: (85) call bpf_trace_printk#-66752
; counter++; 
   7: (61) r1 = *(u32 *)(r6 +0)
   8: (07) r1 += 1
   9: (63) *(u32 *)(r6 +0) = r1
; return XDP_PASS;
  10: (b7) r0 = 2
  11: (95) exit

상기의 output은 verifier를 통과한 이후의 bytecode이다.

 

해당 bytecode는 저수준언어와 유사하게 생겼지만 machine code 급은 아니다(?) 그 사이 어딘가 애매한 상태이다.

eBPF는 JIT compiler를 통해 eBPF bytecode를 machine code로 변환한다.

-> 퍼포먼스를 위해서!

 

JITed code를 assembly어의 형태로!

sudo bpftool prog dump jited name hello

output

int hello(struct xdp_md * ctx):
bpf_prog_d35b94b4c0c10efb_hello:
; bpf_printk("Hello World %d", counter);
   0:   nopl    (%rax,%rax)
   5:   nop
   7:   pushq   %rbp
   8:   movq    %rsp, %rbp
   b:   pushq   %rbx
   c:   movabsq $-99790339293184, %rbx
  16:   movl    (%rbx), %edx
  19:   movabsq $-124354625743600, %rdi
  23:   movl    $15, %esi
  28:   callq   0xffffffffce103260
; counter++; 
  2d:   movl    (%rbx), %edi
  30:   addq    $1, %rdi
  34:   movl    %edi, (%rbx)
; return XDP_PASS;
  37:   movl    $2, %eax
  3c:   popq    %rbx
  3d:   leave
  3e:   retq
  3f:   int3

 

ps. JIT compilation을 활용하기 위해서는 CONFIG_BPF_JIT옵션이 켜져있는 kernel이어야 한다.

상기의 IR은 내가 보던 pdf와 달랐는데, 느낌상으로는 내 pc와 예제의 환경이 상이하여 발생한 차이인듯하다.

 

이제 program이 load된 것을 보았으니 event와 attach하고 trigger해보자.

attach할 때, 유의할 부분이 type이다.

위에서 우린 xdp event에 attach하겠다고 하였으므로 bpftool을 사용하여 eBPF 프로그램에 xdp event를 attach해보자.

sudo bpftool net attach xdp id 101 dev enp5s0

뒷쪽의 id와 device는 적절하게 맞추어 주자!

현재 본인의 경우..

ifconfig로 확인하자~

cf. lo는 loopback interface이다. 해당 interface는 traffic을 내 machine에 보내기 위해 사용

enp5s0

-ref-

https://unix.stackexchange.com/questions/134483/why-is-my-ethernet-interface-called-enp0s10-instead-of-eth0

https://blog.naver.com/snrndi121/220999725150

이후에 network interface가 추가된 것을 볼 수 있다.

sudo bpftool net list

output( enp5s0 interface가 attach된 상태)

xdp:
enp5s0(2) generic id 101

tc:

flow_dissector:

netfilter:

 

ip link명령어로도 확인가능

현 상태에서는 network packet 수신에 대해 output을 매번 trace utput으로 저장해야 되며 아래의 명령을 통해 확인가능하다.

cat /sys/kernel/debug/tracing/trace_pipe

요로코롬

아래의 명령어로도 tracelog를 확인할 수 있다.

sudo bpftool prog tracelog

kernel에 load된 map들을 보고싶다면!

그리고 생성된 program에 대해 section도 관찰할 수 있다.

sudo bpftool map dump name hello.bss

output(.bss section(초기화되지 않은 전역변수)의 전역변수를 볼 수 있다.)

[{
        "value": {
            ".bss": [{
                    "counter": 124555
                }
            ]
        }
    }
]

.rodata section

sudo bpftool map dump name hello.rodata

output

[{
        "value": {
            ".rodata": [{
                    "hello.____fmt": "Hello World %d"
                }
            ]
        }
    }
]

 

attach했던 program을 해제하고 싶다면!

sudo bpftool net detach xdp dev enp5s0

이후에 net list를 입력해보면 비어있는 것을 확인할 수 있다.

다음으로 unload하고 싶다면!

sudo rm /sys/fs/bpf/hello
sudo bpftool prog show name hello

output이 아무것도 없다면 된다. 더이상 load된 program이 없기 때문~

 

마저 진행해보자!

 

tail call에 대해서부터 시작이다.

간단한 예제코드를 먼저 보자.

#include <linux/bpf.h>
#include <bpf_helpers.h>

static __attribute((noinline)) int get_opcode(struct bpf_raw_tracepoint_args *ctx) {
    return ctx->args[1];
}

SEC("raw_tp/")
int hello(struct bpf_raw_tracepoint_args *ctx) {
    int opcode = get_opcode(ctx);
    bpf_printk("Syscall: %d", opcode);
    return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";

- opcode를 추출하는 간단한 함수인데 compiler에서 저 함수만을 지속적으로 처리할 경우, 인라인 처리 하는 것을 방지하기 위해 noinline 속성으로 정의한다.

 

- opcode를 추출하여 printk로 출력

 

이제 이전 글에서 수행했던 것 처럼 파일을 load해보자.

sudo bpftool prog load hello-func.bpf.o /sys/fs/bpf/hello
sudo bpftool prog list name hello

output

166: raw_tracepoint  name hello  tag 3d9eb0c23d4ab186  gpl
        loaded_at 2024-01-25T08:36:33+0900  uid 0
        xlated 80B  jited 62B  memlock 4096B  map_ids 37
        btf_id 207

 

이제 get_opcode() 함수를 보기 위해 eBPF 바이트코드 살펴보자.

sudo bpftool prog dump xlated name hello

output

int hello(struct bpf_raw_tracepoint_args * ctx):
; int opcode = get_opcode(ctx);
   0: (85) call pc+7#bpf_prog_cbacc90865b1b9a5_get_opcode
; bpf_printk("Syscall: %d", opcode);
   1: (18) r1 = map[id:37][0]+0
   3: (b7) r2 = 12
   4: (bf) r3 = r0
   5: (85) call bpf_trace_printk#-66752
; return 0;
   6: (b7) r0 = 0
   7: (95) exit
int get_opcode(struct bpf_raw_tracepoint_args * ctx):
; return ctx->args[1];
   8: (79) r0 = *(u64 *)(r1 +8)
; return ctx->args[1];
   9: (95) exit

- hello()이 get_opcode()를 호출하며 eBPF IR기준으로 offset 0은 0x85이며 이는 "Function call"을 의미한다.

이후에, offset 1에 있는 다음 명령어를 실행하는 대신, 7개의 IR을 건너뛰어(pc+7에 의해) offset 8을 향하게 된다.

 

- 해당 위치에 get_opcode()이 존재한다.

 

하지만 위와 같은 tail call은 stack size가 512byte로 제한적인 eBPF 에서는 많이 활용되기 어렵다.

 

이상으로 chapter3의 내용을 마친다.

 

반응형

'ebpf' 카테고리의 다른 글

CO-RE, BTF, and Libbpf  (0) 2024.02.11
The bpf() System Call  (0) 2024.01.25
eBPF programming  (0) 2024.01.09
Troubleshooting - learning eBPF  (0) 2024.01.01
JIT for BPF  (0) 2023.09.21

반응형

https://cilium.isovalent.com/hubfs/Learning-eBPF%20-%20Full%20book.pdf


상기의 내용을 기반으로 아래의 링크에서 학습하던 와중 생긴 에러에 대한 처리

https://github.com/lizrice/learning-ebpf

 

GitHub - lizrice/learning-ebpf: Learning eBPF, published by O'Reilly - out now! Here's where you'll find a VM config for the exa

Learning eBPF, published by O'Reilly - out now! Here's where you'll find a VM config for the examples, and more - GitHub - lizrice/learning-ebpf: Learning eBPF, published by O'Reill...

github.com

 

처음에 계속 libbpf/src를 시작할떼 make build를 수행해 주었으나 path를 잡지 못해 에러를 처리하지 못하고 있었다.

그렇기에 기본으로 주어지는 makefile을 뜯어보니 참조하는 디렉터리가 달랐기에 수정하였다.

makefile의 -I플래그로 -I/home/username/bpf/learning-ebpf/libbpf/src \ 추가

 -I 플래그 : 해당 디렉터리를 포함하도록 지정

수정된 makefile

TARGETS = hello hello-func

all: $(TARGETS)
.PHONY: all

$(TARGETS): %: %.bpf.o

%.bpf.o: %.bpf.c
        clang \
            -target bpf \
                -I/home/hackin/bpf/learning-ebpf/libbpf/src \
                -I/usr/include/$(shell uname -m)-linux-gnu \
                -g \
            -O2 -o $@ -c $<

clean: 
        - rm *.bpf.o
        - rm -f /sys/fs/bpf/hello 
        - rm -f /sys/fs/bpf/hello-func

또한, 헤더파일들이 위치한 디렉터리는 src디렉터리 이기에 c code들도 모두 수정해 주었다.

각 소스코드의 <bpf/bpf_helper.h> -> <bpf_helper.h> 로 변경

이제 정상적으로 생성된 것을 확인할 수 있다.

 

그리고 정상적으로 이제 덤프가 떠지는 것까지 확인!

 

추후에 상기에 언급해뒀던 pdf 내용들도 좀 정리해야겠다....

 

 

옛날에는 이걸 못해서 학습이 막혔었는데.. 이걸 왜 못했을까...

반응형

'ebpf' 카테고리의 다른 글

Anatomy of an eBPF Program  (0) 2024.01.24
eBPF programming  (0) 2024.01.09
JIT for BPF  (0) 2023.09.21
JIT(Just In Time) Compiler - Verifier - SAT/SMT solver  (0) 2023.09.17
Buzzer(eBPF fuzzer) build  (0) 2023.08.11
반응형

이번 문제는 register의 구조에 대해 먼저 파악해야한다.

<1>

기존에 문제들은 레지스터의 모든 비트를 다 활용하여 문제를 풀어왔다.

하지만 이번엔 한 레지스터의 특정 byte만을 사용하여 풀기 위해 <1>의 사진을 참고해야 한다.

우리가 사용하던 rax는 MSB ~ LSB 즉, Most Significate Bit ~ Lesat Significant Bit로 이루어져 있다.

쉽게 말하면 가장 높은 위치의 데이터 ~ 가장 낮은 위치의 데이터이다.

 

위와 같이 이름이 붙은 이유는 말그대로 가장 중요하기에 덜  변화가 생기고, 덜 중요하기에 많은 변화가 생기기 때문이다.

가령 MSB는 일반적으로 부호형에 쓰인다.

부호가 자주 바뀐다고 생각해보면 값의 변화에 매우 치명적이기에 덜 바뀌어야한다.

 

LSB의 경우, checksum 등으로 쓰인다.

자주바뀌어도 MSB보다 덜 무방하기 때문이다.

 

RAX를 뜯어 보면 하위 32 bit는 EAX, EAX의 하위 16bit는 AX, AX의 상위 8bit는 ah, 하위 8bit는 al로 이루어져 있다.

 

이를 인지하고 문제를 보자.

mov instruction하나만으로 값을 세팅하라고 한다.

간단히 다음과 같이 짜주면 풀린다.

.global _start
.section .text

_start:
        mov $0x42, %ah

 

다음문제를 보자.

이번엔 이전 문제에서 배웠던 mod연산과 register에 대해 인지하고 있다는 전제가 필요하며 추가적인 정보를 알려준다.

요런식으로 나와있는데 한번 읽어보고 뭔소리인가 싶었다 처음엔...

해석해보자면,  만약 y가 2의 거듭제곱이라면 (y = 2^n), x % y의 결과는 x의 하위 n 비트와 같다는 말이다. 즉, x % 256이면 결과는 x의 하위 8비트와 같으며, x % 65536이면 결과는 x의 하위 16비트와 같다.

 

더 자세히 설명해보자면, 이는 2의 거듭제곱으로 나눌 때 발생하는 특이한 상황인데, 예를 들어 "256(2^8)로 나누는 경우, 나머지는 항상 8비트 이하의 값이 된다"는 것은 나머지는 항상 나누는 수보다 크지 않다는 개념을 적용하여 생각해보면 매우 쉽다. 

 

상기의 연산 방법이 일반적인 나머지 연산에 비해 빠르다고 한다.

일반적으로 나머지 연산은 단순한 나눗셈보다 더 많은 하드웨어 리소스를 소비한다고 하는데... 아마 이전에 Assembly crash에서 볼 수 있듯 레지스터의 사용이나 명령어의 수가 늘어나기에 나오는 말 같다. 하지만, 상기의 방법은 간단한 명령만으로 처리되기에 효율적이며 성능상의 이점을 가지고 있다.

 

위의 내용을 인지하고, 문제를 접근해보자.

mov연산만을 사용하라고 한다.

처음엔 대체 어떻게 mov 만을 사용할지 감이 안왔는데, 우선 아래의 사진을 봐야한다.

https://qpakzk.gitbooks.io/operating-systems-development/content/book/chap03.html

좀 더 세부적인 레지스터의 사진이다.

 

상기에서 언급한대로 자신의 하위비트보다 클 수 없으므로 하위비트가 나머지가 된다. 그렇기에 그 하위 비트의 값을 세팅해주면된다.

 

그렇기에 코드는 아래와 같다.

.global _start
.section .text

_start:
        mov %dil, %al
        mov %si, %bx

 

 

mov %dil, %al : rdi의 하위 8비트를 al 레지스터로 복사한다. %dil은 rdi 레지스터의 하위 8비트를 가리키는 것이다

 

mov %si, %bx : rsi의 하위 16비트를 bx레지스터로 복사한다. %si는 rsi레지스터의 하위 16비트를 가리키는 것이다. 

 

 

다음 문제를 위해 인지할 점은 1byte는 8bit라는 것만 알고 있으면 된다.

그리고서 양쪽으로 shift해준 후에 RAX에 삽입해주면 풀리는 문제이다.

 

코드는 아래와 같다.

.global _start
.section .text

_start:
        mov %rdi, %rax
        shl $24, %rax
        shr $56, %rax

그러면 쨘!

 

반응형

'pwn.college' 카테고리의 다른 글

Introduction to ARM - level 1 ~  (0) 2024.03.03
Read file in Linux  (0) 2023.12.04
HTTP 요청 보내기  (0) 2023.12.03

반응형

 

 

1. curl

GET 요청 

curl http://example.com

 

HOST헤더 넣기( -H옵션) + url인코딩하여 보낼 경우에 Host가 들어가야한다.

curl -H "Host: example.com" http://your-url

원하는 경로로 보내고 싶을때,

curl https://example.com/path

JSON형식으로 데이터 전달(-d옵션: data)

curl -X POST -H "Content-Type: application/json" -d '{"a": "value"}' http://example.com/path

 

POST요청

curl -X POST -d "param1=value1&param2=value2" http://example.com/resource

 

쿠키알아내기

curl -v --cookie "USER_TOKEN=Yes" http://127.0.0.1

쿠키정보를 file에 저장하고, redirect를 따라가는 명령

curl http://localhost -v --cookie-jar file -L

 

참고하기에 좋은 사이트

-https://reqbin.com/req/c-bjcj04uw/curl-send-cookies-example

 

2. nc

GET요청(Host헤더 포함)

cf. 이 때, 이유는 모르겠으나, \r\n\r\n 이게 꼭 두번 들어가야지 전달된다.

-> 우선 \r\n은 어떠한 데이터의 끝맺음을 의미한다. \r\n이 도착하지 않는다면 서버는 계속 요청 대기 상태가 된다.

경로를 지정하고 싶다면, / 이후에 추가

echo -e "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" | nc example.com 80

json 형식

// vim req
GET / HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Content-Length: 길이{부터 }까지(괄호 포함)
Content-Type: application/json
User-Agent: curl/7.2

{"a": "value"}

호출

cat req  | nc localhost 80 -v

redirect

echo -e "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: keep-alive\r\nUser-Agent: curl/7.2\r\n\r\n" | nc localhost 80

 

POST요청

printf "POST /path HTTP/1.1\r\nHost: example.com\r\nContent-Length: 11\r\n\r\nHello World" | nc example.com 80

 

쿠키 알아내기

GET /setcookie HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
User-Agent: curl/7.2

 

3. python request 모듈

GET요청(Host헤더 포함)

import requests

url = "https://example.com"
headers = {"Host": "example.com"}

response = requests.get(url, headers=headers)

print(response.text)

80포트로 써야하는 경우

import requests

url = "http://example.com:80"
response = requests.get(url)

print(response.text)

POST요청

import requests

url = "https://example.com"
data = {"key1": "value1", "key2": "value2"}

response = requests.post(url, data=data)

print(response.text)

 

반응형

'pwn.college' 카테고리의 다른 글

Introduction to ARM - level 1 ~  (0) 2024.03.03
Assembly crash level 7~9  (0) 2023.12.14
Read file in Linux  (0) 2023.12.04
반응형

매번 정적 분석만을 주로하다가 동적 분석의 중요성을 느끼게 되었다.

해당 파일은 stripped 되어 있어서 정적 분석을 수행할 수가 없다.

 

그러므로 ida에서 서버 파일을 리눅스로 옮겨 실행시킨 후, 로컬에서 동적 디버깅을 수행해보았다.

 

 

문제의 discription에서

이렇게 나와있으므로 적절한 위치에 breakpoint를 걸어준 후, rax를 확인하면 된다.

사진의 아래쪽에 플래그가 나와있다.

이후에 이어서 바로 check_return_value문제도 풀어봤다.

해당 문제역시 상기의 문제처럼 동적 디버깅으로 풀어야한다.

이전에 수행했던대로 똑같이 파일을 올려서 디버깅을 진행해봤다.

같은 방식으로 플래그를 구하면된다.

원래 ghidra  실습용으로 낸 문제인듯 한데, IDA가 익숙해서 IDA로 우선 진행했고, 추후에 ghidra로도 수행해봐야겠다.

반응형

'Reverse Engineering' 카테고리의 다른 글

Static Analysis vs Dynamic Analysis  (0) 2022.11.01

+ Recent posts