Arm32 execute & debugging env

음.. arm 바이너리에 대한 문제를 풀때마다 아주 환경때문에 곤혹을 치뤄서 이번에는 확실히 정리를 좀 해갈려고 한다.

일단, 오해가 있는게 “arm 바이너리를 실행할때 arm docker나 vm이 맞다” 라는 개념인데 내가 이번에 이걸 구현할려고 라즈베리파이도 해보고 arm32 ubuntu docker , arm32 devian docker 등 다 해보았지만, 결국 문제는 디버깅 환경 구축에 생긴다. 즉, 해당 환경을 만드는 건 쉽다. 근데 pwntools , pwndbg등을 붙일때 문제가 생긴다. 애초에 python을 다운로드 할때도 arm32 등의 버그가 정말 많았다.

그래서 이번에는 확실히 정리해가야 한다.

qemu-user로 하는게 맞다.

C
(pwn) amemoyoi ~$ pwd
/proc/sys/fs/binfmt_misc
(pwn) amemoyoi ~$ ls
python3.12  qemu-aarch64  qemu-arm  qemu-loongarch64  qemu-mips64  qemu-mips64el  qemu-ppc64le  qemu-riscv64  qemu-s390x  register  status

기존 서버에는 다음과 같이 모두 해놓았다. 즉, 이미 모든 아키텍쳐의 바이너리를 읽을 수 있게 설계를 해놓았다.

이제 문제는 해당 바이너리를 읽을 로더와 libc인데 이건 당연히 서버에서 구현하지 못한다 (충돌 , 오류 )

따라서, 우리는 일반적인 효율이 좋은 22.04 우툰투 docker를 만들고 해당 환경에 pwntools , dbg를 다운로드 받은뒤에 거기서 라이브러리와 로더를 다운로드하면 된다.

따라서 다음과 같이

C
#!/bin/bash

IMAGE_NAME=amemoyoi-ctf
CONTAINER_NAME=my_container
DOCKER_USER=$(grep -E '^ENV user ' Dockerfile | awk '{print $3}')
DOCKER_PORT=$(grep -E '^ENV chall_port ' Dockerfile | awk '{print $3}')
if [ -z "$DOCKER_PORT" ]; then
  echo "[*] chall_port not found in Dockerfile. Using default: 10001"
  DOCKER_PORT=10001
fi


#DOCKER_USER=deploy
# 1. Docker 이미지 빌드
docker build . -t $IMAGE_NAME

# 2. 기존 컨테이너 삭제 (있다면)
docker stop $CONTAINER_NAME 2>/dev/null
docker rm $CONTAINER_NAME 2>/dev/null

# 3. 새로운 컨테이너 실행 (권한 부여 및 네트워크 설정)
docker run -d -t --privileged -p $DOCKER_PORT:$DOCKER_PORT  --name=$CONTAINER_NAME $IMAGE_NAME

# docker run -d -t --privileged --name=$CONTAINER_NAME $IMAGE_NAME

# 컨테이너 내부 기본 libc 백업 (gdb에 의해 달라질 수 있으므로)
docker exec -it -u root $CONTAINER_NAME bash -c "
    cp /lib/x86_64-linux-gnu/libc.so.6 /home/$DOCKER_USER/libc-backup.so.6 &&
    chown $DOCKER_USER:$DOCKER_USER /home/$DOCKER_USER/libc-backup.so.6
"

# 4. 컨테이너 내 필수 패키지 설치 (GDB, Pwntools, Pwndbg)
docker exec -it -u root $CONTAINER_NAME bash -c "
    apt-get update && apt-get install -y \
    gdb \
    python3 python3-pip python3-dev python3-setuptools python3-wheel python3-venv \
    git wget curl vim socat tmux file &&
    python3 -m venv /opt/pwnenv &&                
    source /opt/pwnenv/bin/activate &&
    pip3 install --no-cache-dir --upgrade pip &&
    pip3 install --no-cache-dir pwntools &&

   
    pip3 install poetry==1.6.1 &&
    export PATH=\"/root/.local/bin:\$PATH\" &&

    git clone https://github.com/pwndbg/pwndbg /opt/pwndbg &&
    cd /opt/pwndbg && 
    git checkout 2024.08.29 &&
    ./setup.sh && 
    echo 'set disassembly-flavor att' >> ~/.gdbinit
"

# 참고로 source /opt/pwnenv/bin/activate  을 쳐야 가상환경에 들어 갈 수 있음 -> 정상 사용가능


# 위에서 제외된 명단 오류등으로 인한
# echo 'source /opt/pwndbg/gdbinit.py' >> ~/.gdbinit


docker exec -it -u root $CONTAINER_NAME bash -c "
    mkdir -p /home/$DOCKER_USER/.vscode-server &&
    chown -R $DOCKER_USER:$DOCKER_USER /home/$DOCKER_USER/.vscode-server
"

docker exec -it -u root $CONTAINER_NAME bash -c "
    touch /home/$DOCKER_USER/exploit.py 
    chown -R $DOCKER_USER:$DOCKER_USER /home/$DOCKER_USER/exploit.py"
	    

# 6. 사용자 셸에 pwn alias 등록 및 즉시 적용
docker exec -it -u root $CONTAINER_NAME bash -c "
    echo 'alias pwn=\"source /opt/pwnenv/bin/activate\"' >> /root/.bashrc &&
    source ~/.bashrc
"

# 패키지 설치 (줄바꿈 이스케이프, update 포함)
docker exec -it -u root "$CONTAINER_NAME" bash -lc '
  set -e
  apt-get update
  apt-get install -y \
    qemu-user-static \
    gcc-arm-linux-gnueabi \
    gcc-arm-linux-gnueabihf
'

# 스크립트 작성 (HEREDOC, 마지막에 && 제거)
docker exec -it -u root "$CONTAINER_NAME" bash -lc "
  cat > /home/$DOCKER_USER/librarysetting.sh <<'EOF'
# if armel binary
ln -s /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib || true
ln -s /usr/arm-linux-gnueabi/lib/libc.so.6         /lib || true
# if armhf binary
ln -s /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib || true
ln -s /usr/arm-linux-gnueabihf/lib/libc.so.6           /lib || true
EOF
  chown $DOCKER_USER:$DOCKER_USER /home/$DOCKER_USER/librarysetting.sh
"



# 5. 컨테이너에 root 사용자로 접속
docker exec -it -u root $CONTAINER_NAME bash

일반적인 dockerset 파일이지만 핵심은

C
# 패키지 설치 (줄바꿈 이스케이프, update 포함)
docker exec -it -u root "$CONTAINER_NAME" bash -lc '
  set -e
  apt-get update
  apt-get install -y \
    qemu-user-static \
    gcc-arm-linux-gnueabi \
    gcc-arm-linux-gnueabihf
'

다음과 같이 arm32용 라이브러리 ,로더를 설치한다는 것이다. 그리고 바이너리가 armel 이냐 armhf 냐에 따라서

C
ln -s /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib || true
ln -s /usr/arm-linux-gnueabi/lib/libc.so.6         /lib || true

을 하거나 (armel)

C
ln -s /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib || true
ln -s /usr/arm-linux-gnueabihf/lib/libc.so.6           /lib || true

을 하면 된다.( armhf )

그러면 이제 실행하는 것은 어차피 본 서버의 커널에서 하니깐,

C
./arm_training-last

해석이 가능하므로 굳이 qemu-arm 를 안붙여서 다음과 같이 하면 된다.

pwntools & dbg

exploit_arm.py 파일을 “~/pwn_env/Dockerfile_template/arm_env 에 만들어놓았다. 그대로 docker를 올리면 된다.

핵심 부분을 말하자면,

C
elif is_local == 3 :
    
    # p = process(fname)
    p = process(fname , env={ 'LD_PRELOAD' : './libc.so.6' , 'QEMU_GDB' : '1234'  } )

디버깅 부분이 다음과 같이 'QEMU_GDB' : '1234' 가 추가 되었다.

이를 통해서

다른 터미널에서

gdb-multiarch 를 실행한뒤

target remote localhost:1234

을 입력하면 붙일 수 있다.