늘모자란, 개발

늘모자란, 개발


JUST COMPARE ONLY.

with the other value :D


소스를 보니 md5의 해쉬 충돌 취약점에 대해 말하고 싶은듯 하다. 그냥느낌이 그렇다


 <?php
    if (isset($_GET['view-source'])) {
         show_source(__FILE__);
         exit();
    }

    if (isset($_GET['v1']) && isset($_GET['v2'])) {
        sleep(3); // anti brute force

        $chk = true;
        $v1 = $_GET['v1'];
        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$chk = false;}
        if (!is_numeric($v2) ) {$chk = false;}
        if (md5($v1) != md5($v2)) {$chk = false;}

        if ($chk){
            include("../lib.php");
            echo "Congratulations! FLAG is : ".auth_code("md5_compare");
        } else {
            echo "Wrong...";
        }
    }
?>
<br />
<form method="GET">
    VALUE 1 : <input type="text" name="v1" /><br />
    VALUE 2 : <input type="text" name="v2" /><br />
    <input type="submit" value="chk" />
</form>
<br />
<a href="?view-source">view-source</a>



ctype_alpha 는 좀 생소한 PHP function인데, 모두 알파벳인지 확인하는 함수라고 한다.
즉, v1은 전부 알파벳으로만 이루어져야하겠다.
그리고 v2는 전부 숫자여야만 하겠고..

해쉬 충돌은 바이너리 단위로 가야되기때문에 아닌것 같았고 결국 검색어를 다르게 해서,
php md5 vulnerability 로 검색해봤더니... 띠용..

0e 로 시작하게 될경우 hex로 취급되서 지수화 된다고 한다. 정확한 작동원리는 모르겠지만 아니 세상에 뭐 이런게 다 있지 하는 생각이 들었다.

2016/06/08 14:05 2016/06/08 14:05
javascript game.

can you clear with bypass prevent cheating system?


뭐 같은 게임하나 나온다.


게임 소스는 minify 및 난독화된 스크립트가 있는데 처음에 게임을 봤을때 든 생각은 벽 이미지를 다 없애버려서 충돌을 없애거나 점수를 조작하면 되지 않을까였다

그래서 right_wall 과 left_wall 의 width를 전부 0으로 바꿔버렸는데 충돌이 일어났다.
우주선 크기도 1로 했는데 충돌이 일어났다. 뭐지 도대체?
그래서 정상적으로 게임을 해보니까, 벽이 도저히 우주선이 피할 수 없을만큼 좁아져 온다. 에라이...

이것저것 클라이언트로 시도 해봤는데 안될것 같단 결론을 내리고 죽을때 보내는 패킷을 빠르게 낚아채서 점수를 보내보기로 했다
허무하다... 토큰이 지속적으로 바뀌는데 토큰이 바뀌기 전에 재빨리 스코어를 보내주면 그냥.. 키가 온다...

2016/06/07 21:37 2016/06/07 21:37
What kind of this Database?

you have to find correlation between user name and database.


들어가보면 휑한 화면이 있다. 소스를 보면

<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script text="javascript">
function fschk(f){
 if(f.user_id.value=="admin"){
  alert("dont access with 'admin'");
  return false;
 }
}
</script>
<style>
    body {width:100%; height:100%; padding:0; margin:0;}
</style>
</head>
<body>

<div style='width:400px; margin:50 auto; text-align:center;'>
 <h2>BLUEMEMO SYSTEM <sub>0.53 beta</sub></h2>
 <form method="post" action="memo.php" onsubmit="return fschk(this);">
 USER <input type='text' maxlength=16 size=16 name='user_id' /> <input type='submit' value='LOGIN' />
 </form>
</div>

</body>


이렇게 되어 있는데 admin + ' ' 이나 ' ' + admin 으로 들어가면 알아서 trim되기때문에 admin으로 들어갈 수 있다.
수많은 사람들의 흔적을 볼 수 있는데 이걸로 뭘 할 수 있을지 생각해야겠다.
이 문제가 웃긴게 다른 사람들이 한 수많은 삽질을 볼 수 있다는것이다 ㅋㅋ 생각이 다들 비슷비슷한듯하다...


아이디 시도로 요렇게 해봤다.

0x61646d696e
CHAR(97,100,109,105,110) //칸을 늘려야 했음

근데 애초에 뭐 다 처리를 해놨는지 안된다.

그래서 이제 기본적으로 가보기로 했다.
' " < / 를 넣어보기로 한것이다

"엔 반응이 없는데 ' 랑 / 때는 반응이 있었다.
Fatal error: Uncaught exception 'Exception' with message 'Unable to open database: unable to open database file' in /home/www/db_is_really_good/sqlite3.php:7 Stack trace: #0 /home/www/db_is_really_good/sqlite3.php(7): SQLite3->open('./db/wkrm_/.db') #1 /home/www/db_is_really_good/memo.php(14): MyDB->__construct('./db/wkrm_/.db') #2 {main} thrown in /home/www/db_is_really_good/sqlite3.php on line 7


sqlite3.php 를 열려는 와중에 에러가 난다고 하는데
./db/wkrm_/.db 를 찾을 수 없다고 하는것 같다. 확실하게 하기 위해서 슬래쉬 뒤에 a를 붙여서 /a로 시도 해보았다.

Fatal error: Uncaught exception 'Exception' with message 'Unable to open database: unable to open database file' in /home/www/db_is_really_good/sqlite3.php:7 Stack trace: #0 /home/www/db_is_really_good/sqlite3.php(7): SQLite3->open('./db/wkrm_/a.db') #1 /home/www/db_is_really_good/memo.php(14): MyDB->__construct('./db/wkrm_/a.db') #2 {main} thrown in /home/www/db_is_really_good/sqlite3.php on line 7


그러니까 즉 참조하는 주소는 ./db/wkrm_().db 라고 볼 수 있다. admin이 블럭되고 있는 아이디니까 여기에 admin을 넣어서 접근해본다.
php 주소가 하나 적혀있고 해당 주소에서 나오는 key를 입력하여 해결!
2016/06/07 21:14 2016/06/07 21:14
This is another programming language.

Can you read this source code?


문제에 들어가서 파일을 하나 다운받아보면 탭과 스페이스만으로 이루어진 진짜 별 또라이같은 파일을 다운로드할 수 있게 되어 있다.
뭔가 규칙은 있겠지만 그냥 병신같다는 말밖에 안나온다. 문제가 왜 WTF인지 알겠다.
hex로 읽어도, base64 encode해봐도 space, tab, lf밖에 안나온다.

혹시나해서 위 세개로 검색하니 ㅋㅋ brainfuck 마냥 저걸로만 이루어진 언어가 있단다. 진짜 지랄났다고밖에 말을 할수가 없다...
이곳에서 whitespace compiler(-_-)를 지원한다. 복사해서 돌려보면 답이 나온다.

세상에 병신같은건 oak 나 brainfuck 같은류만 있는줄 알았더니 정말 신세계가 따로 없다. 허참ㅋㅋㅋ

2016/06/07 20:50 2016/06/07 20:50
I have accounts. but, it's blocked.

can you login bypass filtering?


인젝션 느낌이 솔솔 난다.
소스를 보라고 권하기 때문에 소스를 보자면

 <?php

if (isset($_GET['view-source'])) {
    show_source(__FILE__);
    exit();
}

/*
create table user(
 idx int auto_increment primary key,
 id char(32),
 ps char(32)
);
*/

 if(isset($_POST['id']) && isset($_POST['ps'])){
  include("../lib.php"); # include for auth_code function.

  mysql_connect("localhost","login_filtering","login_filtering_pz");
  mysql_select_db ("login_filtering");
  mysql_query("set names utf8");

  $key = auth_code("login filtering");

  $id = mysql_real_escape_string(trim($_POST['id']));
  $ps = mysql_real_escape_string(trim($_POST['ps']));

  $row=mysql_fetch_array(mysql_query("select * from user where id='$id' and ps=md5('$ps')"));

  if(isset($row['id'])){
   if($id=='guest' || $id=='blueh4g'){
    echo "your account is blocked";
   }else{
    echo "login ok"."<br />";
    echo "Password : ".$key;
   }
  }else{
   echo "wrong..";
  }
 }
?>
<!DOCTYPE html>
<style>
 * {margin:0; padding:0;}
 body {background-color:#ddd;}
 #mdiv {width:200px; text-align:center; margin:50px auto;}
 input[type=text],input[type=[password] {width:100px;}
 td {text-align:center;}
</style>
<body>
<form method="post" action="./">
<div id="mdiv">
<table>
<tr><td>ID</td><td><input type="text" name="id" /></td></tr>
<tr><td>PW</td><td><input type="password" name="ps" /></td></tr>
<tr><td colspan="2"><input type="submit" value="login" /></td></tr>
</table>
 <div><a href='?view-source'>get source</a></div>
</form>
</div>
</body>
<!--

you have blocked accounts.

guest / guest
blueh4g / blueh4g1234ps

-->



blueh4g라는 아이디로 로그인을 해야되는데 로그인이 안되는 그런 느낌적인 느낌이다.
실제로 시도해봐도 블럭된다고 뜬다. 이제 우린 어떻게 해야할것인가? 특문같은것은 mysql_real_escape_string 덕에 시도할맛이 안나고..
그런데 마지막 주석이 의미심장하다.

가만보니 비교문도 === 가 아니라 == 이다. 이는 대소문자를 가리지 않는다는건데.
혹시나 하는마음에 Guest / guest를 시도하니 패스...

=== 를 써서 비교하는게 맞을 것 같다. 아니면 이상한 아이디랑 비밀번호 쓰지 말든지..
2016/06/07 20:35 2016/06/07 20:35
click the button!

i can't catch it!


느낌이 뭐가 졸라 날아다니고 그걸 클릭해야되는... 느낌이다.


날아다니진 않고 그냥 마우스 절대값에서 X,Y를 추가해 따라다닌다. 일반적으론 클릭할 수 없다.
오른쪽 마우스 클릭을 막아놨지만 메뉴에서 소스보기를 해서 소스를 확인하도록 하자.

<title>Just click it!</title> <style> body {color:#fff; background-color:#000;}; </style> <script src="./p8.js"></script>
<body>&nbsp;</body>
<script>eval(unescape_blue14("%72%7d%71%85%7b%73%7c%84%34%87%82%77%84%73%2c%85%7c%73%83%71%6d%80%73%6b%70%7a%85%73%37%3a%2c%26%29%3a%3a%29%3d%38%29%3d%3d%29%40%3c%29%38%3a%29%3d%3d%29%3d%38%29%3a%3b%29%38%3c%29%3d%39%29%40%39%29%3d%37%29%38%3c%29%38%3a%29%40%39%29%40%3a%29%40%41%29%3d%6d%29%3d%39%29%3a%3b%29%38%3c%29%40%36%29%3d%72%29%40%39%29%3d%3d%29%40%3a%29%3d%3d%29%3d%72%29%3d%71%29%3a%38%29%3c%72%29%3d%36%29%40%39%29%3d%72%29%3d%6d%29%40%3b%29%40%3a%29%3d%39%29%3a%39%29%38%3c%29%3a%3c%29%3a%3a%29%3d%3d%29%3d%71%29%40%36%29%40%3b%29%40%3a%29%38%3a%29%40%3a%29%40%41%29%40%36%29%3d%39%29%3a%3b%29%38%3c%29%3d%36%29%40%3b%29%40%3a%29%40%3a%29%3d%72%29%3d%71%29%38%3c%29%38%3a%29%3d%72%29%3d%71%29%3d%3a%29%3d%72%29%3d%37%29%40%3b%29%40%39%29%3a%3b%29%38%3c%29%3d%71%29%3d%72%29%3d%41%29%40%36%29%38%71%29%38%72%29%3a%39%29%38%3c%29%38%3a%29%3d%72%29%3d%71%29%3d%37%29%3d%6d%29%3d%3d%29%3d%37%29%3d%41%29%3a%3b%29%38%3c%29%40%3d%29%3d%3d%29%3d%71%29%3d%38%29%3d%72%29%40%3d%29%39%3a%29%3d%6d%29%3d%72%29%3d%37%29%3c%72%29%40%3a%29%3d%3d%29%3d%72%29%3d%71%29%3a%3b%29%38%70%29%3a%3d%29%3d%41%29%3d%39%29%40%41%29%3a%3b%29%39%40%29%3D%37%29%39%4B%29%39%4C%29%38%70%29%3a%39%29%38%3c%29%38%3a%29%40%3c%29%3c%72%29%3d%6d%29%40%3b%29%3d%39%29%3a%3b%29%38%3c%29%3d%37%29%3d%6d%29%3d%3d%29%3d%37%29%3d%41%29%38%3a%29%3d%70%29%3d%39%29%38%3b%29%38%3c%29%3a%3c%29%3a%3a%29%39%3b%29%3d%38%29%3d%3d%29%40%3c%29%3a%3c%29%3a%3a%29%3d%3d%29%3d%71%29%40%36%29%40%3b%29%40%3a%29%38%3a%29%40%3a%29%40%41%29%40%36%29%3d%39%29%3a%3b%29%38%3c%29%40%3a%29%3d%39%29%40%40%29%40%3a%29%38%3c%29%38%3a%29%40%38%29%3d%39%29%3c%72%29%3d%38%29%3d%72%29%3d%71%29%3d%6d%29%40%41%29%38%3a%29%40%39%29%40%3a%29%40%41%29%3d%6d%29%3d%39%29%3a%3b%29%38%3c%29%40%3d%29%3d%3d%29%3d%38%29%40%3a%29%3d%3c%29%3a%38%29%39%41%29%39%70%29%39%3c%29%3a%39%29%38%3c%29%38%3a%29%3d%3d%29%3d%38%29%3a%3b%29%38%3c%29%3d%3c%29%3d%3d%29%3d%71%29%40%3a%29%38%3c%29%38%3a%29%40%3c%29%3c%72%29%3d%6d%29%40%3b%29%3d%39%29%3a%3b%29%38%3c%29%3d%38%29%3d%72%29%38%3a%29%40%41%29%3d%72%29%40%3b%29%38%3a%29%40%3d%29%3c%72%29%3d%71%29%40%3a%29%38%3a%29%40%3a%29%3d%72%29%38%3a%29%3d%40%29%3d%72%29%3d%3d%29%3d%71%29%3a%3d%29%38%3a%29%3d%37%29%3c%72%29%40%3a%29%3d%37%29%3d%3c%29%38%3a%29%3d%36%29%40%3b%29%40%3a%29%40%3a%29%3d%72%29%3d%71%29%39%38%29%38%3a%29%3d%3d%29%3d%3a%29%38%3a%29%40%41%29%3d%72%29%40%3b%29%38%3a%29%3d%37%29%3c%72%29%3d%71%29%38%3b%29%38%3c%29%3a%3c%26%2d%2d%43%7d%70%78%45%72%7d%71%85%7b%73%7c%84%34%75%73%84%4d%7a%73%7b%73%7c%84%4a%89%53%72%2c%26%73%83%71%26%2d%43%72%7d%71%85%7b%73%7c%84%34%7d%7c%7b%7d%85%83%73%7b%7d%86%73%45%73%83%71%72%77%86%43%72%7d%71%85%7b%73%7c%84%34%7d%7c%79%73%89%80%82%73%83%83%45%7c%7d%79%80%43%7d%70%78%34%83%84%89%7a%73%34%7a%73%74%84%45%33%38%36%36%43%7d%70%78%34%83%84%89%7a%73%34%84%7d%80%45%33%38%36%36%43%86%6d%82%24%77%45%36%32%6d%88%45%36%32%6d%89%45%38%36%36%32%83%87%45%37%32%82%45%38%36%36%43%72%7d%71%85%7b%73%7c%84%34%75%73%84%4d%7a%73%7b%73%7c%84%4a%89%53%72%2c%2b%73%83%71%2b%2d%34%83%84%89%7a%73%34%84%7d%80%45%33%3b%36%36%43%72%7d%71%85%7b%73%7c%84%34%7d%7c%71%7d%7c%84%73%88%84%7b%73%7c%85%45%7c%7d%79%80%43%72%7d%71%85%7b%73%7c%84%34%7d%7c%83%73%7a%73%71%84%83%84%6d%82%84%45%7c%7d%79%80%43%72%7d%71%85%7b%73%7c%84%34%7d%7c%72%82%6d%75%83%84%6d%82%84%45%7c%7d%79%80%43"));</script>
<script>document.getElementById('hint').value="click button, if you want to get the authentication key";</script>



오우야.. 개빡세게 난독화되어있다.
unescape_blue14라는건 본적이 없기 때문에 사용자 함수라고 생각한다.
하지만 소스상에서는 확인할 수 없었기 때문에 뭔가 단서가 없을까 하다가 로드하는 스크립을 보니 p8.js 라는걸 로드하고 있다.

p8.js 의 소스는 다음과 같다.

function get_br_blue(){
 var br="def";
 if(navigator.appName.indexOf("Netscape")>-1){
  if(navigator.appVersion.indexOf("Macintosh")>-1) br="FFM";
  if(navigator.appVersion.indexOf("Windows")>-1) br="FFW";
 }else if(navigator.appName.indexOf("Microsoft")>-1){
  if(navigator.appVersion.indexOf("MEIE 6.0")>-1) {
   br="ie6";
  }else{
    br="ie";
  }
 }
 return br;
}

function unescape_blue14(str){
 var result="";
 for(i=1;i<str.length;i+=3){
  result+=""+String.fromCharCode(parseInt((str.substr(i,2)).toString(2),14));
 }
 return result;
}

function nokp(){
 document.getElementById("hint").focus();
 return false;
}

function escdiv(e){
 if(get_br_blue()=="FFM" || get_br_blue()=="FFW"){mx=e.clientX;my=e.clientY;}else{mx=event.x;my=event.y;}
 obj.style.left=mx+ax;
 obj.style.top=my+ay;
 if(sw==1){ax--;ay--;if(i==r){sw=2;i=0;}}
 if(sw==2){ax++;ay--;if(i==r){sw=3;i=0;}}
 if(sw==3){ax++;ay++;if(i==r){sw=4;i=0;}}
 if(sw==4){ax--;ay++;if(i==r){sw=1;i=0;}}
 i++;
}



여기까지 하고 이걸 어떻게 풀까 고민하는데.. html 이 보였다.

<div style="position: absolute; left: 186px; top: 692px;" id="esc"><input type="button" value="click me!" onclick="window.location='?key=2c67';" onfocus="nokp();"></div>


아니.. 클릭하면 ?key=2c67로 간다잖아?
URL을 입력하니 단번에 패스... 뭐여 이거 ..........
2016/06/07 20:23 2016/06/07 20:23
javascript puzzle challenge

just enjoy!

webhacking.kr에 비해서 문제가 신선하다는 느낌을 받기엔 좀 이른감이 있지만, QR code 퍼즐이라고 한다.

내가 생각하기로 QR코드 생긴건 정말 멋대로 생겼을거기땜에 결국 QR code가 valid한지 체크하는 검사도 수반되어야할 것 같은 생각이 들었다.

<center>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js'></script>
<script type="text/javascript" src="jquery.jqpuzzle.js"></script>
<script type='text/javascript' src='jquery.color-RGBa-patch.js'></script>
<script type='text/javascript' src='jquery.blockUI.js'></script>
<script type="text/javascript">
/*<![CDATA[*/
 $(function(){ $('#join_img').attr('src',unescape('.%2f%69%6d%67%2f%71%72%2e%70%6e%67'));
  $('#join_img').jqPuzzle({rows:6,cols:6,shuffle:true,numbers:false,control:false,style:{overlap:false}});
  hide_pz();});
 function hide_pz(){
  var pz=$('#join_img div'); if(pz[pz.length-2]){$(pz[1]).remove();$(pz[pz.length-2]).remove();}else{setTimeout("hide_pz()",5);}
 }
/*]]>*/
</script>
<style>
#join_img {padding:15px 15px 0 15px; border:2px solid #999; background-color:#444;}
</style>
<br />
<h1>QR Code Puzzle</h1>
<br />
<img id="join_img" /><br />


소스를 읽어보니 jqPuzzle이라는 라이브러리를 이용해 %2f%69%6d%67%2f%71%72%2e%70%6e%67 , 즉 /img/qr.png를 퍼즐로 만든다.
이렇게까지 말했는데 바보가 아닌이상에야 퍼즐을 다 풀진않겠지?

어쨌든 QR 코드를 찍으면 키가 나온다. 세션에 따라 키가 바뀌는듯하니 그냥 주소를 따서 보면되겠다.
2016/06/07 18:07 2016/06/07 18:07
연구실 선배가 심심해서 하고 있다길래 나도 심심한차에 시작해보기로 했다.

원래 CTF나 워게임같은경우는 암묵적인 룰(??) 이 있는건지 시커먼 배경인데 여긴 되게 깔끔하고 흰색톤이었다. 최근에 만들어진듯...
문제마다 포인트가 있긴한데 문제가 많진않았다. 그게 첫인상... 어쨌든 시작하기로 했으니 잘 해보자 생각한다.

문제마다 힌트가 주어지는 구조인것 같은데 첫 문제는 이런 힌트가 있고,



can you see HTTP Response header?


문제에 들어가니 그냥 you've already got key! :p 만 있다.
시키는데로 순순히 헤더를 까보기로 한다.

GET http://wargame.kr:8080/already_got/ HTTP/1.1
Host: wargame.kr:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ko,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://wargame.kr/challenge
Cookie: ...
Connection: keep-alive


아주 장황한 쿠키가 있는데 아마 쿠키 사이에 flag란게 숨어 있을 느낌이다.
fiddler로 쿠키를 urldecode해봤는데 세션 데이터가 나왔다. 해쉬가 두어개 있길래 입력해봤는데 아니라고 한다.
그래서 이상하다 싶어서 크롬을 열어서 Network 탭에서 보니까 오마이갓.. 정식 헤더가 아니라 그런지 안나온느낌이다.
헤더에 플래그가 있으니 당황하지 말고 입력해주자.
2016/06/07 17:56 2016/06/07 17:56

시작하며


제발 마지막 글이 되었으면 좋겠지만..

두번에 걸쳐 HSL과 delta E (CIE 2000)에 거친 색 정렬을 시도해보았으나 정렬된 결과가 아무래도 맘에 들지 않았다.
도대체 21세기에! 색을 정렬하는 방법이 없나?라는 의문을 가지고 구글링했으나.
맞다. 색을 정렬하는 방법은 없다.... 이상한 영상이 하나 있긴한데 색상을 기준으로 하는건 아닌것 같다.

이 글을 쓰게 된데는 이유가 있다. 바로 이 글을 봤기 때문이다.

Unfortunately this idea runs into a basic problem: You can’t sort colors. Because the human eye has three distinct color sensors (red, green, and blue), color is fundamentally a three-dimensional quantity, and there is no linear ordering that brings together “similar” colors. If you sort first by amount of red, for example, then you may bring together wildly different hues and brightnesses. If you sort by hue, then you bring together wildly different degrees of saturation and brightness, and so on. There’s just no way to do it.

댓글에 애초에 베이스부터가 틀렸다고 색 정렬 못한다고 하는데 이대로 끝낼 순 없지않은가?
이가 없으면 잇몸으로라도 한다고... 정렬하는 척이라도 해야지 않을까?

그래서

처음에 접근한 방법은 휘도, 채도를 이용해 HSL로 색을 정렬하는 방법이었다.
공식에 사용되는 수치는 조금씩 다를 수 있지만 원리 자체는 별차이가 없다.

이 방법은 SQL 쿼리만으로도 뽑아낼 수 있다. (아주 편함)
order by SQRT(`R` * `R` * 0.241 + `G` * `G` * 0.691 + `B` * `B` * 0.068)


다음과 같은 SET을 얻을 수 있었다. 온전히 색의 밝기만으로 정렬하는것이기때문에
색상들이 진행될수록 어두워지는걸 알 수 있다. 하지만 정렬이라고 보긴 어렵다.



그 다음으로 시도한것이 delta E (CIE 2000) 으로 색상사이의 간격을 계산 후 정렬하는것이었다. (color distance or difference)
사실 여러방법으로 시도하는 글이 있고, 여러 알고리즘이 있지만, 결국 원리는 비슷했기때문에 요방법만 해보기로 했다.

HSL 로 정렬한 SET에서 색을 한개씩 뽑아내 그 색으로부터 가장 가까운 색을 뽑는것이다.
이 방법은 꽤 괜찮은 시도였다고 생각한다.



꽤 군집이 생긴것 같지만, 여전히 '완전한' 정렬이 되었다고 보긴 어려웠다. 갑자기 회색톤으로 가다가 빨간색이 나오질 않나..
그래서 정렬하는 과정을 꼼꼼히 살펴보기로 했다. 예를 들변 핫핑크에서 꽃핑크를 선택하는 과정은 다음과 같았다. 길어서 중간은 좀 잘랐다.

[!]블드핫핑크
[INIT] 레몬(금속) 155.26638858087
[CHANGED] 리옐(금속) 152.03349927037 
[CHANGED] 리옐 151.11097423585 
[CHANGED] 핫핑크 10.281657185973 
[CHANGED] 꽃핑크 7.5335844516123 
[ADDED] 꽃핑크

[!]꽃핑크
[INIT] 레몬(금속) 150.23029944029
[CHANGED] 리옐(금속) 147.51696619098 
[CHANGED] 리옐 146.71284135744 
[CHANGED] 딥핑크 13.359231425896 
[CHANGED] 딥핑크(금속) 13.359231425896 
[ADDED] 딥핑크(금속)


색상거리의 최소값을 계산하는 과정인데 하자가 없어야했다. 이론대로라면 완전히, 깔끔하게 정렬되어야 하는데 왜 저런 결과가 나온걸까?
도대체 중간에서 오른쪽쯤에 떨어진 저 빨간색은 왜 거슬리게 저기 있어야만 한걸까?

저 친구의 이름은 블드체리인데, 저 친구도 물론 후보로 거론된적이 잇는 친구였다. 하지만..

[!]체리분홍(금속)
 [CHANGED] 블드체리 16.814328547159 
[CHANGED] 블드탁한체리분홍 6.9852497800933 
[ADDED] 블드탁한체리분홍

[!]블드탁한체리분홍
[CHANGED] 블드체리 15.356974641448 
[CHANGED] 블드체리분홍 10.401343164254 

[CHANGED] 블드체리 22.072876833766 
[CHANGED] 블드핫핑크 5.735796527615 
[ADDED] 블드핫핑크

[CHANGED] 블드체리 18.265066068665 
[CHANGED] 핫핑크 17.31315523967 
[CHANGED] 딥핑크 13.359231425896 
[CHANGED] 딥핑크(금속) 13.359231425896 
[ADDED] 딥핑크(금속)

[CHANGED] 블드베이비핑크 48.921819262293 
[CHANGED] 블드체리 40.790667505018 
[ADDED] 블드체리


밀리고 밀리고 밀리다가 색상차이가 40이나 되는 상관없는 색에게 선택받을 수 있었던것이다.
코드상으로는 최소한의 색상차이가 나는 색상을 뽑았기때문에 문제가 없지만, 이는 정렬이 되었다고 판단할 수 없게 만드는 가장 큰 요소였다.
근소한 값으로 차이가 발생했다고 하더라도 선택되지 않으면 다음 색상 선택에서 완전히 달라진 결과를 도출하기 때문이다.

그래서 오차를 줘서 후보군을 구제해보기로 했다.
블드체리 색상은 11정도의 색상차이로 탈락하기 때문에 얼마나 큰 차이가 있을지 비교해보기위해 시각화해서 비교해보기로 했다. (인간의 눈 기준으로)
다음은 그 결과이다.



뭘 보라고 하는지 모르겠는 사람도 있을 것 같지만, 3번라인을 보면 튀는 값도 없고 정렬됐다고 할 수 있을지도 모른다. 흠..
그렇다면 최종 선발된 값과 탈락한 후보군들과의 색상차를 모두 다시 계산해 준다면 어떨까?

요컨데 이런식이다.

[!]탁메론
...
[CHANGED] 연두 11.174564290748 
[CHANGED] 블드연두 8.8682969251542 
[ADDED] 블드연두
...
[CHOSEN]블드연두 ->블드연한메론 18.812444073324
[CHOSEN]블드연두 ->연두 3.1295681624566
[ADDED2] 연두


과정을 되새김질해서 아깝게 탈락한 후보군들을 재활용해보겠다는것이다.
위와 같이 연두컬러는 블드연두라는 컬러와 겨우 3차이의 색상차가 발생해 탈락하지만, 재검사로 바로 다음에 색상을 배치하는것.
그 결과는...


여전히 카오스지만 꽤나 스무스해졌다(고 생각한다)
이 중에서 제일 괜찮아 보이는 4번라인으로 최종선택을 했다.

결론





결과적으로만 놓고보면... 거의 차이가 없다 ㅠㅠ.. 그치만 블드체리 친구(빨간줄)이 사라졌다....... 이게 소득일까...?

사람이 판단 하는 색상과 기계가 판단하는 색상을 동일하게 볼 수 없다는것이며, 휴먼소팅(...)을 해야 예쁜 그라데이션을 만들 수 있을 것 같다.

이 삽질의 흔적은 싴갤러스 지염 도서관에서 찾아보실 수 있습니다...





2016/05/22 00:43 2016/05/22 00:43
모바일 멜론에서는 노래 추가전 로컬 플레이리스트에서 노래를 검색후, 등록되어 있다면 추가 없이 등재된 노래를 트는 방식인데(환경설정에서 설정가능), PC버전은 어째 있어도 계속 추가되는 방식이다. (기존목록유지라는게 있는데 이걸 체크하고 노래를 틀면 다 날아간다. 언빌리버블)


결국 PC에서 이 노래가 갑자기 생각나 재생하면, 과거의 내가 추가했던 노래가 세개 네개씩 추가된다.



딱히 정은지님께 불만있는게 아닙니다. 1등이라서 튼거에요...
기분상의 문제겠지만 랜덤이 랜덤이 아니게 되는 것 같았다.
그래서 플레이리스트를 유니크하게 만들고 싶다고 생각하던 와중에 멜론이 설치된 경로에 플레이리스트가 저장되고 있음을 알 수 있었다.

열어보니 json 포맷으로 뭔가 잔뜩적혀 있었다.
이녀석들을 처리해주면 되겠다고 생각해서 임의로 지워도 보고 넣어도 보고 했는데 그대로 되는게 확실했다.

그래서, 요녀석을 python을 이용해 중복을 제거해보기로 한다.
독자분의 플레이리스트를 책임지지 않습니다. 저장해놓고 따라하세요...

멜론의 json은 Now playlist라는 대범주내에서 다음과 같은 형태로 저장된다.

{"ALBUM":"장범준 1집","ALBUMID":"2275373","ARTISTID":"","ARTISTLIST":[{"ID":"648551","NAME":"장범준"}],"COMMONINFO":"330001001010000001100000000000000000","CONTENTSFLAG":"1000000000000000","EDUNAME":"","ID":"4821172","ITEMIDTYPE":1,"MVADULTRANK":4,"MVTYPE":0,"PHONEDECFLAG":"1110000000000000","PLAYTIME":203,"SOURCEPATH":"","SOURCETYPE":66,"STATICID":"26020103","THUMBNAILIMAGE":"http://cdnimg.melon.co.kr/cm/album/images/022/75/373/2275373_th120.jpg","THUMBNAILIMAGE_120":"http://cdnimg.melon.co.kr/cm/album/images/022/75/373/2275373_th120.jpg","THUMBNAILIMAGE_500":"http://cdnimg.melon.co.kr/cm/album/images/022/75/373/2275373_500.jpg","TITLE":"주홍빛 거리"}


이때 가장 필요한것은 id이다. id만 넣어도 재생은 된다. 다만 로컬플레이리스트에 제목도 안나오고, 썸네일도 안나온다.
노래를 재생시키면 리얼타임으로 불러오는게 아니고 추가할 당시에 모든 정보를 불러와 json에 넣는 방식인것 같다. 어쨌든, id로 중복체크를 하면 되겠다.

멜론이 설치된 경로에서 파일을 읽어와 출력하려니 잘 되지 않았다. 한글 깨짐 문제인듯 보여 여러 블로그를 참조했으나 도저히 유니코드의 늪에서 벗어나질 못했다. 무엇이 문제란말인가? 그러다가 이녀석이 UTF-8이 아니라 다른형태로 인코딩 되어 있을지 모르겠다는 불길한 생각을 하게 되었고 그게 정답이었다...

나는 이 툴을 이용해 체크를 해봤는데 (PE를 읽어도 된다), 멜론의 로컬 플레이리스트 인코딩은 UTF-16LE이다. 
다국어가 많기때문에 합당한 처사라고 생각되었다. 늦게 눈치챈 나만 탓하기로 했다.
UTF8이 이젠 대세가 아닐테니.. 어쨌든,  이런 형태로 인코딩해주고 json객체로 로드해주면 드디어 파일을 읽어들일 수 있게 된다..

removeDuplicate = { each['ID'] : each for each in jsonObj }.values()


대략 이런식으로 코드를 적어주면 중복이 싹 날아가는데, 금방 끝날줄 알았더니 멜론에서 읽질 못한다. ㅡㅡ 백업을 떠놓고 하길 정말 잘한 것 같다. 간과한게 있었으니.. utf8로 인코딩된 이녀석을 다시 utf-16le 꼴로 만들어줘야만 했던것이다... 그런데 몽땅 다 인코딩하니 또 못읽는다... 한글 파일명이 유니코드로 변신한것이다... "\u120\u359" 막 이렇게 생긴거 있잖아... 다 만들었다 생각했는데 한글이 발목을 턱 잡았다.


그래서 별에별 쌩쇼를 다했다. 한글만 빼내서 replace해볼까 요렇게도 생각하다가... 
결론은 자승자박이었다. 위 툴 스샷은 UTF-8로 저장을 했을때 실패한건데, 정상적인 alst파일을 보면 UTF-16LE라고 나온다.

헌데, python의 codecs를 이용할땐 UTF-16으로만 저장을 해줘야했다. LE붙이지말고.........ㅎ ㅏ.....sigh.......
유니코드를 뭐 앞에 써주고 이러라는데 결국 인코딩은 삽질싸움이다.. (위 인코딩 이슈들은 python3에서 거의 해결된 문제라고 한다. 최신쓰자..)

어쨌든 간만의 긴 삽질 끝에 완성되었다.
요 파이썬 파일을 돌리면 중복이 싹 날아가고, 멜론을 껐다키면 중복된 노래들이 날아감을 확인할 수 있다.
요녀석을 쓰고 싶으면 여길 방문해주세요. 쓸데없는 삽질 글 읽어주셔서 감사합니다!



===

2017.07.24 수정

스크립트를 가만 짜놓고 보니 이 python script를 누가 얼만큼 수정해서 쓸지 생각이 들더군요. 
그래서, 자동으로 플레이 리스트를 찾고 (레지스트리로부터), 백업도 알아서 만들고, 엔터 몇번 누르면 끝나도록 프로그램을 작성했습니다.

별 다른 프로그램이 필요할 것이라고 생각은 들지 않지만,  관리자 권한은 필요할 수 있습니다
(레지스트리를 읽고, 프로세스 리스트를 읽습니다)



여기서 다운로드 받아 실행하시면 됩니다.
2016/04/15 11:18 2016/04/15 11:18