한글 (총 11172자, 0xAC00 ~ 0xD7A3, 가~힣)
0xAC00 : 한글시작코드 (가), 0xD7A3 : 한글종료코드 (힣)
한글자모 (0x3131 ~ 318E) - Hangul Compatibility Jamo
http://unicode.org/charts/nameslist/n_3130.html 참조
유니코드값 = 0xAC00 + 28*21*(초성 index) + 28*(중성 index) + (종성 index)
초성 : ( 유니코드 값 - 0xAC00 ) / (28 * 21)
중성 : (( 유니코드 값 - 0xAC00 ) % (28 * 21 )) / 28
종성 : ( 유니코드 값 - 0xAC00 ) % 28
한글빈도표를 참고해보면
https://docs.google.com/spreadsheets/d/1gmUt8An8uvvfxPQvbY7XXizNgJ87-paNcUj9Prz8qiI/edit?usp=sharing
초성에는 ㅇ,ㄱ 이 가장 많이 나오고
중성에는 ㅏ,ㅣ가 가장 많이 나오며
종성에는 공백,ㄴ 이 가장 많이 나온다
또한 음절은 '이' 가 가장 많이 나온다.
그리고 마지막으로 마침표 앞에는 '다'로 끝나는 경우가 가장 많다.
이런 통계를 기반으로 치환암호를 조금씩 풀면 치환암호도 풀이가 가능하다
아래 소스는 이러한 방법을 기반으로 whitehat 2014 예선문제를 풀이한 결과이다.
#coding: utf8 ''' 초성 테이블 (19) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18 ㄱ,ㄲ,ㄴ,ㄷ,ㄸ,ㄹ,ㅁ,ㅂ,ㅃ,ㅅ,ㅆ,ㅇ,ㅈ,ㅉ,ㅊ,ㅋ,ㅌ,ㅍ,ㅎ 중성,테이블 (21) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20 ㅏ,ㅐ,ㅑ,ㅒ,ㅓ,ㅔ,ㅕ,ㅖ,ㅗ,ㅠ,ㅘ,ㅛ,ㅙ,ㅚ,ㅜ,ㅝ,ㅞ,ㅟ,ㅡ,ㅢ,ㅣ 종성,테이블 (28) 0, 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 ,ㄱ,ㄲ,ㄳ,ㄴ,ㄵ,ㄶ,ㄷ,ㄹ,ㄺ,ㄻ,ㄼ,ㄽ,ㄾ,ㄿ,ㅀ,ㅁ,ㅂ,ㅄ,ㅅ,ㅆ,ㅇ,ㅈ,ㅊ,ㅋ,ㅌ,ㅍ,ㅎ ''' def divide_char(char): num = ord(char) - 0xac00 if num<0: return None cho = num / (21*28) jung = (num % (21*28))/28 jong = num % 28 return cho, jung, jong def join_char(cho,jung,jong): num = 0xac00 + cho*21*28 + jung *28 + jong return unichr(num) unicoder = lambda x:unicode(x.decode('utf8')) #unicoder = lambda x:x original_table = [map(unicoder, ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ","ㅎ"]), map(unicoder, ["ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ","ㅣ"]), map(unicoder, [" ", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ","ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"])] replace_table = [map(unicoder, ["ㅌ", "ㄹ", "ㅇ", "ㄸ", "ㅈ", "ㄹ", "ㄷ", "ㅍ", "ㄱ", "ㅃ", "ㅆ", "ㄲ", "ㅁ", "ㅎ", "ㅆ", "ㄴ", "ㅊ", "ㅂ","ㅅ"]), map(unicoder, ["ㅟ", "ㅗ", "ㅏ", "ㅜ", "ㅛ", "ㅑ", "ㅚ", "ㅡ", "ㅖ", "ㅣ", "ㅐ", "ㅘ", "ㅗ", "ㅔ", "ㅝ", "ㅛ", "ㅟ", "ㅕ", "ㅢ", "ㅝ","ㅓ"]), map(unicoder, ["ㅊ", "ㄱ", "ㄲ", "ㄳ", "ㅆ", "ㅀ", "ㅅ", "ㅇ", "ㅁ", "ㄹ", "ㅊ", "ㄷ", "ㅄ", "ㄺ", "ㅍ", "ㅂ", "ㅆ", "ㄶ", "ㄽ", "ㅅ","ㄴ", "ㄺ", "ㄻ", " ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"])] encrypted = "컃콌 냧쟻 삑띧묯 닔놫 뺯녥 횩늧 퓱몕녥 먗 쭑 몒쨣쾇먗. 쟻뺻쬧 햘팄꽟 쬧뀿 뺟햤 뫷쨨 햧꺌 뫷 쨨녳꾳 뽥놫 푳띴쨫훛. 뱿꼤 햤퐒녥 왳퇓뿇 먔뱯컃쟻 햺녥 쩯쨫늋 컀 띡녰 뽥녥 삙닟힟 턏쟟 딝퇓뿇 뺨혫쾇먗. 컃콌 쩯뽳꾳넀 >콸늧 쟑횿꽟눛 빷직뿇 욞먗넀 콸늧 닑뺽눛 컜짉님혫쾇먗. 뺯혤 횩눛 쨫컃 먥 횇쁓뙃콌 퓱녥 놫뚳 먗 죦 쭟콌 삖녰 핯놫 냧퇄놫 뇿콌 얓먍놫뉓. 캴햤 낯눛 띷 횿컃쟻 틹뺧녥 먳꼤 몒 퍛꺌 힟꽟 퍩퓼쨜녰 넃꽟 뽳햗놝훛. 쾣놝 퍌놫 캴녰 얓먍놫 뉓. 냧똭 컃늧 틷턨놫 먗 쨫뙃 냡녰 얓먍놣쾇먗. 떅콌 캵얓뙃 쨫콁녥 넃낓낓 쨨띨 퍷옟낄놫 닔뽳꼙, 볡꽻뼻 콌 낯놫놛낯놫놛늧 뉻걏, 놢횇눛 놫콌 퍛꺌눛묯 컃콌 뼟꾳닃쬄먗. 컃콌 쟻닎놨뙃 뼻꽟닃 놫 쟙녰 퓱퐒놫 쾣꽜 닜믹 낯 눛 쾣 놫꼘땿 꼙 칯 푳뿇 쪭녳꾳 밆닟 핓꽟님혫쾇먗. 뼻낓컃 놫퓱녥 쳵뭋닔콌 컜쟭늧 닀팄녥 쟜몕뿇 쟟콌 삖녰 혳혳꾳 햧꺋녥 왳퇓콌 삖놨 떍 냧콌 얓먍눛, 삛땷녥 헃 닔콌 혥볠늧 쫠녥 뇾쁓힟 횇 쯫쟏늧 띧헃퍅놫눛 몕닟퍷님혫쾇먗. 뚳 뾱뚗꼙 죦 놫뽳콌 햧꺋늧 쿛꽻콌 콸늧 퇄쟥녥 짫챷뿇 묧쾇먗. 묟쬋쟭뾫 퐥먳햤놫 쟟꼧뿇 먅묯꾝 쨫콏콸놫 푳넃쨫햧 넃 꽟컃꺛 쟜훛. 퓱녥 쿛꽻쨫콌 쟟녤녳꾳 죷몠 떅닟뺯콌 삖녥 햧꺋쬋넻뙃 뼻꽟뿇 컃쨨궃 떛닟뙀 뽥녥 삙닟뺯넻뿜먗." unicoded = unicoder(encrypted) decrypted = u"" stat = {'cho':{}, 'jung' : {},'jong' :{}, 'char' :{} } for i in unicoded: ret = divide_char(i) if not ret: decrypted+=i continue if i in stat['char']: stat['char'][i]=stat['char'][i]+1 else: stat['char'][i]=1 cho = original_table[0][ret[0]] jung = original_table[1][ret[1]] jong = original_table[2][ret[2]] if cho in stat['cho']: stat['cho'][cho]=stat['cho'][cho]+1 else: stat['cho'][cho]=1 if jung in stat['jung']: stat['jung'][jung]=stat['jung'][jung]+1 else: stat['jung'][jung]=1 if jong in stat['jong']: stat['jong'][jong]=stat['jong'][jong]+1 else: stat['jong'][jong]=1 new_ret=list() for i in range(3): new_item = replace_table[i][ret[i]] new_ret.append(original_table[i].index(new_item)) decrypted+=join_char(new_ret[0],new_ret[1],new_ret[2]) print decrypted print '\n[+] 음절 [+]' for x,y in sorted(stat['char'].items(),key=lambda x:x[1], reverse=True): print "%c:%d\t" % ( x,y), print '\n[+] 초성 [+]' for x,y in sorted(stat['cho'].items(),key=lambda x:x[1], reverse=True): print "%c:%d\t" % ( x,y), print '\n[+] 중성 [+]' for x,y in sorted(stat['jung'].items(),key=lambda x:x[1], reverse=True): print "%c:%d\t" % ( x,y), print '\n[+] 종성 [+]' for x,y in sorted(stat['jong'].items(),key=lambda x:x[1], reverse=True): print "%c:%d\t" % ( x,y), print ''