728x90

스택 수열 

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 128 MB 152513 59312 41371 37.955%

문제

스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.

1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.

입력

첫 줄에 n (1 ≤ n ≤ 100,000)이 주어진다. 둘째 줄부터 n개의 줄에는 수열을 이루는 1이상 n이하의 정수가 하나씩 순서대로 주어진다. 물론 같은 정수가 두 번 나오는 일은 없다.

출력

입력된 수열을 만들기 위해 필요한 연산을 한 줄에 한 개씩 출력한다. push연산은 +로, pop 연산은 -로 표현하도록 한다. 불가능한 경우 NO를 출력한다.

예제 입력 1 복사

8
4
3
6
8
7
5
2
1

예제 출력 1 복사

+
+
+
+
-
-
+
+
-
+
+
-
-
-
-
-

예제 입력 2 복사

5
1
2
5
3
4

예제 출력 2 복사

NO

힌트

1부터 n까지에 수에 대해 차례로 [push, push, push, push, pop, pop, push, push, pop, push, push, pop, pop, pop, pop, pop] 연산을 수행하면 수열 [4, 3, 6, 8, 7, 5, 2, 1]을 얻을 수 있다.

 

 스택이 오름차순으로 올라갈 수 있다.

문제를 예를 들어서 설명하면, 만약 3가지 숫자의 배열 1 3 2 를 스택에 넣고 빼서 구현하고 싶다면, 

 

스택에 1을 push

스택 상단에서 1을 pop

스택에 2를 push

스택에 3을 push

스택 상단에서 3을 pop

스택 상단에서 2를 pop

하면 된다.

 

이를 코드로 구현하면, 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct stack {
	int stack[100001];
	int stack_index;
	int high_num; //넣은 수 중 가장 큰 수
} custom_stack;


void push(custom_stack* p, char* ans, int search_value) {
	do {
		p->high_num++;
		p->stack[p->stack_index++] = p->high_num;
		strncat(ans, "+\n", 3);//printf("+\n");
	} while (p->high_num != search_value);
}

/*
* sequence_sort의 반환값은, 
* 1 이상의 정수를 리턴할 경우, 해당 값은 스택 최상단에서 pop 된 값이고
* 0 을 반환할 경우 에러이다.
*/
int sequence_sort(custom_stack* p, char* ans, int search_value, int num) {
	//search and pop
	if (search_value <= p->high_num) {//이전에 넣은 수보다 작은 값을 찾으면 스택 최상단에서 찾고, 
		if (p->stack[p->stack_index - 1] == search_value) {
			strncat(ans, "-\n", 3);
			return p->stack[--(p->stack_index)]; //pop
		}
		else {
			return 0; //case NO
		}
	}
	else if (search_value <= num) { //이전에 넣은 수보다 큰 값을 찾으면 push, 대신 num 범위 안에서만
		push(p, ans, search_value);
		strncat(ans, "-\n", 3);//printf("-\n");
		return p->stack[--(p->stack_index)];
	}
	else
		return 0;
}

int main() {
	int num = 0;
	scanf("%d", &num); //user input 1

	//declare
	int flag = 1;
	int* array = (int*)malloc(sizeof(int) * num);
	char* ans = (char*)malloc(sizeof(char) * 400001);
	custom_stack* my_stack = (custom_stack*)malloc(sizeof(custom_stack));
	
	//initialization
	if (ans != NULL && my_stack != NULL && array != NULL) {
		memset(array, 0, sizeof(array)); //int pointer array
		memset(ans, 0, sizeof(ans)); //char pointer ans
		memset(my_stack->stack, 0, sizeof(my_stack->stack)); //struct pointer my_stack
		my_stack->high_num = 0;
		my_stack->stack_index = 0;
	}
	else {
		printf("Allocation failed\n");
		return -1;
	}

	for (int i = 0; i < num; i++) {
		scanf("%d", &array[i]);  //user input 2~N
	}
	for (int i = 0; i < num && flag; i++) {
		flag = sequence_sort(my_stack, ans, array[i], num);
	}
	if (flag) {
		printf("%s", ans);
	}
	else
		printf("NO\n");

	//free allocated area
	free(array);
	free(my_stack);
	free(ans);

	return 0;
}

 

 코드의 특징으론, 전역변수를 많이 사용하거나 함수 인자를 많이 사용하지 않기 위해 구조체를 사용하였고, 

답안을 제출하기 위해 "ans"  char 포인터를 선언하였고, +,-를 strncpy로 공간에 넣어준다.

또한, 만약에 불가능한 경우가 조건문에 걸린다면, "flag"로 설정한 "sequence_sort" 반환값을 활용하여(오류일 경우 반환값이 0) for 루프의 동작을 멈추고, NO를 출력하도록 하였다.

 

 array의 크기를 100,001로 설정한 이유는, 문제의 조건에서 N의 최댓값이 100,000이라고 하였고, 현재 스택 포인터는 가장 앞(가장 밑)에 비어있는 스택 위치를 가리키도록 설정하였기 때문에, worst-case인 100,000개가 모두 들어왔을 경우, index는 100,001을 가르키고 있기 때문이다.

또한, ans 배열의 크기를 400,001로 설정한 이유는, 한개의 데이터에 push-pop 두가지 연산이 와야하므로 100,000 x 2 = 200,000에, push-pop 각각 "+\n", "-\n" 2글자씩 들어가므로 (\n은 문자 하나로 메모리에 들어간다) 200,000 x 2 = 400,000에, worst-case 경우 null값까지 고려하여 400,001로 설정하였다. 

 

728x90

'자료구조론 > 백준' 카테고리의 다른 글

[백준] 11866 요세푸스 문제 0  (0) 2024.02.25
[백준] 10866 덱  (0) 2024.02.25
[백준] 2164 카드2  (1) 2024.02.24
[백준] 1966 프린터 큐  (1) 2024.02.24
[백준] 10845번 큐  (2) 2023.12.25
728x90

팩토리얼 0의 개수

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 128 MB 77545 36650 30670 46.959%

문제

N!에서 뒤에서부터 처음 0이 아닌 숫자가 나올 때까지 0의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (0 ≤ N ≤ 500)

출력

첫째 줄에 구한 0의 개수를 출력한다.

예제 입력 1 복사

10

예제 출력 1 복사

2

예제 입력 2 복사

3

예제 출력 2 복사

0

 

 

문제 말이 이해하기 어려운데, 

N!에서 뒤에서부터 처음 0이 아닌 숫자가 나올 때 까지의 0의 개수를 구해라고 한다.

여기서 "처음 0이 아닌 숫자"라는 말은, 

예를 들어 2100이 있으면, "뒤에서부터"는 1의 자리수부터를 말하는 것이므로 처음 0이 아닌 수는 "1"을 말하는 것이다.

이때까지의 0의 개수를 구하라고 했으므로, 이 예제에서의 답은 "2"이다.

ex) 42340080000 -> 4

 

N 팩토리얼은 1부터 N까지 하나씩 전부 곱하는 연산이다. 또한, 뒷자리에 0이 오려면 10의 배수여야 한다. 10은 2와 5로 소인수분해 되므로, 소인수분해 했을때, 2의 개수와 5의 개수 중 가장 작은 값이 답이다. (2^5 * 5^6 -> 5)

하지만, 우리는 입력을 팩토리얼로 처리하고, 팩토리얼 연산은 1부터 하나하나 곱하기 때문에 소인수 분해시, 5의 개수가 2의 개수보다 더 많은 경우는 없다. 

따라서, 소인수 분해 시 5의 개수를 구하면 된다.

 

주의점으로, 입력 범위가 0~500이므로, 5^1, 5^2, 5^3 이 연산에 있을 수 있으므로, 해당 경우까지 고려해야 한다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
	int num;
	int check_five = 0;
	scanf("%d", &num);
	
	for (int i = 1; i <= num; i++) {
		if (!(i % 5)) {
			check_five++;
			if (!((i / 5) % 5)) {
				check_five++;
				if (!((i / 25) % 5)) {
					check_five++;
				}
			}
		}
			
	}
	printf("%d", check_five);
}
728x90

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 1929 소수 구하기  (0) 2024.02.22
[백준] 1920 수 찾기  (0) 2024.02.19
[백준] 1654 랜선 자르기  (0) 2024.02.16
[백준] 1181 단어 정렬  (1) 2024.02.09
[백준] 1018 체스판 다시 칠하기  (4) 2024.01.03
728x90

위와 같은 exe 파일 하나가 있고 메세지 박스에는 Wrong이 적혀있다. 임의의 값 혹은 아무값을 입력하지 않고도 Check를 누르면 프로그램이 종료되고, 

WinDbg로 trace해보면, Access violation 오류로 종료되는 것을 확인할 수 있다.

 

어떤 함수가 이를 일으키는지 확인해보자.

 

우선, exe 파일 시작인 winmain에선 DialogBoxParamA를 호출하여, 대화 박스를 호출하고, 4번째 인자는 대화 상자 프로시저에 대한 포인터이므로, 이 부분을 디버깅하면

이와 같은 사실(주석 확인)을 알 수 있었다.

우리가 박스에 입력하여(10진수로) check 버튼을 누르면, 그 값이 dword_4084D0에 16진수 값으로 저장된다.

 

 

그리고 함수를 살펴보면, 

else if ( (unsigned __int16)a4 == 0x3EB )     // 메세지 입력하면 항상 여기로
  {
    dword_4084D0 = GetDlgItemInt(hDlg, 1002, 0, 0);
    sub_40466F(a1);
    sub_404689();
    *(_DWORD *)sub_40466F = -1013972794;
    sub_40466F(&loc_40469F);
    sub_40466F(v6);
    *(_DWORD *)sub_40466F = 1768;
    return 1;
  }

현재, dword_4084d0에 사용자가 입력한 값이 들어간 상태이다. 

이후, 

1. sub_40466f를 a1을 인자로 실행

edi에 있는 a1은 0x00110F02, 무슨 값인진 모른다.

'sub_40466f' 함수

해당 함수는, 사용자가 입력한 값에 어떠한 hex값을 더한 후, inc 연산까지 하고 return 하는 함수이다.

현재 a1인자가 어떤 역할을 하는지 모르겠고, 이는 ida가 해석한 것이니 잘못 표현됐을 가능성을 열어두고 넘어가자.

 

 

2. sub_404689 함수 실행

'sub_404689' 함수

해당 함수 또한 사용자가 입력한 값을 inc하고 return하는 연산이다.

 

 

3. sub_40466f 함수 포인터 지정

sub_40466f 함수 포인터에 0c39000c6 mov 한 후, sub_40466f call, 즉 opcode "c6 00 90 c3"(리틀엔디안에 따라 역순으로) 을 실행하는것이며, 

https://shell-storm.org/online/Online-Assembler-and-Disassembler/?opcodes=c6+00+90+c3&arch=x86-32&endianness=little&baddr=0x00000000&dis_with_addr=True&dis_with_raw=True&dis_with_ins=True#disassembly

해당 opcode는 eax레지스터에 있는 주소에 0x90 바이트 (nop)를 삽입하고, ret를 한다.

 

해당 text영역과 stack view를 연동시켜보면, 

 

스택에 이전에 넣은 opcode가 있는것을 확인할 수 있고, opcode 내용대로  

eax에 있는 60160a9d 주소에 0x90 바이트 쓰기를 하다 엑세스 위반을 한 것을 알 수 있다.

 

이제 사용자의 입력이 처리되는 과정을 동적 디버깅으로 확인해보겠다.

 

사용자 입력에 1을 입력하면, 

GetDIgItemInt의 결과값, 즉 사용자 입력값인 1이 eax에 들어있는 것이 보이고, 해당 값을 dword_4084d0에 저장한다.

이후 sub_40466f 함수를 실행하면, 

loc_40467a를 호출하고, 

현재 eip가 코드로 정의되지 않은(정의는 ida가 함) 부분을 가르키고 있고, 이를 즉각 변환해준다고 한다.

dword_406014+2에 619060eb를 저장하고, inc를 두번 실행하여 (스택에 해당 주소를 넣었는지 두번 실행되는걸 동적 디버깅중 확인하였다) 현재 dword_4084d0의 값이 (사용자 입력 1) + 1 + 1 = 3인걸 확인하였다.

이후 계속 실행하면, 

또 함수를 생성해준다고 하고, 

위와 같은 연산을 한다. 또한, dword_4084d0 값을 확인해보면 

이와 같이 값이 바뀌었는데, ida에서 보여주지 못한 부분이 있다 생각하고 추측하면, 

이전에 mov로 dword_406014+2에 저장한 619060eb와 관련이 있다 생각한다.

이전 inc연산을 두번 함

최종적으로, eax가 사용자가 입력한 1에 601605d0을 더한 값이 되는것을 확인했다.

디버깅을 하면서, ida가 잡지 못한 부분이 있어 (619060eb를 저장하는 부분) 확실하지 않지만, 사용자의 입력이 덧셈으로만 조작된다 가정하면 

사용자 입력 + 601605d0 = EAX이다.

 

최종적으로, 우린 correct의 출력을 막는 401071을 nop로 씌우면 되므로, eax값을 401071로 만들어야 한다.

 

사용자 입력 + 0x601605d0 = 0x401071

사용자 입력 = 0x401071 - 0x601605d0 = FFFFFFFFA02A0AA1

32비트 프로그램이므로, 뒤의 4바이트만 주소에 들어가면 되므로, 

a02a0aa1을 넣어주면, 뒤 4바이트가 401071이 되는것을 확인하였고, 사용자는 정수로 입력해야 하므로 (GetDIgItemInt이므로) 이를 정수로 변환하면, 

2687109793 을 입력하면 된다.

정답!

 

728x90

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

[reversing.kr] Music Player  (1) 2024.02.15
[reversing.kr] Easy Keygen  (0) 2021.04.14
[reversing.kr] Easy_CrackMe  (0) 2020.11.29
728x90

랜선 자르기 

시간 제한            메모리 제한              제출                             정답                        맞힌 사람                정답 비율

 

2 초 128 MB 210053 49585 33532 21.280%

문제

집에서 시간을 보내던 오영식은 박성원의 부름을 받고 급히 달려왔다. 박성원이 캠프 때 쓸 N개의 랜선을 만들어야 하는데 너무 바빠서 영식이에게 도움을 청했다.

이미 오영식은 자체적으로 K개의 랜선을 가지고 있다. 그러나 K개의 랜선은 길이가 제각각이다. 박성원은 랜선을 모두 N개의 같은 길이의 랜선으로 만들고 싶었기 때문에 K개의 랜선을 잘라서 만들어야 한다. 예를 들어 300cm 짜리 랜선에서 140cm 짜리 랜선을 두 개 잘라내면 20cm는 버려야 한다. (이미 자른 랜선은 붙일 수 없다.)

편의를 위해 랜선을 자르거나 만들 때 손실되는 길이는 없다고 가정하며, 기존의 K개의 랜선으로 N개의 랜선을 만들 수 없는 경우는 없다고 가정하자. 그리고 자를 때는 항상 센티미터 단위로 정수길이만큼 자른다고 가정하자. N개보다 많이 만드는 것도 N개를 만드는 것에 포함된다. 이때 만들 수 있는 최대 랜선의 길이를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그 후 K줄에 걸쳐 이미 가지고 있는 각 랜선의 길이가 센티미터 단위의 정수로 입력된다. 랜선의 길이는 231-1보다 작거나 같은 자연수이다.

출력

첫째 줄에 N개를 만들 수 있는 랜선의 최대 길이를 센티미터 단위의 정수로 출력한다.

예제 입력 1 복사

4 11
802
743
457
539

예제 출력 1 복사

200

힌트

802cm 랜선에서 4개, 743cm 랜선에서 3개, 457cm 랜선에서 2개, 539cm 랜선에서 2개를 잘라내 모두 11개를 만들 수 있다

 

해당 문제의 알고리즘은, binary search이다.

이진 탐색은, 중간값을 매개변수에 넣어 조건을 확인하고, 조건보다 크다면 오른쪽 범위를, 작다면 왼쪽 범위를 생각하는 알고리즘이다. 

https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%A7%84_%EA%B2%80%EC%83%89_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int calculate(int* array, int num, int mid) {
	int sum = 0;
	for (int i = 0; i < num; i++) {
		sum += array[i] / mid;
	}
	return sum;
}
int loop(int* array, int num, int goal, int max) {
	long left = 1;
	int right = max;
	long mid;
	int value;

	while (left <= right) {
		mid = left + (right - left) / 2;
		if (calculate(array, num, mid) >= goal) {
			value = mid;
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}
	return value;
}
int main() {
	int num;
	int goal;

	scanf("%d", &num);
	scanf("%d", &goal);

	int* array = (int*)malloc(sizeof(int) * num);

	int max = 0;

	for (int i = 0; i < num; i++) {
		scanf("%d", &array[i]);
		if (max < array[i]) {
			max = array[i];
		}

	}

	printf("%d", loop(array, num, goal, max ));

	free(array);
	return 0;
}

 

주의할 점으로, loop함수의 left, mid 변수는 int 타입보다 큰 long으로 설정해줘야 한다.

위 코드상, mid 변수 또한 left + right 연산을 하지 않기 위해 (overflow 위험성) left + (right - left)와 같이 간격을 통한 연산으로 오류를 방지하려 했으나, while루프의 조건처럼 left == right == int_max인 경우, mid와 left 변수가 overflow가 발생한다. 

(left = mid + 1;부분에서) 따라서, mid, left 변수는 더 큰 long으로 할당하고, 오버플로 가능성이 없는 right는 int로 선언한다.

 

또한, 본래 알고리즘으론 입력값 타입의 최대값인 int_max를 사용하지만, 이 문제에선 가장 큰 선의 길이를 구하여 해당 길이를 가장 큰 값 (bst 범위 중 right값)으로 사용하여 조금의 효율성을 챙겼다.

 

728x90

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 1929 소수 구하기  (0) 2024.02.22
[백준] 1920 수 찾기  (0) 2024.02.19
[백준] 1676 팩토리얼 0의 개수  (0) 2024.02.17
[백준] 1181 단어 정렬  (1) 2024.02.09
[백준] 1018 체스판 다시 칠하기  (4) 2024.01.03
728x90
#    define ATOMIC_ADD(type, var, val) ATOMIC_ADD_##type(var, val)

위 전처리문은 메타 프로그래밍 기법으로, 코드를 작성하는 코드이다.

ATOMIC_ADD(int, a, 5);
ATOMIC_ADD(float, b, 4.5);

위와 같이 선언할 시, 

 

ATOMIC_ADD(type, var, val)은 ATOMIC_ADD_##type(var,val) 로 정의되었으므로

각각

ATOMIC_ADD_int(a,5);

ATOMIC_ADD_float(b,4.5);

와 같이 정의된다.

 

이와 같은 메타 프로그래밍 기법은 코드의 유연성과 확장성을 높이며, 타입별 연산 함수와 같이 여러 버전이 필요할 경우 관리하기 편하게 만들어준다.

728x90
728x90

exe파일 실행시, 음악 플레이어가 나온다.

테스트 오디오를 재생할 시, 1분 제한이 걸리며 시간이 지나면, 

해당 팝업창이 띄워지며 1분 이후로는 재생이 되지 않는다.

 

우선, 팝업창이 띄워지므로 messagebox api를 호출하는 부분을 모두 trace 해본다.

sub_4038d0, sub_4044c0 에서 호출하는 것을 확인하였다.

호출하는 모든 부분에 breakpoint를 설정한다.

이후, 프로그램을 디버깅하여 어떤 위치에서 "1분 미리듣기만 가능합니다" 라는 message box를 호출하는지 알아보자.

 

sub_4044c0

위 위치에서 호출하는것을 확인하였고, 

해당 함수의 코드를 살펴보면, 

// bad sp value at call has been detected, the output may be wrong!
int __stdcall sub_4044C0(int a1)
{
  //선언부분 생략
  if ( !v1 )
    _vbaNew2(dword_40186C, v55 + 13);
  v2 = v55[13];
  v3 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v2 + 68))(v2, v29);
  __asm { fnclex }
  if ( v3 < 0 )
    _vbaHresultCheckObj(v3, v2, dword_40276C, 68);
  v51 = v29[0];
  if ( v29[0] < 60000 )
  {
    if ( v29[0] != -1 )
    {
      (*(void (__stdcall **)(_DWORD *, int))(*v55 + 1784))(v55, v29[0]);
      v24 = (double)v51;
      v5 = v24;
      if ( dword_407000 )
        adj_fdiv_m64(0, 1079574528);
      else
        v5 = v24 / 100.0;
      if ( (v6 & 0xD) != 0 )
        _vbaFPException(v55);
      v10 = _vbaFpI4(v49, v50, v5);
      if ( v10 > 600 )
        _vbaStrCopy(v55 + 14, L"LI");
      if ( !v10 )
        v10 = 1;
      v11 = (*(int (__stdcall **)(_DWORD *))(*v55 + 796))(v55);
      v12 = (int *)_vbaObjSet(&v49, v11);
      v23 = *v12;
      v13 = _vbaI2I4(v10);
      v14 = (*(int (__stdcall **)(int *, int))(v23 + 188))(v12, v13);
      __asm { fnclex }
      if ( v14 < 0 )
        _vbaHresultCheckObj(v14, v12, dword_402B58, 188);
      _vbaFreeObj(&v49);
    }
    if ( !v55[13] )
      _vbaNew2(dword_40186C, v55 + 13);
    v15 = v55[13];
    v16 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v15 + 68))(v15, v29);
    __asm { fnclex }
    if ( v16 < 0 )
      _vbaHresultCheckObj(v16, v15, dword_40276C, 68);
    if ( v29[0] > 60010 )
    {
      v34 = dword_402BDC;
      v33[0] = 8;
      rtcVarBstrFromAnsi(&v45, 114);
      v30[0] = 8;
      v31 = dword_402BE4;
      v17 = _vbaVarCat(v42, &v45, v33);
      v18 = _vbaVarCat(v39, v30, v17);
      v19 = _vbaStrVarMove(v18);
      v20 = _vbaStrMove(&v50, v19);
      _vbaStrCopy(v55 + 16, v20);
      _vbaFreeStr(&v50);
      v48 = v39;
      v47 = v42;
      v46 = &v45;
      ((void (__cdecl *)(int))_vbaFreeVarList)(3);
    }
  }
  else
  {
    v4 = (*(int (__stdcall **)(_DWORD *))(*v55 + 1800))(v55);
    if ( v4 < 0 )
      _vbaHresultCheckObj(v4, v55, dword_4025C0, 1800);
    v37 = -2147352572;
    v40 = -2147352572;
    v43 = -2147352572;
    v36[0] = 10;
    v39[0] = 10;
    v42[0] = 10;
    v34 = dword_402BAC;
    v33[0] = 8;
    _vbaVarDup(&v45, v33, v49, v50);
    v48 = v36;
    v47 = v39;
    v46 = v42;
    v45 = 64;
    ((void (__stdcall *)(int *))rtcMsgBox)(&v45);
    v48 = v36;
    v47 = v39;
    v46 = v42;
    v45 = (int)&v45;
    ((void (__cdecl *)(int))_vbaFreeVarList)(4);
  }
  v54 = 0;
  v48 = (int *)&loc_4047CF;
  (*(void (__stdcall **)(_DWORD *))(*v55 + 8))(v55);
  return v54;
}

  if ( v29[0] < 60000 ) , ~~. else, ~~

구조로 이루어져 있는것을 보아, 해당 조건문은

 

  if ( v29[0] < 60000 ):

         "파일을 재생한지 1분이 지나지 않은 경우"

else

          "1분이 지난 경우"

 

이와 같은 구조로 이루어져 있는것을 확인할 수 있다. 또한 특이점으론, 

 

  if ( v29[0] < 60000 ):

         "파일을 재생한지 1분이 지나지 않은 경우"

        if if ( v29[0] > 60010 ): <<???

else

          "1분이 지난 경우"

 

이와 같이 전제 조건에 맞지 않는 조건문이 안에 들어있는것을 확인하여, 해당 부분 로직을 실행하기 위해 바이너리 패치를 하였다.

패치 전
패치 후

이후, 바이너리를 다시 실행해보면, 

노래는 1분 이후로 재생되지만, 어떠한 동작을 하는 도중 runtime error가 발생한다.

해당 error를 유발하는 함수를 찾기위해, conditional breakpoint를 설정하여 디버깅하였다.

여기서 get_reg_value는 IDC의 ida api로, ida 공식 홈페이지에서 확인할 수 있다.

또한, 'v29[0]'이 60010 초과일때, 또다른 로직이 실행되므로, 해당값 이상일 경우 break하도록 설정하였다.

 

v29[0]이 60073인 시점에 break하였고, 프로시저 하나하나 실행해보면, 

150, 159번 줄의 'vbaHresultCheckObj' 함수를 호출할 시, floating point 에러가 발생하는 것을 알 수 있다.

위 조건문을 건너뛰도록 바이너리 패치를 하면, 

 

위와 같이 flag가 messagebox의 제목으로 나오는것을 볼 수 있다.

728x90

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

[reversing.kr] Replace  (0) 2024.02.16
[reversing.kr] Easy Keygen  (0) 2021.04.14
[reversing.kr] Easy_CrackMe  (0) 2020.11.29
728x90

평균 

시간 제한             메모리 제한                   제출                       정답                         맞힌 사람                    정답 비율

 

2 초 128 MB 267460 134561 109518 49.876%

문제

세준이는 기말고사를 망쳤다. 세준이는 점수를 조작해서 집에 가져가기로 했다. 일단 세준이는 자기 점수 중에 최댓값을 골랐다. 이 값을 M이라고 한다. 그리고 나서 모든 점수를 점수/M*100으로 고쳤다.

예를 들어, 세준이의 최고점이 70이고, 수학점수가 50이었으면 수학점수는 50/70*100이 되어 71.43점이 된다.

세준이의 성적을 위의 방법대로 새로 계산했을 때, 새로운 평균을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 시험 본 과목의 개수 N이 주어진다. 이 값은 1000보다 작거나 같다. 둘째 줄에 세준이의 현재 성적이 주어진다. 이 값은 100보다 작거나 같은 음이 아닌 정수이고, 적어도 하나의 값은 0보다 크다.

출력

첫째 줄에 새로운 평균을 출력한다. 실제 정답과 출력값의 절대오차 또는 상대오차가 10-2 이하이면 정답이다.

예제 입력 1 복사

3
40 80 60

예제 출력 1 복사

75.0

예제 입력 2 복사

3
10 20 30

예제 출력 2 복사

66.666667

10-2 이하의 오차를 허용한다는 말은 정확히 소수 2번째 자리까지 출력하라는 뜻이 아니다.

예제 입력 3 복사

4
1 100 100 100

예제 출력 3 복사

75.25

예제 입력 4 복사

5
1 2 4 8 16

예제 출력 4 복사

38.75

예제 입력 5 복사

2
3 10

예제 출력 5 복사

65.0

예제 입력 6 복사

4
10 20 0 100

예제 출력 6 복사

32.5

예제 입력 7 복사

1
50

예제 출력 7 복사

100.0

예제 입력 8 복사

9
10 20 30 40 50 60 70 80 90

예제 출력 8 복사

55.55555555555556

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
	int num;
	double max, sum, result;
	
	scanf("%d", &num);
	double* val = (double*)malloc(sizeof(double) * num);
	scanf("%lf", &val[0]);
	max = val[0];
	sum = val[0];
	for (int i = 1; i < num; i++) {
		scanf("%lf", &val[i]);
		if (max < val[i])
			max = val[i];
		sum += val[i];
	}
	result = (sum / ((double)num * max)) * 100.0;
	printf("%lf", result);
	free(val);
}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1436 영화감독 숌  (1) 2024.02.10
[백준] 1259 팬린드롬수  (1) 2024.02.10
[백준] 1002번 터렛  (0) 2023.12.26
728x90

영화감독 숌 

 
시간 제한          메모리 제한                    제출                         정답                         맞힌 사람           정답 비율

 

2 초 128 MB 93940 54189 43787 57.363%

문제

666은 종말을 나타내는 수라고 한다. 따라서, 많은 블록버스터 영화에서는 666이 들어간 제목을 많이 사용한다. 영화감독 숌은 세상의 종말 이라는 시리즈 영화의 감독이다. 조지 루카스는 스타워즈를 만들 때, 스타워즈 1, 스타워즈 2, 스타워즈 3, 스타워즈 4, 스타워즈 5, 스타워즈 6과 같이 이름을 지었고, 피터 잭슨은 반지의 제왕을 만들 때, 반지의 제왕 1, 반지의 제왕 2, 반지의 제왕 3과 같이 영화 제목을 지었다. 하지만 숌은 자신이 조지 루카스와 피터 잭슨을 뛰어넘는다는 것을 보여주기 위해서 영화 제목을 좀 다르게 만들기로 했다.

종말의 수란 어떤 수에 6이 적어도 3개 이상 연속으로 들어가는 수를 말한다. 제일 작은 종말의 수는 666이고, 그 다음으로 큰 수는 1666, 2666, 3666, .... 이다. 따라서, 숌은 첫 번째 영화의 제목은 "세상의 종말 666", 두 번째 영화의 제목은 "세상의 종말 1666"와 같이 이름을 지을 것이다. 일반화해서 생각하면, N번째 영화의 제목은 세상의 종말 (N번째로 작은 종말의 수) 와 같다.

숌이 만든 N번째 영화의 제목에 들어간 수를 출력하는 프로그램을 작성하시오. 숌은 이 시리즈를 항상 차례대로 만들고, 다른 영화는 만들지 않는다.

입력

첫째 줄에 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.

예제 입력 1 복사

2

예제 출력 1 복사

1666

예제 입력 2 복사

3

예제 출력 2 복사

2666

예제 입력 3 복사

6

예제 출력 3 복사

5666

예제 입력 4 복사

187

예제 출력 4 복사

66666

예제 입력 5 복사

500

예제 출력 5 복사

166699

 

브루트포싱 알고리즘이다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int calculate(int num);
int check_6(int num);

int main() {
	int user_input;
	scanf("%d", &user_input);
	printf("%d", calculate(user_input));
	return 0;
}

int calculate(int num) {
	int count = 0;
	
	for (int i = 1; i < 2147483647; i++) {
		if (check_6(i))
			count++;
		if (count == num)
			return i;
	}
}


int check_6(int num) {
	char string[11];
	snprintf(string, sizeof(string), "%d", num);
	for (int i = 0; i <= 7; i++) {
		if (string[i] == '6' && string[i + 1] == '6' && string[i + 2] == '6')
			return 1;
	}
	return 0;
}

 

 

 

728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
[백준] 1259 팬린드롬수  (1) 2024.02.10
[백준] 1002번 터렛  (0) 2023.12.26
[백준] 10828  (0) 2023.11.20

+ Recent posts