2018고등해커-AcchiKocchi 라이트업

Featured image

AcchiKocchi Write UP

400P/author:이든솔/0 solved

???:ㄴㄷㅆ


안녕하세요. 고등해커 출제자 P4B에요.

다들 고등해커는 즐겁게 즐기셨나요? 저는 대회 당일 여러분들의 인증기록을 살펴보면서 마음속으로 열심히 응원하고 있었답니다.

이번 대회는 출제하면서 어떤 문제가 초보자가 도전할만하면서 고-수 분들도 즐길수있을까 생각하면서 출제를 했습니다만, 다들 바쁘셨던건지 아니면 빠른 손절을 하셨던건지 많이 안풀린거같아서 슬픕니다.

리버싱 400은 고-수 분들이 닷넷 리버싱을 쉽게 생각하시는 분들이 많길래 ConfuserEx 프로텍터를 써서 좀 어렵게 내봤습니다. 많이 못풀줄은 알았지만 아무도 안푸셨길래 제가 풀이를 적어봅니다.

ConfuserEx가 걸려있는 .Net 앱의 분석법을 자세히 설명해드릴께요.


1. 리버싱안하고 정보 모으기

일단 켜보면 아래같은 화면이 나오는데

시작화면

뭔가를 입력 해보면 아래같이 된다.

플래그인증실패화면

문제 파일의 작성언어는 Detect It Easy나 PEID같은 도구로 얻을 수 있다.

DIE결과

결과를 통해 .Net Framework로 구동되며 ConfuserEx로 프로텍트되어있는걸 알 수 있다.

2.파일 분석하기

디버깅을 병행하며 .Net실행파일을 분석하기위해서 dnSpy를 쓴다.

AcchiKocchi.exe를 dnSpy를 로드하면 module attribute에서 ConfuserEx의 구체적인 빌드버전을 얻을 수 있다.

빌드버전

Entrypoint를 바로 볼수있으면 좋겠지만 Entrypoint함수로 가도 [STAThread]라고 적혀있는 윈폼 프로그램 메인에서 보이는 Attribute외엔 확인할수가 없다. 대신, 모듈의 정적 생성자 .cctor가 있다.

.cctor

2-1.프로텍션 해제

.cctor는 프로그램이 실행되자마자 동작하게 되는데 이곳에서 프로텍션의 일부가 해제되는것을 볼수있다.

한줄 한줄 분석해보자.

이 생성자가 실행하는 첫 명령은 아래 함수다.첫함수

함수의 내용은 대부분이 난독화가 되어있지만 다행이도 ConfuserEx는 오픈소스 프로젝트라서 원본 소스코드를 비교해 볼 수 있다.

해당 함수의 내용의 위를 보면 외부함수를 Import하는걸 볼수있다.VirtualProtect import

이를 깃헙에서 찾아보면 이 AntiTamper모듈이 해당 함수를 Import하고 내용도 비슷한걸 알수있다.

비교할때 소소한 팁을 주자면 흐름제어와 이름 난독화 때문에 함수의 내용이 뒤죽박죽이 되어있기 때문에 소스를 직접 비교하긴 힘드나 typeof 나 Marshal 같은 native 함수(또는 연산자)들은 거의 제대로 나오기 때문에 이런것들의 참조여부를 비교하는것이 분석하기에 유리하다.

이제 그 다음 함수를 보자.

2번째 함수로 가볼까

으아니 보이지가 않아

뭐? 임마?

제대로 보이지가 않는다. AntiTamper를 해제해야 보이는데 해제법은 아아주 간단하다.

breakpoint 설정

일단 AntiTamper함수 밑에 breakpoint를 설정하고 실행한다.

Modules창

CTRL+ALT+U를 눌러서 열수있는 Modules 창에서 AcchiKocchi.exe를 우클릭해서 Open Module from Memory 버튼을 눌러 메모리에서 AntiTamper함수가 동작한후의 바이너리를 연다.

그리고서 다시 두번째 함수를 열면보인다! 2번째함수!

오예!

보인다.

이제는 이 함수의 정체를 밝혀내면 된다. 이때 단서로 쓸수있는 함수는Assembly.Load

바로 이 함수다. Assembly.LoadAppDomain.CurrentDomain.AssemblyResolve를 키워드로 소스를 비교해보면 이 Resource복호화 모듈이란걸 알 수 있다.

이제 F10을 눌러 해당 함수를 실행하면 리소스가 복구된다.

와! 리소스복구!

이러면 이제 복구된 리소스를 볼수있는데, 다음과 같은 이미지 2장을 확인 할수 있다.

Nope과 Good

처음 정보를 모을때 나왔었던 NOPE과 본적이 없는 Good Job이미지가 있다. 이때 우리의 목적은 저 Good Job이미지를 출력하게 하면 된다는것을 알 수 있다.

3,4,5번째 함수는 1,2번째를 알아낸것과 비슷한 방법으로 비교해서 3번째 함수는 상수 복호화 모듈, 4번째 함수는 AntiDump모듈, 5번째 함수는 안티디버그 모듈임을 알아낼수있다. 이제는 키 체크를 분석해야 하므로 메모리에서 불러온 모듈에 코드패치를 해서 5번 함수가 실행 안되도록 하고 AntiTamper가 실행 되지 않게 1번 함수도 패치해 준다.

patching..patching..patching..patching..

패치를 다 한후에 상단 메뉴바에서 File -> Save Module... 을 이용해서 패치되고 코드가 보이는 상태인 모듈을 저장해준다.

이 편이 나중에 다시 디버깅할떄도 편하다.

2-2. 키체크 루틴 분석

1번에서 버튼을 누르면 키체크 루틴이 실행된다는것을 알수있었다.

그러면 분명 button click 같이 생긴 함수가 있…..게…ㅆ

그런거 없다

그런거 없다. 그래도 문제없다. Hex-ray가 스택 크기만 갖고 인자를 알아내듯 우리는 인자로 함수를 유추할수있다.

winform요소에 있는 Event는 objectEventArgs를 인자를 받아야한다. 찾아보면 해당하는 함수를 1개 찾을수 있다.

그 함수

이게 버튼 클릭 이벤트인데 함수가 뭔가 많고 흐름제어가 걸려있어서 분기문을 제대로 볼수가 없다.

이제부터는 노가다다 case마다 소스 한줄이라 생각하고 디버깅을 하면서 흐름과 동작을 알아내야한다

디버깅중...

출제자가 분석하면서 적은 노트는 다음과 같다. 참고가 되면 좋겠다.

27(text=our input)
4(sth=new char[63,63])
1(Initialize sth])
18(text=(text.PadRight(64)).Remove(63))
8(i=0)

20(i<63) -> 23
    23(j=0)
    24(???)

    9(j<63) -> 28
        28(i==31) or 15(j==31) or 13(i==j) or 17(i+j==62) -> 29
            29(sth[i,j]=text[i])
        14(j++) -> 9

???(???)
12(k=62)
2(k>0)->16
    16(l=62)
    21(l>=0)->26
        26(sth[l,k-1]+=sth[l,k])
        25(l--) -> 21

22((new string(Enumerable.Range(0, 63).Select(char[enumnumber,0]).ToArray())
    == "ùşŊŖĻŊűüĸ\u0099ĝ\u0090Ŋńūĝ¥Ŗş\u0099ĝť\u0093\u009FĬ\u0090Ňĝ\u0093\u009FĝఏŊĝŁŊ\u0090ť\u0093Ŋĵĝū\u0090şĝŁŊ\u0090ťĝŊ\u0090Ŝĸ\u0093Ŋĵŷ````"))
    ->11(GOOD JOB)
    //하이라이트
    //비교 대상 상수문자열은 난독화 되어있음
    //== 함수에 인자전달되는거로 확인가능
10(NOPE)

(조건문의 경우 여기있는거 true면)->여기로 옴

나는 보통 분석을 마친후 코드로 재구현 해본다. 그 편이 이해가 더 쉽다.

string ipt = "OUR INPUT";
char[,] sth = new char[63, 63];
sth.Initialize();
ipt = ipt.PadRight(64).Remove(63);
for (int i = 0; i < 63; i++)
{
    for (int j = 0; j < 63; j++)
    {
        if (i == 31 || j == 31 || i == j || i + j == 62)
            sth[i, j] = ipt[i];
    }
}
for (int i = 62; i > 0; i--)
{
    for (int j = 62; j >= 0; j--)
    {
        sth[j, i - 1] += sth[j,i];
    }
}
var a = new string((from i in Enumerable.Range(0, 63)
    select sth[i, 0]).ToArray());
string tt="ùşŊŖĻŊűüĸ\u0099ĝ\u0090Ŋńūĝ¥Ŗş\u0099ĝť\u0093\u009FĬ\u0090Ňĝ\u0093\u009FĝఏŊĝŁŊ\u0090ť\u0093Ŋĵĝū\u0090şĝŁŊ\u0090ťĝŊ\u0090Ŝĸ\u0093Ŋĵŷ````";
if (a == tt){
	Console.WriteLine("Yes");
}
else
{
	Console.WriteLine("No");
}

이제 우리는 YES의 조건을 찾으면 된다. 첫째루프가 sth배열을 채우고 둘째 루프가 해당 줄의 합산을 [i,0]에 넣고 있다.

첫째 루프는 아래 사진처럼 우리가 입력한 문자열을 X와 +를 합친 모양으로 배열에 넣는다.

sth배열

그리고 각 줄의 합을 y=0인곳에 전부 넣기 때문에 복호화 로직을 짤수있다. 심지어 아주 간단하다.

2-3.키 복호화

string tt="ùşŊŖĻŊűüĸ\u0099ĝ\u0090Ŋńūĝ¥Ŗş\u0099ĝť\u0093\u009FĬ\u0090Ňĝ\u0093\u009FĝఏŊĝŁŊ\u0090ť\u0093Ŋĵĝū\u0090şĝŁŊ\u0090ťĝŊ\u0090Ŝĸ\u0093Ŋĵŷ````";
for(int i=0;i<63;i++){
	Console.Write((char)(tt[i]/((i==31)?63:3)));
}

32번째 줄은 63으로 나누고 나머지는 3으로 나눠주면 된다. 플래그가 영어이기 떄문에 아무리 커봐야 (128*63)=8064를 못넘어서 오버플로우의 경우는 생각하지 않아도 된다.

위 복호화 코드를 실행하면 답이 나온다.

Sunrin{Th3_0nly_7ru3_w15d0m_15_1n_kn0w1ng_y0u_kn0w_n0th1ng}

이걸 이제 넣어주면….?

굿 잡!


와! 라업 참 길죠……. 점수를 더 드리고 싶었지만 그러면 진짜로 풀기도 전에 포기하시는분들이 많을꺼같아서 낮게 잡았습니다만, 그 점이 오점 이였을지도 모르겠습니다. 무튼 긴 라이트업 보느라 수고하셨고, 꼭 한번 다시 풀어봐 주시면 감사드리겠습니다. 라이트업에서 이해가 안되는점이 있다면 페북으로 연락주시면 최대한 성의껏 답드리겠습니다.

글이 다소 두서없이 써진 점 죄송히 생각하고 있습니다. 국어공부 열심히 할께요.

본선 오신분들 남은기간 열심히 공부하셔서 본선에서도 좋은 성적 거두시길 바라고, 예선에서 떨어지신분들도 내년을 기약하며 열심히 공부하셨으면 좋겠습니다.

이 라업이 좋은 밑거름이 되길 바라며, 다음에 또 봐요!