예비군과 프로젝트를 마치고 오랜만에 돌아왔습니다 ㅎㅎ
https://forum.tuts4you.com/files/file/1307-lenas-reversing-for-newbies/ 사이트를 참조하여 15강 파일을 다운 받았습니다.
압축을 풀고 files 디렉터리안에 reverseMe. Nags.exe파일을 실행했습니다.
다음과 같은 순서로 프로그램이 실행되고 종료됩니다.
콜 스택이란 프로그램에서 사용하는 서브루틴에 대한 정보를 정장하기 위한 자료구조입니다.
주요 목적은 서브루틴 간 호출 순서를 추적할 수 있는 정보를 저장하는 것입니다. 또한 서브루틴 호출 시 필요한 인수, 지역변수를 저장합니다.
콜 스택을 사용해 처음 NAG 창이 열리는 곳을 찾아보겠습니다.
우선 F9으로 실행 후 F12로 정지시키겠습니다.
그리고 위에 K를 눌러 콜 스택 창을 열어보겠습니다.
여기서 called from은 서브루틴을 호출한 코드를 의미하고 procedure는 호출되는 서브루틴을 의미합니다.
여기서 찾아야 할 것은 NAG창을 띄우는 코드를 찾아야합니다.
밑 줄친 부분이 서브루틴을 호출하는 곳으로 더블클릭 하면 서브루틴 호출 코드로 이동합니다.
코드 케이브란 리버싱을 하다 보면 코드를 수정해야할 일이 생기게 되는데 기존코드보다 크기가 작다면 상관 없지만 크기가 더 크다면 문제가 생깁니다.
그래서, 작은 공간에 큰 코드를 집어넣기 위한 기술입니다.
사용하지 않는 코드 영역에 필요한 코드를 입력하고 수정해야할 부분에는 새로운 코드가 입력된 메모리로 점프라는 코드를 넣는 방법입니다.
앞서 본 0042039A 위의 부분을 보면 TEST 명령어가 있습니다. TEST는 두 값을 AND연산하여 0일시 제로 플래그에 1을 설정, 아니면 0을 설정하는 명령어입니다.
이어서 오는 JE는 점프문으로 제로플래그가 1이면 지정한 주소로 점프하고 아니면 점프하지 않는 명령어입니다.
즉, EAX가 0이면 제로 플래그가 1이되어 NAG창을 띄우는 서브루틴을 스킵하여 주소 004203BA로 이동하고 EAX가 1이라면 제로 플래그가 0이되어 JE점프문을 스킵하고 NAG창을 띄우게 됩니다.
결론적으로, EAX레지스터에 어떤 값이 오느냐에 따라 NAG창을 여는 서브루틴을 호출할지 말지를 결정합니다.
위의 M을 누르면 메모리 맵을 볼 수 있습니다.
Address는 메모리에서 시작 주소, Size는 데이터의 크기, Section은 PE파일의 구성요소, Access는 메모리 맵에 접근할 수 있는 특성을 나타냅니다.
(케이브 코드)코드 입력 시 R,E 권한이 있는 text영역에 작성하고 데이터 입력 시 R,W 권한이 있는 data영역에 작성하여야 합니다.
코드와 메모리 영역을 내리다 보면 프로그램에서 사용하지 않는 부분이 있습니다.
일단 수정할 부분에 점프할 명령어로 수정을 합니다. 아까 본 TEST 부분을 말이죠.
Fill with NOP's 옵션은 새로 들어가는 명령어가 기존 명령어보다 작은 경우 남는 공간을 아무 동작도 하지 않는 NOP 명령어로 채워준다는 의미로 3개의 NOP 명령이 추가 되었습니다.
기존에 있던 JE명령어도 NOP로 교체되었습니다.
TEST가 사라지면서 의미가 없는 명령이 되었기 때문이죠.
원래 총 8바이트였지만 JMP 00437D6A -> E9 EE790100이 되어 5바이트가 필요해 나머지 3바이트는 NOP으로 채운것입니다.
이제 엔터를 눌러 코드를 작성할 00437D6A로 이동하고 브레이크 포인트를 걸어줍니다.
그리고 코드를 작성해줍니다.
1. ADD BYTE PTR DS:[445E90], AL
2. CMP BYTE PTR DS:[445E90], 2
3. JNE 004203BA
4. LEA ECX, [ESP+4C]
5. JMP 0042037F
1. 특정 메모리 영역에 1바이트 단위 덧셈
-> AL은 EAX의 하위 8비트를 의미합니다. 프로그램에서 창을 띄울 때만 EAX가 1로 설정되기 때문에 명령어를 수행한 결과는 주소 445E90에 몇 번째 띄워지는 창인지 순서가 기록됩니다.
2. 특정 메모리 영역에 2가 저장되었는지 확인
-> 주소 445E90에 2가 저장되었는지 확인하고 맞다면 제로 플래그가 1이 설정됩니다.
창이 두번째 창을 띄우는지 검사하기 위한 용도의 명령입니다.
3. 두 번째 창이 아니면 창을 띄우지 않는 로직으로 이동
-> 두 번째 창인 경우에만 제로 플래그가 1이 되므로 JNE 명령어로 제로 플래그가 1이 아닌경우는 창을 띄우지 않는 004203BA로 이동시킵니다.
4. 점프 명령어 입력을 위해 삭제된 원본 코드 입력
-> TEST 부분을 수정할 때 비교와 점프 구문은 단순 비교연산이므로 없어도 되지만 그 후에 나오는 LEA 명령은 프로그램에 필요한 명령이므로 캐이브 코드안에 그대로 넣어줍니다.
5. 두 번째 창이면 창을 띄우는 로직으로 이동
-> CMP연산에 의해 제로 플래그가 1이되면 JNE를 스킵하고 이어가므로 여기서 창을 띄워주는 서브루틴 0042037F를 호출합니다.
프로그램을 재시작하면 변경사항이 모두 사라집니다.
그래서 코드 부분을 우클릭 후 copy to executable -> all modifications을 눌러주면
변경 사항이 모두 나오게되는데 여기서 또 우클릭 후 save file을 하면 변경사항이 저장된 프로그램을 저장할 수 있습니다.
실행 시 NAG창이 더이상 뜨지 않습니다.
우선 전에 풀었던 일련번호 문제와 비슷한 것 같습니다.
로그인 체크 창이 있고 입력 시
오류가 나옵니다.
목적은 일련번호를 알아내는 것이지만 프로그램 내부 메모리 어딘가에 적혀있다면 쉽게 찾아낼 수 있습니다. 그러나 일련번호가 동적으로 생성된다면 프로그램을 분석해야합니다.
올리디버거로 실행시키고 문자열에서 에러메시지를 찾아보겠습니다.
여러 문자열들이 보이네요. 그중에 성공 메시지로 추정되는 메세지가 보입니다. That's right ~~
일단 에러 메세지의 코드 부분으로 들어가보겠습니다.
MessageBoxA 함수를 호출합니다.
위로 조금 올라가면 분기문이 나옵니다. -> 성공과 실패를 가르는 분기문이죠.
CMP연산 후 제로 플래그가 1이 아니면 00401353(에러 메세지)으로 점프를 하게 되있네요.
즉, 제로 플래그를 0으로 만들어주면 되지만 학습이 목적이므로 일련번호 생성과정을 알아야겠습니다.
좀 더 위로 가면 lsrtlen함수를 호출합니다. 문자열의 길이를 EAX에 넣어주는 함수입니다.
그 후 ECX에 EAX값을 넣어주고 반복횟수로 ECX를 사용합니다. 즉 문자열의 길이만큼 반복을 한다는 뜻이죠.
그 후에 403138의 값과 ESI와 비교하여 성공과 실패를 분기합니다.
이제 아이디에 lena151 일련번호 1234를 입력해서 동작을 살펴보겠습니다.
lstrlen에 브레이크를 걸고 실행 시 입력한 아이디가 들어가있습니다.
lstrlen함수를 마치자 EAX에 아이디의 길이인 7이 들어가있습니다.
이제 반복문을 천천히 따라가 보겠습니다.
- 메모리 403038위치에서 4바이트 만큼을 EDX로 복사합니다.
- EAX+403037=403038의 위치의 메모리 값의 1바이트만큼을 EDX의 하위 1바이트인 DL에 복사합니다.
- EDX 와 0FF를 AND연산하여 EDX에 저장합니다
즉, EDX의 하위 2바이트를 제외하고 모두 0으로 변경
- EDX값을 EBX로 복사합니다.
- IMUL은 부호 있는 곱셈으로 (부호 없는 곱셈은 MUL) EBX와 EDX를 곱해주고 EBX에 저장합니다.
- ESI값에 EBX값을 더해줍니다.
- EDX값을 EBX로 복사해줍니다.
- SAR 명령어는 두 번째 인자만큼 첫번째 인자를 오른쪽으로 시프트 연산을 해줍니다.
즉, 비트를 한칸씩 옮겨 2로 나눠주는 효과를 나타냅니다.
- EBX에 3을 더해줍니다.
- EBX와 EDX를 곱하고 EBX에 값을 저장합니다.
- EBX에서 EDX를 빼줍니다.
12,13. ESI에 EBX를 더해주고 ESI에 ESI를 더해줍니다
14, 15. EAX에 1을 더해주고 ECX에 1을 빼줍니다.
16 . 제로 플래그가 1이면 스킵하고 1이 아니라면 다시 반복합니다.
이제 두 번째 반복문에서 일련번호 생성을 살펴보겠습니다.
일단 MOV DL,BYTE PTR DS:[EAX+403037]에서 다음에 사용할 문자열 값을 가져옵니다.
이번에는 EAX값이 2이므로 lena151에서 'e'값을 가져오게 됩니다.
DL의 값이 65로 바뀌었습니다.
지금까지 덧셈, 뺄셈, 곱셈 등 과정을 거치며 난독화 과정을 보았습니다. 이러한 난독화는 상용 프로그램에서 기본적으로 사용되며 익숙해져야 합니다.
이제 반복문을 빠져나와 ESI를 보면 생성된 일련번호가 있습니다.
3EF552값을 아스키 코드로 변경한 것이 일련번호입니다.
인터넷에 변환기가 바로 나오지만 코드 케이브 기술로 직접 변환해보겠습니다.
우선 점프문을 제가 원하는 임의의 주소로 무조건 점프하도록 변경시키겠습니다.
이제 00401385 코드 부분에 코드를 작성합니다.
1. MOV DWORD PTR DS:[403148],ESI
2. JNZ SHORT 00401353
3. JMP SHORT 0040133E
아까와 마찬가지로 재실행시 변경사항이 초기화되므로 파일을 따로 저장하고 실행을 해줍니다.
브레이크 포인트를 걸고 실행 후 메모리 값을 확인했습니다.
이렇게 나와있어서 인터넷 홈페이지에서 돌려보니 'Rõ>' 이것이 나왔습니다.
그래서 프로그램 일련번호에 넣어보니 특수문자 입력되지 않아 여기까지만 했습니다.