codeengn.com/

 

CodeEngn.com [코드엔진]

코드엔진은 국내 리버스엔지니어링 정보공유를 위해 2007년 부터 리버스엔지니어링 컨퍼런스 및 비공개 워크숍을 현업 실무자들과 함께 운영하고 있습니다. 리버스엔지니어링이라는 하나의 큰

codeengn.com

코드엔진 문제입니다.

리버싱 문제인데... 요즘 리버싱 공부를 해보고 싶어져서 풀어보려는 중입니다...

일 벌리기만 하는거같은데 ㅇㅁㅇ;;


바로 문제풀이 시작하죠

 

GetDriveTypeA의 리턴값이 무엇이 되어야 하냐고 하네요.

리턴값을 말하는 것을 보니 저건 함수인 것을 유추할 수 있습니다.

 

올리 디버거로 바로 넘어갑시다!

코드는 이게 끝입니다.

코드를 보아하니 00401018에서 GetDriveTypeA 함수가 실행되는 것 같습니다.

그래서 저부분에 브레이크를 걸어주었고,

또 00401024가 CMP, 즉 비교를 해주는 부분이기 때문에 알아보기 위해서 브레이크를 걸어주었습니다.

 

실행을 해보죠

이렇게 나오는것을 동일하다고 인식하도록 해야하는 것이겠죠?

 

함수 실행 전까지 왔습니다. 이제 이 함수를 직접 들어가서 살펴봐도 좋지만, 리턴값을 찾는 것이므로 이 함수가 실행된 뒤의 레지스터 변화를 찾아보겠습니다.

 

실행 전과 실행 후 레지스터입니다.

EAX값이 바뀌었고, ESP도 살짝 바뀌고, EIP는 당연히 바뀌는거고, 그 이외에는 뭐 큰 변화가 없습니다.

ESP도 상관 없으니까 EAX만 변했다고 볼 수 있겠네요.

 

그리고 이제 한줄씩 실행시켜서 CMP까지 이동해봅시다.

 

CMP까지 왔습니다. EAX와 ESI를 비교하네요. 현재 레지스터 값을 보니

EAX는 2가 줄었고 ESI가 3이 증가했습니다.

그리고 이 값이 같아야하나봅니다!

그러면 EAX의 값은 401003에서 2가 증가한 401005면 되는게 아니냐? 라고 물어보시면 맞습니다!

맞는데...

저기서 ESI는 0041003이 아닌 그냥 3이라서 EAX는 00401005가 아닌 그냥 5가 되는것이 맞습니다.

이게... 401000이 image base라서 그렇다... 라고 다른 블로그에 나오는데

맞긴 한데 이건 잘 모르겠습니다. 다른 디버거에서는 그냥 3이 나오는 경우가 있더라구요...

 

그래서 EAX의 값이 5가 되면 문제는 풀리게 됩니다.

짠! (이것도 EAX에서 401005를 입력해줘야합니다...)

'Hacking-기초 > [REV] CodeEngn.com' 카테고리의 다른 글

Basic RCE L04  (0) 2020.09.04
Basic RCE L03  (0) 2020.09.04
Basic RCE L02  (0) 2020.09.04

reversing.kr/challenge.php

 

Reversing.Kr

Copyright © 2012-2020 Gogil All Right Reserved.

reversing.kr

Reversing.kr 문제입니다.

직접 풀어보시고 확인하길 바랄게요!

 


후우 리버싱 공부는 문제로 시작해야지!

라는 마인드로 일단 이 문제는 해설을 보면서 감을 잡겠습니다 ㅎㅎ

 

프로그램을 먼저 실행시켜봅시다!

비밀번호를 치는 칸이 나오고 여기에 아무거나 치고 확인을 누르면?

 

Incorrect Password가 뜨네요.

 

이제 IDA로 이 파일을 분석해봅시다.

이중에 과연 우리가 필요로하는 부분이 어디에 있을까요...

 

Incorrect Password라는 문장을 통해 검색을 해봐야겠습니다!

 

IDA에서는 Alt+T가 검색입니다.

이렇게 검색을 해주면?

 

여기에 이렇게 있다는 것을 알 수 있고, 이 코드는 sub_401080에 연관되어있다는 것도 알 수 있네요!

 

저 함수 파일에 들어가서 F5를 눌러주게 되면?

이렇게 뭔가 있어보이는 코드가 나옵니다.

 

조건을 만족하면 위의 MessageBoxA를, 만족하지 못하면  아래의 MessageBoxA를 실행하게 되는데,

저 aIncorrectPassw를 더블클릭 해보았더니 Incorrect Password 문장이 있는 곳으로 이동했습니다.

따라서 우리는 if문 밖의 함수를 실행하는것이 목표라는 것을 알 수 있습니다.

 

보니까 여러가지 조건이 있네요 ㅇㅁㅇ...

디버깅으로 확인하는게 더 편할거같은데..!

 

제 아이다는 디버깅이 안되므로... 올리디버거로 디버깅 해보겠습니다 ㅎㅎ

 

대충 처음부터 브레이크포인트 잡아서 실행을 하면?

시작해보겠슴다...

현재 위치한 회색 부분에서 프로그램이 실행되어서

이게 실행됩니다.

 

일단 대충 aaaaaaaaa를 집어넣고 해볼게요

 

보니까 저기 빨간부분부터 CMP가 조금씩 보이길래 브레이크포인트를 또 잡아줬습니다.

아마 저기부터 if문이겠거니 하면서 진행해보져.

CMP 부분까지 왔습니다. ESP+5에서부터 한바이트가 0x61인지 비교하고있습니다.

ESP+5는?

여길 보니까 입력한 0x61이 9개가 보이는걸 보니 ESP+4부터가 입력값의 시작인 것 같습니다.

 

ESP+5라는건 두번째 자리가 a라는거네요.

일단 우연히 맞았으니까 계속 해봅시다.

 

이부분을 CMP가 안보이길래 그냥 지나쳤더니 Incorrect로 이어지네요..ㅎㅎ

보니까 TEST가 있었군요...

 

Push가 두번 되었길래 스택을 보니

이렇게 아스키값이 저렇게 나왔습니다.

아마도 저 aaaaaaa값은 두번째 값이 a인 이후의 a들인것 같습니다. 즉 세번째 문자부터 표현된 것이죠.

이게 맞는지 궁금하신 분들은 aaaaaaaaa를 넣는게 아닌 abcdefgh를 넣어보시면 되겠습니다!

아무튼 저 두개가 push가 되어서...

 

Call Easy_Cra.00401150이라는 함수로 이동해보면

이렇게 비교하는 부분이 있습니다.

당연히 저 EDI와 ESI는 저 아스키문장 두개를 의미하고요.

맞다면 Zero Flag가 1이 되는 것 같습니다.

Zero Flag값을 미리 1로 바꾸고 저 부분을 실행해줬더니...

0으로 바뀐 것을 알 수 있습니다..

 

제가 왜 Zero Flag를 잘 챙기냐 하면...

JNZ가 위의 사진에서 있었는데 이는 Zero Flag에 따라 JMP할지의 여부를 결정하기 때문입니다.

따라서 _a5y________까지는 알 수 있게 되었습니다.

 

이제 다시 해서 지금까지 나온 값을 집어넣고 계속 진행해보겠습니다.

 

 

다시 뭔갈 또 비교하는것 같습니다.

보니까

이 두 문장을 비교하는거같은데

대충 봐서 그냥 5y뒤에 들어갈 것 같습니다...ㅎ

네 진심으로요.

 

한번 다시 다른 값을 집어넣고 다시 시작해볼게요.

aa5ybcdefghij를 집어넣어보겠습니다.

네 맞습니다.

뒤에 들어갈 9자리가 저거였군요.

 

계속 진행해봅시다.

 

aa5yR3versing을 입력해서 넘어왔더니 마지막 CMP가 기다리고 있었습니다. ESP+4의 값이 0x45여야한다는데요. 0x45의 값은 E이기 때문에 최종적인 답은 Ea5yR3versing인 것 같습니다!

 

넵 제대로 넘어왔습니다!

 

이제 문제를 풀었으니 저 값을 Reversing.kr의 Auth란에 넣어주면 점수를 얻을 수 있게 됩니다!

 

끄읕!

'Hacking-기초 > Reversing' 카테고리의 다른 글

x64 기초  (0) 2020.05.18
리버스 엔지니어링이란  (0) 2020.05.18

nesquitto.tistory.com/95?category=423447

 

x64 기초

본 포스팅은 DreamHack 사이트의 x64 기초 강의 내용을 요약한 것입니다. 자세한 내용은 사이트에 들어가셔서 보시기 바랍니다. https://dreamhack.io/ 해커들의 놀이터, DreamHack 해킹과 보안에 대한 공부��

nesquitto.tistory.com

이 포스팅에서 어셈블리 언어 부분만 빼서 가져왔습니다.

 


Movement

mov a, b
b를 a에 옮깁니다.

lea a, b
b의 주소를 a에 저장합니다.

 

Arithmetic

-Unary

inc a
++a와 같은 의미입니다.

dec a
--a와 같은 의미입니다.

neg a
-a와 같이 부호를 바꿉니다.

not a
~a와 같이 비트를 반전합니다.

-Binary

add a, b
a에 b의 값을 더합니다.

sub a, b
a에 b의 값을 뺍니다.

imul a, b
a에 b의 값을 곱합니다.

and a, b
a와 b를 and연산한 결과를 a에 저장합니다.

or a, b
a와 b를 or 연산한 결과를 a에 저장합니다.

xor a, b
a와 b를 xor 연산한 결과를 a에 저장합니다.

-Shift

shl a, b
a<<b의 비트 연산을 수행합니다.
a를 b만큼 왼쪽으로 이동합니다.

shr a, b
a>>b의 비트 연산을 수행합니다.
a를 b만큼 오른쪽으로 이동합니다.

sal a, b
a<<b의 연산을 수행하며, 부호가 보전됩니다.(최상위비트는 바뀌지 않습니다.)

sar a, b
a>>b의 연산을 수행하며, 부호가 보전됩니다.(최상위비트는 바뀌지 않습니다.)

 

Conditional

test a, b
and 논리연산을 하지만 결과값을 저장하지 않습니다.
연산 결과가 음수라면 SF가 1이 되고, 연산 결과가 0이라면 ZF가 1이 됩니다.

cmp a, b
sub 논리연산을 하지만 결과값을 저장하지 않습니다.
a, b가 같을 때에는 ZF가 1이 되고, 같지 않을 때에는 0이 됩니다.
a<b일 때에는 CF가 1이 되고, a>b일 때에는 0이 됩니다.
jmp a
a의 주소로 점프합니다.

jcc(je, jne, jg, jge, jl, jle, ja, jb, js, jns) a
특정 조건이 만족되면 a로 점프합니다.

 

Stack

프로그램이 동작하는 함수 내에서는 지역 변수를 사용할 때가 많습니다.

이 지역 변수는 함수 내에서 사용되고 이후 함수가 종료되면 더 이상 사용되지 않기 때문에 쉽게 쓰고 지울 수 있는 스택에 데이터를 저장합니다. 그리고 이 스택은 메모리에 저장이 되는 것이죠. 

intel 아키텍쳐에서의 스택은 새로운 데이터가 추가될 때마다 더 낮은 메모리 주소에 쌓이게 됩니다. 따라서 데이터가 증가할수록 저장되는 주소는 점점 작아지게 됩니다.

 

push rdi
스택에 새로운 데이터를 집어넣습니다.
sub rsp, 8
mov [rsp], rdi
위의 어셈블리어와 동일하게 동작합니다.
데이터가 들어갈 공간을 만들어주고 그 안에 복사하는 것입니다.

pop rdi
스택의 맨 위에 있는 데이터를 뺍니다.
mov rdi, [rsp]
add rsp, 8
위의 어셈블리어와 동일하게 동작합니다.

 

여기까지! 입니다!

nesquitto.tistory.com/95?category=423447

기본적인 내용은 위의 링크에 정리되어 있으며, 여기도 비슷한 내용이 있습니다.

 

위의 링크에서  operand는 함수와 인자의 관계에서 인자에 속한다고 설명했습니다.

이 인자는 어떻게 사용하는지 간단하게 설명하려고 합니다.

 

add rax 0x1234

rax의 값에 0x1234를 더함

add rax rbx

rax의 값에 rbx의 값을 더함

add rax [0x1234]

rax의 값에 0x1234 주소의 값을 더함

 

기본적으로 위와 같이 사용됩니다.

 

Operand로 올 수 있는 값은 다음과 같습니다.

1. 상수값: 일반적인 상수(0x1337, 0xbeef)의 값이 사용됩니다.

2. 레지스터: 레지스터(rax, rbx...)에 들어있는 값이 사용됩니다.

3. 주소값: 레지스터에 저장된 메모리 주소에 들어있는 값이 사용됩니다. C언어의 포인터 개념과 비슷합니다.

레지스터 -> 메모리 주소 -> 참조값  과 같은 형식입니다.

mov   [rcx],rax                   ; *rcx = rax
-mov의 결과로 rax에 들어있는 값을 rcx 레지스터에 들어있는 주소값의 메모리에 저장합니다.

mov   byte ptr [rcx],al           ; *rcx = al
-mov의 결과로 al에 들어있는 값을 rcx 레지스터에 들어있는 주소값의 메모리에 1바이트만 저장합니다.
-al은 ax레지스터가 16bit일 때 뒷부분 8bit를 가리킵니다. 앞부분 8bit는 ah라고 표현합니다.

mov   dword ptr [rbp-1Ch],eax      
-mov의 결과로 eax에 들어있는 값을 rbp에서 1Ch만큼 떨어진 위치의 메모리 주소에 4바이트만 저장합니다.

mov   byte ptr [rdi+rcx*4+3],0xFF
-mov의 결과로 rdi+rcx*4+3의 위치의 메모리에 1바이트 값인 0xff가 저장됩니다.

 

끝!

'Hacking-기초 > 알쓸신잡_해킹' 카테고리의 다른 글

Intel에서의 스택 구조  (0) 2020.09.07
Assembly 문법? 조금 정리  (0) 2020.09.02
리틀 엔디안, 빅 엔디안  (0) 2020.08.31

메모리에 데이터를 저장할 때 사용하는 최소 단위는 비트(bit)입니다. 하지만 비트 단위로 데이터를 관리하게 된다면 매우 복잡하고, 불편할 가능성이 높습니다. 그래서 일반적인 연산 장치나 메모리는 바이트(Byte) 단위로 그룹화하여 관리하게 됩니다. 

따라서 데이터를 저장할 때 사용하는 최소 단위는 비트(bit)지만, 프로그램이 처리될 때 사용되는 최소 단위는 바이트(Byte)라고 생각할 수 있습니다.

 

메모리와 같은 1차원 공간에 여러개의 연속된 대상을 배열하는 방법을 '엔디안' 이라고 합니다.

엔디안에는 여러가지 방법이 있을 수 있습니다.

쉽게 우리가 사용하는 언어(한국어)를 기준으로 설명하자면 한국어는 왼쪽에서 오른쪽으로, 위에서 아래로 읽습니다. 하지만 모든 언어가 다 이와같이 읽는것은 아니죠?, 아랍어같은 경우는 오른쪽에서 왼쪽으로, 위에서 아래로 읽는것과 같이 언어의 종류마다 문자를 배열하는 방식을 다를 수 있습니다. 이런 방법들을 모두 '엔디안' 이라고 부릅니다.

 

컴퓨터에 저장되는 데이터는 바로 이 엔디안 방식에 따라 다르게 배열되어 저장됩니다.

위에서 프로그램이 처리될 때 사용되는 최소 단위는 바이트라고 생각할 수 있다고 했습니다. 따라서 프로그램 상에서 데이터를 배열할 때에는 바이트 단위로 배열하며 이를 바이트 순서(Byte Order)라고 합니다.

 

이런 방식에는 일반적으로 빅 엔디안, 리틀 엔디안, 미들 엔디안으로 3가지가 존재합니다. 뭐 다른 방법들도 얼마든지 있겠지만요....

 

빅 엔디안은 큰 단위가 앞에 나오는 방식입니다.

일반적으로 0x12345678과 같은 16진수 정수로 나타내는 방식과 동일합니다. 큰 단위가 앞에 나오기 때문에 프로그램 상에서 사용할 때에도 12345678과 같은 순서로 사용하게 됩니다.

 

리틀 엔디안은 작은 단위가 앞에 나오는 방식입니다.

일반적으로 0x12345678과 같은 16진수 정수로 나타낼 때 바이트 단위로 작은 단위부터 표현하게 됩니다. 포너블을 공부하거나, 문제를 풀 때 GDB상에서 데이터를 입력할 때(인텔 포맷일 경우) 거꾸로 입력하는 방식이 바로 이 방식입니다.

1바이트는 8비트이며, 16진수는 한 자리수가 4비트이기 때문에 바이트 단위로 정렬하게 되면 16진수 두자리씩 묶어서 거꾸로 배열하는 방식입니다. 프로그램 상에서 사용할 때 78563412와 같은 순서로 사용하게 됩니다.

 

미들 엔디안은 빅 엔디안과 리틀 엔디안에 비해서 중요도가 높지 않습니다. 이 게시글에서는 넘어가도록 하겠으며, 나중에 미들 엔디안의 개념이 필요할 때 정리해서 올리도록 하겠습니다.

+ Recent posts