블로그 이미지
Nehoy
경기대학교 / kknock

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Notice

Tag

2020. 2. 22. 18:07 Hack/리버싱

verifier.zip
0.00MB

정해진 문법에 맞추어 코드를 입력하면 실행해주는 프로그램이다.

 

입력한 코드를 a_interp를 통해 시뮬레이션을 돌려보고 에러가 없으면, interp를 통해 실제 구동을 한다.

 

플래그는 위와 같이 Print 구문을 통해 얻을 수 있다. 실제 구동에서 출력할 변수 값이 음수이면 플래그를 출력하는데, 시뮬레이션 도중 출력할 변수 값이 음수이면 print domain error을 발생시킨다.

 

IfElse, While, Cond, Num, Random 구문이 위 Print 구문처럼 시뮬레이션 코드와 실제 구동 코드가 다르다. 예시로 IfElse 구문을 살펴보자.

 

실제 구동 코드에서는 조건 값에 따라 실행하는 코드가 달라지고 그에 따라 변수의 값이 변하는데, 시뮬레이션의 경우 조건문에 사용되는 변수를 참이 되는 범위와 거짓이 되는 범위로 나누고 변수가 변할 수 있는 값의 범위를 확인한다. 간단히 하면, 시뮬레이션의 경우 변수가 변할 수 있는 모든 범위를 확인한다고 할 수 있다.

 

가능한 모든 범위를 확인하는 IfElse와 달리, While 구문의 경우 반복문을 5번만 실행해본다. 여기서 env_join은 코드를 실행한 후 변수의 값을 갱신하는 것이고, env_widen은 코드를 실행한 후 변수의 변경 추이에 따라 값이 증가하면 inf, 값이 감소하면 -inf로 설정하는 것이다.

132줄을 통해 -inf, inf로 변한 값들 중 조건문에서 사용되는 변수의 경우 134줄의 cond.a_interp를 거치면서 -inf, inf값이 조건문의 범위에 맞게 변하지만, 그 이외의 변수들은 계속 -inf 혹은 inf값을 갖고 있는다.

 

그래서 위와 같은 코드에서 a가 음수가 될 수 없음에도 시뮬레이션 코드에서는 -inf가 되기 때문에 domain error가 발생한다.

우회 방법은 env_widen을 할 때 출력할 변수의 값을 감소시키지 않는 것이다.

따라서, 반복이 4번을 넘을 때부터 감소를 하면 되는데, 아래와 같이 안되는 방법도 있으니 주의해야 한다.

 

 위와 같은 방법은 136번줄에서 a의 범위가 -1~0이 되기 때문에 안된다.

 

위 코드의 경우 136번줄에서 a의 범위가 0~1이 되기 때문에 우회가 가능하다.

Flag: CODEGATE2020{4bstr4ct_1nt3rpr3tat10n_f0r_54f3_3v41uat10n}

 

 

 

'Hack > 리버싱' 카테고리의 다른 글

Codegate2020 Preliminary: Halffeed  (0) 2020.02.22
Windows Exception  (0) 2020.01.18
[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
posted by Nehoy
2020. 2. 22. 18:03 Hack/리버싱

Halffeed.zip
0.00MB

입력 값을 암호화해주는 프로그램이다.

 

3Execute 메뉴에 복호화 했을 때 cat flag가 나오는 암호문과 태그 값을 입력하면 플래그를 얻을 수 있다.

 

하지만 서버에서는 cat flag가 포함된 평문을 암호화할 수 없다.

 

암호에 사용되는 키 값 또한 서버에 파일로 존재하기 때문에 알 수 없다.

평문을 암호화하는 코드는 위와 같다. 코드를 살펴보면 T와 평문을 xor 연산하여 암호문을 생성하고 있다. 여기서 평문에 00000000000000000000000000000000를 넣으면 T leak할 수 있다는 걸 알 수 있다.

 

00000000000000000000000000000000 ;cat flag;000000

00000000000000000000000000000000 3B63617420666C61673B303030303030

우리는 평문을 위와 같이 넣었을 때 나오는 암호문과 태그 값을 원하기 때문에 다음과 같이 평문을 넣어 블록 연산에 사용되는 T를 구한다. (블록이 두개인 이유는 이후에 서술할 collision 때문이다.)

00000000000000000000000000000000 00000000000000000000000000000000

T2_1 = ec8e2e8ec0319fbaa0e7d4d819006e28

T2_2 = e6048fd38996e7cb945879d068a612bd

T를 알아냄으로써 우리는 우리가 원하는 암호문을 다음과 같이 얻을 수 있다.

CHIPER_TEXT = ec8e2e8ec0319fbaa0e7d4d819006e28 dd67eea7a9f08baaf36349e05896228d

 

이제 태그 값을 알아내야 하는데 위 코드를 참고하면, 우리가 원하는 태그 값이 다음과 같다는 것을 알 수 있다.

하지만 AES_Encrypt에 사용되는 키 값을 모르기 때문에 태그 값을 바로 구하지는 못하고, 우리가 알 수 있는 ANS 값을 이용해 태그 값을 구해야 한다.

 

P2 = 00000000000000000000000000000000 3B63617420666C61673B303030303030

P1 = ?

P1를 입력했을 때의 ANS값과 P2를 입력했을 때의 ANS값을 같게 하고, P1을 서버에서 암호화하면 우리가 원하는 태그 값을 얻을 수 있다는 게 핵심이다.

여기서 블록을 두개 사용하는 이유가 나오는데, 첫번째 블록이 달라지면 두번째 블록에서 사용되는 T_2값이 달라지기 때문에 두개의 블록을 사용하는 것이다.

 

P1_1 = 11111111111111111111111111111111

P1의 첫번째 블록에 사용할 값은 위와 같다. T1_2를 알아내기 위해 다음과 같은 값을 서버에서 암호화한다.

11111111111111111111111111111111 00000000000000000000000000000000

T1_2 = 00928a21ee7ae49344e7336d666bdba6

 

이제 어떤 값을 P1_2에 넣어야 ANS가 나오는지 계산한다.

P1 = 11111111111111111111111111111111 ddf56486478a6f39673B303030303030

 

P1을 서버에 전송함으로써, 우리가 원하는 태그 값을 얻을 수 있다.

TAG = 04e46eb38401a4727f15967a29784f17

 

위에서 얻은 CHIPER값과 TAG값을 Execute의 입력 값으로 넣으면 플래그를 얻을 수 있다.

Flag: CODEGATE2020{F33D1NG_0N1Y_H4LF_BL0CK_W1TH_BL0CK_C1PH3R}

'Hack > 리버싱' 카테고리의 다른 글

Codegate2020 Preliminary: Verifier  (0) 2020.02.22
Windows Exception  (0) 2020.01.18
[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
posted by Nehoy
2020. 1. 18. 14:26 Hack/리버싱

1. SEH (Structured Exception Handling)

  • SEHWindows OS에서 지원하는 예외처리 방식으로 __try, __except, __finally 문법으로 이루어진다. 안티디버깅 용도로도 사용된다.
  • __try 블록 내에서 예외가 발생하면 __except 문에 해당되는 예외 핸들러(Exception Handler)를 호출하여 예외를 처리한다.
  • __except 블록 안에서 GetExceptionCode 함수를 이용해서 예외 종류를 확인하고 세 가지 선택(exception filter)을 할 수 있다. __except 문의 예외 핸들러 실행 (EXCEPTION_CONTINUE_EXECUTION), 예외의 원인을 해결하고 예외 발생한 곳에서 이어서 실행, 다음 예외 핸들러 찾기 (EXCEPTION _CONTINUE_SEARCH)
  • __try 영역 밖을 벗어나게 되면 __finally 영역 내의 코드(Termination Handler)를 실행한다. 스레드 강제 종료, 프로세스 종료를 제외한 모든 제어문(return, break )을 통해 영역 밖을 나갈 때도 실행된다.

2. VEH (Vectored Exception Handler)

  • VEHSEH를 응용프로그램 범위로 확장한 것으로 프로그램에서 예외가 발생하면 제일 먼저 호출된다. AddVectoredExceptionHandler라는 함수로 추가 할 수 있다.

3. VCH (Vectored Continue Handler)

  •  VCHVEHSEH에서 EXCEPTION_CONTINUE_EXECUTE가 반환되었을 때 호출 된다.

4. UEH (Unhandled Exception Handler)

  • UEH는 모든 예외 핸들러에서 처리되지 못한 예외를 처리하는 곳이다.

5. Unwind

  • Local Unwindtry-finally 구문에서 try 블록 내부에 return, goto 등의 제어문이 있을 때 발생한다. finally 블록을 실행 후 이동을 해야 하기 때문에 임시 변수를 생성해서 상태를 저장하는 것이다.
  • Global Unwind는 예외 핸들러의 exception filterEXCEPTION_EXECUTE_HANDLER인 경우 발생하는데, try 블록 내부에서 예외가 발생하면 finally보다 except를 먼저 찾아 처리하기 때문에 예외를 처리하기 전에 제일 안에 있는 finally부터 처리해 except 블록까지 내려오는 행위다.

'Hack > 리버싱' 카테고리의 다른 글

Codegate2020 Preliminary: Verifier  (0) 2020.02.22
Codegate2020 Preliminary: Halffeed  (0) 2020.02.22
[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
posted by Nehoy
2017. 9. 20. 16:09 Hack/리버싱

tablez.tar.gz


 입력 받은 문자열을 get_tbl_entry() 함수로 변환 시킨다. 변환 시킨 문자열이 주어진 문자열과 일치하면 성공한다.


 get_tbl_entry() 함수는 매개변수로 넘어온 문자가 table[2*i]와 같으면 table[2*i + 1]을 반환한다.


table 자료는 위와 같이 data 영역에 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
void main() {
    char map[510= { 0, }, ans[37];
    char buffer[38= {0, };
    int i, j;
 
    // Read File
    FILE *file = fopen("map""rb");
    fread(map, sizeof(char), 510, file);
    fclose(file);
 
    file = fopen("target""rb");
    fread(ans, sizeof(char), 37, file);
    fclose(file);
 
    // Compare
    for (i = 0; i < 37; i++) {
        for (j = 0; j <= 0xFE; j++) {
            if (map[j * 2 + 1== ans[i]) {
                buffer[i] = map[j * 2];
                break;
            }
        }
    }
 
    printf("%s\n", buffer);
}
 
cs

 table 자료(map 파일)와 목적 문자열(target 파일)을 가져온다음, 역으로 추적하는 프로그램을 만들었다.



후기.

trans_tbl과 byte_201281 사이에 있는 1byte랑 끝에 있는 1byte를 빼고 map 파일을 만들어서 고생했다..

'Hack > 리버싱' 카테고리의 다른 글

Codegate2020 Preliminary: Halffeed  (0) 2020.02.22
Windows Exception  (0) 2020.01.18
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] imagePrc  (0) 2016.05.30
posted by Nehoy
2017. 5. 20. 13:39 Hack/리버싱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import md5
import string, itertools
 
def encode(input_string):
    h = md5.md5(input_string[:4]).hexdigest()
    table = {
        'a'1,
        'b'2,
        'c'3,
        'd'4,
        'e'5,
        'f'6,
        'g'7,
        'h'8,
        'i'9,
        'j'0
    }
    out = ""
    prev = ""
    stage1 = []
    stage2 = []
    stage3 = ""
    passbyte = -1
    for ch in input_string:
        if ch in table.keys():
            stage1.append(table[ch])
        else:
            stage1.append(ch)
 
    for index, ch in enumerate(stage1):
        if len(stage1) <= index+1:
            if index != passbyte:
                stage2.append(ch)
            break
 
        if passbyte != -1 and passbyte == index:
            continue
 
        if type(ch) == int and type(stage1[index+1])==int:
            tmp = ch << 4
            tmp |= stage1[index+1]
            stage2.append(tmp)
            passbyte = index+1
        else:
            stage2.append(ch)
    
    for ch in stage2:
        if type(ch) == int:
            stage3 += chr(ch)
        else:
            stage3 += ch
    
    for index, ch in enumerate(stage3):
        if index >= len(h):
            choice = 0
        else:
            choice = index
 
        out += chr(ord(ch) ^ ord(h[choice]))
 
    return out
 
def main() :
    encoded = "~u/\x15mK\x11N`[^\x13E2JKj0K;3^D3\x18\x15g\xbc\\Gb\x14T\x19E"
    assume_input_list = []
 
    res = itertools.product(string.printable, repeat=4)
    for tmp in res :
        assume_input = ''.join(tmp)
        
        assume_out = encode(assume_input)
        if assume_out[:3== encoded[:3] :
            assume_input_list.append(assume_input)
            print assume_input
 
main()


출력 가능한 문자 4개로 조합한 문자열 중에 encode된 값이 주어진 encoded와 3자리가 같으면 출력하는 프로그램이다.
3자리만 비교하는 이유는 stage1~stage2 때문이다.
4번째 문자가 table내에 존재하는 문자라 숫자로 변했을 경우에 5번째 문자가 존재하지 않아 문자로 변환되지 않는다.
이 경우에는 4번째 문자를 비교하지 못하기 때문에 3자리만 비교한다.

이 프로그램의 결과는 다음과 같다.


이제 이 값들을 decode하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import md5
import sys
import string
 
table = {
    1'a',
    2'b',
    3'c',
    4'd',
    5'e',
    6'f',
    7'g',
    8'h',
    9'i',
    0'j'
}
 
def decode(assume_out) :
    global table
 
    res = ''
    for ch in assume_out :
        if (string.printable.find(ch) == -1 or ch == '\t'and ch != ' ':
            ch_ascii = ord(ch)
            temp1 = ch_ascii & 0xF
            temp2 = (ch_ascii >> 4& 0xF
            
            if temp1 < 10 and temp2 < 10 :
                res += table[temp2]
                res += table[temp1]
            else :
                res += ch
        else :
            res += ch
 
    return res
 
def main(file_name) :
    file = open(file_name, 'r')
    assume_out_list = file.readlines()
    file.close()
 
    encoded = "~u/\x15mK\x11N`[^\x13E2JKj0K;3^D3\x18\x15g\xbc\\Gb\x14T\x19E"
 
    for assume_out in assume_out_list :
        h = md5.md5(assume_out[:4]).hexdigest()
 
        i = 0
        out = ''
        for ch in encoded :
            if i >= len(h):
                choice = 0
            else:
                choice = i
            i += 1
 
            out += chr(ord(ch) ^ ord(h[choice]))
 
        res = decode(out)
 
        print assume_out + " : " + res
 
if len(sys.argv) > 1 :
    main(sys.argv[1])
 
cs

decode하는 방법을 나열하면 다음과 같다.
1. 입력된 문자열을 md5 인코딩한 값이랑 주어진 encoded 값을 xor 연산
2. xor 연산된 값 중 출력 불가능한 문자랑 \t인 문자를 stage1 ~ stage2 역연산

위 프로그램의 결과는 아래 사진과 같다.

Flag 문자열을 decode한 값이 정답으로 보인다.
중간중간에 들어가있는 j를 없애주면 flag가 된다.
FLag is {compress_is_always_helpful!}


'Hack > 리버싱' 카테고리의 다른 글

Windows Exception  (0) 2020.01.18
[CSAW 2017] tablez  (0) 2017.09.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Music Player  (0) 2016.05.18
posted by Nehoy
2016. 12. 20. 10:07 Hack/리버싱

위 사진과 같이 Drive의 타입 값을 가져와서 CD-ROM인지 확인하는 프로그램이다.


401024-26을 보면 EAX와 ESI를 비교하여 분기하게 된다. 


401026에서의 EAX와 ESI의 값은 위와 같게 된다.



EAX와 ESI의 값이 같으면 되는 것이므로 MOV EAX, ESI로 어셈블리어를 변경하여 항상 분기하게 한다.



배운점.

Dump Debugged Process를 할 때에 OEP가 현재 실행위치로 변경되므로 꼭 신경써서 본 OEP로 바꿔주자.

'Hack > 리버싱' 카테고리의 다른 글

[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Music Player  (0) 2016.05.18
[Reversing.kr] Easy Unpack  (0) 2016.05.12
posted by Nehoy
2016. 5. 30. 18:53 Hack/리버싱

 프로그램을 실행한 후 Check 버튼을 누르면 Wrong이라는 메세지가 뜨는 것을 볼 수 있다.



 따라서 Wrong 문자열이 있는 곳을 찾아가보면, 그 위쪽에 이미지를 불러오고 비교하는 부분(004013A8)을 찾을 수가 있다.


 프로그램을 실행시킨 후 이 부분에서 ds:[eax+ecx]가 가리키는 곳으로 가보면 이미지 파일로 의심되는 바이너리가 있다.


 이 부분을 복사한 후 적절한 bmp 헤더를 붙이면, 다음 이미지가 나오게 된다.


Auth : GOT

'Hack > 리버싱' 카테고리의 다른 글

[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] Music Player  (0) 2016.05.18
[Reversing.kr] Easy Unpack  (0) 2016.05.12
posted by Nehoy
2016. 5. 18. 23:09 Hack/리버싱








 일단 "1분 미리~"를 띄우는 메세지 박스 함수의 주소를 찾는다.

 (rtcMsgBox함수의 주소가 0x0040104C인 것을 볼수있다!)


 이 함수 주소에 하드웨어 브레이크 포인트를 사용하여, 어느 부분에서 이 함수를 통해 "1분 미리~"라는 문자열을 출력하는지 찾아낸다.


 브레이크 포인트 때문에 프로그램이 중지되었을 때, Call Stack을 통해서 함수를 호출한 코드의 주소를 참조한다.

 

 그러면 이렇게 rtcMsgBox함수가 사용된 것을 볼 수 있는데, 이 메세지는 1분이 지날 때에만 출력이 되므로 분기가 있을거란걸 예상할 수 있으므로, 이 함수를 호출하는 코드 부분을 감싸고 있는 분기문을 찾으면

 

 이 곳이 되는데 여기가 First Check Routine인 것을 알 수가 있다!


 일단, 1분이 지나도 이 코드 부분이 실행되지 않도록 jl을 jmp로 바꿔준다.


//


하지만 이렇게만 했을 경우에는 

이런 오류 메세지가 뜨는 것을 볼 수가 있다.


나는 이 때 First Check 코드 부분을 제외하고도 1분이 지났을 때에만 실행이 되는 코드 부분이 있을거라고 생각했고, First Check Routine 밑 코드들 중 의심스러운 모든 분기문에 break point를 사용하여 Second Check Routine와 Third Check Routine를 찾아내었다.



이 두 routine 중 에러 메세지가 출력 되기 전에 실행되는 코드를 break point로 찾아내면, 그 부분이 third check routine인 것을 알 수 있는데


 이 부분 또한 아까와 같이 jge를 jmp로 바꾸어준다.



 이제 다시 실행해보면 1분이 넘어갔을 때, 프로그램의 타이틀이 Password is LIstenCare로 변한 것을 볼 수 있다! 끝!

'Hack > 리버싱' 카테고리의 다른 글

[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Easy Unpack  (0) 2016.05.12
posted by Nehoy
2016. 5. 12. 23:37 Hack/리버싱

 일단, Unpack을 하기 위해서는 popad(popal)부분을 찾고 그 다음 jmp구문을 찾아야한다.


맨 처음 디버거를 통해 Unpack파일을 열고 F9키를 눌러 실행을 하면 다음 화면이 나온다.


 이 화면에서 위로 조금 올려보면 다음과 같이 0040A021 부분에 popal이 있는 것을 확인할 수 있다.


 popad(popal)부분을 찾았으므로 jmp구문을 찾으면 되는데, 이를 찾기 위하여 아래로 쭉 내리다보면 다음 화면과 같이 jmp구문이 있는 것을 볼 수 있다.


 이 jmp하는 00401150주소가 unpacking된 코드가 있는 부분이고 우리가 찾던 OEP(Original Entry Point)인 것이다.


따라서, Auth에 00401150을 입력하면 된다.


느낀점.

 디버거로 프로그램을 연 다음 꼭 F9키를 눌러주자.

'Hack > 리버싱' 카테고리의 다른 글

[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Music Player  (0) 2016.05.18
posted by Nehoy
prev 1 next