rev-baic-9
rev-basic-9: https://dreamhack.io/wargame/challenges/23/
rev-basic-9
Reversing Basic Challenge #9 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출
dreamhack.io
eax : strlen
- 주로 함수 return 값을 eax에 저장하기 때문
cdq : 나누기 연산시 사용
- eax 부호비트를 edx까지 확장 -> edx는 0으로 설정됨
- 나누기 연산 시 몫을 eax에 나머지를 edx에 넣기 위해 사용됨
test : and 연산 수행, 결과값은 저장하지 않음
jz : 왼쪽 인자 값이 0이면 점프
-> eax가 0이 되어야함 -> 7로 나누었을 때 몫과 나머지가 같아야함 -> 문자열 길이는 8의 배수-1
: (strlen+1) % 8 = 7 이면 loc_7FF7403C1030로 점프
loc_7FF7403C1030
[rsp+38h+var_18]을 0으로 초기화하고 이동
int i = 0; 으로 예측 가능
loc_7FF7403C1045
jge: 왼쪽 인자 값이 오른쪽 값보다 크거나 같으면 점프
i >= strlen([rsp+38h+var_14])+1 이면 loc_7FF7403C106B로 점프(왼쪽)
-> i < strlen+1 인 동안 반복
점프하지 않은 경우는(오른쪽),
rax와 rcx를 주소 str+i로 세팅하고 함수 sub_7FF7403C10A0 부르고
loc_7FF7403C103A로 점프
loc_7FF7403C106B
memcmp(buf1, buf2, 0x19) 호출
buf2는 { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D, 0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC, 0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 }
buf1이 연산으로 구해지는 듯함, 따라서 buf1이 buf2처럼 만들어져야함!
문자열 길이를 추측해보면, 우선 strlen은 8의 배수- 1이어야함.
따라서 25(0x19)가 아닌 23로 추측되고 마지막은 '0x00'(문자열 마지막)
나머지 하나는 연산하며 추가되는 걸로 추측해볼 수 있다.
(실제로, bp를 걸고 실행했을 때 문자열 길이가 23인 경우에만 bp가 실행되고 24, 25는 그냥 종료되었다.)
loc_7FF7403C103A
i를 8 증가시킨다.
아무래도 아까 call한 함수에서 8개의 문자로 연산을 진행하는듯
F7로 해당 함수를 들어가 살펴보아야할 필요가 있어보임
sub_7FF7403C10A0
여기서는 I_am_KEY 문자열과 0x10번의 반복문이 있다는 것을 확인 가능함
밑으로 내려가면,
또 반복문이 있는 것을 확인
따라서 여기서는 중첩반복문을 사용했음을 알 수 있다.
그리고 오른쪽 아래의 박스에서 연산을 진행함을 알 수 있다.
연산이 복잡해서 실제 코드로 작성해 정리하며 분석했다.
정리한 코드 아래와 같다.
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
void ROR(int& data, int c) {
for (int i = 0; i < c; i++) {
if (data % 2 == 0) {
data /= 2;
}
else {
data /= 2;
data += pow(2, 7);
}
}
}
int main() {
int array[256];
cout << "array : ";
for (int i = 0; i < 256; i++) { // 입력
int k; cin >> hex >> k;
array[i] = k;
}
int Buf1[25] = { 0, };
for (int i = 0; i < 23; i++) { // 입력
char a;
cin >> a;
Buf1[i] = (int)a;
}
for (int i = 0; i < 23+1; i += 8) {
string iam = "I_am_KEY";
for (int j = 0; j < 0x10; j++) {
for (int k = 0; k < 8; k++) {
int var_48 = Buf1[i + k];
var_48 ^= (int)iam[k];
int arr = array[var_48];
int eax = arr + Buf1[i + ((k + 1) % 8)];
eax %= 256;
ROR(eax, 5);
Buf1[i + ((k + 1) % 8)] = eax;
}
cout << endl;
}
}
}
void ROR(int& data, int c) | ROR연산을 수행하는 함수 |
int array[256] | byte_7FF741E54020에 저장된 값 (코드 실행시 입력하여 사용) |
int Buf1[25] | 사용자가 입력하는 값 |
for(int k=0;k<8;k++){} | 어셈블리어로 되어있는 연산을 이해하기 쉽도록 분석 후 소스코드로 작성 |
: 명령어를 해석하고 헷갈리는 부분은 값을 넣고 실행하며 진행과정을 보면서 해석할 수 있었다!
이제 연산을 반대로 진행하여 원하는 답을 구하면 문제를 해결할 수 있다.
답을 구하는 코드는 아래와 같다.
#include <iostream>
#include <string>
using namespace std;
void ROL(int& data, int c) {
for (int i = 0; i < c; i++) {
if (data >= 128) {
data %= 128;
data = data * 2 + 1;
}
else {
data *= 2;
}
}
}
int main() {
int array[256];
cout << "array : ";
for (int i = 0; i < 256; i++) {
int k; cin >> hex >> k;
array[i] = k;
}
int Buf[25] = { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D,
0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC,
0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 };
int str[25] = { 0, };
string iam = "I_am_KEY";
for (int i = 0; i < 24; i += 8) { // 연산 순서에 맞게 변경
for (int j = 0; j < 8; j++) {
if (j == 0) str[i + j] = Buf[i + j];
else str[i + j] = Buf[i + 8 - j];
}
}
for (int i = 0; i < 23 + 1; i += 8) {
for (int j = 0; j < 0x10; j++) {
for (int k = 0; k < 8; k++) {
int c_var = str[i + k];
int m_var = str[i + (k + 1) % 8];
ROL(c_var, 5); // ROR(eax, 5); ok
m_var ^= (int)iam[7 - k]; // var_48 ^= (int)iam[k];
int arr = array[m_var]; // int arr = array[var_48];
str[i + k] = c_var - arr; // int eax = arr + Buf1[i + ((k + 1) % 8)];
if (str[i + k] < 0) str[i + k] += 256; //eax %= 256
}
}
}
for (int i = 0; i < 24; i += 8) { // 다시 되돌리기
for (int j = 0; j < 8; j++) {
if (j == 0)Buf[i + j] = str[i + j];
else Buf[i + j] = str[i + 8 - j];
}
}
for (int i = 0; i < 24; i++) {
cout << char(Buf[i]);
}
return 0;
}
void ROL(int& data, int c) | ROL연산을 수행하는 함수 |
int array[256] | byte_7FF741E54020에 저장된 값 (코드 실행시 입력하여 사용) |
int Buf[25] | memcmp에서 연산 결과와 비교하는 값 -> 이 값을 반대로 연산해서 답을 얻을 수 있다. |
int str[25] | 연산을 반대로 진행하기 위해 순서를 바꾼 배열 -> 값을 저장한 순서가 Buf에서는 8 1 2 3 4 5 6 7 이기 때문에 8 7 6 5 4 3 2 1이 되도록 수정함 |
for(int k=0;k<8;k++){} | 반대로 연산을 진행 |
cout<< char(Buf[i]) | 연산하기 편하도록 순서를 변경했으므로 다시 원래대로 순서를 변경해서 Buf를 출력해 답 확인 |
위 코드에 array값을 입력하면 답을 얻을 수 있다.
<답>