'분류 전체보기'에 해당되는 글 64건

  1. 2015.05.28 2015 fetival HUST
  2. 2015.05.15 Ascii_easy
  3. 2015.05.13 ASCII
  4. 2015.04.01 프로그램에서 일어날수 있는 레이스컨디션의 예제 8
  5. 2015.03.27 echo1
  6. 2015.03.08 Hidden - onion with NC
  7. 2015.03.03 pixiv.py
  8. 2015.02.23 mysql python
  9. 2015.02.06 profile
  10. 2014.12.29 error based 1

2015 fetival HUST

Write_UP 2015. 5. 28. 16:05



저희팀은 이버네도 아쉽네요.. 마지막 300점 짜리 문제를 푸는 도중 대회가 5분정도 남아서 충분히 인증할수 있는 시간이 되었는데,

중간에 서버가 뻗어서 다시 해야 하는 바람에 그게 제대로 되질 않아서 인증을 못했네요

아래는 제가 푼 문제 라이트업입니다.

서버가 닫혀서 스크린샷등은 첨부를 못하고 입해킹 문서를 적게됬네용

편의상 반말인건 양해 부탁드려요.


- Web 200 (블로그 문제)

시작하면 개인이 운영하는 블로그가 하나 있다.

블로그 답게 자신이 쓴 글이 즐비하게 있고 

login 을 할수 있는 버튼이 있으며 운영자와 contact 할수 있는 공간도 마련되어 있었다.

지금은 서버가 닫혀있어 첨부할수 없지만, 로그인을 한 후 화면을 클릭하면 분홍색 네모가 하나씩 떨어지는걸 볼 수 있고

이렇게 클릭을 한번 할때마다 score가 오른다.

처음 이 문제를 접했을때 score를 조작하여 flag를 얻거나, admin계정의 score에 flag가 박여있을것으로 생각하고

수년간 단련한 오른손의 힘으로 재빨리 클릭을 했는데 이것이 brute force로 판명되어 IP ban을 당했다.

요청을 하여 ban을 풀고 기다리니 get_score 라는 힌트가 공개되었고, 조금 어리둥절 하던 마당에 login이 있으니

sql이 될수 있지 않냐 라고 하는 팀원의 말에 따라 우선 set_score 부분에서의 blind sql이 가능하다는걸 발견했다.

그렇게 id를 확인하니 root라는걸 알아내고 webshell을 올리는 문제라고 판단했다. 하지만 우리가 파일을 쓸 수 있는 디렉토리를

찾는데 문제가 발생하여 단순 SQL injection이 아니겠냐는 팀원의 말을 듣고 DB를 뒤져보기로 했다.

하지만 불안정한 서버의 상황때문에 blind sql로 테이블을 가져와서 확인한다는건 명백하게 무리가 있었다. 때문에 우리는 서버에서 제공하는

js파일을 받아 임의로 변조하여 score 대신 받아오는 json을 그대로 출력하게 만들어서 출력되는 문자를 점수 뿐 아닌 정보 그 자체를 출력하게 만들고

get_score를 통한 sql injection을 시도하였다. 다행히도 쿼리는 정상작동 하였고, keytable 이라는 테이블 밑의 value 라는 컬럼을 발견했다.

이를 통해 키 값을 얻어냈는데, 인증이 되지 않아서 팀원들과 고민한 끝에 가져온 값 중에 key_is 에 해당하는 문자열을 떼고 인증하니

정상적으로 인증이 되었다.


- Reversing 500? (마리오문제)

게임을 시작하니 단순한 마리오게임인줄 알았는데 뭔가 넘을수 없는 4차원의 구덩이가 있었다.

처음 접근은 클리어를 하면 키 값이 출력될것 같아서 String을 먼저 찾아보기로 했다.

문자열이 그림으로 출력되어서 간단히 찾을수 없다는걸 깨닫고 다른 접근 방법을 하기로 하였는데, 이때 팀원이 fceux라는 프로그램을 발견, 디버깅에 들어갔다.

해당 툴에서는 맵을 개조할수 있는 여러가지 기능이 있었는데, 자세한 사용법을 몰라 해매던 도중 나는 메모리 디버깅 메뉴를 발견하여 실행해 보았다.

메모리디버깅에서는 특정 오프셋에 있는 메모리가 얼마나 자주 변화하였고 현재값이 어떤 값인지 알려주는 간단한 테이블이 눈에 들어왔다.

여기서 만약 내가 점프를 하면 마리오의 현재 y축 위치, 점프 횟수, 점프 가속도 등이 어딘가에 세팅되어 있을것 같아서 점프하고 일시정지를 반복하는 방법으로

메모리가 변하는 곳을 추려내어 해당 오프셋을 찾았는데, 점프를 할때 올라가는값은 1,2,3,4 내려가는 값은 -1,-2,-3,-4 등으로 변하는걸 볼 수 있었다.

해당 롬을 에뮬레이터에 올리고 치트엔진을 attach 시켜서 점프를 하고 난 뒤 값을 검색, 내려왔을때 값을 검색.. 을 반복하여 점프에 해당하는 메모리 주소 3개를

찾아서 모두 양수로 만들고 값을 고정시켰더니 마리오가 무한점프 하며 하늘을 날기 시작했다.

이렇게 넘을수 없는 4차원의 구덩이를 넘고 그 뒤에 있는 넘을수 없는 4차원의 벽을 넘으니 벽돌로 KEY IS 라는 문자가 보이기 시작했고

해당 문자를 다 읽을때 까지 마리오는 하늘을 날았고 키 값을 인증했다.



Posted by IzayoiSakuya
,

Ascii_easy

2015. 5. 15. 10:00

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

ASCII

2015. 5. 13. 17:12

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

구글에 레이스 컨디션 이라고 검색하니 공격기법만 난무해서, 간단한 예제와 함께 포스팅을 시작하려 합니다.


우선 레이스 컨디션이란 단어에 대한 뜻은 위키 백과에 나와 있는 부분을 가져왔습니다.



전산학에서 경쟁 상태란 공유 자원에 대해 여러 개의 프로세스가 동시에 접근을 시도하는 상태를 말한다. 동시에 접근할 때 자료의 일관성을 해치는 결과가 나타날 수 있다. 이를 방지하기 위해서는 프로세스 협력 기법이 필요하다. 

-http://ko.wikipedia.org/wiki/경쟁_상태


사실 프로세스가 아니더라도 경쟁상태에 있을수 있는것은 수많은 것들이 있습니다. 그것은 스레드가 될수도 있고, 다른 어떤 자원이 될수도 있겠죠!


사실 이러한 레이스 컨디션을 이야기 하기전에 조금 다른 이야기 일수 있는 fork, system, execl, thread 등의 프로세스 내부에서 다른 프로세스나 스레드를 생성하는 것들에 대해서 이야기 해봅시다.


복잡한 코딩없이 시스템 함수를 실행시키기 편한 system의 예제부터 보도록 하죠.


system.c

#include <stdio.h>

#include <unistd.h> int main() { printf("I'm Parent!\n"); system( "./call_exec"); printf("is over!\n"); }


call_exec.c

#include <stdio.h>
int main()
{
        int i;
        printf("I'm child!\n");
        for(i =0; i <100; i++)
                printf("now.... %d\n",i);
        while(1)
        {}
}



system.c와 call_exec를 컴파일 하고 돌려봅시다.

물론, call_exec의 무한반복루프 때문에 프로그램이 종료되지 않은채 살아 있을겁니다.

컨트롤+c 키를 이용해서 무한반복문을 빠져나와 보죠.


맨 마지막의 is over! 도 출력이 정상적으로 되는걸 볼 수 있습니다. 예상했던 결과대로 프로그램이 흘러가는군요.

system은 내부적으로 결과값을 리턴하기 전까지 부모 프로세스가 죽지 않았다는걸 알수 있습니다.

이제는 일반적인 pwnable 환경에서 자주 쓰이는 execl 등의 함수를 알아보도록 하죠.


예제코드는 아래와 같습니다.


exec.c

#include <stdio.h>

#include <unistd.h> int main() { printf("I'm Parent!\n"); execl( "./call_exec", "./call_exec", NULL); printf("is over!\n"); }

 


두번째 인자값은 argv[0]로 들어가는 인자값이라고 합니다. argv[0]는 자신이 실행된 경로가 적힌 arg로,

int main(int argc, char** argv)를 사용하셔서 확인하실수 있습니다.

이제 이것을 컴파일하고 실행시켜보죠.


예상과는 다르게 execl이 프로그램을 실행시키든 말든 is over!라는 문자열을 출력해주지 않는군요.

왠지 바이너리 자체가 다른걸로 교체된것 같은 느낌을 강하게 받습니다. 그럼 fork는 어떨까요?

fork.c

#include <stdio.h> #include <stdlib.h> int global_secret_number; int main() { int main_secret_number = 1; global_secret_number = 3; int pid; pid = fork(); if(pid>0) { main_secret_number++; global_secret_number++; printf("I'm Parent!\n main: %d global: %d\n",main_secret_number,global_secret_number); printf("PID %d\n",pid); } else { printf("I'm child!\n main: %d global: %d\n",main_secret_number,global_secret_number); printf("PID: %d\n",pid); } }


소스가 이전것들과 비해 좀 복잡한 느낌이 있습니다. fork는 부모의 프로세스를 그대로 '복사' 하여 한번 더 실행한것과 같은 효과를 가집니다.

부모와의 차이를 두기위해 pid로 자식/부모를 구분하며, pid가 0이면 자식, 0 이상이면 부모으로 판별하는 방법을 주로 사용합니다.

여기서 복사 하였다는 표현을 사용했는데, 아래 결과를 보면 어느정도 짐작할 수 있습니다.



전역변수로 사용한 global의 값을 자식프로세스에서 +1을 더했지만, 부모프로세스에서는 변화가 없는걸 보실수 있습니다.

이것은 fork를 하면서 모든 메모리 영역을 똑같이 복사해서 자식만의 메모리를 가졌기 때문입니다.

(똑같은 행동을 하는 프로세스를 하나 더 실행했다고 생각하시면 이해가 편하실겁니다.)


그럼 이쯤에서 레이스 컨디션의 이야기로 넘어가 보도록 하죠.

우선 아래와 같은 상황을 가정해봅시다.


1. 두 프로세스가 공통된 파일에 접근한다.

2. 하나의 프로세스에 있는 두개의 쓰레드가 공통된 파일에 접근한다

3. 부모 프로세스가 값을 생성하고, 그 값을 자식 프로세스가 가져와 사용하려한다.


이러한 상황에서 발생할수 있는 문제가 있을까요?

우선 간단한 thread를 통한 예제를 살펴봅시다.


thread.c

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <time.h> #define bool char #define STDOUT 1 bool islock; bool outlock; bool inlock; int money; void* input(void* arg) { char buf[50] = {0,}; int i; //for busy waiting while(islock && inlock) {} outlock = 0; for(i =0; i<100; i++) { int len; len = sprintf(buf,"INPUT MONEY : %d\n",money); money += 100000; printf("%s",buf); } } void* output(void *arg) { char buf[50] = {0,}; int i; //for busy waiting while(islock && outlock) {} inlock =0; for( i =0; i <100; i++) { int len; len = sprintf(buf,"OUTPUT MONEY : %d\n",money); money -=100000; write(STDOUT,buf,len); } } int main() { money = 100000; pthread_t thread[2]; islock= inlock= outlock= 1; //Do input! printf("THREAD MAKE!\n"); pthread_create(&thread[0],NULL,&input,NULL); pthread_create(&thread[1],NULL,&output,NULL); sleep(1); islock = 0; sleep(1); }



우선 input과 output의 함수의 속도 차이를 주기 위해서 하나는 저수준의 입출력을, 하나는 단순 printf문을 사용해서 프로그램을 설계해보았습니다. 물론, 저수준의 입출력이 보통의 printf보다 약간은 더 빠르겠죠?

그리고 예상되는 결과를 생각하시고 컴파일후 실행해 보시길 바랍니다.

제가 실행해 보았을때는 아래와 같이 2군데 에서 이상한 점을 찾을 수 있었습니다.




쓰레드(프로세스)를 만들고 나서 OUTPUT MONEY에 돈이 100000 증가한 점,

그리고 OUTPUT MONEY 이후에 INPUT MONEY의 금액이 갑자기 급증한 점.

과연 관계가 있을까요?


여기서 예상가능한 상황은 다음과 같습니다.


1. OUTPUT MONEY 가 동작한 이후, INPUT MONEY가 동작하면서 전역변수 money의 값이 변경되었다.

2. 하나의 전역변수 money에 대해서 두개의 쓰레드(프로세스)는 각자 다른값을 가지고 자신이 취해야 하는 행동을 했다.

3. 그 결과, 100000이 유지되어야 할 전역변수 money의 값이 서로 다르게 책정되었고, 원하지 않는 결과를 가져왔다.


그림으로 나타내면 이렇게 되겠죠!




이게 레이스 컨디션입니다.

OUTPUT 함수가 실행될 동안 어떠한 함수나 프로세스, 쓰레드는 전역변수 money에 접근해서는 안되며,

INPUT 함수가 실행될 동안 어떠한 함수나 프로세스, 쓰레드는 전역변수 money에 접근해서는 안된다 라는 원칙을 정해주지 않았기 때문에 

서로가 가지고 있는 값이 다르게 된 것이죠.


이 예시를 좀더 와닿을수 있는 예제 코드를 준비해 보았습니다.


bank.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> // for logging!

#define ACCOUNT "ACCOUNT"
#define LOG "LOG"
#define DEPOSIT 1
#define DRAW 2
#define ASK 3

//is not important in this example. it just write log.
void WriteHistory(unsigned int menu, unsigned int amount, unsigned int money)
{
	FILE* log = fopen(LOG,"a");
	char action[10] = {0,};
	char buf[100] = {0,};
	time_t current;
	time(&current);

	fwrite("--------------------------------\n",1,33,log);
	switch(menu)
	{
		case DEPOSIT:
			sprintf(buf,"MONEY :: %d (DEPOSIT :: %d) \n TIME :: %s\n\n",money,amount,ctime(&current));
			break;
		case DRAW:
			sprintf(buf,"MONEY :: %d (DRAW :: %d) \n TIME :: %s\n\n",money,amount,ctime(&current));
			break;
		case ASK:
			sprintf(buf,"ACCOUNT ASK \n TIME :: %s\n",ctime(&current));
			break;
	}
	fwrite(buf,1,strlen(buf),log);
	fclose(log); //LOG EXIT
}

void clear()
{
	system("clear");
}
void printmenu()
{
	clear();
	printf("+--------------------------------+\n");
      	printf("|     WELCOME TO SAKUYA ATM      |\n");
        printf("+--------------------------------+\n");
	printf("|[*]HERE IS YOUR MENU            |\n");
        printf("+--------------------------------+\n");
        printf("|1. Deposit                      |\n");
        printf("|2. Draw                         |\n");
        printf("|3. Ask my account               |\n");
        printf("+--------------------------------+\n");
        printf(">> ");

}

FILE* openaccount(char* mode)
{
	FILE* ret;
	while(1)
	{
		ret = fopen(ACCOUNT,mode);
		if(ret>0)
			return ret;
	}	
}

void deposit()
{
	//OPEN MY ACCOUNT
	FILE* Raccount = openaccount("r");
	unsigned int money;
	char buf[10] = {0,};
	unsigned int input;
	fscanf(Raccount,"%d",&money);
	fclose(Raccount);	//this file no more use for read mode. aren't you?

	//DO Deposit
	clear();
	printf("How much depoist in your account?\n>> ");
	scanf("%d",&input);
	
	money += input;

	sprintf(buf,"%d",money);
	
	FILE* account = openaccount("w");
	fwrite(buf,1,strlen(buf),account);
	fclose(account);
	
	//logging
	WriteHistory(DEPOSIT,input,money);	
}

void draw()
{
	//OPEN MY ACCOUNT
	FILE* Raccount = openaccount("r");
	unsigned int money;
	char buf[10] = {0,};
	unsigned int output;
	fscanf(Raccount,"%d",&money);
	fclose(Raccount);	//this file no more use for read mode. aren't you?

	//DO Draw
	while(1)
	{
		clear();
		printf("Your account has %d won!\n",money);
		printf("How much draw in your account?\n>> ");
		scanf("%d",&output);
		if(money < output)
			continue;
		else
		{
			money -= output;
			break;
		}
	}
	
	sprintf(buf,"%d",money);	//for fwrite!

	//OPNE MY ACCOUNT to write my money
	FILE* account = openaccount("w");
	fwrite(buf,1,strlen(buf),account);
	fclose(account);

	//logging
	WriteHistory(DRAW,output,money);
}


void ask()
{
	WriteHistory(ASK,0,0);
	clear();
	system("cat ./LOG");
	printf("For security reason, we will show this log 5 second.\n");
	sleep(5);
}

int main()
{
	int sel;
	while(1)
	{
		printmenu();
		scanf("%d",&sel);
		switch(sel)
		{
			case DEPOSIT:
				deposit();
				break;
			case DRAW:
				draw();
				break;
			case ASK:
				ask();
				break;
		}
	}	
}


소스가 좀 길군요. 실행화면을 가져와 볼께요.


간단한 ATM 기계네요!

입금, 출금을 할수 있고 내 계좌에 대해 조회도 할 수 있습니다.

우선 간단히 입금과 출금 기능을 살펴 볼게요.

아,참 이 프로그램은 관리자만 ACCOUNT에 접근해야 하므로 setuid를 걸어놓았습니다. 아무래도 그게 더 현실적이니까요 :D



우선 입금 기능을 살펴보도록 하죠.

원하는 액수만큼 돈을 넣으시면 됩니다.

간단하죠?



출금기능에선 내가 가진 잔고를 보여주고 그 잔고 이상을 출금하려고 하면 출금이 안됩니다.



그리고 계좌조회 기능에서는 내가 어떤행동을 언제 했는지 친절히 보여주는 프로그램입니다. 전체적으로 간단한 기능을 가진 프로그램들이죠.


여기서 레이스컨디션이 일어날수 있을까요?


우선 이 프로그램을 2개 켜보도록 합시다.그리고 돈을 출금해보도록 하죠.

그런데, 정말 우연히도,

제 계좌에 누군가가 돈을 송금했습니다. 그것도 무려 1000만원이나 되는 금액을요!

제가 계좌에서 돈을 뺴 가기도 전에 말이죠!

그림으로 보면 다음과 같은 상황일겁니다


돈이 필요해서 20만원을 모두 빼려는데,


누군가 저에게 천만원을 입금해 줬고!


그 금액이 그대로 들어가서 돈이 총 1020만원이 되었지만


제 계좌에는 0원이 남아있군요!


순서대로 정리를 하면, 



1번 상황에서 제가 가지고 있는 돈은 200000입니다. 

그리고 2번 상황이 오죠 (입금). 여기서도 물론 제가 가지고 있는 돈은 200000입니다.

3번처럼 입금이 완료되고, 원래 흐름이었던 출금으로 돌아왔을때 기억하고 있는 금액은 200000이죠. 입금이 더해진 금액이 아닙니다.


그리고 출금을 완료하고 기록을 하게되면, 금액은 0원이 되겠죠.


이것 또한 제 계좌에 대해서 한번에 2개이상의 프로세스가 접근했기 때문에 발생하는 것입니다.

제가 출금을 하고있을때 다른분이 제 계좌에 접근하지 못했다면, 좀더 정확히는 제 계좌의 돈이 동기화가 됬다면 이 문제는 발생하지 않았겠죠.


사실 이러한 문제를 일으키는 코드들이 도처에 널려있을지도 모릅니다.



------------------------------------------------

int -> unsigned int :: thx to chofly 

설명을 위한 그림추가

fork 관련 내용 수정 :: thx to bongbong

'System' 카테고리의 다른 글

How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
사기를 위한 프로그램 분석  (0) 2014.12.26
Posted by IzayoiSakuya
,

echo1

2015. 3. 27. 13:16

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Hidden - onion with NC

2015. 3. 8. 15:11

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

pixiv.py

2015. 3. 3. 09:18

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

mysql python

Web 2015. 2. 23. 01:50


python과 mysql의 만남!

은 자주 안씀..


MySQL-python-1.2.5.tar


'Web' 카테고리의 다른 글

Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (1) 2015.10.28
Posted by IzayoiSakuya
,

profile

backup 2015. 2. 6. 12:33




'backup' 카테고리의 다른 글

htaccess trick  (0) 2015.08.20
PHP - unserialize  (0) 2015.08.18
Hidden - onion with NC  (0) 2015.03.08
pixiv.py  (0) 2015.03.03
ip-time n150ua  (0) 2014.11.25
Posted by IzayoiSakuya
,

error based

I Think.. 2014. 12. 29. 20:12

@_@ 더이상 짱구 굴려봐야 안될거 같아서 guessing으로 알아낸것 적어둡니다. 능력자분들 해결좀.

환경 : mysql(ver : 5.1.41-community), root 권한
쿼리 : SELECT concat(floor(rand(0)*K)a FROM (SELECT 1 UNION... ...SELECT J)b GROUP BY a HAVING min(0);

여기서 K와 J는 양의 정수

random표

보는법 : *K(최소 필요 컬럼갯수, duplication이 일어나는값)
floor(rand(0)*K)의 리턴값

*2(3,1)
0,1,1,0,1,1,0,0,1,1,1,0,1,1,1,0,1,0,0,0,1,1,1,0,1,1,0,0,0,0,1,0,0,1,1,0,1,1,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,1

*3(5,2)
0,1,1,0,2,2,0,1,2,2,2,1,2,1,2,1,2,0,1,1,1,2,1,0,2,2,1,0,0,1,1,0,0,1,1,0,2,2,0,1,0,2,2,1,0,1,2,1,0,0,0,0,1,1,0,2,1,0,2,0,0,0,0,1,0,1,0,0,1,0,1,0,2,0,1,1,2,2,1,1,2,2,1,1,1,0,0,1,2,2,2,1,1,2,0,1,2,0,1,1,0,0,0,1,2,1,2,2,0,0,0,1,2,0,1,2,0,1,2,1,1,0,0,1,2

*4(3,2)
0,2,2,1,2,2,1,1,3,3,3,1,3,2,3,1,3,0,1,1,2,3,2,1,2,2,1,0,0,1,2,0,0,2,2,1,2,2,1,1,0,3,2,2,0,1,3,1,0,0,1,1,1,1,0,3,1,0,2,0,0,1,1,2,1,2,0,0,1,1,1,0,3,0,2,2,2,3,2,1,3,2,1,1,2,0,0,2,2,3,3,2,2,3,0,2,3,0,1,2,0,0,1,1,3,2,3,2,0,1,0,1,3,0,2,3,0,1,2,1,2,0,0,2,3

*5(3,3)
0,3,3,1,3,3,1,1,4,3,4,1,4,2,3,2,4,1,2,1,2,4,2,1,3,3,2,0,0,2,3,0,0,3,3,1,3,3,1,1,0,4,3,3,0,2,4,1,0,0,1,1,2,2,0,4,1,0,3,0,0,1,1,3,1,3,1,1,1,1,1,0,4,1,3,2,3,4,3,1,4,3,2,2,3,0,0,3,3,4,4,2,2,4,0,3,4,0,2,3,0,0,1,1,4,2,4,3,0,1,0,1,4,0,3,4,0,1,3,1,3,1,1,2,4

*6(6,4)
0,3,3,1,4,4,1,2,5,4,5,2,5,3,4,2,5,1,2,2,3,5,3,1,4,4,2,0,0,2,3,0,0,3,3,1,4,4,1,2,0,5,4,3,0,2,5,2,0,0,1,1,2,2,0,5,2,1,4,0,0,1,1,3,1,3,1,1,2,1,2,0,5,1,3,3,4,5,3,2,5,4,2,2,3,0,0,3,4,4,4,3,3,5,0,3,4,1,2,3,0,0,1,2,5,3,5,4,0,1,0,2,5,0,3,5,0,2,4,2,3,1,1,3,4

*7(6,5)
1,4,4,2,5,4,2,2,6,5,6,2,6,3,5,3,6,1,2,2,4,6,4,2,5,5,3,0,1,2,4,0,0,4,4,2,5,4,2,2,0,6,4,4,0,2,6,2,0,0,2,1,3,3,0,6,2,1,5,0,0,2,2,4,2,4,1,1,2,2,2,0,5,1,4,3,4,6,4,2,6,4,3,2,4,0,0,4,5,5,5,4,3,6,0,4,5,1,3,4,0,0,2,2,6,3,6,5,0,1,0,2,5,0,4,6,0,2,4,2,4,1,1,3,5

*8(7,2)
1,4,5,2,5,5,2,2,7,6,7,2,7,4,6,3,7,1,3,2,4,6,4,2,5,5,3,0,1,3,5,0,1,4,5,2,5,5,2,3,0,7,5,5,0,3,7,2,0,0,2,2,3,3,0,7,3,1,5,0,0,2,2,5,2,5,1,1,3,2,3,0,6,1,5,4,5,7,5,3,7,5,3,3,5,0,1,5,5,6,6,4,4,7,0,4,6,1,3,5,0,0,2,3,7,4,6,5,0,2,0,3,6,0,4,7,0,3,5,2,5,1,1,4,6

*9(6,6)
1,5,5,2,6,6,2,3,8,6,8,3,8,4,7,4,8,2,3,3,5,7,5,2,6,6,4,0,1,3,5,0,1,5,5,2,6,6,2,3,0,8,6,5,0,3,8,3,0,0,2,2,4,4,0,8,3,1,6,0,0,2,2,5,2,5,2,1,3,2,3,0,7,2,5,4,6,8,5,3,8,6,4,3,5,0,1,5,6,7,7,5,4,8,0,5,7,1,3,5,0,0,2,3,8,4,7,6,0,2,0,3,7,0,5,8,0,3,6,3,5,2,2,4,7|

*10(6,7)
1,6,6,3,7,7,2,3,9,7,9,3,9,5,7,4,9,2,4,3,5,8,5,2,7,7,4,1,1,4,6,0,1,6,6,3,7,6,2,3,0,9,6,6,0,4,9,3,0,0,3,2,4,4,0,9,3,1,7,0,0,3,3,6,3,6,2,2,3,3,3,0,8,2,6,5,7,9,6,3,9,6,4,4,6,0,1,6,7,8,8,5,5,9,0,6,8,1,4,6,0,0,3,3,9,5,8,7,0,2,0,3,8,0,6,9,0,3,6,3,6,2,2,5,8,5

*11(6,10)
1,6,7,3,8,7,3,4,10,8,10,3,9,5,8,5,10,2,4,4,6,9,6,3,7,7,5,1,1,4,7,0,1,6,6,3,8,7,3,4,0,10,7,6,0,4,10,3,0,0,3,3,5,5,0,10,4,1,7,0,1,3,3,7,3,7,2,2,4,3,4,0,9,2,7,5,7,10,7,4,10,7,5,4,7,0,1,6,8,8,8,6,5,10,0,6,8,1,4,7,0,0,3,4,10,5,9,8,1,2,0,4,9,0,6,10,0,4,7,3,7,2,2,5,8,5

*12(7,4)
1,7,7,3,8,8,3,4,11,9,11,4,10,6,9,5,11,2,4,4,7,10,7,3,8,8,5,1,1,5,7,0,1,7,7,3,8,8,3,4,0,11,8,7,0,5,11,4,0,0,3,3,5,5,0,11,4,2,8,0,1,3,3,7,3,7,2,2,4,3,4,0,10,2,7,6,8,11,7,4,11,8,5,5,7,0,1,7,8,9,9,7,6,11,1,7,9,2,5,7,0,0,3,4,11,6,10,8,1,3,0,4,10,0,7,11,0,4,8,4,7,2,2,6,9,6

*13
2,8,8,4,9,9,3,4,12,10,12,4,11,6,10,5,12,3,5,4,7,11,7,3,9,9,5,1,1,5,8,0,1,7,8,4,9,9,3,4,0,12,9,8,0,5,12,4,0,1,3,3,6,6,1,12,5,2,9,0,1,4,4,8,4,8,2,2,5,4,5,0,11,3,8,6,9,12,8,4,12,8,6,5,8,0,1,8,9,10,10,7,7,12,1,7,10,2,5,8,0,0,4,4,12,6,11,9,1,3,0,5,10,0,7,12,0,4,9,4,8,3,3,6,10,6

*14
2,8,8,4,10,9,4,5,13,10,13,4,12,7,11,6,12,3,5,5,8,12,8,4,10,10,6,1,2,5,9,0,1,8,8,4,10,9,4,5,0,13,9,8,0,5,12,5,0,1,4,3,6,6,1,12,5,2,10,0,1,4,4,9,4,8,3,2,5,4,5,0,11,3,9,7,9,13,9,5,13,9,6,5,9,0,1,8,10,11,11,8,7,13,1,8,11,2,6,9,0,0,4,5,13,7,12,10,1,3,0,5,11,0,8,13,0,5,9,4,9,3,3,7,11,7

* 추측한 내부동작

1. concat이 포함된 SELECT 구문을 컬럼으로 들고 있는다
2. FROM 구문으로 리턴될 테이블을 가지고 있는다
3. GROUP BY 구문을 통해 합친다. 이 때 1 에서 들고 있는 컬럼을 값으로 들고 있는것이 아닌 함수 원형 모습 그대로 들고 있다.
-> floor(rand(0)*K)

4. GROUP BY 구문을 통해 합칠때, 해당 컬럼을 group_key 라는 특수한 KEY의 형태로 등록함으로써 중복 입력을 방지한다

5. HAVING 절을 실행하기 위해 1에서 들고 있는 컬럼값을 조회한다. 이때 HAVING절이 실행되면서 row의값을 확인하기 위해 floor(rand(0)*K)의 값을 푼다.
-> HAVING절이 일반 상수가 아닌 함수의 리턴값이어야 하는 부분. 일반 상수 혹은 BOOLEAN 식으로 적어버리면 T/F를 먼저 판단해버리고 GROUP BY 구문으로 통해 합쳐진 테이블 내에 있는 floor(rand(0)*K) 값을 그대로 출력/비출력 으로 나누어진다. 아마 이 부분이 함수로 처리되어 있으면 floor(rand(0)*K) 값이 나중에 처리되어야 할 명령어로써 스택에 저장이 되고 결국 한번 더 풀리게 되는것 같음.
이 예시로, *11의 값을 보게 되면 6개의 row를 출력하는 테이블에서 Duplication이 난다고 적어 두었는데, 처음부터 6개의 값을 보았을때 10 이라는 값은 존재하지 않는다. 이후에 나오는 7번째 부터 12번째 값을 보면 10 이라는 숫자가 2개 존재함을 볼 수 있다.

6. HAVING절을 통해 한번 더 실행된 rand에 따라 GROUP BY된 테이블을 참조하여 INSERT 구문을 내부적으로 실행한다. 이때 만약 겹치는 key가 있다면 error.

라는것 같은데 군데군데 조금씩 의문점이 확실히 해결이 안되네요..

이 상태에서 5시간째 해매는중

'I Think..' 카테고리의 다른 글

[BoB 2nd] Scientific Research - OS, Memory, malloc  (0) 2016.02.22
Posted by IzayoiSakuya
,