반응형

1. Renaming

1.1 ClassRename

class renaming은 class의 이름를 "Base64Coder"처럼 식별할 수 있는 문자대신, "pabd63bb"와 같이 무슨 의미인지 알 수 없게 바꾸어 준다. 따라서 역공학으로 해당 파일을 보더라도 무슨 역할을 하는 지 알아차리기 힘들어 역공학을 방해한다. [1]

 

python3 -m obfuscapk.cli -o ClassRename ../../sample_apps/app_1.apk
apktool b app_1/ -o app_1_ClassRenamed.apk

 

ClassRename 난독화를 하고 해당 앱을 리패키징하여 jadx로 비교하였다.

class의 이름이 Base64Coder에서 pabd63bb로 바뀐 것을 알 수 있다.

 

기존앱을 virustotal로 검사하면 30개의 백신에서 탐지가 된다.

하지만 ClassRename을 하고 난 뒤에는 20개의 백신에만 탐지가 된다.

1.2. MethodRename

Method Rename method의 이름을 식별하기 힘들게 변경하는 것이다. 따라서 역공학시 해당 메소드가 어떠한 역할을 하는지 명시적으로 알기 어려워져 분석하기 어려워진다.[1]

python3 -m obfuscapk.cli -o MethodRename ../../sample_apps/app_2.apk

 

리패키징을 해준뒤 jadx로 기존앱과 비교하면

c.ca/a 자바 클래스 파일의 메소드 f m8fa14cdd로 변경됨을 알 수 있다.

 

백신탐지 비교 시

(왼쪽 : 기존 앱, 오른쪽 : MethodRename 적용)

Method Rename을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

2. Encryption

2.1. ConstStringEncryption

소스 코드의 문자열 상수를 암호화한뒤 decrypString함수를 통하여 복호화하여 원래의 문자열 상수를 얻도록 한다. 따라서 역공학 분석시, 암호화된 문자열이 어떠한 것을 가리키는 지 인식하기 힘들어 역공학을 어렵게한다.[2]

python3 -m obfuscapk.cli -o ConstStringEncryption ../../sample_apps/app_5.apk

app_5의 connector파일을 보면 "http://" String(왼쪽사진)이 암호화(오른쪽사진)되어 있는 것을 알 수 있다.

(jadx 사용)

\

 

백신탐지 비교시

왼쪽 : 기존앱, 오른쪽 : ConstStringEncryption적용)

ConstStringEncryption을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

2.2. AssetEncryption

python3 -m obfuscapk.cli -o AssetEncryption ../../sample_apps/app_2.apk

 

Asset 디렉토리에 있는 리소스를 복제, 수정으로부터 보호한다.[4]

 

smali/android/annotation 안에 DecryptAsset.smali가 생긴 것을 알 수 있다.

[그림] DecryptAsset.smali

 

또한 com.decryptassetmanager.DecryptAsset 파일이 생겼음을 알 수 있다.

[그림] com.decryptassetmanager.DecryptAsset

 

Assets에 접근 시 복호화하여 파일을 불러온다.

 

백신탐지 비교 시

(왼쪽 : 기존 앱, 오른쪽 : AssetEncryption 적용)

AssetEncryption을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

3. Code

 

3.1. Goto

java에는 없는 goto bytecode를 추가함으로써, control-flow를 수정하고 control-flow를 이해하기 어렵게 한다. [3]

python3 -m obfuscapk.cli -o Goto ../../sample_apps/app_2.apk

[그림]"smali/android/view/a.smali" 비교

 

처음에 메소드의 끝을 가리키는 goto가 있고, 메소드 끝에 메소드의 처음을 가리키는 goto가 추가되어 bytecode 흐름이 달라졌다.

 

 

백신탐지 비교시

(왼쪽 : 기존앱, 오른쪽 : Goto옵션 적용)

Goto 옵션을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

3.2. Reorder

Reorder는 역공학하여 분석하기 어렵도록, 메소드의 명령어 흐름을 복잡하게 한다. 왼쪽의 그림이 Reorder된 app_3의 BandManager.smali이다. [2] 오른쪽의 기존과 method와 다르게 추가된 내용도 생기고 흐름도 goto문이 생기는 등 복잡하게 바뀐 것을 알 수 있다.

python3 -m obfuscapk.cli -o Reorder ../../sample_apps/app_3.apk

[그림]"smali/com/BandManager.smali"비교

 

 

백신탐지 비교시

(왼쪽 : 기존앱, 오른쪽 : Reoreder 적용)

Reoreder 옵션을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

3.3. Nop

Nop 명령어를 추가하여 bytecode 흐름을 수정한다. 따라서 control flow를 이해하기 어렵게 한다. [3]

python3 -m obfuscapk.cli -o Nop ../../sample_apps/app_2.apk

왼쪽의 Nop 옵션이 적용된 app_2/smali/c/ca/a.smali 파일을 보면 오른쪽의 기존 파일과 다르게 nop 명령어들이 추가된 것을 알 수 있다.

[그림] "smali/c/ca/a.smali"비교

 

백신탐지 비교 시

(왼쪽 : 기존앱, 오른쪽 : nop 옵션 적용)

nop 옵션을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

 

 

 

 

 

3.4. ArithmeticBranch

ArithmetiBranch 옵션은 의미 없는 코드를 삽입하여 명령어 흐름을 복잡하게 만들다. 따라서 역공학시 분석을 하기 어렵게 한다. [2] 왼쪽의 ArithmeticBranch 옵션이 적용된 app_4/smali/com/example/eroplayer/MainActivity을 보면 오른쪽의 기존 파일과 다르게 junk code가 삽입된 것을 알 수 있다.

[그림] “smali/com/example/eroplayer/MainActivity.smali" 비교

 

백신 탐지 비교 시

 

ArithmetiBranch 옵션을 적용한 경우에 백신 탐지가 덜 되는 것을 알 수 있다.

 

 

 

 

 

일반적인 개발자의 입장에선, 안드로이드 난독화는 역공학을 어렵게하여, 개발자가 만든 소스코드, 파일 등을 보호할 수 있게 해주는 좋은 수단이다.

악성 앱 개발자의 입장에선, 안드로이드 난독화는 control-flow를 바꾸거나, 암호화, renaming등으로 백신이 악성코드를 탐지하게 어렵게 악용하는 수단이다. 

 

 

참고 논문

[1] 난독화에 강인한 안드로이드 앱 버스마킹 기법 김 동 진Š , 조 성 제° , 정 영 기* , 우 진 운**, 고 정 욱***, 양 수 미**** Android App Birthmarking Technique Resilient to Code Obfuscation Dongjin KimŠ , Seong-je Cho° , Youngki Chung* , Jinwoon Woo**, Jeonguk Ko***, Soo-mi Yang****

 

[2] 안드로이드 어플리케이션 역공학 보호기법 하 동 수*, 이 강 효*, 오 희 국*

 

[3] Android Code Protection via Obfuscation Techniques: Past, Present and Future Directions Parvez Faruki, Malaviya National Institute of Technology Jaipur, India Hossein Fereidooni, University of Padua, Italy Vijay Laxmi, Malaviya National Institute of Technology Jaipur, India Mauro Conti, University of Padua, Italy Manoj Gaur, Malaviya National Institute of Technology Jaipur, India

 

[4] APK에 적용된 난독화 기법 역난독화 방안 연구 및 자동화 분석 도구 구현* 이 세 영,† 박 진 형, 박 문 찬, 석 재 혁, 이 동 훈고려대학교 정보보호대학원

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

cwe119 버퍼 오버플로우 방어  (0) 2021.06.02
Widthness bugs  (0) 2021.06.02
암호화 기본 - 2  (0) 2021.06.02
암호화 기본 - 1  (0) 2021.06.01
apk 난독화  (0) 2021.05.17
블로그 이미지

아상관없어

,
반응형

기존에는 멀티 스레드를 사용하지 않는 작업 하이퍼 스레딩이 필요하지 않음따라서 남는 레지스터를 활용하자는 생각을 .

 

Loop Splitting 사용하는 프로그램을 만들어, 병렬의 개수를 늘려가며 하이퍼 스레딩 사용 유무에 따라 비교를 해보았다.

#include<stdio.h>
#include<Windows.h>
#include<stdint.h> 
/*
vmware의 리눅스에서 하려고 하였다. 하지만 하이퍼 스레딩 유무에 따라 CPU정보 명령어( cat /proc/cpuinfo, grep -c processor /proc/cpuinfo, grep "physical id" /proc/cpuinfo | sort -u | wc -l, grep "cpu cores" /proc/cpuinfo | tail -1)를 사용하여 확인하였지만 차이가 없어 윈도우환경에서 하였다.
*/
//windows에는 gettimeofday함수가 없으므로 인터넷에서 참고하였다.
int gettimeofday(struct timeval* tp, int* tz)
{
 LARGE_INTEGER tickNow;
 static LARGE_INTEGER tickFrequency;
 static BOOL tickFrequencySet = FALSE;
 if (tickFrequencySet == FALSE) {
  QueryPerformanceFrequency(&tickFrequency);
  tickFrequencySet = TRUE;
 }
 QueryPerformanceCounter(&tickNow);
 tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart);
 tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
 
 return 0;
}

void main() {
	struct timeval startTime, endTime;

	double diffTime;

	int limit = 100000;
	int i;
    int loop = 1;
	int data[100000];
	int a;
	for (a = 0; a < limit; a++) {
		data[a] = 1;
	}
	
	int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19;
	x0 = 1;
	x1 = 1;
	x2 = 1;
	x3 = 1;
	x4 = 1;
	x5 = 1;
	x6 = 1;
	x7 = 1;
	x8 = 1;
	x9 = 1;
	x10 = 1;
	x11 = 1;
	x12 = 1;
	x13 = 1;
	x14 = 1;
	x15 = 1;
	x16 = 1;
	x17 = 1;
	x18 = 1;
	x19 = 1;
    printf("loop %d,  %d loop splitting\n", limit, loop);
    gettimeofday(&startTime, NULL);
    printf("starttime %d.%d s\n",startTime.tv_sec, startTime.tv_usec);
	for (i = 0; i < limit; i += loop) {
		x0 = x0 * data[i];
		/*x1 = x1 * data[i + 1];
		x2 = x2 * data[i + 2];
		x3 = x3 * data[i + 3];
		x4 = x4 * data[i + 4];
		x5 = x5 * data[i + 5];
		x6 = x6 * data[i + 6];
		x7 = x7 * data[i + 7];
		x8 = x8 * data[i + 8];
		x9 = x9 * data[i + 9];
		x10 = x10 * data[i + 10];
		x11 = x11 * data[i + 11];
		x12 = x12 * data[i + 12];
		x13 = x13 * data[i + 13];
		x14 = x14 * data[i + 14];
		x15 = x15 * data[i + 15];
		x16 = x16 * data[i + 16];
		x17 = x17 * data[i + 17];
		x18 = x18 * data[i + 18];
		x19 = x19 * data[i + 19];*/
	}

	gettimeofday(&endTime, NULL);
    printf("endtime %d.%d s\n",endTime.tv_sec, endTime.tv_usec);
}

테스트를 해보기 하이퍼 스레딩 기능을 켰다.

 

다음은 하이퍼 스레딩이 켜진 상태에서 실행한 결과이다.

 

<병렬 1>

<병렬 2>

<병렬 4>

<병렬 5>

<병렬 8>

<병렬 10 >

<병렬 20>



그리고 나서 하이퍼 스레딩을 끄고 테스트를 해보았다.

 

 

<병렬 1>

<병렬 2>

<병렬 4>

<병렬 5>

<병렬 8>

 

<병렬 10 >

<병렬 20>

 

테스트 결과들을 표로 정리하면 다음과 같다.

 

startT endT 시간() 평균 시간
1 병렬      
158.286745 158.286334 0.000411 0.000529
159.294495 159.293966 0.000529  
159.941791 159.941145 0.000646  
2 병렬      
205.491576 205.491193 0.000383 0.000429
206.256555 206.256076 0.000479  
206.781915 206.781489 0.000426  
4 병렬      
233.779762 233.779216 0.000546 0.000466
234.638762 234.638302 0.00046  
235.315725 235.315334 0.000391  
5 병렬      
276.227503 276.227081 0.000422 0.001615
277.362467 277.361994 0.000473  
278.18864 278.18469 0.00395  
8 병렬      
305.11175 305.10701 0.00474 0.003192
306.78751 306.78311 0.0044  
306.816079 306.815644 0.000435  
10 병렬      
373.782009 373.781564 0.000445 0.000421
374.588579 374.588149 0.00043  
375.25087 375.250482 0.000388  
20 병렬      
411.261493 411.261106 0.000387 0.000412
412.976789 412.976307 0.000482  
413.531929 413.531562 0.000367  
HTon      

<하이퍼 스레딩 켰을 >

 

 

startT endT 시간() 평균시간
1 병렬      
1193.879285 1193.878775 0.00051 0.001763
1195.55735 1195.55297 0.00438  
1195.954497 1195.954098 0.000399  
2 병렬      
1250.718352 1250.718071 0.000281 0.000349
1251.968818 1251.968417 0.000401  
1252.790355 1252.789991 0.000364  
4 병렬      
1333.682861 1333.682653 0.000208 0.000284
1336.171769 1336.171549 0.00022  
1337.950726 1337.950303 0.000423  
5 병렬      
1449.503198 1449.502993 0.000205 0.000939
1452.7616 1452.75931 0.00229  
1452.823454 1452.823132 0.000322  
8 병렬      
1589.8527 1589.852293 0.000407 0.00044
1590.653918 1590.653423 0.000495  
1591.37499 1591.374572 0.000418  
10 병렬      
1652.622676 1652.622495 0.000181 0.001527
1653.424747 1653.424336 0.000411  
1654.68126 1654.67727 0.00399  
20 병렬      
1875.417252 1875.416802 0.00045 0.001983
1876.366084 1876.365556 0.000528  
1877.5143 1877.50933 0.00497  
HToff      

<하이퍼 스레딩을 껐을 >

 

하이퍼 스레딩을 켰을 경우와 껐을 경우의 컴퓨터 환경을 최대한 동일하게 하기 위해 네트워크를 차단하고 켜진 프로그램도 최대한 동일하게 유지하였다. (따라서, 환경이 동일한 환경이라고 가정하고 차이를 보았다.)

병렬개수별 시간을 여러 측정하여 평균치를 사용하였다. 병렬개수별 시간 차이는 다음과 같다

 

 

차이를 보기 쉽게 시간에 10^5만큼 곱하였다.

병렬 차이(HTon - HToff)
1 52.86490367
2 8.066666663
4 18.2
5 67.59999999
8 275.1666667
10 -110.6333333
20 -157.0666667

병렬의 개수를 많이 늘렸을 하이퍼 스레딩을 껐을 경우가 시간이 오래 걸리는 것으로 보아, 하이퍼 스레딩을 껐을 , 남은 Architectural state(레지스터) 활용하지 못하는 것으로 판단하였다.

 따라서 이러한 단일 프로세스만 수행해야하는 상황에서 어떻게 하면 효율적으로 수행할 있을까 생각해보게 되었고, 결국 레지스터를 어떻게 하면 최대한 많이 넣고 많이 사용할 잇을까에 대해 생각해보게 되었다.

 

문제 인식 무어의 법칙을 근거로 다양한 기능 시도

1. 병렬적 작업을 하는 단일 프로세스만 수행할 , 하이퍼 스레딩기능을 끄고 하이퍼 스레딩을 위한 레지스터를 활용하면 많은 레지스터를 있지 않을까?

 


PROPOSAL

             <기존의 멀티코어 시스템>

 


예전 Pentium D processor 듀얼 코어와 달리 캐시와 버스 인터페이스를 cpu간에 공유하기 시작하면서 본격적으로 멀티코어 기술에 대한 관심이 많아졌고, 중에 Architectural State부분에서 아래와 같이 cpu내부에 스레드를 여러 두어 문맥 교환을 os 아닌 cpu에서 처리하도록 Hyper threading 기술이 있고 더욱 효율적인 Architectural state 가지로 제안을 하게 되었다

 

1.   기존의 cpu 번째 제안

 

1-1            기존의
 cpu 번째 제안

 

1- 2 제안

           

728x90
반응형

'공부 > 운영체제&시스템프로그래밍' 카테고리의 다른 글

myshell  (0) 2020.07.16
mycp  (0) 2020.07.16
EXT2 file system 실습 (6번 디렉토리안의 82번 파일 찾기)  (0) 2020.07.16
블로그 이미지

아상관없어

,
반응형

병행 수행

- 여러 사용자가 DB를 동시 공유할 수 있게 여러개의 트랜잭션을 동시에 수행하는 것

- 여러 트랜잭션이 차례로 번갈아 수행되는 인터리빙 방식으로 진행

(단위 시간에 많은 트랜잭션을 처리하기 위해)

(트랜잭션 제각각 처리 시간이 다르므로, 처리시간이 긴 연산 수행 동안 다른 연산 수행)

 

병행제어

- 병행 수행시, 같은 데이터에 접근하여 연산을 실행해도 문제가 발생하지 않고 정확한 수행 결과를 얻을 수 있게 트랜잭션 수행을 제어하는 것.

- 정확한 수행 결과가 아닌 경우가 발생하지 않게 병행제어를 잘 해야한다.

 

 

병행 수행 시 발생할 수 있는 문제점

1. 갱신 분실

- 하나의 트랜잭션이 수행한 데이터변경 연산의 결과를 다른 트랜잭션이 덮어서 변경연산 무효화됨

- 여러 트랜잭션이 동시에 수행되더라도 순차적으로 수행한것과 같은 결과값을 얻을 수 있어야된다.

=> T1에 대해 갱신 분실이 발생한다.

 

트랜잭션을 순차적으로 수행하면 해결된다.

 

 

2. 모순성

- 하나의 트랜잭션이 여러 개 데이터 변경 연산을 실행할때, 일관성 없는 상태의 데이터베이스에서 데이터를 가져와 연산 => 모순된 결과가 발생

T1이 X, Y를 다른 상태의 데이터베이스에서 가져와 

트랜잭션 T1의 결과가 X= 4000, Y = 4000이 아닌 X=2000, Y=2500의 잘못된 결과가 나온다.

T1이 완료된후 T2가 실행되면 모순성 문제가 발생하지 않는다.

 

3. 연쇄복귀

- 트랜잭션이 완료되기전, 장애가 발생하여 rollback연산을 수행 => 장애 발생 전에 트랜잭션이 변경한 데이터를 가져가 변경연산을 실행한 다른 트랜잭션도 연쇄적으로 rollback연산을 해야함

==> 장애로 잘못된 결과가 나오고 그 잘못된 결과를 가져가 다른 트랜잭션이 연산을 수행했으므로

(T1 -> 결과(문제발생) -> T2 -> 결과(문제발생!, 잘못된 결과로 연산하였으므로)

 

T2는 T1이 변경한 X를 가져가 연산을 수행한다.

T1 트랜잭션 완료 전 장애가 발생한다.

T1을 rollback해야하지만, T2는 연산이 이미 끝나 rollback이 불가하다.

순차적으로 수행 (T1완료후  -> T2수행)하면 연쇄 복귀 문제가 발생하지 않는다.

 

 

트랜잭션 스케줄

1. 직렬 스케줄 => 인터리빙이 아닌, 각 트랜잭션별로 연산을 순차적으로 실행 => 시간이 길어짐

2. 비직렬 스케줄 => 인터리빙 방식으로 트랜잭션 병행해서 수행 => 병행성 문제 발생

3. 직렬 가능 스케줄 => 직렬스케줄처럼 정확한 결과를 생성하는 비직렬 스케줄 => 시간을 줄이고 병행성 문제 해결

 

1. 직렬 스케줄

- 다른 트랜잭션의 방해를 받지 않고 독립적으로 수행 => 항상 모순 없는 결과

- 병행 수행이 아님 => 시간이 오래걸림

- 다양한 직렬 스케줄을 만들 수 있음

 

T1 -> T2

T2 -> T1

 

2. 비직렬 스케줄

- 트랜잭션이 번갈아 연산 실행 => 하나의 트랜잭션 완료전 다른 트랜잭션 연산 실행가능

- 갱신분실, 모순성, 연쇄복귀 문제 발생하여 결과의 정확성 보장 X

- 다양한 비직렬 스케줄 생성가능 (그중엔 잘못된 결과를 생성하기도함)

 

비직렬 스케줄이지만 병행 수행에 성공하여 정확한 트랜잭션 수행 결과를 생성한다. => 직렬가능 스케줄

 

병생 수행에 실패하여 잘못된 결과를 생성한다.

 

 

 

 

3. 직렬 가능 스케줄

- 비직렬 스케줄 중에서 수행 결과가 동일한 직렬 스케줄이 있는 것.

- 인터리빙 방식으로 병행수행하면서 정확한 결과 얻음

- 직렬 가능 스케줄인지 판단한는것은 복잡한 작업

- 따라서 직렬가능성을 보장하는 병행제어 기법 사용 

 

 

 

병행 제어 기법

- 병행 수행하면서 직렬 가능성도 보장

- 모든 트랜잭션이 준수하면 직렬가능성이 보장되는 rule 정의

- lock 기법이 있음

 

lock기법 - 상호배제

- 병행 수행되는 트랜잭션들이 같은 데이터에 동시에 접근하지 못하게 lock/unlock연산

lock - 트랜잭션이 데이터에 대한 독점권 요청

unlock - 트랜잭션이 데이터에 대한 독점권 반환

 

- 데이터에 접근하기 전 lock하여 독점권을 획득 (read/write 전 lock)

- 다른 트랜잭션에 의해 이미 lock이 걸린 데이터는 lock 불가& 실행 불가

- 연산 수행 끝나면 unlock

 

lock 단위가 커지면 병행성은 낮아지지만 제어가 쉬워지고

단위가 작아지면 제어가 어렵지만 병행성은 높아진다.

 

lock 효율 향상 => 동시성이 높아짐

공용 lock => read만 가능

- read연산 수행가능 ,write 불가 

- 다른 트랜잭션도 동시에 공용 lock 가능

- 데이터 사용권을 여러 트랜잭션이 함께 가지는 것 가능

 

전용 lock => write하기 위해

- read/write 둘다 가능

- 다른 트랜잭션은 어떠한 lock 연산도 실행 불가

- 데이터에 대한 독점권 가짐

 

T1이 X에 대해 빨리 unlock을 해버려 T2가 일관성 없는 데이터에 접근하여 잘못된 결과가 나온다.

(T1이 끝날때까지 lock을 해야한다.)

 

2단계 lock rule = 축소, 확장

- 위와 같은 기본 lock rule의 문제 해결

- 직렬 가능성 보장 위해 lock/unlock연산 수행 시점에대한 새로운 rule 추가

- 트랜 잭션이 처음수행되면 확장단계 => lock만 가능 (lock을 검)

- unlock 연산을 실행하면, 축소단계 => unlock만 가능

- 트랜잭션은 첫번째 unlock 연산전, 필요한 lock 연산을 실행해야한다.

 

하지만 이러한 방법도 

T1 -> X (lock)  ====> Y가 unlock 되길 기다림

T2 -> Y (lock) =====> X가 unlock 되길 기다림

과 같은 경우 데드락이 발생 할 수 있다.

따라서 교착 상태가 발생되지 않게 예방하거나 빨리 탐지하여 조치를 해야한다.

 

 

 

 

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

회복과 병행제어 - 회복  (0) 2021.06.03
관계 대수  (0) 2021.06.03
파이썬 연동 예제  (0) 2021.06.02
index  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
블로그 이미지

아상관없어

,
반응형

병행제어 : 수많은 사람들이 동시에 데이터베이스를 이용하는데 문제가 발생하지 않게 제어해주어야한다.

 

트랜젝션

- 하나의 작업을 수행하는데 필요한 데이터베이스 연산을 모은것.

- 작업 수행에 필요한 SQL문 집합

- 논리적 작업단위

- 장애발생시, 복구나 병행제어 작업을 위한 중요한 단위로 사용

- DB의 무결성과 일관성을 보장하기 위해서 작업 수행에 필요한 연산들을 하나의 트랜잭션으로 제대로 정의하고 관리해야함.

ex 1)

1번 작업과 2번 작업이 하나의 트랜잭션이다.

처리 순서는 중요하지 않지만, 두개의 update 명령이 정상적으로 실행되어야한다.

 

ex 2)

insert와 update 모두 정상적으로 실행되어야 상품주문 트랜잭션이 성공적으로 수행된다.

 

 

트랜잭션 특성

1. 원자성 => 회복기능

- all or nothing

- 트랜잭션의 연산들이 모두 정상적으로 실행 or 하나도 실행 X

- 트랜잭션 수행중 장애가 발생되면? => 실행한 연산을 모두 취소하고 DB를 트랜잭션 작업 전으로 복구

- 따라서 원자성 보장을 위해 회복 기능이 필요하다.

ex)

DB = A: 10000, B : 0

-계좌이체 트랜잭션-

(1) A계좌에서 5000원 인출

(2) B계좌에 5000원 입금

DB = A: 5000, B: 5000

 

1,2번 작업 중 장애가 발생하면 실행한 작업을 취소하고

DB = A: 10000, B : 0

상태로 되돌린다.

 

2. 일관성 => 병행 제어

- 트랜잭션이 성공적으로 수행된 후에도 DB가 일관된 상태를 유지해야한다.

ex)

DB = A: 10000, B : 0  ==> 합계 10000

-계좌이체 트랜잭션-

(1) A계좌에서 5000원 인출

(2) B계좌에 5000원 입금

DB = A: 5000, B: 5000  ==> 합계 10000

 

 

 

3. 격리성 => 병행 제어

- 수행 중인 트랜잭션이 완료될때까지, 다른 트랜잭션이 중간 연산 결과에 접근 불가

- 다른 트랜잭션과 연결 X, 독립됨

- 여러 트랜잭션이 동시에 수행되더라도, 순서대로 하나씩 수행되는 것처럼 정확하고 일관된 결과를 얻게 제어해야함

- 트랜잭션 서로 간섭 X

 

DB = A: 10000, B : 0

-계좌이체 트랜잭션-

(1) A계좌에서 5000원 인출

(2) B계좌에 5000원 입금

DB = A: 5000, B: 5000

 

여기서 B계좌에 1000원을 입금하는 다른 트랜잭션이 동시에 수행되어 (1)작업 후 수행되면

계좌입금 트랜잭션은 

-계좌입금 트랜잭션-

DB = A: 5000, B : 0

(1) B 계좌에 1000원 입금

DB = A: 5000, B : 1000

이 되어 두 트랜잭션이 완료되면

DB = A: 5000, B: 5000

DB = A: 5000, B : 1000

B의 계좌 잔액이 일치하지 않게 된다.

 

이러한 문제가 발생한 원인은 두 트랜잭션이 간섭을 일으키기 때문이다. 

따라서 계좌이체 트랜잭션이 끝날때 까지 기다린후, 계좌 입금 트랜잭션을 수행하면 같은 정보를 update하는 충돌이 발생하지 않는다.

즉, 간섭(충돌)이 되는 작업은 기다리고 아니면 동시수행한다.

 

 

 

4. 지속성 => 회복기능

- 트랜잭션이 성공적으로 완료된 후, 데이터베이스에 반영한 수행결과는 영구적이여야 함.

- 트랜잭션을 성공했으면 결과는 어떤일이 일어나더라도 유지되야한다.

(만약 트랜잭션을 성공했는데 시스템 다운으로 데이터가 날라가 결과에 데이터가 없으면 안된다.)

- 지속성의 보장을 위해선 장애 발생시 회복기능이 필요하다. (다시 복구)

 

 

 

트랜 잭션의 주요연산

1. commit - 트랜잭션이 성공적으로 수행되었음을 선언 (작업 완료)

=> 트랜잭션의 수행 결과가 데이터베이스에 반영되고 일관된 상태를 지속적으로 유지

2. rollback - 트랜잭션을 수행하는데 실패했음 선언 (작업 취소)

=> 지금까지 실행한 연산 결과를 취소하고 트랜잭션 수행 전의 데이터베이스로 상태 되돌림

 

 

트랜 잭션 상태

활동(수행중) -> 실패 -> rollback -> 철회

                 -> 부분완료 -> commit -> 완료

                 -> 부분완료 -> 실패 -> rollback -> 철회

 

활동

- 트랜잭션 수행중

 

부분 완료

- 트랜잭션의 마지막 연산이 실행을 끝낸 직후

 

완료

- 트랜잭션이 성공적으로 완료되어 commit 연산 실행한 상태, 결과를 DB에 반영, DB가 새로운 일관된 상태가 되고 트랜잭션 종료

 

실패

- 장애가 발생하여 트랜잭션 수행 중단된 상태

 

철회

- 트랜잭션 수행실패로 rollback 연산 실행한 상태,

- 지금까지 실행한 트랜잭션 연산을 모두 취소 후 트랜잭션 수행 전으로 DB되돌리고 트랜잭션 종료

- 철회 상태로 종료된 트랜잭션은 다시 수행되거나 폐기됨(상황에 따라)

 

장애 

휘발성 저장장치(소멸성 ) : 장애가 발생하면 저장된 데이터 손실

비휘발성 저장장치(비소멸성) : 장애가 발생해도 저장된 데이터 손실X, 저장장치 자체에 이상이 발생하면 데이터 손실

안정 저장장치 : 비휘발성 저장장치 이용, 데이터 복사본 여러개, 어떤 장애가 발생되도 데이터 손실X, 데이터 영구적 저장

 

트랜잭션 수행을 위해 필요한 데이터 이동 연산

응용프로그램 <-(read) - 메인메모리 <-(input) - 디스크

응용프로그램 ->(write) - 메인메모리 - (output)-> 디스크

read/write에 비해 input, output은 느리다.

트랜잭션에 read/write, input/output 연산이 포함된다.

 * 메인 메모리 데이터 베이스 = 디스크에 있다가 실행 시점에 전부 메인메모리로 가져와 사용한다. (규모가 작고 빠른 R/W시 사용한다.) (주로 read인 경우 - select)

 

디스크와 메인메모리 간의 데이터 이동연산은 block단위로 수행된다.(한번 읽는 시간이 느리므로 block 단위로 읽어옴)

 

 

회복(복구)를 위해 데이터베이스 복사본을 만드는 방법 => 데이터 중복!

덤프(dump) = 데이터베이스 전체를 다른 저장장치에 주기적으로 복사

로그(log) = 데이터베이스에서 변경 연산이 실행될 때마다, 데이터를 변경하기 이전값이후값을 별도의 파일에 기록

ex)

5일마다 backup을 한다면, 원본과 복사본 간에 시간차가 생긴다.

DB -(5일 시간차)-> 백업DB 

따라서, log를 사용 =>  5일전 복사본에 log로 변한 것을 적용하면 현재 DB상태가 된다.

 

 

회복(복구)를 위한 기본 연산

redo(재실행) = 가장 최근에 저장한 DB복사본을 가져온 뒤, log를 이용해서 복사본이 생성된 후의 모든 변경 연산을 재실행하여 장애 발생전의 DB상태로 복구

undo(취소) = log를 이용하여 실행된 모든 변경 연산을 취소하여 DB를 원래 상태로 복구(변경중이었거나, 변경된 내용만 신뢰성을 잃은 경우)

* log : 데이터를 변경하기 이전값과 변경한 후의 값을 기록한 파일, 레코드 단위로 트랜잭션 수행과 함께 기록

 

 

 

DB 회복(복구) 기법

- 로그 회복기법(즉시 갱신, 지연갱신 회복기법)

- 검사 시점 회복기법

- 미디어 회복기법

 

1. 로그 회복 - 즉시갱신 회복기법

- 트랜잭션 수행 중 데이터 변경 연산 결과를 DB에 즉시 반영

- 장애 발생에 대비하기 위해, 데이터 변경에 관한 내용을 log에 기록

- 데이터 변경 연산이 실행되면, log파일에 log 레코드를 기록하고 DB에 변경 연산 반영

- 장애 발생 시점에 따라 redo, undo 연산을 하여 DB 복구

바로 바로 반영한다.

 

1) 트랜 잭션이 완료되기 전에 장애가 발생

=> <T1, start>레코드는 있지만 commit레코드는 없다, 따라서 undo 연산

2) 트랜 잭션 완료 후 장애 발생

=> start, commit 로그 레코드 둘다 존재(정상적으로 실행되었다 판단), 따라서 redo 연산

1 => 일부이므로 undo 연산

2 => T2는 일부만 시행했으므로 undo, T1은 commit했으므로 redo

 

2. 로그 회복기법 - 지연갱신 회복

- 트랜 잭션 수행중 데이터 변경 연산 결과를 로그에만 기록

- 트랜 잭션이 부분완료된 후 로그에 기록된 내용을 DB에 한번에 반영 (commit을 만나면 DB에 반영)

- 트랜 잭션 수행중, 장애 발생하면 로그기록을 버리면 DB가 원래 상태 유지하게 됨 (DB에 반영은 나중에 하므로)

- log만 삭제하면 되므로 undo 연산은 필요없고, redo 연산만 사용한다.

- 로그 레코드에는 변경 이후 값만 기록하면 됨.

 

1) 트랜 잭션이 완료되기 전에 장애가 발생

=> <T1, start>레코드는 있지만 commit레코드는 없다, 따라서 기록된 log 버림

2) 트랜 잭션 완료 후 장애 발생

=> start, commit 로그 레코드 둘다 존재(정상적으로 실행되었다 판단) => 데이터베이스에 반영, 따라서 redo 연산

 

 

3. 검사시점 회복 기법

- 로그 기록 이용

- 일정 시간 간격으로 checkpoint생성

- checkpoint 시점이 되면 모든 log 레코드를 log에 기록 => 데이터 변경 내용을 안정저장창지에 기록 => 검사시점을 표시하는 <checkpoint L> 로그 레코드를 로그 파일에 기록

- 백업본 + log로 복구

- 백업후 시행한 것 모두 redo or undo 하여야한다 => 양이 많을 수 있음

- 따라서 일정 시간 마다 안정저장장치에 변경 내용 저장

- 마지막 check point 이후 작업만 처리한다. => 가장 최근 검사 시점 이후의 트랜잭션에만 회복 작업 수행(즉시 갱신 or 지연 갱신 복구)

- 백업후 시행한 것 모두 redo or undo 하여야한다 => log 전체를 회복기법 적용하는 것 보다 효율성이 좋음

- 검사 시점으로 작업 범위가 정해지므로 불필요한 회복 작업이 없어서 시간 단축

 

 

4. 미디어 회복 기법

- 디스크에 발생할 수 있는 장애에 대비

- 덤프(복사본) 이용 => 전체 DB내용을 일정 주기마다 다른 안전한 저장장치에 복사

- 디스크 장애 발생시 => 가장 최근에 복사해둔 덤프를 이용하여 장애 발생 이전의 DB 상태로 복구, 필요에따라 redo연산 수행

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

회복과 병행제어 - 병행제어  (0) 2021.06.04
관계 대수  (0) 2021.06.03
파이썬 연동 예제  (0) 2021.06.02
index  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
블로그 이미지

아상관없어

,
반응형

관계 데이터 연산 

- 관계형 모델에서 제공하는 데이터를 다루기 위한 방법

- 원하는 데이터를 얻기위해 릴레이션에 필요한 처리 요구를 수행하는 것

- 관계대수: 원하는 결과를 얻기 위해 데이터의 처리과정을 순서대로 기술

- 관계해석: 원하는 결과를 얻기 위해 처리를 원하는 데이터가 무엇인지만 기술

 

관계대수와 관계해석

데이터 언어의 유용성을 검증하는 기준

=> 언어가 적합한지(관계대수/해석으로 할 수 있는 것을 제시한 언어 문법체계로 다 구현가능한지?)

관계 대수나 관계 해석으로 기술할 수 있는 모든 질의를 기술할 수 있는 데이터 언어 = 관계적으로 완전

 

관계 해석

어떻게 검색(X)

무엇을 검색(O)

무엇을 검색할것인가만 기술하는 선언적 표현법을 사용하는 비절차적 질의어

SQL등 많은 상업용 관계언어들이 관계해석에 기반을 두고있음

 

 

관계대수

- 원하는 결과를 얻기 위해 릴레이션의 처리 과정을 순서대로 기술하는 언어(절차 언어)

- 릴레이션을 처리하는 연산자들의 모임

- 폐쇄 특성이 존재(닫힌 성질) => 피 연산자도 릴레이션이고, 연산의 결과도 릴레이션임, input형태 = output형태 동일

 

관계대수와 관계해석 차이

- 관계해석 : 하나의 선언적, 해석식으로 검색질의를 명시, 비절차적 언어

- 관계대수 : 연산들을 순차적으로 사용하므로 절차적인 성질을 가짐

두 표현의 표현력은 동등

 

 

관계대수 연산자

1. 일반 집합 연산자

합집합 => 교환적, 결합적 특징, 차수는 릴레이션들의 차수와 동일, 카디널리티(행의 수)는 두 릴레이션의 카디널리티를 더한 것과 같거나 작음

교집합 => 교환적, 결합적 특징, 차수(컬럼수)는 두 릴레이션의 차수와 동일, 카디널리티(행 수)는 두 릴레이션 중 어떤 카디널리티보다 크지 않다.

차집합 (R-S) => R에는 존재하지만,  S에는 존재하지 않느 투플로 결과 릴레이션 구성, 차수는 R,S의 차수와 같음, 카디널리티는 R의 카디널리티와 같거나 적음, 교환적, 결합적 특징이 없음

 

카티션 프로덕트 => 교환적, 결합적 특징, R에 속한 투플과 S에 속한 투플을 모두 연결함. 차수는 R,S의 차수를 합한것과 동일, 카티널리티는 R과 S의 카디널리티를 곱한것과 같음

 

- 피연산자가 2개 필요하다

- 합집합, 교집합, 차집합은 피연산자인 두 릴레이션이 합병가능해야한다.

(합병가능 : 두 릴레이션의 차수(컬럼의 수)가 동일, 두 릴레이션에서 서로 대응되는 속성(칼럼)의 도메인이 같아야함(자료형이 동일해야)

 

순수 관계 연산자 => 릴레이션의 구조와 특성을 이용

셀렉트 σ(조건)(R) => 릴레이션 R에서 조건을 만족하는 투플들(행)을 반환 (행단위로 반환)

"릴레이션 where 조건식" 과 동일

비교연산자, 논리 연산자(∧, ∨, ¬(and, or, not))를 이용하여 작성

ex) 고객 릴레이션에서 등급이 gold인 투플을 검색하시오

σ(등급 = 'gold')(고객) = 고객 where 등급 = 'gold'

 

프로젝트  π(속성 리스트)(R) => 릴레이션 R에서 주어진 속성들의 값으로만 구성된 투플을 반환 (열단위로 반환)

ex) 고객이름, 등급, 적립금을 검색

π(고객이름, 등급, 적립금)(고객)

 

조인 R⋈S(자연조인) => 공통 속성을 이용하여 R,S의 투플들을 연결함

고객 ⋈ 주문

외래키는 지정이 되지 않아도 조인시 문제가 없지만 무결성 문제가 발생한다.

공통속성인 (고객.고객아이디), (주문.주문고객)은 한번만 나타난다. => 고객 아이디만 나타남

차수(속성)의 개수 = 두차수의 합 - 공통 속성의 개수

 

세타조인 R⋈(JOIN 조건)S => 자연조인에 비해 일반화된 조인, 

주어진 조인조건을 만족하는 두 릴레이션의 모든 투플을 연결하여 생성된 새로운 투플이 결과 릴레이션

결과 릴레이션의 차수는 두 릴레이션의 차수를 더한것

ex) 

고객⋈(고객아이디=주문고객)S

자연조인과 다르게 공통 속성이 둘다 표시된다.

 

디비전 R ÷ S => 릴레이션 S의 모든 투플과 관련 있는 릴레이션 R의 투플 반환

R이 S의 모든 속성을 포함하고 있어야 연산이 가능하다

고객 릴레이션에서 gold 등급을 가진 투플을 고름. 나누기에서 몫과 유사

 

 

 

외부 조인 (R ⋈+ S)=> 자연조인 연산에서 제외되는 투플도 결과 릴레이션에 포함시킴, 즉 두 릴레이션에 잇는 모든 투플을 결과 릴레이션에 포함 시킴

 

 

 

관계대수 예시)

1. 모든 부서의 이름과 지역명을 보이시오

2. 모든 사원의 이름과 담당업무를 보이시오

3. 담당업무가 ‘salesman’ 인 사원의 이름, 입사일자를 보이시오

4. 입사일자가 ‘1999-02-31’ 이후인 사원의 사원번호, 이름, 입사일자를 보이시오

5. 모든 사원의 이름과 부서명을 보이시오

6. 급여액(연봉)1000 이상인 사원의 이름, 급여액, 부서명을 보이시오

7. 입사일자가 ‘1999-02-31’ 이후인 사원의 사원번호, 이름, 부서명을 보이시오.

8. ‘Dalls’ 에 근무하는 사원의 이름, 급여액을 보이시오

9. 입사일자가 ‘1998-01-01’ 이전인 사원중 급여액이 1000 미만인 사원의 사원번호, 이름, 부서명을 보이시오.

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

회복과 병행제어 - 병행제어  (0) 2021.06.04
회복과 병행제어 - 회복  (0) 2021.06.03
파이썬 연동 예제  (0) 2021.06.02
index  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
블로그 이미지

아상관없어

,
반응형

Mysql my_db 와 연동하여 다음 내용을 실행하는 python 프로그램 

- 사원번호가 7521인 사원의 이름을 출력한다

- 이름이 SCOTT 인 사원의 부서이름을 출력한다

- 담당업무가 SALESMAN 인 모든사원의 이름을 출력한다.

- 모든 부서의 이름을 출력한다.

 

#mysql - python connection test

import pymysql #mysql과 연동하기 위해

#필요한 기본 DB 정보
host = "localhost"
user = "root"
pw = "password"
db = "my_db"

#DB에 접속
conn = pymysql.connect(host= host, user = user, password = pw, db = db)

#사용할 sql문
sql1 = "SELECT ename FROM emp WHERE empno=7521"
sql2 = "SELECT dname FROM emp, dept WHERE dept.deptno=emp.deptno and ename='scott'"
sql3 = "SELECT ename FROM emp WHERE job='salesman'"
sql4 = "SELECT dname FROM dept"

querys = [sql1, sql2, sql3, sql4]

for sql in querys:
    print(sql)
    
    # sql문 실행/데이터 받기
    curs = conn.cursor() #sql 실행시 결과를 담고 있는 버퍼를 정렬
    curs.execute(sql)	#앞의 sql문 실행 
    row = curs.fetchone() #sql실행결과 모두 가져오기, fetchall 모든 행 가져옴, fetchone은 하나의 행만 가져옴
    while(row):
        print(row)
        row	= curs.fetchone()	
        
    print("==========================================================")
    
#db 접속 종료
curs.close()
conn.close()

 

 

 

Mysql world 데이터베이스와 연동하여 다음과 같이 국가명을 입력하면 해당 국가의 정보를 출력하는 윈도우 프로그램

- 매치되는 국가가 없으면 모든 항목에 공백 출력

import sys
import pymysql
from PyQt5.QtWidgets import *

def connectDB():
    #필요한 db 정보
    host = "localhost"
    user = "root"
    pw = "Changmin97"
    db = "world"
    
    #db 접속
    conn = pymysql.connect(host=host, user =user, password =pw, db =db)
    return(conn)  #connection  리턴

def disconnectDB(conn):
    conn.close()
    
    

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        
    
    # UI 디자인 함수
       def initUI(self):
        #QLable 문자열 출력
        label1 = QLabel('Country name')
        label2 = QLabel('Continent')
        label3 = QLabel('Population')
        label4 = QLabel('GNP')
        label5 = QLabel('Captial city')
        label6 = QLabel('Language')
    
    

        #text_Country_name
        self.text_Country_name = QTextEdit()
        self.text_Country_name.setFixedWidth(200)
        self.text_Country_name.setFixedHeight(30)
    
    
        #버튼 생성, 버튼 클릭시 btn_1_clicked 실행
        btn_1 = QPushButton('Query')
        btn_1.clicked.connect(self.btn_1_clicked)
    
        #text_Continent
        self.text_Continent = QTextEdit()
        self.text_Continent.setFixedWidth(200)
        self.text_Continent.setFixedHeight(30)
    
        #text_Population
        self.text_Population = QTextEdit()
        self.text_Population.setFixedWidth(200)
        self.text_Population.setFixedHeight(30)
    
        #text_GNP
        self.text_GNP = QTextEdit()
        self.text_GNP.setFixedWidth(200)
        self.text_GNP.setFixedHeight(30)
        
        #text_Captial_city
        self.text_Captial_city = QTextEdit()
        self.text_Captial_city.setFixedWidth(200)
        self.text_Captial_city.setFixedHeight(30)
        
        #text_Language
        self.text_Language = QTextEdit()
        self.text_Language.setFixedWidth(200)
        self.text_Language.setFixedHeight(30)
        
        
        
    
        #화면 배치, grid를 만들어 배치
        gbox = QGridLayout()
    
        gbox.addWidget(label1,0,0)
        gbox.addWidget(self.text_Country_name, 0, 1)
            
        gbox.addWidget(btn_1, 0, 2)
            
        gbox.addWidget(label2,1,0)
        gbox.addWidget(self.text_Continent, 1, 1)
            
        gbox.addWidget(label3,2,0)
        gbox.addWidget(self.text_Population, 2, 1)
        
        gbox.addWidget(label4,3,0)
        gbox.addWidget(self.text_GNP, 3, 1)
        
        gbox.addWidget(label5,4,0)
        gbox.addWidget(self.text_Captial_city, 4, 1)
        
        gbox.addWidget(label6,5,0)
        gbox.addWidget(self.text_Language, 5, 1)
    

        self.setLayout(gbox)
        self.setWindowTitle('Country Info')
        self.setGeometry(300, 300, 480,250) #창뜨는 위치, 크기
        self.show()
        
        
    #버튼 클릭 처리
    def btn_1_clicked(self):
    
        #사용자가 입력한 값 받아서 empno 저장
        Country_name = self.text_Country_name.toPlainText()
            
        sql = "SELECT country.name, continent, country.population, gnp, city.name, language \
                From country, city, countrylanguage\
                where country.code = city.countrycode \
                    and country.code = countrylanguage.countrycode\
                    and country.capital=city.id\
                    and IsOfficial = 'T' \
                    and country.name ="+"'"+Country_name+"'"
        #ex) ename '"+ename+"'" =>문자일 경우 따옴표 주의!
        conn = connectDB()
        curs = conn.cursor()
        curs.execute(sql)
            
        result = curs.fetchone() # sql 실행 결과 가져오기

        if result:    
            self.text_Country_name.setText(result[0])
            self.text_Continent.setText(result[1])
            self.text_Population.setText(str(result[2]))
            self.text_GNP.setText(str(result[3]))
            self.text_Captial_city.setText(result[4])
            self.text_Language.setText(result[5])
        else:
            self.text_Country_name.setText("") #매치되는 국가가 없으면 모든 항목에 공백 출력
            
            
        curs.close()
        disconnect(conn)
            
#END Class


#프로그램 실행, class를 생성하고 실행
if(__name__ == '__main__'):
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())            
    
        

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

회복과 병행제어 - 회복  (0) 2021.06.03
관계 대수  (0) 2021.06.03
index  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
DB 파이썬 연동  (0) 2021.05.18
블로그 이미지

아상관없어

,

index

공부/데이터베이스 2021. 6. 2. 23:50
반응형

ex) select ename from emp where empid = 7934;

위 명령을 수행할때

1. 순차적으로 검색한다.

=> 요청이 많을 경우 처리시간이 오래 걸리게 된다.

 

2. 이진 검색 (데이터가 정렬되어 있는 경우)

데이터가 갱신 될때마다 정렬작업을 하여야하므로 비용부담이 크다.

 

3. index 이용 (index = keyword + address)

index는 해당 값의 주소를 가리킨다. 

그리고 index는 항상 sorting된 상태이다.(실제 데이터는 정렬되어 있을수도 있고 아닐수도 있다.)

index가 있는 column의 값을 기준으로 조회하면 빨라진다.

 

대부분의 DBMS는 기본키, 외래키에 대해 자동으로 index를 설정한다.

=> 테이블 생성시 기본키를 생성하면 자동으로 인덱스생성

외래키 컬럼에 대해서도 인덱스가 생김

기본키가 되었단 말은 검색의 기준으로 많이 선택되니 인덱스를 붙이는 것이 속도를 빠르게 할 수 있음

외래키도 테이블과 테이블을 조인하여야되는데, 매치되는 항목을 찾아야하므로 인덱스를 걸어주어 join연산을 빠르게 해줌

기본키 - empno

자기자신참조 - mgr

외래키 - deptno

에 대해 자동으로 인덱스가 생성됨

=> 인덱스가 없는 column을 기준으로 검색시 속도가 느림

따라서 검색 기준이 될만한 column들은 인덱스를 생성하는 것이 좋음

 

만약 모든  column에 index를 생성하면??

인덱스는 항상 정렬된 상태를 유지해야하므로, overhead가 커진다. 따라서 최소한의 index를 유지하는 것이 좋다.

 

 

SQL문으로 index생성하기

create index idx_job on emp (job);
show index on emp;

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

관계 대수  (0) 2021.06.03
파이썬 연동 예제  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
DB 파이썬 연동  (0) 2021.05.18
저장 함수 예제  (0) 2021.05.15
블로그 이미지

아상관없어

,
반응형

DBMS의 목적 중 하나는 데이터 베이스에 저장된 정보를 안전하게 보호하는 것이다.

이를 수행하기 위한 수단으로

추가 로그인

암호화 => 민감한 정보

사용자 관리 => 등록된 계정만

권한 관리

가 있다.

 

사용자 관리

사용자가 DB를 이용하기 위해선 계정이 필요함

사용자가 DBMS에 로그인하면 DBMS는 등록된 사용자인지 검사함

따라서 이를 위해 사전에 계정생성이 필요하다.

 

root

최고권한을 가진 사용자 계정으로 데이터베이스 및 DBMS에 대한 모든 권한을 가진다.

따라서 비밀번호가 노출되면 위험하고 일반 사용자에게 root권한을 주는 것은 위험하다.

그러므로 일반 사용자들은 별도의 계정을 만들어 사용하도록 해야한다.

 

사용자계정 생성시

localhost => DBMS 서버에 직접 접근 -> 원격접속 불가

% => DBMS에 원격 접속 ->직접접근 불가

 

서버에 직접 접속, 원격접속 둘다 하려면 같은이름의 local, remote계정 두가지 많들어야한다. 

 

권한은

1. 모든 DB

2. 패턴에 매칭되는 DB(이름으로 검색), 선택한 DB

에 접근할 수 있는 권한을 줄 수 있고,

select, insert, update, delete, execute, show view, create, alert, index, drop..... 와 같은 권한을 줄 수 있다

 

 

Mysql workbench는 DB단위로 권한을 부여하고 회수한다. => 세부적으로 되지 않는다.

따라서 sql 명령문 (grant, revoke)를 이용하여 테이블 단위로 권한을 부여하고 회수할 수 있다.

 

GRANT select ON my_db.emp TO user_1@localhost;
GRANT select, insert, update ON my_db.dept TO user_1@localhost;

 

 

root 사용자일 경우 sql문을 사용하여 사용자를 생성하고 권한부여, 회수가 가능하다.

localhost 사용자의 경우'

create user user_2@localhost identifited by '4321';

 

원격접속 사용자의 경우

create user 'user_2'@'%' identified by '4321';

 

생성된 사용자 확인

SELECT * from mysql.user;

mysql => system 카탈로그 정보가 있는 데이터 베이스

user => user테이블에 사용자 정보가 저장

 

 

권한의 부여

my_db에 대한 모든 권한 부여
grant all privileges on my_db.* to user_2@localhost;


일부 권한 부여
grant select, insert on my_db.* to user_1@localhost;


테이블에 대한 모든 권한 부여
grant all privilleges on my_db.emp to user_1@localhost;


테이블에 대한 일부 권한 부여
grant select, insert on my_db.emp to user_1@localhost;

 

 

with grant option

자신의 권한을 다른 user에게 부여할 수 있게 된다.

grant all privileges on my_db.* to user_2@localhost with grant option;

user_2로 로그인하여 다른 유저에거 자신의 권한을 grant 할 수  있다.

 

부여된 권한을 확인하려면

flush privileges; //변경된 내용을 메모리에 반영(권한 적용)
show grant for user_1@localhost;

 

 

권한 회수 => revoke

revoke delete on my_db.emp from user_2@localhost;

 

사용자 삭제

drop user user_2@localhost;

 

 

사용자 관리 - role

ex) 영업업무를 하는 user1, user2, user3이 있고 필요한 권한은 select, update일 경우

create role sales_role; //역할 생성
grant select, update on my_db.emp to sales_role; //역할에 권한 부여

grant sales_role to user1@localhost;
grant sales_role to user2@localhost;
grant sales_role to user3@localhost;

user1,2,3에 각각 역할을 준다.

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

파이썬 연동 예제  (0) 2021.06.02
index  (0) 2021.06.02
DB 파이썬 연동  (0) 2021.05.18
저장 함수 예제  (0) 2021.05.15
저장 프로시저 예제  (0) 2021.05.15
블로그 이미지

아상관없어

,
반응형

cwe 119

메모리 버퍼의 경계가 있는데 경계와 관련된 연산에 대한 제한, 제약을 적절히 처리하지 못할때 생길 수 있는 약점

어떤 소프트웨어는 메모리 버퍼와 관련된 연산을 할 수 있다.

하지만 그 연산은 버퍼의 의도된 경계 바깥쪽의 영역을 read/wrtie할 수 있음

연산의 제약조건은 버퍼의 의도된 경계내에서 연산해야

버퍼의 경계내의 메모리를 읽고 쓰기 해야됨

 

어떤 언어는 직접적으로 메모리 위치를 참조하는데 그 위치가 실제로 유효한지 아닌지 확인 하지 않음

프로그램이 참조할 수 있는 유효한 주소인지 체크하지 않아 다른 변수나 자료구조 내부 프로그램 메모리 영역을 읽거나 쓸 수 있음 = >연산 제약을 제대로 처리 하지 않았기 때문에

공격으로 연결되면 임의의 코드를 실행할 수 있고 임의의 제어흐름으로 변조, 민감한 정보 읽기, 시스템이 망가짐

 

cwe 119 = > 버퍼 오버플로우라고도 함

버퍼 오버플로우는 사람마다 다른 의미로 사용가능

어떤 도구는 버퍼의끝을 넘어서서 write하는것

다른 개발자는 버퍼의 경계밖(버퍼의 시작점, 끝 밖의 공간)에서 읽거나 쓰는것.

또 어떤 사람들은 버퍼의 끝 이후에 어떠한 행동을 하는 것(읽기, 쓰기 가능)

사람마다 다르기때문에 혼란스러운 용어임

 

따라서 메모리 경계에서 부적절한 연산 제한이 올바른 표현이다.'

 

이러한 약점의 결과는 CIA가 깨짐

인가되지 않은 코드,명령실행, 메모리 변조 가능

한바이트만 조작할 수 있음

 

가용성, 기밀성

어떤 메모리를 읽을수 잇고 시스템이 비정상적으로 종료될수 잇음 (dos) cpu같은 자원을 소모(메모리도 가능)

메모리 경계를 넘어서면 메모리의 붕괴를 가져와 시스템이 붕괴됨

해당 프로그램이 공격을 받아서 무한루프상태로 갈수도 있다.

 

기밀성

read memory

주어진 자료구조 범위를 넘어서서 읽으면 민감한 정보를 읽을 수도 잇다.

메모리의 현재 버퍼의 위치와 같은 정보

 

어떻게 완화??

소프트웨어 보안은 개발생명 전 주기에서 고려

각 단계에서 취약점을 완화할 숭 ㅣㅆ다

요구사항분석 => 언어선택을 잘 해야. cwe119에 강한 언어를 사용하며 ㄴ좋다, c보단 자바와 같은 언어(자체적으로 메모리 관리를 함) ada나 c#은 오버플로우 보호기법을 적용하고 잇음

그렇지만 이러한 언어들은 binary가 아님 실제 실행시 native코드로 변환 되어야함. 따라서 native code와 상호작용하는 인터페이스는 오버플로우 약점에 취약(자바 가상머신은 c로 만들어져잇음)

언어를 윗단에서 좋은 언어를 사용하더라도 완전하게 해결하는 것은 아님

 

아키텍쳐및 설계

라이브러리나 프레임워크를 잘 사용

검증이된 라이브러리나 프레임 워크 사용 => safe c String 라이브러리, strsafe.h

string 관련 오버플로우 문제를 해결 => 완벽X 

많은 오버플로우는 string고 ㅏ연관 없음, 하지만 string관련 문제를 해결가능

cwe119취약점을 방어할 숭 ㅣㅆ다.

 

빌드 컴파일 단계

컴파일 할때 최신 컴파일러사용, 컴파일 옵션을 버퍼 오버플로우를 탐지할 수 잇게 사용

MS visual studio 에선 GS flag, Fedora/red hat에선 fortify source gcc flag사용 

공격이나 취약점을 조기에 탐지할 숭 ㅣㅆ음

하지만 완변하지 ㄴ않음

 

 

구현단계 

자신이 작성중인 버퍼가 원하는 대로 작동하는지 두번이상 확인

strcpy -> strncpy

gets->fgets

memcpy보다 안전한 것 사용

안전한 라이브러리 함수 사용

 

운영단계

운영 환경을 좀더 안전하게 만들어라

관리자는 aslr , position-independent executable (pie)와 같은 기능을 사용해서 특정한 주소를 공격자가 예측하지 못하게 해라

공격자는 버퍼의 주소를 예측하기 어려움

NX비트 활용 (hw기법)

임의의 코드가 스택이나 데이터 세그먼트에 들어갈 확률이 높은데,  data execution protection(nx) 를 하면 코드 세그먼트가 아닌 스택이나 데이터 세그먼트 코드는 실행하기 못하게함.기본적으로 프로세스의 메모리 레이아웃에서는 코드, 텍스트 세그먼트의 코드만 실행해야함. 공격자들은 자기가 실행하고 싶은 코드를 스택이나 ㄴ=데이터 세그먼트에 넣음, 

기본적으로 코드(텍스트) 세그먼트는 read only 여서 코드 주입 불가

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

Android 앱 난독화 실험 및 분석  (0) 2021.06.24
Widthness bugs  (0) 2021.06.02
암호화 기본 - 2  (0) 2021.06.02
암호화 기본 - 1  (0) 2021.06.01
apk 난독화  (0) 2021.05.17
블로그 이미지

아상관없어

,

Widthness bugs

공부/보안 2021. 6. 2. 15:41
반응형
#include <stdio.h>

int main(){
	int l;
    short s;
    char c;
    
    l = 0xdeadbeef;
    s=l; //short에 int
    c=l; //char에 int
    
    
    printf("l=0x%x (%d bits)\n", l, sizeof(l) * 8);
    printf("l=0x%x (%d bits)\n", s, sizeof(s) * 8);
    printf("l=0x%x (%d bits)\n", c, sizeof(c) * 8);
    
    
    return 0;
}
    

short : 16bit = 2byte이고, b -> 최상위비트가 1이므로 0xffffbeef가 된다.

char : 8bit = 1byte이고, e -> 최상위비트가 1이므로 0xffffffef가 된다.

 

signed int n;

+65535 = 0x0000ffff

+65536 = 0x00010000

 

unsigned short s = +65536 (?)

 

 

./ex2 65536 hello

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
	unsigned short s;
    int i; 
    char buf[80];
    
    if(argc < 3) return -1;
    
    i = atoi(argv[1]); //65536
    s=i; //65536 => 오버플로우로 0이됨
    
    //s는 0이므로 if 통과
    if(s>=80){
    	printf("oh no you don't!\n");
        return -1;
        }
        
    printf("s=%d\n", s);
    memcpy(buf, argv[2], i); //memcpy에선 i가 unsigned int형으로 변환됨
    //buf의 크기는 80인데 65536만큼 복사하려함 (dest, source, size)
    buf[i] = \0';
    printf("%s\n", buf);
    return 0;
}

 

 

65535 = 0x0000ffff

65536 = 0x00010000

 

부동소수점 정밀도 문제 => 실수는 무한히 많은데 이 실수를 유한 개의 비트로 표현하기 위해서는 근삿값으로 표현해야 하기 때문이다. =>부동소수점 반올림 오차

(컴퓨터가 실수를 저장할때 가수부와 지수부로 나누어 저장을 한다.)

(int형으로의 캐스팅은 반올림하지 않고 소수점이하를 잘라버린다.)

 

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

Android 앱 난독화 실험 및 분석  (0) 2021.06.24
cwe119 버퍼 오버플로우 방어  (0) 2021.06.02
암호화 기본 - 2  (0) 2021.06.02
암호화 기본 - 1  (0) 2021.06.01
apk 난독화  (0) 2021.05.17
블로그 이미지

아상관없어

,
반응형

혼돈 : 암호문의 통계적 특성과 암호키 값과의 관계를 가능한 복잡하게 하는 기법, 암호문을 보고 원문을 유추하기 힘들다.

- 평문의 통계적 특성들을 잘 숨겨야한다.

- 복잡한 치환 알고리즘을 사용한다.

- 예시 : hill cipher

- 암호문의 각 문자는 키의 일부분에 의존함.

 

확산 : 암호문의 통계적 특성이 평문의 통계적 특성과 무관하게 하는 기법, 알고리즘의 패턴을 추론하기 힘들다.

- 평문의 통계적인 구조가 암호문에 골고루 흩어지게함

- 암호문에서 평문의 통계적 특성이 안나타남

- 평문 블럭 또는 암호화키의 bit하나가 달라지면 암호문에서 큰 변화가 됨

 

시저암호화 비즈네로 암호화는 혼돈과 확산의 속성을 갖고 있지 않다. 따라서 빈도수 공격에 취약하다.

많은 현대 암호화들은 혼돈과 확산의 속성을 가지고 설계되었다.

 

 

DES : 혼돈과 확산을 가지는 블록 암호화이다.

AES : 혼돈과 확산을 가지며, substituion-permuatation network이다.

 

 

Block 암호화 : 64/128 bit block

- DES(64)

- AES(128)

- SEED

Block 단위로 처리되어 빈도수와 중복패턴을 줄여주기 때문에, block의 크기는 클수록 안전하다. 

=> 시저암호는 블럭단위로 처리하지 않아 빈도수 공격에 취약하고 hill 암호화는 블럭단위로 처리하여 빈도수 공격에 안전하다.

 

Stream 암호화 : 1 bit 혹은 byte 단위로 처리한다.

- RC4 stream cipher

- otp => 빠르게 하기 위해 xor 연산을 한다. 키가 반복될시 위험하다. 

 

Block 암호화 Stream 암호는 처리단위가 다르다.

 

DES와 AES 둘다 대칭키 암호화알고리즘이다.

DES는 64bits block이지만 AES는 128bits block이다.

키의 길이 또한 AES가 더 길다.

DES는 Substituion, permutation을 사용하지만, AES는 substitution, shift, bit mixing을 사용

DES 알고리즘은 비공개이고 AES는 공개되었다.

=> kerckhoff's principle: 키 제외 나머지는 모두 공개,  키의 길이가 길수록 안전성이 높아진다. (전사적 공격이 어려워짐)

 

대칭키

- 키 분배의 문제가 발생된다.

따라서

Diffie-Hellman 키 교환 : 안전하지 않은 통신 채널로도 키를 공유할 수 있게 된다.

Alice는 a, g, p를 정하고 Bob에게 g, p, A를 전달한다.

Bob은 b를 정하고 B를 Alice에게 전달한다.

Bob과 Alice는 각각 A, B로 같은 S를 얻는다.

 

 

위와 같은 방식으로 대칭키를 분배할 수 있다.

 

- 대칭키, 비대칭키 키의 개수

먼저, 대칭키는 n명이 있을때 n(n-1)/2만큼의 키가 필요하다 => O(n^2)

비대칭키는 n명이 있을때 2n만큼의 키가 필요하다 => O(n)

따라서 비대칭키가 필요한 키의 개수가 적다.

 

비대칭키를 사용하지 않고 대칭키를 사용하는 이유는 대칭키는 계산량이 비교적 적은 반면 비대칭키(공개키)는 계산량이 많기 때문이다.

 

비대칭키는 서명에도 사용된다.

A의 개인키로 암호화한다면 A의 공개키로 복호화 가능하므로 A라고 확인 할 수 있다.

 

대칭키 : 서로 비밀키를 공유, 키의 개수 n(n-1)/2, 메시지 암호화에 사용, 덜 복잡하여 빠르다. 기밀성, DES, AES, SEED, IDEA

공개키 : 인증, 서명, 짧은 메시지 암호화에 사용, 공개키로 암호화시 보낸사람을 확인하는 서명이 필요하다. 알고리즘이 복잡하여 느리다. 기밀성, 인증, 부인방지(전자서명), 공개키 관리(Public Key Insfrastructure), RSA, ECC, ElGamal

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

cwe119 버퍼 오버플로우 방어  (0) 2021.06.02
Widthness bugs  (0) 2021.06.02
암호화 기본 - 1  (0) 2021.06.01
apk 난독화  (0) 2021.05.17
Anti-Reverse Engineering  (0) 2021.05.14
블로그 이미지

아상관없어

,
반응형

codebook & chiper

code : 어떤 메시지를 암호화하는데 사용하는 방법(단어나 구가 다른 어떤것으로 변환하게 하는 방법)

codebook : 단어나 구절을 암/복호화 하는데 사용하는 문서

cipher : 개별적인 문자, bit 수준에서 메시지 암호화

메세지가 코드에 의해 변환되고 그 뒤 chiper에 의해 변환된다.

chiper : 암호, 암/복호화 시스템/알고리즘

평문 : plaintext, clear text

암호문 : chipertext, cypertogram

Cryptography : 암호기법 (암호화 방법)

Cryptanalysis : 암호해독 (키 없이 복원) => Cryptanalyst (암호문 깸)

Cryptology : 암호학(키로 암/복호화) => Cryptographers (암호문 생성)

Cryptography : 암호기법 (암호화 방법)

메시지를 쉽게 이해할 수 없는 형태로 바꾸고, 다시 원래의 형태로 복원하는 것.

기밀성, 데이터 무결성, 부인방지, 인증을 포함하는 정보보안을 제공하는 수단이나 방법

Symmetric key, Asymmetric key

key => 기밀성, 무결성, 인증 등을 하기위해 사용하는 암호화 알고리즘의 input

키를 안전하게 보관하는 것이 중요 => TPM(trusted platform module), secure co-processor, smartcards...

keyspace

모든 가능한 키들의 집합이다.

keyspace가 크면 안전하지만 bit길이가 길어지고, 그만큼 계산량도 많아진다.

crypto(암호화)

Only the key is secret => 케르크호프스의 원칙

공격자는 시스템에대해 알고 오직 키만 모른다.

암호화 알고리즘은 비밀이 아니며, 오직 키만 비밀이다.

비밀 알고리즘은 노출 되었을때 위험해지고, 언젠가는 노출된다.

따라서 open하여 취약점들을 없앤다.

Substitution(대치,치환)/Transposition(전치(위치를 바꿈))

Substitution(대치, 치환)

1:1 혹은 1:多 대응

하나의 symbol을 또 다른것으로 대체함

예시)

1. caeasar 암호(= shift chiper, Additive cipher)

shift chiper, Additive cipher

k = key만큼 shift함

ex) key = 7

C = (P + k) mod 26

P = (C - k) mod 26

Brute-force attack

위의 방법으로 암호화된 암호문을 Key가 1일때, 2,3,4,5,...26 일때의 경우를 모두 조사하면 평문을 찾을 수 있다.

따라서 위의 암호화 방법은 안전하지 않으므로 keyword를 추가하여 복잡하게 만들 수도 있다.

2. keyword 추가(전수조사 막기위해)

예를 들어 keyword가 ZEBRAS이면

ZEBRAS를 먼저 놓고, Z, E, B, R, A, S를 제외한 알파벳을 놓아 평문을 치환한다.

=> 가능한 keyCaesar 암호화보단 깨기 어려워진다.

또는

알파벳을 순차적으로 대응하지 않고 섞어서 대응하여도 복잡해진다.

하지만, 위의 방식들은 모두 1대1 대응이므로 문자의 빈도수를 세면 유추가능해진다.

항상 고정적으로 하나의 평문 문자가 하나의 암호문 문자로 대응되어 유추하기 쉽다. => 단일문자 치환 암호

(자주나오는 모음이나 diagram(2개문자), tigram(3개문자))

3. 비즈네로 암호 (빈도수 유추 막기위해)

예시) keyword - pascal

먼저 평문의 알파벳을 순서에 맞는 숫자에 대응하고

키워드 문자를 숫자에 대응하여

C = (P + K) mod 26 하여 암호문을 구한다.

s -> H, h- >H

s -> H, s -> S

와 같이 1대1 대응이 아니라 1대多 대응이 된다.

따라서 빈도수 공격을 완화시킬 수 있다.

비즈네로 암호는 이러한 방법을 사용한다.

실제론, 아래와 같이 비즈네르 table을 이용하여

키워드(Darkly)를 대응시켜 (K, P)와 같이 대응되는 문자를 테이블에서 찾아 암호문을 만든다.

하지만 이 방법도 키워드가 반복되므로 주기(키 길이)를 예측 할 수 있어 유추 가능해진다(암호 원리 파악 가능).

(평문 내용과 키워드 위치가 반복된다, 언어구조와 빈도 정보로 유추)

따라서 keyword의 길이를 평문길이만큼으로 하는 one-time-password을 사용하면 해결할 수 있지만, keyword를 관리하는 것이 어렵다.

4. one time pad

깨질 수 없는 안전한 암호 기법이다. 그 이유는 ...

  1. 키(s)의 길이가 평문과 같거나 길다. => 암호문은 평문에 대한 어떠한 정보도 제공하지 않는 것처럼 보인다.

  2. 키(s) 정보는 두 당사자만 알고 있다.

  3. otp는 랜덤 생성해야하며 한번만 사용하므로 이전에 사용한 키는 사용되지 않는다.

하지만 키의 분배가 어려운 문제가 있다. 따라서 이론적으로만 안전하다.

c= m xor s, 평문을 키와 xor연산하여 암호문을 얻는다.

(평문이 8개의 문자를 사용하여 8bit 키를 사용함)

복호화 역시 같은키로 한다.

하지만 여기서 Alice와 Bob이 키를 주고 받을때, 중간에 이중첩자 Charlie가 다른키를 Bob에게 알려준다면, Bob은 복호화문을 이해하기 어렵거나 확인을 위해 Alice에게 직접 찾아가야한다.

혹은 Alice가 포로로 잡혓을때, 적들에게 잘못된 키를 알려준다면 적들은 해당 키로 복호화한 평문이 진짜인지 아닌지 판단할 수 없다.

다른 방법으론,

5. keyword 뒤 평문을 배치하여 키 길이를 맞춘다. (키 = keyword + 평문) (keyword 반복 문제 해결)

현대 암호에서는 이러한 방식을 응용한다.

2bit의 평문을 S-Box(substitution box)를 통하여 4bit로 확장시킨다.

정리하면,

monoalphabetic cipher(단일문자 치환 암호)는 전체 메시지에 대해 고정된 치환(1대1 대응)을 하여 빈도 수 공격으로 유추 가능해진다.

polyalphabeti cipher(복합 문자 치환 암호)는 평문에서 문자의 위치에 따라 대체되는 문자가 다르다 (1:多 대응),

하지만 기본적으로 문자 단위로 처리되므로, 키워드 반복시 주기를 알 수 있고, (모음), (모음은 자음에 의해 분리된다), (diagrams(연속되는 2개의 문자)), (Q뒤에는 U가 따라옴)과 같은 정보로 유추할 수 있다.

6. polygraphic substituion ciper (다중문자 대치) => Hill Cipher => Block 사용

하나의 평문 문자를 또 다른 암호문자 하나로 대응하면 즉, 문자 하나씩 암호문으로 변환할 경우에 빈도수 공격에 취약해진다.

다중문자 대치는 이웃한 문자를 그룹으로 만들어 그룹단위로 취급한다. => block 단위block의 크기를 n이라 하면

_n = 2일 경우) _

평문 : MISSISSIPPI

Block : MI SS IS SI PP IK

(Block 크기를 위해 더미 k를 추가함)

MI -> EQ, SS->GC ....와 같이 치환한다.

n, 즉 block이 커질 수록 빈도수 공격에 강해진다.

Playfiar cihper (n = 2) => 2x2 hill ciper이다.Y = Ax mod26(Y = 암호문, A = 키, x = 평문)다음과 같이 표현할 수 있다.

평문 : AT, Key : CDDG 인 경우)C -> 3, D->4, D->4, G->6

따라서

k를 표현할 수 있고, 평문 AT를 k로 암호화하면(k는 정방행렬로 만들어야한다. 암호화는 행렬의 곱이고 복호화는 역행렬을 만들어 곱을한다. 따라서 계산식을 쉽게 만들 수 있어야한다.)

5, 10 => FK가 나온다.(mod 26인 이유는 알파벳을 숫자로 표현했기 때문)

n을 3으로한 경우)

3x4 즉, 블럭의 크기를 12로 하여도 된다. => block 크기 가변적

복호화는 k의 역행렬을 곱하고 mod26을 하여 평문을 얻을 수 있다.

긴 평문 예시)

예시 - WANT_HELP.

WAN/ T_H/ ELP/ ... (더미 .. 추가)

22 0 13/ 19 28 7/ 4 11 15/ 26 26 26 (알파벳을 숫자로 바꿈)

키 = A

25 23 21/ 23 14 1/ 17 4 14/ 19 11 12

암호문 : ZWV XOB REO TLM

Transposition(전치(위치를 바꿈))

문자들의 순서를 바꾼다.

Columnar Transposition

1. simple

컬럼의 크기를 정하고 그에 맞게 평문을 자른다.

ENEMY TANKS APPROACHING HILL EIGHT SIX THREE

만약 컬럼의 개수의 배수가 아니면 더미를 추가한다.

그후, 컬럼별로 가져온다.

ENOHHR NKAITE ESCLSE MAHLIS YPIEXT TPNITO ARGGHP

만약 block의 크기가 5라면 5씩 잘라 표현한다.

ENOHH RNKAI TEESC LSEMA HLISY PIEXT TPNIT OARGG HPxxx (더미 추가)

2. 컬럼 순서 지정

컬럼에 순서(key)를 지정하여 순서에 따라 컬럼을 읽는다.

혹은 알파벳(key)으로 순서를 줄 수도 있다.,

=> EATI THIH MEXN ETMG MEDT

3. Double Transposition (행과 열 둘다 사용)

평문 : attackxatxdawnx (공백 대신 x를 사용하였다.)

먼저 행을 (3,5,1,4,2)의 순서로 바꾸고 열을 (1,3,2)순으로 바꾼다.

열로 읽었을 경우 : xwaxa txtak antdc

행으로 읽을 경우 : xta wxn att xad akc

4. Route Transposition Cipher

4.1. Rail-Fence Cipher

예시 rail 2개)

평문 = Meet me after the toga party

예시 rail 3개)

평문 = WE ARE DISCOVERED. FLEE AT ONCE

WECRL TEERD SOEEF EAOCA IVDEN

4.2. 위 방법외에 입력위치를 다르게 할 수 도 있다.

ex)

REINFORCEMENTS ARRIVING NOW

NMRGI FEEAR NNEOC NSIIO RRTVW

혹은 삼각형 모양으로도 할 수 있다.

4.3. Triangular Pattern

RMIFE VEONI RIRTN NCSGE ANROW

P- box

현대의 전치암호는 P(permutaiotion) box를 사용한다.

그냥 순서를 바꿔주는 p-box,

압축을 해주는 p-box

확장을 해주는 p-box

현대 암호인 AES는 Pbox를 사용한다.

튜링

튜링 머신

what can be computed. => 실제로 어디까지 계산가능한가?

어떤것, 어디까지 계산을 할 수 있는지 조사하는데 도움을 줄수 잇는 단순한 추상화된 컴퓨팅 기기

컴퓨터가 어디까지 가능한지(computability)를 나타내는 기본적인 모델임.

튜링 테스트

Can Machine think?

기계가 생각을 할 수 있는 가?

A : 기계 (질문시 사람인척 할 것임)

B : 사람

C(사람) : A, B가 사람인지 기계인지 판단(text기반 질문으로)

만약 C가 A, B를 구분하지 못한다면, A(기계)는 인공지능을 가진것으로 판단한다.

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

Widthness bugs  (0) 2021.06.02
암호화 기본 - 2  (0) 2021.06.02
apk 난독화  (0) 2021.05.17
Anti-Reverse Engineering  (0) 2021.05.14
Reverse Engineering  (0) 2021.05.14
블로그 이미지

아상관없어

,
반응형

HTTP


웹은 HTTP를 이용해서 통신하는 클라이언트와 서버로 구성

 

HTTP 메시지 구조

 

요청 메시지 : 요청라인 - 헤더 - 공백 - 바디

Request line => 요청방식(method) - 요청페이지 - 프로토콜/버전 으로 구성

ex) GET /dir/page.html HTTP/1.1

(공백으로 정보 구분)

  • GET(메소드)
  • /dir/page.html (URL)
  • HTTP/1.1 (프로토콜:HTTP 버전:1.1)

헤더 : General Headers, Response Headers, Entity Headers

바디 : 응답 메시지(HTML)

 

 

 

응답 메시지 : 상태라인 - 헤더 - 공백 - 바디

Status line => 프로토콜/버전 - 응답상태코드 - 응답상태메시지 로 구성

ex) HTTP/1.1 200 OK

  • HTTP버전 1.1
  • 200(상태코드)
  • OK(응답이유)

헤더 : General Headers, Response Headers, Entity Headers

바디 : 응답 메시지(HTML)

 

 

** 요청방식(method)

  • GET : 자원요청(기본값), 요청 파라미터를 URL에 붙여서 전송 , 서버로부터 데이터 획득
GET /HTTP/1.1
Host: foo.com
  • POST : 자원 요청, 요청 파라미터를 Body에 붙여서 전송, 서버에 데이터 추가
POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom
  • HEAD : HTTP Header 정보만 요청
HEAD /index.html
  • PUT : URL 자원을 생성하도록 요청 (파일 업로드 가능), 서버에 데이터 갱신
PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16

<p>New File</p>
  • DELETE : URL 자원을 삭제하도록 요청 (파일 삭제 가능), 서버에 데이터 삭제
DELETE /file.html HTTP/1.1
  • TRACE : 진단 목적, 서버는 응답바디에 요청받은 자료와 동일한 내용 보냄
TRACE /index.html
  • OPTION : 응답 가능한 HTTP 메소드 요청

 

 

 

** RESTful API

http에는 여러 매소드가 있다.

하지만 REST는 CRUD(Create, Read, Update, Delete) 4가지 메소드만 사용한다.

create = post, read = get, update = put, delete =delete

 

 

 

HTTP 응답 상태 코드

1xx : 정보 제공

2xx : 요청 성공적

3xx : 요청한 자원이 다른곳에 있음

4xx : 요청에 문제있음

5xx : 서버에 에러있음


 

 

 

 

세션과 쿠키


HTTP 비연결지향 : 클라이언트-서버 연결 후 하나의 요청과 응답을 처리하고 연결 종료(응답이 끝나면 연결 종료)=> 클라이언트 정보가 서버에서 관리 되지 않음, 연결유지를 위한 리소스를 줄여 더 많은 연결을 가능하게 함, 연결/연결종료에 대한 오버헤드 발생

HTTP 무상태 프로토콜 : 연결을 종료하는 순간 클라이언트-서버 통신 끝, 클라이언트 상태 정보 유지안함(이전 통신 정보 모름)

따라서 쿠키와 세션이 필요

 

 

쿠키

- http 통신시 사용자 정보 유지하기 위해 사용

- 서버에서 생성하여 응답 헤더에 붙이고 클라이언트에 전송

- 쿠키는 요청을 전달할 때 이름=값 형식으로 요청 헤더에 포함되어 서버에 전달

  • 쿠키 제약사항 : 브라우저당 최대 300개, 호스트마다 20개 가능, 크기 4kb\

예시) 인터넷 쇼핑몰 장바구니, 로그인 페이지 ID/PW 자동저장

 

세션 쿠키 : 브라우저가 열려 있는 동안 유효한 쿠키, 서버 이용하는 동안 사용자 정보를 유지하기 위해

 

영속 쿠키 : 디스크에 유효기간 동안 저장되는 쿠키, 재방문시 정보를 기억하기 위해

 

 

쿠키 발행시 옵션 속성 지정 가능

  • Domain : 브라우저가 쿠키 값을 전송할 서버의 도메인 지정

cookie.setDomain("test.com");

  • Path : 브라우저가 쿠키 값을 전송할 URL 지정

cookie.setPath("/경로");

  • Expires : 쿠키 유효기간 설정

cookie.setMaxAge(값);

  • Secure : SSL 통신 채널 연결시에만 쿠키 전송하게 설정

cookie.setSecure(true);

  • HttpOnly : 자바 스크립트에서 쿠키값 읽지 못하게 설정

cookie.setHttpOnly(true);

 

 

세션

  • 클라이언트와 서버의 논리적인 연결
  • 일정 시간동안 같은 클라이언트로부터 송신되는 request를 하나의 상태로 인식하고 상태를 유지함.
  • 브라우저로 웹 서버 접속 후 브라우저 종료될때까지 연결이 유지되는 상태
  • 서버는 사용자 정보를 유지하기 위해(http를 사용하는 경우) 서버의 메모리에 브라우저별 세션 영역 할당
  • 각 영역은 세션 ID로 구분 => 세션 ID = 세션 토큰
  • 각 클라이언트마다 고유 ID 가짐, 서버에 저장하므로 쿠키보다 안전
  • 서버에 저장하므로 서버 저장공간 필요, 서버가 처리하므로 부하 발생

 

세션 ID를 클라이언트에 전달하는 방법

1. HTML 히든 필드 이용

  • 세션 ID를 HTML의 히든 필드로 클라이언트에 전달
  • 히든 필드이므로 웹페이지에서 안보임
  • 소스 보기를 통해 세션 ID값 추출 가능

 

 

2. URL rewriting을 이용

  • 클라이언트 브라우저가 쿠키 사용을 제한한 경우에 사용
  • 웹 서버에 서비스되는 페이지의 모든 URL에 세션 ID를 request parameter로 붙여 사용

https://dololak.tistory.com/579
https://dololak.tistory.com/579

 

3. 쿠키 이용

  • 클라이언트 브라우저가 쿠키 사용을 허용하는 경우

서버가 세션 ID를 생성 -> 쿠키에 저장 -> 헤더에 세션ID 저장하여 전달 -> 클라이언트 식별

 

 

 

세션을 이용한 클라이언트-서버 응답/요청

  • 클라이언트별 유일한 세션 ID 부여 => 클라이언트 식별
  • 클라이언트 첫 요청시 세션ID 생성, 전달
  • 두번째 요청부턴 세션 ID 사용

https://tansfil.tistory.com/58

  쿠키 서버
저장위치 클라이언트 서버
만료시점 쿠키저장시 설정 정확한 시점 모르
리소스 클라이언트 리소스 서버 리소스
용량제한 O X

 

 

 

 

 

정규식


  • 특정한 규칙을 가진 문자열 집합을 표현하는데 사용하는 형식 언어

ex)

String str = "abcdefghijk";

System.out.println(str.replaceAll("[abchij]", "굿"));

=> 정규표현식 "[abchij]"는 a,b,c,h,i,j를 개별적으로 의미함


 

 

 

 

인코딩 스키마


  • 문자 인코딩

문자들의 집합을 컴퓨터에서 저장 or 통신에 사용할 목적으로 부호화하는 방법 = 인코딩

웹 애플리케이션에서 사용되는 데이터들 => 네트워크 통해 여러 종류 시스템으로 전달됨

따라서 인코딩 스키마를 적용하여 안전하게 데이터 사용

 

  1. URL 인코딩

URL에 포함된 문자들이 안전하게 웹 서버에 전달 될 수 있도록 특수한 기능을 가진 메타 문자들을 브라우저가 인코딩해 전달 => 브라우저 특성에 따라 특수문자 표현 변형 오류 막기 위해

 

  1. HTML 인코딩

HTML문서에서 특별한 기능을 수행하는 문자들의 의미를 제거하고 일반적인 문자로 표시하기 위해 사용되는 인코딩 방식

 

  1. Base64 인코딩

모든 바이너리 데이터들을 출력 가능한 ASCII 문자들을 이용해 안전하게 나타내도록 하는 방식 => 이메일 첨부 파일을 인코딩해서 SMTP를 통해 안전하게 전송 or HTTP 승인을 사용하는 경우 사용자 인증서를 인코딩할때 사용

 

  1. 멀티바이트 인코딩

EUC-KR, MS949, 유니코드, UTF-8 등의 인코딩 방식

1~4바이트를 사용하여 다양한 문자를 표시

 

  1. MS 스크립트 인코딩

MS에서 제공하는 인코딩 기법으로 IE에서만 사용가능

 

예시들)

https://ande226.tistory.com/121


728x90
반응형
블로그 이미지

아상관없어

,
반응형
# -*- coding: utf-8 -*-
"""
Created on Mon Feb 22 14:47:47 2021

@author: changmin
"""
'''
msg = "It is Time"
print(msg.upper()) #대문자로 결과물만, msg는 그대로 
print(msg.lower()) # 결과물만 소문자로
print(msg)

tmp = msg.upper()
print(tmp)
print(tmp.find('T')) # 문자열에서 해당 문자 인덱스 번호 반환(처음 발견한)
print(tmp.count('T')) # 문자열에서 해당 문자 개수 반환
print(msg[:2]) # 슬라이싱 => 처음부터 2개만
print(msg[3:5]) # 3번 인덱스부터 4번 인덱스 까지
print(len(msg)) # 문자열 길이 반환

for i in range(len(msg)):
    print(msg[i], end =" ") #  옆으로 출력
    
print()

for x in msg:
    print(x, end = " ")
print()

for x in msg:
    if x.isupper():
        print(x, end =' ')
    elif x.islower():
        print(x, end = ' ')
        
print('\n\n')
for x in msg:
    if x.isalpha():
        print(x, end=' ')

print()        
tmp = 'AZ'
for x in tmp:
    print(ord(x)) # 아스키 넘버 출력
    

tmp =65
print(chr(tmp))
'''

'''
리스트와 내장함수(1)
'''
'''
import random as r
a=[]
print(a)
b = list()

a=[1, 2, 3, 4, 5]
print(a)
print(a[0])

b = list(range(1, 11))
print(b)

c=a+b
print(c)

print('\n\n\n')

print(a)
a.append(6)
print(a)

a.insert(3, 7) # 3번 인덱스에 7넣음
print(a)

a.pop() # 맨 뒤 인덱스 없앰
print(a)

a.pop(3) # 3번 인덱스 없앰
print(a)

a.remove(4) # 4라는 값 제거
print(a)

print(a.index(5)) # 5라는 값이 있는 인덱스 번호를 반환

d=list(range(1, 11))
print(d)

print(sum(d)) # 리스트 안의 합을 반환

print(max(d)) # 리스트중 가장 큰값 찾아줌

print(min(d)) # 작은값 반환

print(min(7, 5)) # 인자값들 중 최소값을 찾아줌

print(min(7, 3, 5))

print(d)
r.shuffle(d) # 값 섞음
print(d)

d.sort() # 오름차순
print(d)

d.sort(reverse = True) # 내림차순
print(d)

d.clear() # 리스트 값들 다 삭제
print(d)
'''



'''

'''
#리스트와 내장함수 2
'''

a =[23, 12, 36, 53, 19]
print(a[:3]) # 슬라이싱
print(a[1:4]) 
print(len(a)) # 길이 출력

for i in range(len(a)):
    print(a[i], end=' ')    
print()

for x in a:
    print(x, end = ' ')
print()

for x in a:
    if x%2 == 1:
        print(x, end = ' ')
print()
    
# 튜플로 출력
# enumerate => (0,23) (1,36) ..... (4,19) 
for x in enumerate(a):
    print(x[0], x[1])
    
for index, value in enumerate(a):
    print(index, value)
print()

# all => 모두 참이면 참을 리턴
if all(50>x for x in a):
    print('Yes')
else:
    print('No')
    
# any => 한번이라도 참이면 참
if any(15>x for x in a):
    print('Yes')
else:
    print('No')

'''
#튜플
'''

b=(1, 2, 3, 4, 5)
print(b[0])
#b[0]=7 # 에러 튜플의 값은 변경 불가, 리스트의 값은 변경가능
#print(b[0])


'''

"""
'''
2차원 리스트 생성과 접근
'''

a= [0]*3
print(a)

a = [[0]*3 for _ in range(3)] # 크기가 3인 일차원 리스트 언더바는 변수없이 반복문만 돔 => 일차원 리스트 3번 만듦

a[0][1] = 2
print(a)
print()
#표처럼 출력
for x in a:
    print(x)

for x in a:
    for y in x:
        print(y, end =' ')

"""

'''
람다 함수 => 함수 이름이 없음
'''
'''

def plus_one(x):
    return x+1

print(plus_one(1))
'''

plust_two = lambda x: x+2

print(plust_two(1))


a = [1, 2, 3]
# map => 함수명, 자료 => 
def plus_one(x):
    return x+1

print(plus_one(1))

print(list(map(plus_one, a))) # a에 대해 plus one을 함

print(list(map(lambda x:x+1, a))) # a에 대해 plus one을 함
728x90
반응형

'공부 > 파이썬 알고리즘' 카테고리의 다른 글

문자열  (0) 2021.09.16
연결리스트  (0) 2021.09.15
배열  (0) 2021.09.15
etc  (0) 2021.07.01
파이썬 문법  (0) 2021.06.28
블로그 이미지

아상관없어

,
반응형

mysql연동위해 pymysql 설치

 

conda install pymysql

 

 

#mysql - python connection test

import pymysql #mysql과 연동하기 위해

#필요한 기본 DB 정보
host = "localhost"
user = "root"
pw = "XXXX"
db = "my_db"

#DB에 접속
conn = pymysql.connect(host= host, user = user, password = pw, db = db)

#사용할 sql문
sql = "SELECT * FROM emp LIMIT 10"

# sql문 실행/데이터 받기
curs = conn.cursor() #sql 실행시 결과를 담고 있는 버퍼를 정렬
curs.execute(sql)	#앞의 sql문 실행

data = curs.fetchall() #sql실행결과 모두 가져오기, fetchall 모든 행 가져옴, fetchone은 하나의 행만 가져옴
type(data)				#data 자료구조
data					#data 내용출력
data[0]					#첫번째 행
str(data[0][0])			#첫번째 행의 첫번째 컬럼

#db 접속 종료
curs.close()
conn.close()

sql = "SELECT * FROM emp LIMIT 10"

curs = conn.cursor()
curs.execute(sql)

row = curs.fetchone()
while(row):
	print(row)
    row = curs.fetchone()
    
# cursor는 한행씩 내려간다

 

 

GUI DB 프로그램

import sys
import pymysql
from PyQt5.QtWidgets import *

def connectDB():
    #필요한 db 정보
    host = "localhost"
    user = "root"
    pw = "Changmin97"
    db = "my_db"
    
    #db 접속
    conn = pymysql.connect(host=host, user =user, password =pw, db =db)
    return(conn)  #connection  리턴

def disconnectDB(conn):
    conn.close()
    
    

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        
    
    # UI 디자인 함수
    def initUI(self):
        #QLable 문자열 출력
        label1 = QLabel('ID')
        label2 = QLabel('ename')
        label3 = QLabel('job')
        label4 = QLabel('department')
    
    
        #self => 외부에서 접근 가능
        #text박스 생성 = QTextEdit
        self.text_id = QTextEdit()
        self.text_id.setFixedWidth(200)
        self.text_id.setFixedHeight(30)
    
    
        #버튼 생성, 버튼 클릭시 btn_1_clicked 실행
        btn_1 = QPushButton('Query')
        btn_1.clicked.connect(self.btn_1_clicked)
    
        #text박스 생성 = QTextEdit
        self.text_ename = QTextEdit()
        self.text_ename.setFixedWidth(200)
        self.text_ename.setFixedHeight(30)
    
        #text박스 생성 = QTextEdit
        self.text_job = QTextEdit()
        self.text_job.setFixedWidth(200)
        self.text_job.setFixedHeight(30)
    
        #text박스 생성 = QTextEdit
        self.text_dept = QTextEdit()
        self.text_dept.setFixedWidth(200)
        self.text_dept.setFixedHeight(30)
    
        #화면 배치, grid를 만들어 배치
        gbox = QGridLayout()
    
        gbox.addWidget(label1,0,0)
        gbox.addWidget(self.text_id, 0, 1)
            
        gbox.addWidget(btn_1, 0, 2)
            
        gbox.addWidget(label2,1,0)
        gbox.addWidget(self.text_ename, 1, 1)
            
        gbox.addWidget(label3,2,0)
        gbox.addWidget(self.text_job, 2, 1)
        
        gbox.addWidget(label4,3,0)
        gbox.addWidget(self.text_dept, 3, 1)
    

        self.setLayout(gbox)
        self.setWindowTitle('My Program')
        self.setGeometry(300, 300, 480,250) #창뜨는 위치, 크기
        self.show()
        
        
    #버튼 클릭 처리
    def btn_1_clicked(self):
    
        #사용자가 입력한 값 받아서 empno 저장
        empno = self.text_id.toPlainText()
            
        sql = "SELECT ename, job, danme \
                From emp e, dept d\
                where e.deptno = d.deptno\
                and empno = "+ empno
        #ex) ename '"+ename+"'" =>문자일 경우 따옴표 주의!
        conn = connectDB()
        curs = conn.cursor()
        curs.execute(sql)
            
        result = curs.fetchone() # sql 실행 결과 가져오기
            
        self.text_ename.setText(result[0])
        self.text_job.setText(result[1])
        self.text_dept.setText(restul[2])
            
        curs.close()
        disconnect(conn)
            
#END Class


#프로그램 실행, class를 생성하고 실행
if(__name__ == '__main__'):
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())            
    

 

ex 1) 

- 사원번호가 7521인 사원의 이름을 출력한다

- 이름이 SCOTT 인 사원의 부서이름을 출력한다

- 담당업무가 SALESMAN 인 모든사원의 이름을 출력한다.

- 모든 부서의 이름을 출력한다.

import pymysql #mysql과 연동하기 위해

#필요한 기본 DB 정보
host = "localhost"
user = "root"
pw = "Changmin97"
db = "my_db"

#DB에 접속
conn = pymysql.connect(host= host, user = user, password = pw, db = db)

#사용할 sql문
sql1 = "SELECT ename FROM emp WHERE empno=7521"
sql2 = "SELECT dname FROM emp, dept WHERE dept.deptno=emp.deptno and ename='scott'"
sql3 = "SELECT ename FROM emp WHERE job='salesman'"
sql4 = "SELECT dname FROM dept"

querys = [sql1, sql2, sql3, sql4]

for sql in querys:
    print(sql)
    
    # sql문 실행/데이터 받기
    curs = conn.cursor() #sql 실행시 결과를 담고 있는 버퍼를 정렬
    curs.execute(sql)	#앞의 sql문 실행 
    row = curs.fetchone() #sql실행결과 모두 가져오기, fetchall 모든 행 가져옴, fetchone은 하나의 행만 가져옴
    while(row):
        print(row)
        row	= curs.fetchone()	
        
    print("==========================================================")
    
#db 접속 종료
curs.close()
conn.close()

 

 

 

 

ex 2)

Mysql world 데이터베이스와 연동하여 다음과 같이 국가명을 입력하면 해당 국가의 정보를 출력하는 윈도우 프로그램을 작성하시오

- 매치되는 국가가 없으면 모든 항목에 공백 출력

import sys
import pymysql
from PyQt5.QtWidgets import *

def connectDB():
    #필요한 db 정보
    host = "localhost"
    user = "root"
    pw = "Changmin97"
    db = "world"
    
    #db 접속
    conn = pymysql.connect(host=host, user =user, password =pw, db =db)
    return(conn)  #connection  리턴

def disconnectDB(conn):
    conn.close()
    
    

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        
    
    # UI 디자인 함수
    def initUI(self):
        #QLable 문자열 출력
        label1 = QLabel('Country name')
        label2 = QLabel('Continent')
        label3 = QLabel('Population')
        label4 = QLabel('GNP')
        label5 = QLabel('Captial city')
        label6 = QLabel('Language')
    
    

        #text_Country_name
        self.text_Country_name = QTextEdit()
        self.text_Country_name.setFixedWidth(200)
        self.text_Country_name.setFixedHeight(30)
    
    
        #버튼 생성, 버튼 클릭시 btn_1_clicked 실행
        btn_1 = QPushButton('Query')
        btn_1.clicked.connect(self.btn_1_clicked)
    
        #text_Continent
        self.text_Continent = QTextEdit()
        self.text_Continent.setFixedWidth(200)
        self.text_Continent.setFixedHeight(30)
    
        #text_Population
        self.text_Population = QTextEdit()
        self.text_Population.setFixedWidth(200)
        self.text_Population.setFixedHeight(30)
    
        #text_GNP
        self.text_GNP = QTextEdit()
        self.text_GNP.setFixedWidth(200)
        self.text_GNP.setFixedHeight(30)
        
        #text_Captial_city
        self.text_Captial_city = QTextEdit()
        self.text_Captial_city.setFixedWidth(200)
        self.text_Captial_city.setFixedHeight(30)
        
          #text_Language
        self.text_Language = QTextEdit()
        self.text_Language.setFixedWidth(200)
        self.text_Language.setFixedHeight(30)
        
        
        
    
        #화면 배치, grid를 만들어 배치
        gbox = QGridLayout()
    
        gbox.addWidget(label1,0,0)
        gbox.addWidget(self.text_Country_name, 0, 1)
            
        gbox.addWidget(btn_1, 0, 2)
            
        gbox.addWidget(label2,1,0)
        gbox.addWidget(self.text_Continent, 1, 1)
            
        gbox.addWidget(label3,2,0)
        gbox.addWidget(self.text_Population, 2, 1)
        
        gbox.addWidget(label4,3,0)
        gbox.addWidget(self.text_GNP, 3, 1)
        
        gbox.addWidget(label5,4,0)
        gbox.addWidget(self.text_Captial_city, 4, 1)
        
        gbox.addWidget(label6,5,0)
        gbox.addWidget(self.text_Language, 5, 1)
    

        self.setLayout(gbox)
        self.setWindowTitle('Country Info')
        self.setGeometry(300, 300, 480,250) #창뜨는 위치, 크기
        self.show()
        
        
    #버튼 클릭 처리
        def btn_1_clicked(self):
    
        #사용자가 입력한 값 받아 저장
        Country_name = self.text_Country_name.toPlainText()
            
        sql = "SELECT country.name, continent, country.population, gnp, city.name, language \
                From country, city, countrylanguage\
                where country.code = city.countrycode \
                    and country.code = countrylanguage.countrycode\
                    and country.capital=city.id\
                    and IsOfficial = 'T' \
                    and country.name ="+"'"+Country_name+"'"
        #ex) ename '"+ename+"'" =>문자일 경우 따옴표 주의!
        conn = connectDB()
        curs = conn.cursor()
        curs.execute(sql)
            
        result = curs.fetchone() # sql 실행 결과 가져오기

        if result:    
            self.text_Country_name.setText(result[0])
            self.text_Continent.setText(result[1])
            self.text_Population.setText(str(result[2]))
            self.text_GNP.setText(str(result[3]))
            self.text_Captial_city.setText(result[4])
            self.text_Language.setText(result[5])
        else:
            self.text_Country_name.setText("") #매치되는 국가가 없으면 모든 항목에 공백 출력
            
            
        curs.close()
        disconnect(conn)
            
#END Class


#프로그램 실행, class를 생성하고 실행
if(__name__ == '__main__'):
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())            
    	

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

index  (0) 2021.06.02
데이터 베이스 보안  (0) 2021.06.02
저장 함수 예제  (0) 2021.05.15
저장 프로시저 예제  (0) 2021.05.15
뷰 예제  (0) 2021.05.15
블로그 이미지

아상관없어

,

apk 난독화

공부/보안 2021. 5. 17. 21:44
반응형

Renaming


 

- ClassRename

obfuscapk를 사용하여 app_1.apk를 ClassRename 난독화를 한다.

python3 -m obfuscapk.cli -o ClassRename ../../sample_apps/app_1.apk

그 후 jadx로 원본 앱과 비교하기 위해 패키징을 해준다.

apktool b app_1/ -o app_1_ClassRenamed.apk

 

jadx로 같은 파일을 확인하면, class의 이름이 Base64Coder에서 pabd63bb로 바뀐 것을 알 수 있다.

class renaming은 class의 이름를 "Base64Coder"처럼 식별할 수 있는 문자대신, "pabd63bb"와 같이 무슨 의미인지 알 수 없게 바꾸어준다. 따라서 역공학으로 해당 파일을 보더라도 무슨 역할을 하는 지 알아차리기 힘들어 역공학을 방해한다. [1]

 

기존앱을 virustotal로 검사하면 30개의 백신에서 탐지가 된다.

하지만 ClassRename을 하고 난 뒤에는 20개의 백신에만 탐지가 된다.

 

 

- MethodRename

python3 -m obfuscapk.cli -o MethodRename ../../sample_apps/app_2.apk

 

리패키징을 해준뒤 jadx로 기존앱과 비교하면

 c.ca/a 자바 클래스 파일의 메소드 f가 m8fa14cdd로 변경됨을 알 수 있다.

 

Method Rename은 method의 이름을 식별하기 힘들게 변경하는 것이다. 따라서 역공학시 해당 메소드가 어떠한 역할을 하는지 명시적으로 알기 어려워져 분석하기 어려워진다.[1]

 

백신탐지 비교

(왼쪽 : 기존 앱, 오른쪽 : MethodRename 적용)


 

 

 

 

 

Encryption


- ConstStringEncryption

소스 코드의 문자열 상수를 암호화한뒤 decrypString함수를 통하여 복호화하여 원래의 문자열 상수를 얻도록 한다. 따라서 역공학 분석시, 암호화된 문자열이 어떠한 것을 가리키는 지 인식하기 힘들어 역공학을 어렵게한다.[2]

python3 -m obfuscapk.cli -o ConstStringEncryption ../../sample_apps/app_5.apk

app_5의 connector파일을 보면 "http://" String(왼쪽사진)이 암호화(오른쪽사진)되어 있는 것을 알 수 있다.

(jadx 사용)

 

 

 

백신탐지 비교

(왼쪽 : 기존앱, 오른쪽 : ConstStringEncryption적용)

 

- AssetEncryption

 

python3 -m obfuscapk.cli -o AssetEncryption ../../sample_apps/app_2.apk

smali/android/annotation 안에 DecryptAsset.smali가 생긴 것을 알 수 있다.

new-instance v0, Ljava/io/FileInputStream;

=> FileInputStream 객체 생성

invoke-static {p0, p1}, Lcom/decryptassetmanager/DecryptAsset; >decryptAssetFileUsingContext(Landroid/content/res/AssetManager;Ljava/lang/String;)Ljava/io/File;

=> decryptAssetFileUsingContext메소드 호출, java/io/File로 반환

invoke-direct {v0, v1}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V

=> FileInputStream 객체를 생성하여 파일을 읽어들인다.

 

decryptAssetFileUsingContext 메소드

 AES방식으로 암호화되고 ,

invoke-virtual {p0, p1}, Landroid/content/res/AssetManager;->open(Ljava/lang/String;)Ljava/io/InputStream;

파일을 열고

invoke-static {v0}, Lcom/decryptassetmanager/DecryptAsset;->readBytes(Ljava/io/InputStream;)[B

값을 읽고

invoke-virtual {v1, v7}, Ljavax/crypto/Cipher;->doFinal([B)[B

복호화 하는 것 같다.

 

백신탐지 비교

(왼쪽 : 기존 앱, 오른쪽 : AssetEncryption 적용)


 

 

 

 

Code


- Goto

java에는 없는 goto bytecode를 추가함으로써, control-flow를 수정하고 control-flow를 이해하기 어렵게 한다. [4]

 

python3 -m obfuscapk.cli -o Goto ../../sample_apps/app_2.apk

"smali/android/view/a.smali" 비교

처음에 메소드의 끝을 가리키는 goto가 있고, 메소드 끝에 메소드의 처음을 가리키는 goto가 추가되었다.

 

백신탐지 비교

(왼쪽 : 기존앱, 오른쪽 : Goto옵션 적용)

 

- Reorder

Reorder는 역공학하여 분석하기 어렵도록, 메소드의 명령어 흐름을 복잡하게 한다. 왼쪽의 그림이 Reorder된 app_3의 BandManager.smali이다. [2] 오른쪽의 기존과 method와 다르게 추가된 내용도 생기고 흐름도 goto문이 생기는 등 복잡하게 바뀐 것을 알 수 있다.

python3 -m obfuscapk.cli -o Reorder ../../sample_apps/app_3.apk

"smali/com/BandManager.smali"

 

백신탐지 비교

(왼쪽 : 기존앱, 오른쪽 : Reoreder 적용)

 

- Nop

Nop 명령어를 추가하여 bytecode 흐름을 수정한다. 따라서 control flow를 이해하기 어렵게 한다. [4]

python3 -m obfuscapk.cli -o Nop ../../sample_apps/app_2.apk

"smali/c/ca/a.smali"

왼쪽의 Nop 옵션이 적용된 app_2/smali/c/ca/a.smali 파일을 보면 오른쪽의 기존 파일과 다르게 nop 명령어들이 추가된 것을 알 수 있다.

 

백신탐지 비교

(왼쪽 : 기존앱, 오른쪽 : nop 옵션 적용)

 

- ArithmeticBranch

ArithmetiBranch 옵션은 의미 없는 코드를 삽입하여 명령어 흐름을 복잡하게 만들다. 따라서 역공학시 분석을 하기 어렵게 한다. [2] 왼쪽의 ArithmeticBranch 옵션이 적용된 app_4/smali/com/example/eroplayer/MainActivity을 보면 오른쪽의 기존 파일과 다르게 junk code가 삽입된 것을 알 수 있다.

python3 -m obfuscapk.cli -o ArithmeticBranch ../../sample_apps/app_4.apk

"smali/com/example/eroplayer/MainActivity.smali"

e

 

일반적인 개발자의 입장에선, 안드로이드 난독화는 역공학을 어렵게하여, 개발자가 만든 소스코드, 파일 등을 보호할 수 있게 해주는 좋은 수단이다.

악성 앱 개발자의 입장에선, 안드로이드 난독화는 control-flow를 바꾸거나, 암호화, renaming등으로 백신이 악성코드를 탐지하게 어렵게 악용하는 수단이다. 

이번 과제를 하면서, 개발자의 자산을 보호하는 것도 중요하지만, 악성코드를 탐지를 위해 난독화를 탐지하는 기술도 중요하다고 느꼈다.

 

참고논문

[1] 난독화에 강인한 안드로이드 앱 버스마킹 기법 김 동 진Š , 조 성 제° , 정 영 기* , 우 진 운**, 고 정 욱***, 양 수 미**** Android App Birthmarking Technique Resilient to Code Obfuscation Dongjin KimŠ , Seong-je Cho° , Youngki Chung* , Jinwoon Woo**, Jeonguk Ko***, Soo-mi Yang****

 

[2] 안드로이드 어플리케이션 역공학 보호기법 하 동 수*, 이 강 효*, 오 희 국*

[4] Android Code Protection via Obfuscation Techniques: Past, Present and Future Directions Parvez Faruki, Malaviya National Institute of Technology Jaipur, India Hossein Fereidooni, University of Padua, Italy Vijay Laxmi, Malaviya National Institute of Technology Jaipur, India Mauro Conti, University of Padua, Italy Manoj Gaur, Malaviya National Institute of Technology Jaipur, India

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

암호화 기본 - 2  (0) 2021.06.02
암호화 기본 - 1  (0) 2021.06.01
Anti-Reverse Engineering  (0) 2021.05.14
Reverse Engineering  (0) 2021.05.14
간단한 Android game app hacking  (0) 2021.05.03
블로그 이미지

아상관없어

,
반응형

(1) 사원이름을 입력하면 사원의 매니저 이름을 리턴하는 함수를 작성하시오 (f_mgr)

다음과 같이 함수를 테스트한 결과를 보이시오.

select empno, ename, f_mgr(ename) as manager

from emp

CREATE FUNCTION f_mgr (e_name varchar(10))
RETURNS varchar(10)
BEGIN
    declare manager varchar(10);
    select m.ename into manager 
    from emp e, emp m
    where e.mgr=m.empno and e.ename = e_name;
RETURN manager;
END

 

 

(2) 부서번호를 입력하면 부서의 위치를 출력하는 함수를 작성하시오 (f_loc)

다음과 같이 함수를 테스트한 결과를 보이시오.

select empno, ename, job, f_loc(deptno) as loc

from emp

CREATE FUNCTION f_loc (d_no int)
RETURNS varchar(10)
BEGIN
    declare d_loc varchar(10);
    
    select loc into d_loc
    from dept
    where deptno = d_no;
    
RETURN d_loc;
END

 

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

데이터 베이스 보안  (0) 2021.06.02
DB 파이썬 연동  (0) 2021.05.18
저장 프로시저 예제  (0) 2021.05.15
뷰 예제  (0) 2021.05.15
저장 프로시저  (0) 2021.05.15
블로그 이미지

아상관없어

,
반응형

1. 사원번호를 매개변수로 입력 받아 사원번호, 이름, 담당업무, 연봉, 소속부서명을 보여주는 stored procedure 를 작성하시오 (p_emp_sel_1)

CREATE PROCEDURE p_emp_sel_1(id int)
BEGIN
	select empno, ename, job, sal, dname from emp, dept
	where emp.deptno=dept.deptno and empno = id;
END

 

2. 부서번호, 부서명, 위치를 매개변수로 입력 받아 새로운 부서 정보를 생성하는 stored procedure 를 작성하시오 (p_dept_insert_1)

CREATE  PROCEDURE p_dept_insert_1(d_num int , d_name varchar(10), d_loc varchar(10))
BEGIN
	insert into dept values(d_num, d_name, d_loc);
END

 

3. 사원번호, 사원 이름을 매개변수로 입력 받아 이름을 수정하는 stored procedure 를 작성하시오 (p_emp_update_1)

CREATE PROCEDURE p_emp_update_1 (e_num int, e_name varchar(10))
BEGIN
	update emp set ename=e_name where empno = e_num;
END

 

 

1. 사원번호를 매개변수로 입력 받아 사원의 담당업무가 ‘CLERK’ 이면 급여를 20% 올리고, 아닌 경우는 10%를 올리는 stored procedure 를 작성하시오 (p_emp_update_2)

CREATE PROCEDURE p_emp_update_2 (e_num int)
BEGIN
	declare e_job varchar(10);
    	select job into e_job
    	from emp
    	where empno=e_num;
    
    	if(e_job = 'clerk') then
			update emp 
			set sal=sal*1.2 
			where empno=e_num;
    	end if;
    
    	if(e_job != 'clerk') then
			update emp
        	set sal=sal*1.1
        	where empno=e_num;
		end if;
END

 

2. 사원번호를 매개변수로 입력 받은 후에 그 사원이 속한 부서 사람들의 연봉 합계를 구하여 출력하는 stored procedure 를 작성하시오 (p_emp_sel_2)

CREATE PROCEDURE p_emp_sel_2 (e_num int)
BEGIN
    select sum(sal) 
    from emp
    where deptno = (select deptno from emp where empno = e_num);
END

 

3. 사원번호를 매개변수로 입력 받은 후에 사원의 급여가 평균급여 이상이면 해당 사원의 근무지를 보이고, 그렇지 않으면 사원의 직무를 보이는 stored procedure 를 작성 하시오. (p_emp_sel_3)

CREATE PROCEDURE p_emp_sel_3 (e_num int)
BEGIN
	declare e_sal decimal(10,4);
    	declare avg_sal decimal(10,4);
    
    	select sal into e_sal
    	from emp
    	where empno=e_num;
    
   	 	select avg(sal) into avg_sal
    	from emp;
    
   
    	if(avg_sal <= e_sal) then
			select loc from empd where empno=e_num;
		else
			select job from emp where empno=e_num;
		end if;
END
728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

DB 파이썬 연동  (0) 2021.05.18
저장 함수 예제  (0) 2021.05.15
뷰 예제  (0) 2021.05.15
저장 프로시저  (0) 2021.05.15
  (0) 2021.05.15
블로그 이미지

아상관없어

,
반응형

1. emp dept 테이블로 부터 다음과 같은 조건을 만족하는 뷰를 생성하시오

create view empd as

select empno, ename, hiredate, sal, job, dname, loc

from emp, dept

where emp.deptno = dept.deptno;

 

2. 생성된 뷰를 이용하여 다음의 질의에 대한 sql 문을 작성하시오

모든 사원의 이름, 부서명을 보이시오

SELECT ename, dname FROM empd;

 

급여가 2500 보다 많은 사원의 사원번호, 이름, 급여, 부서위치를 보이시오

SELECT empno, ename, sal, loc FROM empd where sal >2500;

 

부서위치가 DALLAS 인 사원의 이름과 입사일자를 보이시오

SELECT ename, hiredate FROM empd where loc = 'dallas';

 

SMITH 사원의 부서위치를 보이시오

SELECT loc FROM empd where ename = 'smith';

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

저장 함수 예제  (0) 2021.05.15
저장 프로시저 예제  (0) 2021.05.15
저장 프로시저  (0) 2021.05.15
  (0) 2021.05.15
데이터베이스 예제 4  (0) 2021.04.20
블로그 이미지

아상관없어

,
반응형

저장 프로시저는 MySQL 5.0 이상 버전부터 사용가능하다.

 

여러 sql문을 하나의 sql문처럼 정리하여 call 명령으로 실행할 수 있게 만든 것.

=> 데이터베이스 내에 미리 컴파일되어 저장된 sql 코드

=> 사용자 프로그램에서 호출하거나, 다른 저장 프로시저에서 호출가능

=> 결과를 서버에 저장, 컴파일 하고 캐쉬에 저장한뒤 실행함, 

장점

=> 데이터베이스 내에서 sql 명령을 컴파일할때 캐시를 이용할 수 있어 처리가 빠름

=> 어플리케이션마다 복수의 SQL문 기술할 필요 없이 만들어진 저장 프로시저를 사용하면 다른 어플리케이션을 수정하여 컴파일 할 필요 없음.

=> 저장 프로시저는 만들어지는 순간에 구문이 검사됨, 따라서 DBA의 에러를 감소시킬 수 있음(실행전 검사)

=> SQL문을 직접 실행시킬 수 없는 사용자들도 저장 프로시저만 실행시킬 수 있는 권한을 가지게 할 수 있다.

 

단점

=> 접하기 어렵다.

=> DBMS 제품마다 문법이 다르다(비표준화)

=> 저장프로시저를 남발하는 경우 유지보수가 어렵다.

 

함수와의 차이점

저장프로시저 : 일반적으로 return 값이 없는 프로그램, CALL에 의해서 호출

함수 : return 값이 있는 프로그램, MAX(), min()과 같이 SQL문 안에서 사용

 

- 저장 프로시저 생성

CREATE PROCEDURE 저장프로시저이름()
BEGIN 
	SQL문 1;
    	SQL문 2;
END

BEGIN ~ END 사이에 원하는 SQL문들을 작성하면 된다.

 

** 구분문자 문제

위와 같이 작성시 BEGIN ~ END안에서 SQL문이 작성이 덜 되었더라도 ;를 만나게되면 CREATE PROCEDURE 을 실행하게되어 완성되지 않은 상태로 저장프로시저가 생성된다.

CREATE PROCEDURE 저장프로시저이름(인수이름 자료형)
BEGIN 
	SQL문 1;
    	SQL문 2
END

와 같이 될 경우 MySQL 콘솔창은 ;이 입력되면 ;이전단계까지의 명령문을 실행하게 되기때문이다.

 

따라서 

저장 프로시저에서 END를 입력하고난 뒤 CREATE PROCEFURE 명령이 실행되게 해야한다.

 DELIMITER를 사용하여 구분문자를 ;대신 다른 문자로 변경한다.

EX) DELIMITER // 혹은 DELIMITER =

와 같이원하는 문자를 적으면 된다.

 

DELIMITER //                  =>구분 문자를 ;에서 //로 변경
CREATE PROCEDURE pr1()
BEGIN
	SELECT * FROM tb;
    	SELECT * FROM tb1;
END
//                            =>//구분문자를 만났으므로 이전까지 입력된 명령을 수행
DELIMITER ;                   => 구분문자를 다시 ;로 변경

 

 

- 저장 프로시저 실행

CALL 저장프로시저이름;

ex) CALL pr1();를 실행하면

자동으로 SELECT * FROM tb;와 SELECT * FROM tb1;가 실행된다.

 

- 저장 프로시저 인수 사용

PROCEDURE 저장프로시저이름(인수이름 자료형);

예를 들어 sales가 200 이상인 값을 보려고 할때

select * from tb where sales>=d;와 같이 설정하면

DELIMITER //                 
CREATE PROCEDURE pr1(d INT)
BEGIN
	SELECT * FROM tb WHERE sales>=d;
END
//                            
DELIMITER ;                  

d를 인자로 받아들여 pr1(200)을 실행하면 

select * from tb where sales>=200;이 된다.

 

 

- 작성된 저장 프로시저 내용 표시

SHOW CREATE PROCEDURE 저장프로시저이름;

ex) SHOW CREATE PROCEDURE pr1;

pr1의 프로시저 내용을 볼 수 있게 된다.

 

- 저장 프로시저 삭제

DROP PROCEDURE 저장프로시저이름;

 

 

- 저장 함수

저장 프로시저와 유사하지만, 실행했을 때 값을 반환한다.

CREATE FUNCTION 저장함수이름(인수이름 자료형) RETURNS 반환값자료형
BEGIN
	SQL문 ....
    	RETURN 반환값식
END

 

* DECLARE

저장 함수에서 변수를 사용하려면 DECLARE로 정의해야한다.

DECLARE 변수이름 자료형;

예시를 보면

CRETAE FUNCTION func() RETURNS DOUBLE          => 함수의 반환형은 double
BEGIN
	DECLARE r DOUBLE;                      => 변수 r 선언
    	SELECT AVG(sales) INTO r FROM tb;      => AVG(sales)값을 r에 저장
       	RETURN 	r;                             => r을 반환
END

 

- 저장함수 삭제

DROP FUNCTION 저장함수이름;

 

- 저장함수 내용 표시

SHOW CREATE FUNCTION 저장함수이름;

저장 함수의 내용을 볼 수 있게된다.

 

 

- 트리거

테이블에 대해 어떤 처리를 실행하면 이에 반응하여 설정한 명령이 자동으로 실행되는 구조

INSERT, UPDATE, DELETE 등 명령이 실행될 때, 트리거로 설정한 명령이 자동으로 실행되게 할 수 있다.

EX) 테이블의 레코드 변경시, 변경한 내용을 다른 테이블에 기록하도록 트리거를 만들 수 있음

따라서 트리거는 처리를 기록하거나, 처리가 실패할 경우를 대비하여 만들어 놓으면 좋다.

 

트리거는 INSERT, UPDATE, DELETE 등 명령이 실행되기 직전(BEFORE) 또는 직후(AFTER)에 호출되어 실행된다.

 

즉, 어떤 데이터를 처리하기 전에 호출되거나 어떤 데이터를 처리한 후에 호출된다.

 

또한 테이블에서 어떤 데이터를 처리하기 전의 값은 OLD.칼럼이름.

처리한 후의 값은 NEW.칼럼이름으로 얻을 수 있다.(추출할 수 있다.)

 

명령에 따라서 칼럼 값을 추출할 수도 있고 없을 수도 있다.

명령 실행전(old.칼럼이름) 실행후(new.칼럼이름)
insert old.칼럼이름 추출 불가 가능
delete 가능 new.칼럼이름 추출불가
update 가능 가능

 

 

- 트리거 생성

CREATE TRIGGER 트리거이름 BEFORE(또는 AFTER) DELETE(UPDATE,INSERT 등과 같은 명령)
ON 테이블 이름 FOR EACH ROW
BEGIN
	변경전(OLD.칼럼이름)을 이용한 처리                      ====> 또는 변경후(NEW.칼럼이름)
END

 

 

EX) tb1에서 삭제한 레코드를 tb1m에 삽입하는 트리거 작성

DELIMITER //
CREATE TRIGGER tr1 BEFORE DELETE ON tb1 FOR EACH ROW                  
===> delete에 반응, 삭제하기 직전의 값을 넣으므로 before, for each row 각 행에 대해 수행
BEGIN
	INSERT INTO tb1m VALUES(OLD.number, OLD.name, OLD.age);
END
//
DELIMIER ;

tb1에서 레코드가 삭제될때, tb1m에 삭제된 레코드가 입력되게 된다.

DELETE FROM tb1;을 하여 모든 레코드를 삭제하더라도 tb1m에 삭제된 레코드들이 저장되게 된다.

따라서  INSERT INTO tb1 SELECT * FROM tb1m;과 같이 다시 복원을 할 수도 있다.

 

 

- 트리거 확인

SHOW TRIGGERS;

 

- 트리거 삭제

DROP TRIGGER 트리거이름;

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

저장 프로시저 예제  (0) 2021.05.15
뷰 예제  (0) 2021.05.15
  (0) 2021.05.15
데이터베이스 예제 4  (0) 2021.04.20
데이터베이스 예제 3  (0) 2021.04.20
블로그 이미지

아상관없어

,

공부/데이터베이스 2021. 5. 15. 02:00
반응형

select를 한 결과를 가상 테이블에 저장한 것

가상 테이블이 뷰임

=> 하나의 테이블에 대하여 서로 다른 관점으로 보기 원할때,

=> 일반 사용자에게는 감추어야할 컬럼이 있을때 그것을 제외하고 뷰를 만들어 제공하여 보안 유지

=> 자주 사용하는 복잡한 질의문을 미리 뷰로 정의하여 두고 간편하게 사용할 때

=> 물리적인 데이터를 갖지 않음

=> 뷰 갱신연산은 경우에따라 실행(view에 정의된 것)될 수 도 안될(뷰에 정의되지 않은 값 ex) not null인데 null입력) 수도 있음

 

- 뷰 생성

CREATE VIEW 뷰이름 AS SELECT 칼럼이름 FROM 테이블이름 WHERE 조건;

EX) CREATE VIEW v1 AS SELECT name, age FROM tb1;

SELECT * FROM v1;

 

- 뷰에서 칼럼 값 변경

뷰는 참조 테이블의 일부분을 표시함

따라서 참조 테이블의 값이 변경되면 뷰의 값도 변경

뷰는 참조 테이블의 일부분을 표시하면서 참조테이블의 데이터 창구이기도 함

따라서 뷰의 값이 변경되면 참조 테이블의 값도 변경

ex) UPDATE v1 SET name='주임 강신우' WHERE name='강신우';

 

- 뷰에서 INSERT ??

뷰는 가상테이블이므로, 뷰에 INSERT하는 것은 테이블의 일부에만 데이터를 추가하게 되는 것임

값을 추가하지 않은 칼럼은 default 값이 입력된다.

UNION이나 JOIN, 하위질의를 사용하는 뷰에선 INSERT나 UPDATE를 사용할 수 없다.

 

- 뷰의 조건에 맞지 않는 값 뷰에 추가

ex) create view v3 as select number, sales from tb where sales>=100;

sales가 100이상인 조건의 뷰 v3를 만들고 v3에 sales가 100보다 작은 값을 넣으면 어떻게 될까?

INSERT INTO v3 VALEUS('test', 90);

 

SELECT * FROM v3;   ==> 기존과 동일하게 보인다. 

SELECT number, sales FROM tb1;  ==> test, 90이라는 값이 추가된 것을 알 수 있다.

 

 

- 뷰의 조건에 맞지 않는 값 뷰에 추가시 제한걸기

create view v4 as create number, sales from tb where sales>100 with check option;

뷰 생성시 with check option을 추가하면 조건에 맞지 않는 데이터를 추가하지 못하게 한다.

위 예에선 sales가 100이하인 값을 추가하지 못하게 된다.

 

- 뷰 덮어쓰기

이미 같은 이름의 뷰가 존재할때 덮어쓰기하여 뷰를 생성

 

CREATE OR REPLACE VIEW v1 AS  select now();

와 같이 CREATE OR REPLACE VIEW라고 해주면 된다.

 

그러면 기존 뷰 v1은 삭제되고 now를 표시하는 새로운 뷰 v1이 생성된다.

뷰의 존재 여부에 상관없이 뷰를 생성할 수 있게 해주는 방법이다.

 

- 뷰 칼럼 구조 변경

ALTER VIEW 뷰이름 AS SELECT 칼럼이름 FROM 테이블이름;

EX) ALTER VIEW v1 AS SELECT name, age FROM tb1;

 

- 뷰 삭제

DROP VIEW 뷰이름;

*삭제할 뷰가 없는 경우 오류가 발생하므로 IF EXISTS 사용

ex) DROP VIEW IF EXISTS v1;

 

 

 

 

 

728x90
반응형

'공부 > 데이터베이스' 카테고리의 다른 글

뷰 예제  (0) 2021.05.15
저장 프로시저  (0) 2021.05.15
데이터베이스 예제 4  (0) 2021.04.20
데이터베이스 예제 3  (0) 2021.04.20
데이터베이스 예제 2  (0) 2021.04.20
블로그 이미지

아상관없어

,
반응형

역공학을 방지함

Why? 

- sw 자체가 수익임 따라서 불법복제를 막아야함

- 중요한 logic 유출 방지

- DRM 기술 또한 저작권 보호를 위해 역공학이 필요함

- sw 개발 플랫폼은 기본적으로 역공학 대책을 제공하고 있음

ex) 구글 proguard 제공

 

java, c#언어는 디컴파일, disassemble하기 편함 => binary 코드들에 비해서 byte코드들이 봉합되기 더 쉬움

따라서 안드로이드에서는 proguard (최적화 난독화 기법 제공)

 

- obfuscator (난독화 도구)

프로그램에 있는 정보를 수정, 제거함으로써 가독성을 감소 시켜주는 도구 => 분석하기 어려워짐 

 

Anti-reversing 방법

 

1.  symbolic 정보를 제거함

소스코드를 컴파일할 때 symbol정보를 제거함 (함수이름, 변수이름 ...) => 컴파일러가 컴파일 단계에서 시행함 따라서 디버깅하기 어려움

 

2. 코드 암호화

코드를 암호화하여 정적분석을 막음 

 

3. anti-Debugging

 

4. 프로그램 난독화

난독화 = 여러가지 기법을 뜻함

일반적으로 프로그램의 가독성을 떨어뜨리거나, 취약점을 제거하거나, 정적분석을 어렵게 하기 위해 사용되는 기법

따라서, 난독화를 하면 역공학이 어려워짐

ex) 레이아웃 변경, 프로그램 로직 변경, 데이터를 변경, 구성자체를 변경  => 프로그램을 분석하기 어렵게 함

 

- code 난독화

기능은 유지하면서, 사람이 코드를 분석하기 어렵게 변경하는 것

 

- 난독화 도구

ProGuard, DexGuard, Allatori ...

NHN 토스트 앱가드 -> 식별자 난독화(이름 재지정), 문자열 암호화, 제어흐름 난독화 등

 

* de-obfuscator : 난독화된 악성코드를 분석시 사용, 난독화를 해제시켜주는 도구

 

분석 지연 난독화 클래스, 메소드, 변수명 등을 의미 없는 문자나 식별할 수 없는 문자로 치환, 제어흐름 난독화
암호화 문자열, 리소스 등 암호화
코드 분리 핵심 로직이 담긴 코드를 분리하고 실행 중에 동적으로 적재
환경 탐지 디버거 탐지 프로세스 ID 확인, 디버깅 탐지 API 결과 값 반환
에뮬레이터 탐지 단말 ID, 전화번호, 빌드 값, IP 조사
플랫폼 해킹 탐지 OS 해킹 여부 확인
앱 변조 탐지 앱 위변조 여부, 앱 서명 값 확인

 

 

 

코드 난독화 기법 분류

 

- 레이아웃 난독화

예시) 식별자 난독화 (identifier renaming)

package iAmDriving{
	public class LetsNavigate{...}
}

pacakge iAmConnecting {
	public class MyBluetoothHandle{...}
}

pacakge userIsActive{
	public class MainActivity{...}
}



|
V


package a{
	public class a{...}
}

pacakge b {
	public class b{...}
}

pacakge c{
	public class c{...}
}

 

- 제어흐름 난독화 : 제어흐름 은닉, 추가코드 삽입(의미없는), disassemble 복잡하게 만듬

의미 없는 switch 문장 추가

중복되는 operator 추가

제어흐름의 순서 조작

제어흐름 flatten시킴(ex 여러개 함수를 하나의 함수로)

 

- 데이터 난독화 (자료구조 난독화)

data aggregation : 배열 변수를 합쳐서 (일차원 두개 합쳐서, 이차원으로), 클래스 상속을 조작하여 이해하기 어렵게함

data storage and encoding : encoding변경(기법 변경), 데이터 수정 

data ordering : 메소드나 array의 순서를 조작

 

- 예방적 변환

anti De-compilation

String encryption

 

 

 

728x90
반응형

'공부 > 보안' 카테고리의 다른 글

암호화 기본 - 1  (0) 2021.06.01
apk 난독화  (0) 2021.05.17
Reverse Engineering  (0) 2021.05.14
간단한 Android game app hacking  (0) 2021.05.03
SEED Lab Return-to-Libc Attack  (0) 2021.05.03
블로그 이미지

아상관없어

,

Reverse Engineering

공부/보안 2021. 5. 14. 22:21
반응형

Revere Engineering(Backwards, back engineering)

 

Forward Engineering

요구사항분석 -> 설계 -> 구현

요구사항분석 <(요구사항 복구)- 설계 <(설계 복구)- 구현

역공학은 이를 역으로 복구하는 것을 reverse engineering이라 함

  • 오래된 프로그램을 유지보수해야할 때, 분석하여 설계원칙과 요구사항을 알아내야하므로 역공학을 사용하기도 함

 


예시) 안드로이드 컴파일 과정은 Forward Engineering의 일부임

소스코드를 자바로 작성(.java) ->컴파일 -> java Bytecode ->Proguard(최적화, 난독화) -> dex(davik executable)로 변환 -> android runtime에 의해서 기계어로 번역되어 자동

 

.java ->java Bytecode (.class) -> minimized Bytecode -> Dex Bytecode (.dex) -> Machine code

최신 안드로이드에선 Android Runtime 사용(native code 로 동작, 성능상의 이유), kotlin도 사용

안드로이드 역공학

apk = android application package (dex, resource 등 파일이 들어가 있음)

de-complier를 이용하여 dex는 smali (중간 어셈블리 언어)로 바뀌어짐,

asset = resource관련

META-INFO = sign관련

smali to java를 이용하여 smali코드를 java코드로 바꾸어줌

따라서 java 소스코드를 볼 수 있다.


 

 

역공학은 주로 어떤 정보가 이용가능 하지 않을 때(오래전에 만들었거나 소스코드가 공개되지 않을때), 실행코드로부터 놓친 지식이나, 아이디어, 설계 원리를 파악하기 위해 수행한다.

시스템의 일부로 어떤 기능, 어떤 설계원리가 있는지 분석하는 과정이라 할 수 있다.

악성코드 분석가들은 역공학이 필요 => 악성코드는 소스가 공개되어 있지 않기 때문

 

 

 

Software Reverse Engineering

sw뿐만아니라 hw에도 적용될 수 있음

  • 프로그램을 분해해서 그 내부 구조를 파악 하는 것.
  • 기술과 컴퓨터, sw개발에 대한 이해가 필요함
  • code breaking, puzzle solving, programming, logival analysis 필요

 

Binary reverse engineering

  • 소스코드를 사용할 수 없을 때 사용하는 기법
  • reverse code engineering

 

역공학이 필요한 경우

  • 연구목적
  • 취약점을 분석하기 위해
  • 이미 배포되어 사용되는 프로그램들에 패치를 해야 할 때(binary level에서 패치할때 필요함, 보안과 관련잇음)
  • 프로그램 내의 비밀 정보를 획득하기 위해(공격자 관점)
  • 프로토콜을 분석하기 위해 (공격을 위한 정보 탐색)
  • 보호기법을 우회하기 위해 (copy 목적)

 

역공학 응용

1. 보안에 관련해서 사용

  • 악의적인 프로그램의 binary auditing
  • 암호 알고리즘 역공학 => 연구자들이 암호화 제품을 평가해야함(얼만큼 안전한가).
  • Digital Right Management (DRM) - 디지털 컨텐츠 권리 관리, 저작권 관리

(비용을 지불한 만큼만 사용할 수 있게)

(cracker들을 copy protection 기법을 우회하려 함)

 

1.1 악성 프로그램

  • 멀웨어 작성자, 분석가 둘다 역공학함
  • 멀웨어 작성자 => os나 다른 sw의 취약점을 파악하여, 악성코드를 감염(전파)시키려함.

(바이러스는 양성 프로그램에 기성하여 돌아감, 매크로 프로그램을 조작시켜 자신을 전파 시켜야함)

(악성코드는 자기자신을 전파시키기 위해 os나 다른 sw의 취약점을 이용함 ex)Morris Internet Worm)

  • anti-virus 개발자

악성코드를 분해하여 분석하기 위해 => 치료가능

 

1.2 Auditing Program Binaries

소스코드를 사용할 수 없는 경우 & 중요한 프로그램일 경우

중요한 프로그램의 취약점을 찾을 때

ex) bug bounty, 모의 해킹

 

1.3 DRM

불법 복제 방지위해 copy protection 기술을 사용함.

DRM도 그와 유사 (DRM은 적용되는 컨텐츠가 주로 디지털 미디어 컨텐츠임)

공격자들은 DRM기술을 우회하기 위해 역공학을 사용함.

 

2. sw 개발에서

  • sw 품질 향상, 평가하기 위해
  • 유지보수를 위해

 

Tool or Methods

  • disassembler, decompiler => 정적 분석 (실행할 필요 X)

(IDA Pro, JADAX(Dex to Java Decomplier), dex2jar, apktool)

(ILDasm (disassembler for MS Intermediate language, MSIL)

  • Debugger ( 동적 코드 분석 )

(JEB)

  • Hex Editor

binary(실행 파일 분석)

  • PE Analyzer

MS windows는 PE포맷을 사용 (안드로이드는 Dex 사용)

728x90
반응형
블로그 이미지

아상관없어

,
반응형

요구사항

- train set 으로 학습, validation val set 이용, test set 으로 테스트 accuracy 도출

- 자신만의 deep neural network을 설계하여 문제를 해결 할 것

(전이학습 모델 이용하면 안됨)

 

 

모델 개선 과정

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

먼저 데이터를 X_train/y_train, X_test/y_test, X_val, y_val로 나누어 학습을 진행하였습니다.

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.preprocessing.image import load_img, img_to_array

import numpy as np
from pathlib import Path
import glob
import pandas as pd
from skimage.io import imread # reading image as data

data_dir = Path('chest_xray')

train_dir = data_dir / 'train'
val_dir = data_dir / 'val'
test_dir = data_dir / 'test'

normal_cases_dir = train_dir / 'NORMAL'
pneumonia_cases_dir = train_dir / 'PNEUMONIA'

normal_cases_t = normal_cases_dir.glob('*.jpeg')
pneumonia_cases_t = pneumonia_cases_dir.glob('*.jpeg')

# Training data as a list
X_train = []
y_train = []

# Normal cases

for img in normal_cases_t:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    # Convert grayscale image
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    # CV2 uses BGR format, so we need to convert it to RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # Normalizing the pixel values by dividing by its maximum
    img = img.astype(np.float32)/255.
    X_train.append(img)
    y_train.append(0)

# Pneumonia cases        
for img in pneumonia_cases_t:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.float32)/255.
    X_train.append(img)
    y_train.append(1)

X_train = np.array(X_train)
y_train = np.array(y_train)
normal_cases_dir = val_dir / 'NORMAL'
pneumonia_cases_dir = val_dir / 'PNEUMONIA'

normal_cases_v = normal_cases_dir.glob('*.jpeg')
pneumonia_cases_v = pneumonia_cases_dir.glob('*.jpeg')

# Training data as a list
X_val = []
y_val = []

# Normal cases
for img in normal_cases_v:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    # Convert grayscale image
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    # CV2 uses BGR format, so we need to convert it to RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # Normalizing the pixel values by dividing by its maximum

    img = img.astype(np.float32)/255.
    X_val.append(img)
    y_val.append(0)

# Pneumonia cases        
for img in pneumonia_cases_v:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.float32)/255.
    X_val.append(img)
    y_val.append(1)

X_val = np.array(X_val)
y_val = np.array(y_val)

normal_cases_dir = test_dir / 'NORMAL'
pneumonia_cases_dir = test_dir / 'PNEUMONIA'

normal_cases_test = normal_cases_dir.glob('*.jpeg')
pneumonia_cases_test = pneumonia_cases_dir.glob('*.jpeg')
# Training data as a list
X_test = []
y_test = []

# Normal cases
for img in normal_cases_test:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    # Convert grayscale image
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    # CV2 uses BGR format, so we need to convert it to RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # Normalizing the pixel values by dividing by its maximum
    img = img.astype(np.float32)/255.
    X_test.append(img)
    y_test.append(0)

# Pneumonia cases        
for img in pneumonia_cases_test:
    img = cv2.imread(str(img))
    img = cv2.resize(img, (224,224))
    if img.shape[2] ==1:
        img = np.dstack([img, img, img])
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.float32)/255.
    X_test.append(img)
    y_test.append(1)

X_test = np.array(X_test)
y_test = np.array(y_test)

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D

seed = 100
np.random.seed(seed)


model=Sequential()

a=3

model.add(Convolution2D(32, kernel_size=(a, a), padding='same', strides=(1, 1), input_shape=(image_size, image_size, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(64, kernel_size=(a, a), padding='same',  activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(128, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(256, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Flatten())

model.add(Dense(256,activation='relu'))
model.add(Dropout(0.5))

model.add(Dense(1,activation='sigmoid'))

model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])

# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("loss: %.2f" % scores[0])
print("acc: %.2f" % scores[1])

'''
loss: 2.00
acc: 0.75
'''


관련 자료를 찾던 중 ImageDataGenerator을 알게되었습니다.


CNN
은 영상의 2차원 변환인 회전(Rotation), 크기(Scale), 밀림(Shearing), 반사(Reflection), 이동(Translation)와 같은 2차원 변환인 Affine Transform에 취약합니다.

, Affine Tranform으로 변환된 영상은 다른 영상으로 인식하므로, Data Generator를 사용하여 이미지에 변화를 주면서 컴퓨터의 학습자료로 이용하여 더욱 효과적이고 과적합을 방지하는 방식인, ImageDataGenerator으로 학습하도록 하였습니다.

 

ImageDataGenerator 사용

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from keras.applications.mobilenet import preprocess_input

image_size = 224
batch_size = 32
seed = 100
datagen = ImageDataGenerator (
            rescale = 1./255, 
            shear_range=0.2,
            zoom_range=0.2,
            horizontal_flip=True,
            )

# load the data using data generators
train_generator  =    datagen.flow_from_directory(
                             train_dir,
                             seed=seed,
                             target_size = (image_size,image_size),
                             batch_size =batch_size ,               
                             class_mode = 'binary',
                            )

test_generator = datagen.flow_from_directory(
                             val_dir,
                             seed=seed, 
                             target_size = (image_size,image_size),
                             batch_size = batch_size ,               
                             class_mode = 'binary',
                            )

validation_generator = datagen.flow_from_directory(
                             test_dir,
                             seed=seed, 
                             target_size = (image_size,image_size),
                             batch_size = batch_size ,               
                             class_mode = 'binary',
                            )

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D

seed = 100
np.random.seed(seed)

model=Sequential()

a=3

model.add(Convolution2D(32, kernel_size=(a, a), padding='same', strides=(1, 1), input_shape=(image_size, image_size, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(64, kernel_size=(a, a), padding='same',  activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(128, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(256, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Flatten())

model.add(Dense(256,activation='relu'))
model.add(Dropout(0.5))

model.add(Dense(1,activation='sigmoid'))


model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])


# Final evaluation of the model
scores = model.evaluate(test_generator, verbose=0)
print("loss: %.2f" % scores[0])
print("acc: %.2f" % scores[1])

'''
loss: 1.12
acc: 0.69
'''

 

 

Epoch10으로 하였을 때, ImageDataGenerator를 사용하지 않았을 때, acc = 0.75, 사용하였을때 acc =0.69로 사용하지 않았을 경우가 더 높게 나왔습니다.

좀 더 확실히 비교하기 위해 epoch20으로 늘려 테스트해보았습니다.

 

ImageDataGenerator 사용 X

Acc :0.71

 

ImageDataGenerator 사용 O

Acc :0.75

 

여러 차례 수행하였을 때 비슷하게 결과가 나왔습니다.

ImageDataGenerator를 쓰지 않은 경우가 속도가 더 빠르므로 ImageDataGenerator를 쓰지 않고 가장 좋은 결과가 나왔을 때, 그 경우로 ImageDataGenerator를 사용하여 비교해보기로 하였습니다.

 

 

Layer

먼저 flatten 전까지의 layer의 개수(feature (32) 동일,batch_size = 200)를 점차 늘려 보았습니다.


 [Layer4개 일 때]

 

[Layer5개 일 때 ]

 

[Layer6개 일 때]

 

[Layer7개일 때]

점점 lossacc가 안좋아지므로 layer 수는 5개가 가장 좋은 것으로 확인되었습니다.

 

그리고 feature의 수를 조절하려 하였습니다. Feature 갯수가 이렇게 많아지게 되면 컴퓨팅 속도가 저하되고 오버피팅의 위험이 생길 수 있으므로 적절한 feature 수가 무엇일지 생각하였습니다.

http://www.hellot.net/new_hellot/magazine/magazine_read.html?code=202&sub=&idx=42517의 글을 참조하여 ‌Krizhevsky - ‘ImageNet classification with deep convolution neural network’논문의 내용을 참고하여 feature수를 layer가 깊어질수록 증가시켰습니다.

 

Feature

[논문의 모델과 동일하게 구성]

 

 

 

[32->32->64->64->128]

 

 

 

[32->64->128->256->256]

 

 

 

[64->64->128->128->256]

 

점진적으로 증가되는 것이 좋은 결과를 보여주었습니다.

 

 

Batch_size

다음으로는 batch size를 늘려보았습니다.

[32]

 

 

[64]

 

 

[128]

 

 

 

[256]

 

 

 

[512]

 

 

[1024]

 

Batch size가 커질수록 좋아지는 것을 알 수 있었습니다.

 

 

하지만 일정 크기 이상으로 커질 경우

[2048]

 

좋지 않은 것을 알 수 있었습니다.

또한 https://blog.naver.com/qbxlvnf11/221449595336사이트의 글을 참조하여 batch size를 상대적으로 높이지만 너무 커지면 일반화 성능이 감소하므로, 적절히 값을 다른 데이터들을 바탕으로 종합적으로 모델을 만든 후 값을 바꾸어 가며 테스트를 하기로 하였습니다.

 

 

그 다음으로 kernel size를 조절하였습니다.

Kernel size는 작은 것을 여러 개 쓰는 방법이 좋으므로 5부터 1까지 테스트하였습니다

 

Kernel size

[5]

 

 

 

 

[4]

 

 

[3]

 

 

 

[2]

 

 

[1]

 

Kernel size1일 때 가장 좋은 결과를 보여주었습니다.

 

 

위 결과들을 바탕으로 layer를 구성하여 테스트하였습니다.

위 요소들을 섞어가며 테스트 중 batch size300이 넘어갈 경우 colab에서 ResourceExhaustedError 에러가 크게 하지 못하였습니다.

또한 세션이 다운되는 문제를 겪었습니다.

 

다른 요소로 정확도를 향상시키기 위해https://teddylee777.github.io/tensorflow/keras-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98-vol-01를 참고하여 ReduceLROnPlateau를 사용하여 학습률을 동적으로 조정하였습니다.

 

여러 요소들을 조절해가며 테스트하였을 때 밑의 모델이 가장 좋은 예측력을 보여주었습니다.

 

결과

 

그리고 이 모델을 ImageDataGenerator를 사용하여 테스트하였습니다.

시간은 더 오래 걸리지만 확실히 더 좋은 성능을 보여주었습니다.

 

그 후 다시 한번 batch size feature, image_size, epoch를 조절해보았습니다.

하지만 0.87의 정확도를 넘어서지 않았습니다.

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

 

<최종>

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.preprocessing.image import load_img, img_to_array

import numpy as np
from pathlib import Path
import glob
import pandas as pd
from skimage.io import imread # reading image as data

data_dir = Path('chest_xray')

train_dir = data_dir / 'train'
val_dir = data_dir / 'val'
test_dir = data_dir / 'test'

normal_cases_dir = train_dir / 'NORMAL'
pneumonia_cases_dir = train_dir / 'PNEUMONIA'

'''
======================================================================================================================
'''
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from keras.applications.mobilenet import preprocess_input

image_size = 224
batch_size = 256
seed = 100
datagen = ImageDataGenerator (
            rescale = 1./255, 
            shear_range=0.2,
            zoom_range=0.2,
            horizontal_flip=True,
            )

# load the data using data generators
train_generator  =    datagen.flow_from_directory(
                             train_dir,
                             seed=seed,
                             target_size = (image_size,image_size),
                             batch_size =batch_size ,               
                             class_mode = 'binary',
                            )

validation_generator = datagen.flow_from_directory(
                             val_dir,
                             seed=seed, 
                             target_size = (image_size,image_size),
                             batch_size = batch_size ,               
                             class_mode = 'binary',
                            )

test_generator = datagen.flow_from_directory(
                             test_dir,
                             seed=seed, 
                             target_size = (image_size,image_size),
                             batch_size = batch_size ,               
                             class_mode = 'binary',
                            )

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
model=Sequential()

a=3

model.add(Convolution2D(32, kernel_size=(a, a), padding='same', strides=(1, 1), input_shape=(image_size, image_size, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Convolution2D(64, kernel_size=(a, a), padding='same',  activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Convolution2D(128, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Convolution2D(256, kernel_size=(a, a), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))


model.add(Flatten())

model.add(Dense(256,activation='relu'))
model.add(Dropout(0.5))

model.add(Dense(1,activation='sigmoid'))

from keras.callbacks import ReduceLROnPlateau
learning_rate_reduction = ReduceLROnPlateau(monitor='val_loss', factor=0.3, patience=2, verbose=2, mode='auto')

model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])

history = model.fit_generator(train_generator, epochs = 10, validation_data = validation_generator, callbacks = [learning_rate_reduction])

# summarize history for accuracy
plt.plot(history.history['accuracy'])

plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper left')
plt.show()

# Final evaluation of the model
scores = model.evaluate(test_generator, verbose=0)
print("loss: %.2f" % scores[0])
print("acc: %.2f" % scores[1])

 

<그래프>

 

<model>

<epoch>

728x90
반응형

'공부 > 딥러닝' 카테고리의 다른 글

CIFAR-10 의 레이블중 하나를 예측  (0) 2021.05.09
classification 경진대회  (0) 2021.05.03
딥러닝 4  (0) 2020.10.29
딥러닝 3  (0) 2020.10.09
딥러닝 2  (0) 2020.09.29
블로그 이미지

아상관없어

,
반응형

이미지 파일을 업로드하면 CIFAR-10 의 레이블중 하나를 예측하여 보여주는 소프트웨어를 개발

예측 모델은 전이학습을 이용하여 만들고 CIFAR-10을 이용하여 학습

 

"""
Created on Sun Dec  6 02:22:39 2020

@author: Changmin
"""
from urllib.request import urlopen
from tkinter import *
import requests
from PIL import Image, ImageTk
from io import BytesIO

from keras.engine import Model

from keras.preprocessing.image import img_to_array

from tensorflow.keras.applications.efficientnet import preprocess_input

import io

def decode_pred(pred):
    arr = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']
    index = pred.argmax() #0,1
    #label = [arr[index], pred[0,index]]
    return arr[index]

def click():
    image_url = str.get()
    data = urlopen(image_url).read()
    image = Image.open(io.BytesIO(data))
    img = image.resize((400,300))
    
    img = ImageTk.PhotoImage(img)
    img_label.configure(image=img)
    img_label.photo = img
    img_label.grid(column=0,row=3, sticky = W+N)
    
    
    #이미지 예측#
    ##############################
    # load an image

    image = image.convert('RGB')
    image = image.resize((32,32), Image.NEAREST)
    
    # convert the image pixels to a numpy array
    image = img_to_array(image)
    
# reshape data for the model
    image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
    
    # prepare the image
    image = preprocess_input(image)
    
    
    # load model
    from keras.models import load_model
    model = load_model('epoch_80.h5')
    
    # predict the probability across all output classes
    pred = model.predict(image)
    
    # convert the probabilities to class labels
    answer = decode_pred(pred)
    
    
    L2 = Label(window, text="This image is {}".format(answer))
    L2.grid(column=1, row=3, sticky=W+S)
    
    window = Tk();
window.title("Image Classification")
window.geometry("1000x700+100+100")
window.resizable(True, True)


str = StringVar()
answer = StringVar()
L1 = Label(window, text="Image classification")
L1.grid(column=0,row=0, sticky=W)

textbox = Entry(window, width=50, textvariable=str)
textbox.grid(column=0,row=1)


img_label = Label(window,image="")

upload_Button = Button(window, width=10, text="Upload Image", fg='white', bg='blue', command = click)
upload_Button.grid(column=1,row=1, sticky = W)


window.mainloop();

 

<모델>

from keras import optimizers
from keras.datasets import cifar10
from keras.engine import Model
from keras.layers import Dropout, Flatten, Dense
from keras.utils import np_utils
import numpy as np

from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
from tensorflow.keras.applications.efficientnet import preprocess_input, decode_predictions

from tensorflow.keras.applications import EfficientNetB0

def decode_pred(pred):
    arr = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']
    index = pred.argmax() #0,1
    label = [arr[index], pred[0,index]]
    return label

#이미지 예측#
##############################

img_width, img_height = 32, 32

#top false => top layer 삭제
base_model = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(32, 32, 3), drop_connect_rate=0.4)

nb_epoch = 80 # 50 is good
nb_classes = 10

# load dataset
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
y_train = np_utils.to_categorical(y_train, nb_classes)
y_test = np_utils.to_categorical(y_test, nb_classes)

# Extract the last layer from third block of model
last = base_model.get_layer('top_activation').output

# Add classification layers on top of it
x = Flatten()(last)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
output = Dense(10, activation='softmax')(x)
    
model = Model(inputs = base_model.input, outputs= output)
    
    
model.compile(loss='binary_crossentropy', optimizer=optimizers.SGD(lr=1e-3, momentum=0.9), metrics=['accuracy'])
model.summary()
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs =nb_epoch, batch_size=64, verbose=1)

# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("loss: %.2f" % scores[0])
print("acc: %.2f" % scores[1])

#  모델 저장하기
from keras.models import load_model
model.save('epoch_80.h5')


from google.colab import files
files.download('epoch_80.h5') 


 

 

실행화면

 

모델을 만들 때 efficientnet 모델을 사용하였지만, 소요되는 시간이 너무 많았다.

 

그래서 방법을 찾던중 colab에서 gpu로 코드를 돌릴 수 있다는 것을 알게되었고, 5시간 정도의 작업이 30분정도로 줄어들어 epoch을 더 늘려 높은 정확도를 얻을 수 있었다.

파이썬 GUI를 처음 사용하면서 url로 불러온 이미지를 어떻게 처리할지 어려웠는데, Byte단위로 읽어와 처리해주고, 표시되는 이미지를 resize하여 동일한 크기로 보이게 하여 gui로 나타낼 때 좀더 간결하게 표현하였다.

 

또한 decode_prediction1000개의 이미지에 맞추어져 있다는 사실을 까먹고 자꾸 이상한 값이 나와 이 문제를 해결하는데 시간이 많이 소요되었다.

728x90
반응형

'공부 > 딥러닝' 카테고리의 다른 글

흉부 X-ray 사진으로 폐렴 진단 모델  (0) 2021.05.09
classification 경진대회  (0) 2021.05.03
딥러닝 4  (0) 2020.10.29
딥러닝 3  (0) 2020.10.09
딥러닝 2  (0) 2020.09.29
블로그 이미지

아상관없어

,
반응형

 

728x90
반응형

'공부 > 기타' 카테고리의 다른 글

DSP MATLAB Project - Image Processing With GPU  (0) 2022.04.26
웹 애플리케이션 기본지식  (0) 2021.05.20
대칭키로 평문 암,복호화 (대칭키 외부 저장)  (0) 2021.05.09
네이밍 컨벤션  (0) 2021.05.02
Github SourceTree  (0) 2020.09.03
블로그 이미지

아상관없어

,
반응형

암,복호화.pdf
0.73MB

대칭키k 평문 DES암호화 -> RSA공개키로 대칭키k 암호화 후 외부파일 저장 -> 대칭키k로 암호화된 평문 복구

 

<암호화된 대칭키 파일>

 

728x90
반응형
블로그 이미지

아상관없어

,
반응형

Problem 3: 주어진 희소행열을 표현하기 위한 적합한 자료구조를 기술하고 행열 내 모든 값의 합을 구하는 독립된 함수를 작성하시오.

1. 정수값을 입력 받아 희소행렬의 크기를 결정

2. 주어진 행렬내 원소의 약 10% 이하가 0이 아닌 값으로 임의 수를 생성 하여 대입

//h.h




int** insertRand(int** Matrix, int m);
int* NoDupRand(int* rand_num, int n);
int** createMatrix(int m, int n);
void printMatrix(int** Matrix, int m);
void showPercent(int** matrix, int m1);
void SumMatrix(int** matrix, int SparesMatrix_row);
//f.c


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include"h.h"

void SumMatrix(int** matrix, int SparesMatrix_row) {

	int m1, sum, size;
	sum = 0;
	size = SparesMatrix_row;
	//matrix[] 갯수
	for (m1 = 0; m1 < size; m1++) {
		sum += matrix[m1][2];
	}
	printf("==================\n");
	printf("sum is %d\n", sum);
	printf("==================\n");
}

void showPercent(int** matrix, int matrix_size) {
	int spares_size = matrix_size;
	spares_size -= matrix[matrix_size - 1][3];
	matrix_size /= 10;
	float percent;

	percent =((float)spares_size / (float)matrix_size);
	//행렬안에 값이 0인 경우를 빼줌
	printf("Percent : %0.3f%%\n", percent);
	printf("==================\n");

}

void printMatrix(int** Matrix, int m) {
	int m1, n1;
	for (m1 = 0; m1 < m; m1++) {
		for (n1 = 0; n1 < 3; n1++) {
			if (n1 == 2) {
				printf("|%4d|\n", Matrix[m1][n1]);
			}
			else
				printf("|%4d ", Matrix[m1][n1]);
		}
	}
}
//중복되지 않는 랜덤값을 가져오기위해
int* NoDupRand(int* rand_num, int n) {
	int index1, index2, i, temp;
	srand(time(NULL));

	//n번
	for (i = 0; i < n; i++) {
		rand_num[i] = i;
	}
	//섞기 n/2번
	for (i = 0; i < (n / 2); i++) {
		index1 = rand() % n;
		index2 = i;
		temp = rand_num[index1];
		rand_num[index1] = rand_num[index2];
		rand_num[index2] = temp;
	}
	return rand_num;
}
//(m x n) matrix
int** insertRand(int** Matrix, int m) {
	int m1, n1;
	int count;
	int* NoDupRand_m;
	int* NoDupRand_n;

	NoDupRand_m = (int*)malloc(sizeof(int) * m);
	NoDupRand_n = (int*)malloc(sizeof(int) * m);

	NoDupRand_m = NoDupRand(NoDupRand_m, m);
	sleep(1);
	NoDupRand_n = NoDupRand(NoDupRand_n, m);
	//원소를 넣을 (행,열)위치 생성
	for (m1 = 0; m1 < m; m1++) {
		//행(0~m-1) 열(0~n-1) 위치 저장
		//랜덤값 중복체크를 하면 시간, 공간복잡도가 늘어남으로
		//행과 열이 겹치는 경우
		//    행 열 값  행 열값
		//ex) [1][1][] [1][1][]
		Matrix[m1][0] = NoDupRand_m[m1];
		Matrix[m1][1] = NoDupRand_n[m1];
	}
	/*   0  1   3
	  0 [1][1][값1]
	  1 [1][1][값2]
	  .
	  .
	  n [n][m][ ]

	  위와 같은 경우 발생가능
	  */
	  //배열에 n까지 값을 저장하고 섞음으로 랜덤값 중복체크보다 시간 줄임

	  //0이 아닌 원소가 10%라고 가정하고 만든행렬이므로
	count = 0;
	for (m1 = 0; m1 < m; m1++) {
		Matrix[m1][2] = rand() % 10;
		//printf("[%d][3] = %d\n", m1, Matrix[m1][2]);
		if (Matrix[m1][2] == 0) {
			count++;
		}
	}
	//행열값
	//[][][0]
	//0값이 들어간 경우가 발생하는경우를 만들어줫으므로
	//값이 0인 (행,열)의 갯수를 셈
	Matrix[m-1][3] = count;
	return Matrix;
}


int** createMatrix(int m, int n){
	int** matrix;
	int m1;
	//10%이하여야하므로
	m1 = (m * n) / 10;

	matrix = (int**)malloc(sizeof(int*) * m1);
	for (int m2 = 0; m2 < (m1 - 1); m2++) {
		//행, 열, 값 3개
		matrix[m2] = (int*)malloc(sizeof(int) * 3);
	}
		//0갯수 저장
		matrix[(m1-1)] = (int*)malloc(sizeof(int) * 4);



	return matrix;
}
//m.c


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include"h.h"

void main() {
	//mxn 행렬
	int m, n;
	int Msize;
	int** spares_matrix;
	float percent;

	printf("희소 행렬 크기 입력(m, n값): ");
	scanf("%d %d", &m, &n);

	printf("\n");

	Msize = (m * n / 10);
	//희소행렬의 0이 아닌값만 저장할 행렬
	//10%이하 여야함
	spares_matrix = createMatrix(m, n);
	spares_matrix = insertRand(spares_matrix, Msize);
	printf("    행    열    값\n");
	printf("------------------\n");
	printMatrix(spares_matrix, Msize);
	//희소 행열의 덧셈 결과 비교 출력, 0이 아닌 비율 출력
	SumMatrix(spares_matrix, Msize);
	showPercent(spares_matrix, Msize);

	printf("program End!\n(by32153682이창민)\n");
	printf("==================\n");
}

 

//makefile

app:m.o f.o
	gcc -o app m.o f.o

m.o: h.h m.c
	gcc -c m.c

f.o:h.h f.c
	gcc -c f.c

 

 

 

Problem 4: Infix 표현을 Postfix로 변환시키는 프로그램을 설계하고 구현하시오.

//toPostfix.h



//toPostfix by 이창민
/*
   *Stack
  중위표기수식만큼 크기 할당(괄호 빼고)
  (괄호 빼고 => 괄호 스택에 넣어 판단)

  *Infix_to_Postfix
  피연산자의 순서는 동일, 연산자들의 순서만다름
  즉, 연산자만 스택에 저장됨

  1. 피연산자를 만나면 그대로출력
  2. 연산자를 만나면 스택에 저장
  3. 스택에 있는 연산자보다 낮은순위의 연산자가 나오면 출력
  4. 왼쪽괄호는 연산자 우선순위가 가장 낮다고 판단
  5. 오른쪽 괄호를 만나면 출력(왼쪽괄호빼고)
 */

#include<stdio.h>

typedef struct{
	char* stack_data;
	int top;
}Stack;

void infix_to_postfix(Stack* s, char* exp);
void create(Stack* s, int exp_size);
int is_full(Stack* s, int exp_size);
void push(Stack* s, char data, int exp_size);
int is_empty(Stack* s);
char pop(Stack* s);
char peek(Stack* s);
void stack_free(Stack* s);
int size_nums(char* exp);
void checking(char* exp);
int order(char op);
//InfixToPostfix.c



#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"toPostfix.h"
int order(char op) {
	switch (op) {
	case '(': case')':
		return 0;
	case '+': case '-':
		return 1;
	case '*': case '/':
		return 2;
	default:
		return -1;
	}
}

void checking(char* exp) {
	char ch;
	char check;
	int i;
	int size = strlen(exp);
	Stack test;

	create(&test, size);

	for (i = 0; i < size; i++) {
		ch = exp[i];

		switch (ch) {
		case '(':
			push(&test, ch, size);
			break;
		case ')':
			check = pop(&test);
			if (check == 'f') {
				printf("error!! \")\" checking failed.\n");
				exit(1);
			}
			break;
		}
	}
}

void infix_to_postfix(Stack* s, char* exp) {
	char ch, op, result;
	int expSize;
	expSize = strlen(exp);
	for (int i = 0; i < expSize; i++) {
		ch = exp[i];
		switch (ch) {
		case '+': case '-': case '*': case '/': {
			//스택이 비어있지 않고 스택의 연산자보다 낮은 우선순위일때 pop
			if (!is_empty(s) && order(ch) <= order(peek(s))) {
				printf("%c", pop(s));
			}
			push(s, ch, expSize);//push ch 스택에
			break;
		}
		//왼쪽 괄호를 만나면 스택에 저장
		case '(': {
			push(s, ch, expSize);
			break;
		}
		//왼쪽 괄호가 나올때까지 출력
		case ')': {
			result = pop(s);
			while (result != '(') {
				printf("%c", result);
				result = pop(s);
			}
			break;
		}
		default: {
			printf("%c", ch);
			break; }

		}
	}
	while (!is_empty(s)) {
		printf("%c", pop(s));
	}
}

 

//size.c


#include<stdio.h>
#include<string.h>
#include"toPostfix.h"

int size_nums(char* exp) {
	int count = 0;
	int size = strlen(exp);
	for (int i = 0; i < size; i++) {
		char data = exp[i];

		switch (data) {
		case '(': case ')':
			count++;
			break;
		}
	}

	return size = size - count;

}

 

//stack.c



#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"toPostfix.h"
//스택 생성
void create(Stack* s, int exp_size) {
	s->stack_data = (char*)malloc(exp_size);
	s->top = -1;
}

//스택이 비었는지 확인
int is_empty(Stack* s) {
	return (s->top == -1);
}

int is_full(Stack* s, int exp_size) {
	return (s->top == (exp_size - 1));
}

void push(Stack* s, char data, int exp_size) {
	int top = s->top;
	if (is_full(s, exp_size)) {
		printf("stack is full!\n");
	}
	else {
		s->stack_data[++(s->top)] = data;
	}
}

char pop(Stack* s) {
	int top;
	if (is_empty(s)) {
		printf("stack is empty!\n");
		return 'f';
	}
	else {
		top = s->top;
		(s->top)--;
		return s->stack_data[top];
	}
}

char peek(Stack* s) {
	if (is_empty(s)) {
	}
	else {
		return s->stack_data[s->top];
	}
}

void stack_free(Stack* s) {
	free(s->stack_data);
}

 

//testing.c


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"toPostfix.h"

int main(int argc, char* argv[]){
	if(argc==1){
		printf("USEAGE : \"expression\" \"expression\"...\n");exit(1);
	}
	char* exp;
	int exp_size;
	Stack s;
	for (int i = 1; i < argc; i++) {
		exp = argv[i];
		printf("before : %s\n",exp);
		//괄호 이상여부 체크
		checking(exp);
		exp_size = size_nums(exp);
		create(&s, exp_size);
		printf("after : ");
		infix_to_postfix(&s, exp);
		printf("\n");
		stack_free(&s);
	}

	printf("\n");
	printf("Program End!\n");

	return 0;
}

 

//makefile

head = toPostfix.h
objects = testing.o InfixToPostfix.o size.o stack.o
files = InfixToPostfix.c size.c stack.c

app : $(head) $(objects)
	gcc -o app $(objects) 
testing.o : $(head) $(files)
	gcc -c testing.c
InfixToPostfix.o : $(head) InfixToPostfix.c
	gcc -c InfixToPostfix.c
size.o : $(head) size.c
	gcc -c size.c
stack.o : $(head) stack.c
	gcc -c stack.c
728x90
반응형
블로그 이미지

아상관없어

,
반응형
//BinaryTreeNode.c

#include "h.h"

/*BIANRY TREE*/
//////////////////////////////////////////////////////////////////////////////////
/*
   트리를 생성하는 함수
   => postfix 배열을 인자로 받은 뒤(postfix배열은 infix를 postfix로 바꾼 문자열)
   stack 배열에 값을 넣어주고
   연산자이면 자식노드를 생성후 오른쪽 자식노드부터 pop하여 값삽입
   피연산자이면 자식노드를 생성하지 않고 pop하여 값만 삽입
   */
/*ABOUT CREATE*/	
//============================================================================

void insertData(BinaryTreeNode* node, char value){
	node->data = value;
	//printf("insert data : %c\n", node->data);
	node->right = NULL;
	node->left = NULL;
}

BinaryTreeNode* createRight(BinaryTreeNode* parent){
	BinaryTreeNode* RightChild = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	parent->right = RightChild;
	RightChild->right = NULL;
	RightChild->left = NULL;
	return RightChild;
}

BinaryTreeNode* createLeft(BinaryTreeNode* parent){
        BinaryTreeNode* LeftChild = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
        parent->left = LeftChild;
	LeftChild->right = NULL;
	LeftChild->left = NULL;
	return LeftChild;
 }


void createExpTree(Stack* s, BinaryTreeNode* root){
	//postfix의마지막값은 root
	//ex) a*b+c => ab*c+ 여기서 +가 루트가 됨
	int ch;
	if(s->top > -1){
		ch = peek(s);
		if((ch != '*') && (ch != '/') && (ch != '-') && (ch != '+')){
			insertData(root, pop(s));	
		}
		//연산자 일때
		//연산자노드는 무조건 두개의 자식 노드를 가지게됨
		else{
			//연산자 삽입후 
			insertData(root, pop(s));
			createExpTree(s, createRight(root));
			createExpTree(s, createLeft(root));
		}
	}
}


//==================================================================================

/*ABOUT TRAVERSE*/
//====================================================================================

void inorder(BinaryTreeNode* ROOT){
	if(ROOT != NULL){
		inorder(ROOT->left);
		printf("%c",ROOT->data);
		inorder(ROOT->right);
	}
}

void preorder(BinaryTreeNode* ROOT){
	if(ROOT != NULL){
		printf("%c", ROOT->data);
		preorder(ROOT->left);
		preorder(ROOT->right);
	}
}		

void postorder(BinaryTreeNode* ROOT){
	if(ROOT != NULL){
		postorder(ROOT->left);
		postorder(ROOT->right);
		printf("%c", ROOT->data);
	}
}

void levelorder(BinaryTreeNode* ROOT){
	QueueType q;
	init_queue(&q);
	if(ROOT == NULL)
		return ;
		
	enqueue(&q, ROOT);
	while(!isempty(&q)){
		ROOT = dequeue(&q);
		printf("%c", ROOT->data);
		if(ROOT->left)
			enqueue(&q, ROOT->left);
		if(ROOT->right)
			enqueue(&q, ROOT->right);
	}
}
	


//====================================================================================

//////////////////////////////////////////////////////////////////////////////////////////
//main.c

#include "h.h"


/*
   post스택을 사용하여 후위표기식으로 바꾸어 postfix배열에 저장한다.
   그리고 하나씩 pop하여 트리에 값을 넣는다.
   */

void main(int argc, char* argv[]){
	int i;
	int size;
	char* infix;
	char* postfix;
	Stack post;
	BinaryTreeNode* ROOT = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));

	for(i = 1; i<argc; i++){
		infix = argv[i];

		size = size_nums(infix);
		create(&post, size);
		//스택 생성
		postfix = (char*)malloc(size);

		infix_to_postfix(&post, infix, postfix);
		printf("infix to postfix\n");
		for(int j =0; j<size;j++){
			printf("%c",postfix[j]);
		}
		printf("\n");
		Stack s;
		(&s)->stack_data = postfix;
		(&s)->top = size -1;
		createExpTree(&s, ROOT);

		printf("in : ");
		inorder(ROOT);
		printf("\n");
		printf("pre : ");
		preorder(ROOT);
		printf("\n");
		printf("post : ");
		postorder(ROOT);
		printf("\n");
		printf("level : ");
		levelorder(ROOT);
		printf("\n");
		free(postfix);
	}
}
//Queue.c


#include "h.h"

// 오류 함수
void error(char* message)
{
	fprintf(stderr, "%s\n", message);
	exit(1);
}
//공백 상태 검출 함수
void init_queue(QueueType* q)
{
	q->front = q->rear = 0;
}

//공백 상태 검출 함수
int isempty(QueueType* q)
{
	return (q->front == q->rear);
}

//포화 상태 검출 함수
int isfull(QueueType * q)
{
	return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}


//삽입 함수
void enqueue(QueueType * q, element item)
{
	if (isfull(q))
		error("큐가 포화상태입니다");
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
	q->data[q->rear] = item;
}

//삭제 함수
element dequeue(QueueType * q)
{
	if (isempty(q))
		error("큐가 공백상태입니다");
	q->front = (q->front + 1) % MAX_QUEUE_SIZE;
	return q->data[q->front];
}

//삭제함수
element queue_peek(QueueType * q)
{
	if (isempty(q))
		error("큐가 공백상태입니다");
	return q->data[(q->front + 1) % MAX_QUEUE_SIZE];
}
//stack.c


#include "h.h"
//////////////////////////////////////////////////////////////////////////////////////////
/* STACK */
/*
typedef struct {
	char* stack_data;
	int top;
}Stack;
*/

//스택 생성
void create(Stack* s, int exp_size) {
	s->stack_data = (char*)malloc(exp_size);
	s->top = -1;
}

//스택이 비었는지 확인
int is_empty(Stack* s) {
	return (s->top == -1);
}

int is_full(Stack* s, int exp_size) {
	return (s->top == (exp_size - 1));
}

void push(Stack* s, char data, int exp_size) {
	int top = s->top;
	if (is_full(s, exp_size)) {
		printf("stack is full!\n");
		exit(1);
	}
	else {
		s->stack_data[++(s->top)] = data;
	}
}

char pop(Stack* s) {
	int top;
	if (is_empty(s)) {
		printf("stack is empty!\n");
		exit(1);
	}
	else {
		top = s->top;
		(s->top)--;
		return s->stack_data[top];
	}
}

char peek(Stack* s) {
	if (is_empty(s)) {
		printf("stack is empty!\n");
		exit(1);
	}
	else {
		return s->stack_data[s->top];
	}
}

void stack_free(Stack* s) {
	free(s->stack_data);
}

int size_nums(char* exp) {
	int count = 0;
	int size = strlen(exp);
	for (int i = 0; i < size; i++) {
		char data = exp[i];

		switch (data) {
		case '(': case ')':
			count++;
			break;
		}
	}

	return size = size - count;

}

int order(char op) {
	switch (op) {
	case '(': case')':
		return 0;
	case '+': case '-':
		return 1;
	case '*': case '/':
		return 2;
	default:
		return -1;
	}
}

void infix_to_postfix(Stack* s, char* infix, char* postfix) {
	char ch, op, result;
	int expSize;
	expSize = strlen(infix);
	int index = 0;
	for (int i = 0; i < expSize; i++) {
		ch = infix[i];
		switch (ch) {
		case '+': case '-': case '*': case '/': {
			//스택이 비어있지 않고 스택의 연산자보다 낮은 우선순위일때 pop
			if (!is_empty(s) && order(ch) <= order(peek(s))) {
				//printf("%c", pop(s));
				postfix[index++] = pop(s);
			}
			push(s, ch, expSize);//push ch 스택에
			break;
		}
		//왼쪽 괄호를 만나면 스택에 저장
		case '(': {
			push(s, ch, expSize);
			break;
		}
		//왼쪽 괄호가 나올때까지 출력=>to postfix array
		case ')': {
			 result = pop(s);
			while (result != '(') {
				//printf("%c", result);
				postfix[index++] = result;
				result = pop(s);
			}
			break;
		}
		default: {
			postfix[index++] = ch;
			break; }

		}
	}
	while (!is_empty(s)) {
		postfix[index++] = pop(s);
		//printf("%c", pop(s));
	}
}
//STACK
///////////////////////////////////////////////////////////////////////////////////////////////////

//h.h


//이창민
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/*tree*/
typedef struct BinaryTreeNode{
        char data;
        struct BinaryTreeNode* right;
        struct BinaryTreeNode* left;
}BinaryTreeNode;

/*stack*/
typedef struct {
        char* stack_data;
        int top;
}Stack;

/*queue*/
#define MAX_QUEUE_SIZE 20
typedef BinaryTreeNode * element;
typedef struct { //큐타입
	element data[MAX_QUEUE_SIZE];
	int front, rear;
} QueueType;

/*TREE*/
///////////////////////////////////////////////////////////
void createExpTree(Stack* s, BinaryTreeNode* root);
void insertData(BinaryTreeNode* node, char data);

BinaryTreeNode* createRight(BinaryTreeNode* parent);
BinaryTreeNode* createLeft(BinaryTreeNode* parent);

void inorder(BinaryTreeNode* ROOT);
void preorder(BinaryTreeNode* ROOT);
void postorder(BinaryTreeNode* ROOT);
void levelorder(BinaryTreeNode* ROOT);
////////////////////////////////////////////////////////////

/*STACK*/
//////////////////////////////////////////////////////////////
void create(Stack* s, int exp_size);
int is_empty(Stack* s);
int is_full(Stack* s, int exp_size);
void push(Stack* s, char data, int exp_size);
char pop(Stack* s);
char peek(Stack* s);
void stack_free(Stack* s);
int size_nums(char* exp);
int order(char op);
void infix_to_postfix(Stack* s, char* infix, char* postfix);
////////////////////////////////////////////////////////////////


/*Queue*/
////////////////////////////////////////////////////////////
void error(char* message);
void init_queue(QueueType* q);
int isempty(QueueType* q);
int isfull(QueueType* q);
void queue_print(QueueType* q);
void enqueue(QueueType* q, element item);
element dequeue(QueueType* q);
element queue_peek(QueueType* q);
////////////////////////////////////////////////////////////////

728x90
반응형

'공부 > 자료구조' 카테고리의 다른 글

Stack, Queue 응용  (0) 2021.05.09
탐색트리, 정렬 알고리즘과 계산시간 비교  (0) 2021.05.09
행렬곱  (0) 2021.05.09
다항식 연산  (0) 2021.05.09
Huffman 트리  (0) 2021.05.09
블로그 이미지

아상관없어

,
반응형

<heap.c>

<heap.h>

 

<main.c>

 

<merge.c>

 

<merge.h>

 

<quick.c>

 

<quick.h>

 

 

<결과>

728x90
반응형

'공부 > 자료구조' 카테고리의 다른 글

Stack, Queue 응용  (0) 2021.05.09
연산자 우선순위를 반영하는 산술식의 이진트리 설계와 구현  (0) 2021.05.09
행렬곱  (0) 2021.05.09
다항식 연산  (0) 2021.05.09
Huffman 트리  (0) 2021.05.09
블로그 이미지

아상관없어

,