오랜만에 짬을 내서 문제를 계속해서 진행해보았다.

sub_20C의 가장 큰 블럭 중 하나를 분석했었는데 나머지 두 블럭도 추가적으로 분석하였다.
(1) 0x309 ~ 0x0310

이구간을 제외하면 이전글에서 분석했던 블럭과 크게 차이는 나지 않는다.
이구간에서 0xAD2에 저장된 값을 바꾸는데 내가 입력했던 문자열 8개를 레지스터에 계속 저장한다.
그 값을 0xAD2에 복사한다.
또 더하면서 carry flag가 set 되면 그 값을 r9 레지스터에 저장하는데 이 값은 나중에 사용된다.

(2) 0x32B ~ 0x3CB

이러한 코드가 계속해서 반복되고있다. 확인해보니 AVR reverse engineering (5) 에서 분석하였던 구간과 마찬가지로 
0xAE4 에서 값을 가져오고 이 값을 다른값과 and 연산을 한 후
sub_1E7의 인자값으로 이용한다
여기서 리턴된 값과 0x100을 더한 주소에 저장된 값을 불러온다.

이러한 과정을 AD2~ADB까지 모두 적용한뒤 0xAE4를 가지고 있는 레지스터는 1 증가 0x100를 가지는 레지스터에는 4씩 증가시키고
이를 총 8회 반복한다.


 (3)0x3D4 ~

이전 포스트에서 이 구간에서도 값을 변경했었던 것을 확인하였는데 

여기서 아까 언급하였던 r9 레지스터에 저장되었던 값이 Y+2 에 복사되었는데, 이 값을 Z+9 즉 0xADB 번지에 복사한다.


그 아래를 보면 Z (0xAD3 으로 초기화되어있음)에 r16레지스터 값을 저장하는게 있는데 r16과 r22는 각각 Z번지에 저장된 값으로 조건이 맞다면  rotate 연산과 shift연산을 수행한 후 or 연산한다.
따라서 shift와 rotate 연산 진입 조건을 만족시키지 않는다면 해당값은 변경이 없다.

비교대상값이 이 구간에서 변형이 없었다면 좋겠지만, 보장할 수 없기 때문에 이 구간에서 얼마나 변형되는지를 확인할 필요가 있었는데
(1)에서 구한 r8과 r9 레지스터의 저장된 값이 영향을 준다는 것을 확인하였다. 또 정말 운좋게도 우연히 입력했던 테스트 값이 비교 대상의 첫번째 바이트와 동일하였다.
첫번쨰 바이트는 (2)에서 r8 레지스터의 값(문자들의 합)이 xor 연산을 거듭하면서 변형된것이기 때문에 거꾸로 비교대상의 문자들의 총합을 유추 할 수 있었다.
해서 문자들의 총합을 유지하면서 값을 변경시켜봤을 때 계속 고정된 위치만 덮어씌워지는것을 확인하였다.

해서 변경된 바이트를 제외하고 브루트포싱으로 구할 수 있는지 확인해보고자 한다.

연산하는 코드를 위와 같이 c로 작성하였다.

작성한 코드로 기준문자와 비교하여 한문자씩 변화하였을 때 어떻게 영향을 주는지 확인하였다. 하지만 예상했던것과 다르게 결과가 나오는것을 확인하였다. 
아마 결과값 바이트 Y 와 결과를 만드는 바이트 X가 서로 같은 위치가 아니라 다른 위치이기 때문이라고 본다.

 
가장 윗줄은 처음에 입력된 바이트이고 화살표가 가리키는 것은 이 입력값이 어떤위치의 바이트라고 보면
그 밑에 있는 결과 값 중 노란색 원은 네번째 바이트에 의해서 만들어졌지만, 그 다음 줄의 결과값을 보면 두번째 줄의 초록색 원이 영향을 줬지만
그 초록색원은 첫번째 줄의 가장 왼쪽에 있는 초록색원에 의해서 생성된 값이다. 즉, 단계가 진행되면서 입력했던 네번째 바이트 뿐만아니라 다른 바이트에 의해서도 변할 수 있는 것이다.

따라서 그냥 연산으로 한문자씩 변화시켜가면서 구할 수는 없다는 것을 확인하였고 결국 역연산 코드를 짜야 되는 것으로 결론이 났다.

(3)에서 나오는 명령어들이 무엇을 수행하는지 코드를 보니 잘 몰랐는데 전체적인 기능은 레지스터를 대상으로 로테이트 명령어를 수행하는 녀석이었다.
그리고 첫번째 바이트가 (2)에서 첫번째 바이트를 바꾸는것을 확인하여 첫번째 바이트외에 다른 바이트가 연산에 포함되지 않았다고 판단하여
제일 첫번째 바이트가 동일하다면 역연산에 끝 바이트도 동일할 것이다.
역연산 끝에 나타나는 바이트는 입력한 문자를 모두 합한것이었기 때문에,  (3)에서 발생하는 스왑도 동일할 것이라 가정하였다. 

그래서, 비교대상 10 바이트 9A 7D 72 57 D5 78 49 E6 F2 02 는 9A 7D 72 75 EA 78 a4 E6 F2 02 가 된다고 생각하고 역연산 코드에 넣어보기로 했다.

중간에 반복횟수와 초기 입력값에 혼동이 있어 시간이 걸렸지만
.
.
.
.



비밀번호를 구했다!




성공적으로 로그인 되는것을 확인하였다. 그리고 리드미를 읽으니 플래그가 출력되었다 감동 ㅠㅠ
아쉽게도 비밀번호 문자열과 리드미가 읽는것과 어떤영향을 주는지 정확하게 파악하진 못했지만, 플래그를 얻었으니 여기서 마무리 지어야겠다.



끄읏

 

'reversing.kr' 카테고리의 다른 글

AVR reverse engineering (5) CustomShell  (0) 2018.08.06
reversing.kr CRC1  (0) 2018.02.07
AVR reverse engineering (4) CustomShell  (0) 2018.02.05
AVR reverse engineering (3) CustomShell  (0) 2018.02.05
AVR reverse engineering (2) AVR ATmega128  (0) 2018.01.30
sub_20c 를 본격적으로 분석하기 시작했다.


0x23A 까지 실행하였을 때, 0xAD3(AD4가 아니였음)으로 입력한 패스워드 문자열 8개를 복사했다.



그 아래에 있는 베이직블록에서 진행한것은 r19,r18,r03,r21,r20 에 AD3에 복사한 1,2,3,4,7 번째 문자 이다.
Y+5,Y+4,Y+3 에 5,6,8 번째 문자를 복사 했다.


loc_24C 에 진입하기 직전 r24 = 0xFF , r30 = 0x76 , r31 = 1 이었는데
loc_251 로 진입하면서 모두 0으로 되었다.

한줄 한줄 분석하고 있는데, 넘나 피곤하다. Hex ray의 위대함을 실감하고 있다.
대략 알 수 있는 것은
(1) 0x23B ~ 0x302 까지의 코드가 8번 반복이 될 것이라는 점과
(2) and를 하고 있는 친구는 0xAE4 부터 하나씩 가져오는 문자인데, 매 루프 마다 증가할 예정이다. 0xAE4 ~ ... 의 값이 매번 고정이었던거같은데 확실하지는 않음.
(3)sub_1DC에 의해서 가져오는 값은 0x00, 0x02, 0x03 세 종류이다. 따라서 XOR 하게되는 r24 친구는 0x100, 0x102, 0x103 셋 중 하나로 결정된다. 
(4)일련의 반복과정으로 0xAD2 ~ 0xADB 번지에 차곡차곡 갱신된다. 

이런식으로 끝까지 블럭을 분석하였다.

얼핏보면 길지만 결국에는 입력한 여덟문자를 토대로 어떤문자열과 연산하여 열개의 문자열을 생성해내는 것이었다.

나머지 아랫부분의 긴 블럭 또한 위쪽처럼 열문자에 대해 어떤 변화를 줄 것이다.
그래프뷰의 아랫쪽에 긴 블럭 이후에도 AD2~ ADB가 영향을 받는지 확인하고 싶었다. 정확히는 0x3D4 까지 코드가 실행 된 이후에도 영향을 받는지 확인하고싶었다. 영향을 안 받는다면 그 이후 것은 해석할 필요가 없기 때문
(1) 0x3D4 까지 실행된 직 후

(2)0x3F7 까지 실행된 직 후



AD2~ ADB 전체는 아니지만 일부는 영향을 받는다.

'reversing.kr' 카테고리의 다른 글

AVR reverse engineering (6) CustomShell  (0) 2018.08.06
reversing.kr CRC1  (0) 2018.02.07
AVR reverse engineering (4) CustomShell  (0) 2018.02.05
AVR reverse engineering (3) CustomShell  (0) 2018.02.05
AVR reverse engineering (2) AVR ATmega128  (0) 2018.01.30
CRC 알고리즘 연산방법이 왜 그런건가 끙끙 거리다가 그냥 받아드리니 마음이 편해졌다.

CRC 테이블을 생성했을 때, CRC-32 알고리즘 기준으로 설명을 해보자면 CRC-32 초기값은 0xFFFFFFFF 이다.

이 때, 내가 입력한 문자열이 "abcd1234" 라면

첫번째 문자를 CRC-32 값(현재 0xFFFFFFFF)의 최하위 바이트와 xor 을 한다. 

그럼 0x61 ^ 0xFF = 0x61 이고 이 값이 CRC-32 의 테이블 인덱스 값이 된다.

해당 값이 0xAABBCCDD 라고 가정한다면,

새로운 CRC-32 값은 (기존의 CRC-32 값 ( 현재는 0xFFFFFFFF ) <<  8) ^ ( 직전에 구한 테이블 값 (0xAABBCCDD)) 가 된다. 

즉 0x554433DD 

이제 그 다음은 b 에 대해서

CRC-32 값 최하위 바이트 DD 와 b(0x62) 를 연산하면 0xBF 이다

0xBF의 테이블 값이 0x11223344 라고 가정한다면,

새로운 CRC-32 값은 0x4433DD00( 0x554433DD << 8 을 한 값이다.) ^ 0x11223344

0x5511EE44

이것을 문자열 끝까지 반복하는 알고리즘이다.

이제 문제를 IDA를 사용하여 확인해보면


GUI 윈도우 프로그램인데, WinMain을 보면 sub_401070을 호출하고 있다. 이 함수를 분석을 해야한다.

해당 함수를 HexRay 기능을 사용하여 보면 아래와 같다.


이 코드가 전부이고 문자를 여덟개만 받는다. 그 문자열을 256바이트짜리 문자에 0 , 8 , 16 ... 간격으로 넣는데 난 처음에 테이블에다가 넣는다고 착각했는데 사실 이 친구는 CRC 검사를 할 대상 문자열이다.

이 문자열을 대상으로 CRC 값을 생성해내고, 기준값과 검사해서 아니면 Wrong 맞으면 Correct 인데 우리는 CRC 값을 조작해내는게 아니라 기준 CRC 값을 생성해내는 문자열을 알아내는게 문제이다.

이 과정에서 역연산과 연산을 사용하여 문자열을 찾아내면 된다. 문제를 해결하는 키포인트는 결국에는 브루트 포싱이라는 것 그렇다면 n^8 인데 어마어마한 연산량이기 때문에 쉽지 않다.

그렇기 때문에 역연산과 연산을 사용하여 저 시간복잡도를 줄일 수가 있다. 대충 (n^4) * (n^4) 으로 줄일 수 있고 여기에 각종알고리즘을 적절히 사용하면 속도를 더 끌어 올릴 수 있다.


참조한 글

 CRC 알고리즘과 취약점 - anch0vy : http://anch0vy.tistory.com/60

그리고 문제해결에 결정적인 단서를 받은 

브루트포싱 해야된다고 쐐기를 박아주셔서 삽질할 시간을 줄여주셨다.

이것보다 CRC2는 훠얼씬 어렵다는데 무서워서 못 풀겠다.

+ Recent posts