티스토리 뷰
CTF_WriteUps/2015_CTF
[2015_TrendMicro_CTF] (Programming 500) BlackJack
hackability 2015. 10. 26. 17:59최종 수정: 2015-10-26
hackability@TenDollar
안녕하세요. Hackability 입니다.
이번 포스팅 할 내용은 2015 TrendMicro CTF 에서 있었던 프로그래밍 문제인 BlackJack 에 대해 포스팅 합니다.
이번 문제는 블랙잭 특정 상황에서 Hit이나 Stand 를 했을 경우 이길 확률이 어떻게 되는지 구하는 문제입니다.
I'm playing heads up blackjack. I'm dealt Ace-2 and the dealer has 3 showing. What's the probably of winning if: 1) I stand or 2) I kept playing.
시작은 플레이어가 Ace와 2를 갖고 있고 딜러는 3이 펼쳐 졌을 경우
1) stand 를 했을 때 이길 확률
2) 계속 플레이를 했을 때 이길 확률
을 계산하는 문제 입니다.
추가적으로 플레이어는 플레이에 제약이 없지만 (더블 다운이나 슬플릿은 없음) 딜러의 경우 17 이상일 경우 무조건 stand 한다는 조건이 있습니다.
먼저, 이런 문제를 풀기 위해서 확률 모델을 설계를 해야 하는데 확률 모델에는 deterministic (확정형) 모델과 stochastic (추계형) 모델이 존재합니다. 확정형 확률 모델은 변수들의 관계가 확실하여 예측을 정확히 할 수 있는 경우에 쓸수 있는 확률 모델이고 추계형 확률 모델은 프로세스의 부분 부분이 특정 확률에 의해 계속 변하게 되어 예측할 수 없을 경우 사용됩니다.
따라서, 일반적으로 확정형 모델에서는 분석적 해를 찾을 수 있지만 추계형 모델에서는 이런 분석적 해를 찾기 힘든 경우가 많습니다. 따라서 이런 문제를 해결하기 위해서는 수치적 난수를 크게 발생 시켜 시뮬레이션을 통해 해에 근접하는 결과를 도출하는 것이 일반적입니다.
따라서, 이 문제를 해결하기 위해 추계형 확률 모델에서 시뮬레이팅을 하기 위해 사용되는 Monte Carlo 기법을 이용하여 해결을 합니다.
번외로 Monte Carlo 기법은 통계 자료가 많고 입력값의 분포가 고를수록 정밀한 시뮬레이션이 되기 때문에 일반적으로 컴퓨터를 이용해 난수를 생성하여 시뮬레이션을 합니다. 또한 이론적 배경이나 복잡한 수식을 계산해야 하는 경우, 근사치를 계산하기 위해서도 몬테카를로 기법이 많이 사용되고 있는데 몬테 카를로 이름의 부터 몬테 카를로라는 유명한 도박 도시에서 도박사들이 여러번의 임의 추출을 바탕으로 특정 카드 조합이 나올 때까지 계산을 하여 붙여진 이름이라고 합니다. 또한 폰 노이만은 원자폭탄 개발 계획인 맨하탄 프로젝트에서 중성자 확산 시뮬레이션에 이 기법을 사용했고, 최근에는 위험관리, 품질 관리, 금융공학 등에서도 이 기법이 사용되고 있다고 합니다.
기본적으로 몬테 카를로 시뮬레이션은 다음의 절차를 따릅니다.
1. 입력 값의 범위를 정함
2. 확률 분포에 따라 입력값을 랜덤하게 만듬
3. 입력값에 대한 계산
4. 계산 결과를 통합
wiki 에서 원주율을 구하는 몬테 카를로 기법으로 다음과 같이 정의하고 있습니다.
1. 정사각형을 그리고 그 안에 원을 그린다.
2. 정사각형 안에 수 많은 점을 고르게 분포 한다.
3. 분포한 전체 점의 개수와 원 안에 분포한 점의 개수를 센다.
4. 전체 점의 개수와 원 안의 점 개수의 비율로 원주율 파이를 계산한다.
이제 우리가 풀려고 하는 문제를 몬테 카를로 시뮬레이션 절차로 만들어 봅니다.
1. 카드가 2 Deck 사용되는데 초반에 3장이 오픈 되었기 때문에 (52 * 2 - 3)의 카드가 존재
2.
- 플레이어가 Hit 을 하는 경우 한장 가져옴
- 만약 플레이어가 Stand를 하는 경우, 딜러가 한장 가져옴
- 딜러는 최소 17이 될 때 까지 Hit을 함
3. 확률을 계산
4. 소수점 4자리까지 반복
한 가지 더 추가 사항으로 딜러가 특정 상황일 때 플레이어가 해야 할 플레이에 대해 다음과 같습니다.
검은색으로 칠한 부분이 딜러가 처음에 3 일경우 플레이어가 플레이 해야 될 테이블 입니다. 이를 이용하여 Monte Carlo Simulation 을 구현해 봣습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | import random # 0: Stand, 1: Hit DEBUG = 1 def generate_decks(n_decks): lst_tmp = [] for i in range(0, n_decks): for j in range(1, 10): lst_tmp.append(j) lst_tmp.append(j) lst_tmp.append(j) lst_tmp.append(j) # 10, J, Q, K for j in range(0, 4): for k in range(0, 4): lst_tmp.append(10) random.shuffle(lst_tmp) return lst_tmp n_iter = 1000000 n_decks = 2 p_win = 0.0 p_lose = 0.0 p_draw = 0.0 if DEBUG == 0: for i in range(0, n_iter): if (i+1) % 10000 == 0: print "[%6d] : %f" % (i, (p_win / (p_win+p_lose))) lst_deck = generate_decks(n_decks) player = [1, 2] dealer = [3] sum_d = 0 lst_deck.remove(1) lst_deck.remove(2) lst_deck.remove(3) j = -1 while sum_d < 17: j = j + 1 dealer.append(lst_deck[j]) if 1 in dealer: sum_d = 0 count_a = 0 for k in range(0, len(dealer)): if dealer[k] == 1: count_a = count_a + 1 else: sum_d = sum_d + dealer[k] if count_a >= 1: if (sum_d + 11) + (count_a-1) <= 17: sum_d = sum_d + 11 + count_a else: sum_d = sum_d + count_a else: sum_d = sum(dealer) if sum_d > 21: p_win = p_win + 1 else: p_lose = p_lose + 1 print "result : %f" % (p_win / (p_win+p_lose)) else: bj_table_with_a = { '3': 'H', '4': 'H', '5': 'H', '6': 'H', '7': 'S', '8': 'S', '9': 'S', '10': 'S' } bj_table_without_a = { '12': 'H', '13': 'S', '14': 'S', '15': 'S', '16': 'S', '17': 'S', '18': 'S', '19': 'S', '20': 'S', '21': 'S' } for i in range(0, n_iter): if (i+1) % 10000 == 0: print "[%6d] : %f" % (i, (p_win / (p_win+p_lose+p_draw))) lst_deck = generate_decks(n_decks) player = [1, 2] dealer = [3] sum_p = 0 sum_d = 0 lst_deck.remove(1) lst_deck.remove(2) lst_deck.remove(3) turn_player = True for j in range(0, len(lst_deck)): if turn_player : player.append(lst_deck[j]) sum_p = sum(player[1:]) if sum_p >= 2 and sum_p <= 10: if sum_p <= 6: continue else: sum_p = sum_p + 11 turn_player = False continue else: if sum_p == 11: continue else: sum_p = sum_p + 1 turn_player = False continue else : dealer.append(lst_deck[j]) if 1 in dealer: sum_d = 0 count_a = 0 for k in range(0, len(dealer)): if dealer[k] == 1: count_a = count_a + 1 else: sum_d = sum_d + dealer[k] if count_a >= 1: if (sum_d + 11) + (count_a-1) <= 17: sum_d = sum_d + 11 + (count_a-1) else: sum_d = sum_d + count_a else: sum_d = sum(dealer) if sum_d >= 17: break if sum_p > 21: p_lose = p_lose + 1 elif sum_d > 21: p_win = p_win + 1 elif sum_p > sum_d: p_win = p_win + 1 elif sum_p == sum_d: p_draw = p_draw + 1 else: p_lose = p_lose + 1 print "result : %f" % (p_win / (p_win+p_lose+p_draw)) | cs |
100 만번 씩 시뮬레이션 결과 (원래는 특정 소수점 밑 까지 수렴할 때까지 해야 함)
처음에 Stand 할 경우의 이길 확률은 result : 0.3756
처음에 Hit 할 경우 이길 확률은 result : 0.5106
정답으로 인증한 팀의 경우 처음 Stand 할 경우 이길 확률이 0.3770, 처음에 Hit 할 경우 이길 확률이 0.5094 로 나왓는데 저랑 조금 차이가 나는 이유는 정확히 계산하는 알고리즘 차이도 있지만 난수 생성에도 있습니다. 제가 사용한 난수는 python random 모듈의 shuffle을 사용했는데 python의 random 모듈이 난수 생성 성능이 그다지 좋지 않다고 나와 있습니다. python document 에서는 random 모듈 보단 urandom 을 사용하라고 권장을 합니다.
좀 아쉽긴 하지만 추후에 이런 Monte Carlo 시뮬레이션 문제가 나오면 numpy나 matlab 같은 전문 툴을 이용해서 좀 더 정확히 구현을 해야 할 것 같습니다.
'CTF_WriteUps > 2015_CTF' 카테고리의 다른 글
[2015_TrendMicro_CTF] (Programming 100) click on the different color (0) | 2015.10.27 |
---|---|
[2015_CSAW_CTF] (Reversing 300) FTP (0) | 2015.10.27 |
[2015_Layer7_CTF] (Reversing 150) ReverseMe (0) | 2015.10.27 |
[2015_Hackover_CTF] (Pwnable 75) easy_shell (0) | 2015.10.26 |
[2015_HITCON_CTF] (Pwnable 200) nanana (0) | 2015.10.26 |
댓글
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- shellcode writing
- 2015 School CTF
- TenDollar
- School CTF Writeup
- IE 11 UAF
- IE 11 exploit development
- IE UAF
- IE 10 리버싱
- WinDbg
- IE 10 익스플로잇
- TenDollar CTF
- shellcode
- IE 11 exploit
- 윈도우즈 익스플로잇 개발
- 힙 스프레잉
- 쉘 코드
- Windows Exploit Development
- IE 10 God Mode
- data mining
- heap spraying
- UAF
- 2014 SU CTF Write UP
- IE 10 Exploit Development
- 데이터 마이닝
- Mona 2
- expdev 번역
- School CTF Write up
- Use after free
- 쉘 코드 작성
- CTF Write up
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함