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
블로그 이미지

아상관없어

,